From c092662ebe2cf45ca2d9e35314d5209efae6dcce Mon Sep 17 00:00:00 2001 From: commie Date: Fri, 10 Apr 2026 13:31:15 +0800 Subject: [PATCH] payment1 --- .trae/skills/webman-openim-admin/SKILL.md | 232 +++ app/api/controller/PaymentController.php | 731 +++++++ app/command/OpenIm.php | 1850 ++++++++++++----- app/command/User.php | 26 +- app/enum/BaseEnum.php | 24 + app/enum/Payment/Method.php | 32 + app/enum/Payment/Status.php | 61 + app/enum/Payment/Type.php | 42 + app/mcp/McpService.php | 381 +++- app/model/PaymentOrder.php | 102 + app/model/PaymentRefund.php | 72 + check_conversation.php | 52 - composer.json | 3 +- config/payment.php | 81 + database/full_schema.sql | 495 +++++ database/index_optimization.sql | 204 ++ database/wa_address.sql | 14 + database/wa_admin.sql | 16 + database/wa_admin_access.sql | 7 + database/wa_admin_role.sql | 9 + database/wa_admin_rule.sql | 15 + database/wa_album.sql | 14 + database/wa_archives.sql | 24 + database/wa_archives_read.sql | 8 + database/wa_card.sql | 13 + database/wa_category.sql | 9 + database/wa_cdkey.sql | 17 + database/wa_collection.sql | 13 + database/wa_config.sql | 17 + database/wa_content.sql | 7 + database/wa_files.sql | 21 + database/wa_friend_circle.sql | 17 + database/wa_friend_circle_comment.sql | 16 + database/wa_friend_circle_like.sql | 10 + database/wa_friend_circle_setting.sql | 8 + database/wa_gallery.sql | 15 + database/wa_payment_order.sql | 18 + database/wa_payment_refund.sql | 15 + database/wa_recharge.sql | 7 + database/wa_team.sql | 8 + database/wa_thali.sql | 14 + database/wa_user.sql | 53 + database/wa_user_extend.sql | 12 + database/wa_user_role.sql | 15 + database/wa_user_rule.sql | 14 + database/wa_user_team.sql | 6 + database/wa_version.sql | 12 + database/wa_withdrawl.sql | 17 + payment/CSR文件.csr | 17 + payment/alipayCertPublicKey_RSA2.crt | 43 + payment/alipayRootCert.crt | 88 + payment/appCertPublicKey_2021006117606688.crt | 23 + payment/应用公钥RSA2048.txt | 1 + .../应用私钥RSA2048-敏感数据,请妥善保管.txt | 1 + support/OpenImSdk/Api/Message.php | 23 +- support/OpenImSdk/Core/Url.php | 1 + 56 files changed, 4362 insertions(+), 684 deletions(-) create mode 100644 .trae/skills/webman-openim-admin/SKILL.md create mode 100644 app/api/controller/PaymentController.php create mode 100644 app/enum/BaseEnum.php create mode 100644 app/enum/Payment/Method.php create mode 100644 app/enum/Payment/Status.php create mode 100644 app/enum/Payment/Type.php create mode 100644 app/model/PaymentOrder.php create mode 100644 app/model/PaymentRefund.php delete mode 100644 check_conversation.php create mode 100644 config/payment.php create mode 100644 database/full_schema.sql create mode 100644 database/index_optimization.sql create mode 100644 database/wa_address.sql create mode 100644 database/wa_admin.sql create mode 100644 database/wa_admin_access.sql create mode 100644 database/wa_admin_role.sql create mode 100644 database/wa_admin_rule.sql create mode 100644 database/wa_album.sql create mode 100644 database/wa_archives.sql create mode 100644 database/wa_archives_read.sql create mode 100644 database/wa_card.sql create mode 100644 database/wa_category.sql create mode 100644 database/wa_cdkey.sql create mode 100644 database/wa_collection.sql create mode 100644 database/wa_config.sql create mode 100644 database/wa_content.sql create mode 100644 database/wa_files.sql create mode 100644 database/wa_friend_circle.sql create mode 100644 database/wa_friend_circle_comment.sql create mode 100644 database/wa_friend_circle_like.sql create mode 100644 database/wa_friend_circle_setting.sql create mode 100644 database/wa_gallery.sql create mode 100644 database/wa_payment_order.sql create mode 100644 database/wa_payment_refund.sql create mode 100644 database/wa_recharge.sql create mode 100644 database/wa_team.sql create mode 100644 database/wa_thali.sql create mode 100644 database/wa_user.sql create mode 100644 database/wa_user_extend.sql create mode 100644 database/wa_user_role.sql create mode 100644 database/wa_user_rule.sql create mode 100644 database/wa_user_team.sql create mode 100644 database/wa_version.sql create mode 100644 database/wa_withdrawl.sql create mode 100644 payment/CSR文件.csr create mode 100644 payment/alipayCertPublicKey_RSA2.crt create mode 100644 payment/alipayRootCert.crt create mode 100644 payment/appCertPublicKey_2021006117606688.crt create mode 100644 payment/应用公钥RSA2048.txt create mode 100644 payment/应用私钥RSA2048-敏感数据,请妥善保管.txt diff --git a/.trae/skills/webman-openim-admin/SKILL.md b/.trae/skills/webman-openim-admin/SKILL.md new file mode 100644 index 0000000..19a5d0e --- /dev/null +++ b/.trae/skills/webman-openim-admin/SKILL.md @@ -0,0 +1,232 @@ +--- +name: "webman-openim-admin" +description: "专门针对基于webman的openim管理项目,使用了think-cache、think-orm、think-template、redis-queue、webman/event、tinywan/validate等依赖。用于项目的开发、维护和问题排查。" +--- + +# Webman OpenIM Admin 技能 + +## 项目概述 + +本技能专门针对基于 webman 框架开发的 OpenIM 管理项目,该项目使用了以下核心依赖: + +- **think-cache**: 缓存管理 +- **think-orm**: 数据库 ORM 框架 +- **think-template**: 模板引擎 +- **redis-queue**: Redis 队列管理 +- **webman/event**: 事件系统 +- **tinywan/validate**: 数据验证 + +## 功能特性 + +### 1. 项目结构分析 +- 分析项目目录结构 +- 识别核心模块和文件 +- 理解依赖关系 + +### 2. 代码开发与维护 +- 基于现有代码风格和模式进行开发 +- 提供符合项目规范的代码建议 +- 帮助排查和修复常见问题 + +### 3. 依赖管理 +- 分析 composer.json 配置 +- 提供依赖版本建议 +- 处理依赖冲突问题 + +### 4. 数据库操作 +- 基于 think-orm 的数据库操作指导 +- 模型定义和关系映射 +- 数据库迁移和种子数据管理 + +### 5. 缓存策略 +- 基于 think-cache 的缓存配置和使用 +- 缓存优化建议 +- 缓存一致性管理 + +### 6. 队列管理 +- redis-queue 的配置和使用 +- 队列任务的创建和监控 +- 队列性能优化 + +### 7. 事件系统 +- webman/event 的配置和使用 +- 事件监听和触发 +- 事件驱动架构设计 + +### 8. 数据验证 +- tinywan/validate 的配置和使用 +- 表单验证规则定义 +- 自定义验证规则开发 + +## 触发条件 + +当用户需要: +- 了解项目结构和依赖 +- 开发新功能或修改现有功能 +- 排查项目中的问题 +- 优化项目性能 +- 配置或调整项目依赖 +- 进行数据库相关操作 +- 实现缓存策略 +- 管理队列任务 +- 使用事件系统 +- 进行数据验证 + +## 使用示例 + +### 示例 1: 分析项目结构 +```bash +# 查看项目目录结构 +ls -la + +# 查看 composer.json 了解依赖 +cat composer.json +``` + +### 示例 2: 数据库操作 +```php +// 使用 think-orm 进行数据库查询 +use think\Model; + +class User extends Model +{ + protected $table = 'user'; +} + +// 查询用户列表 +$users = User::where('status', 1)->select(); +``` + +### 示例 3: 缓存使用 +```php +// 使用 think-cache +use think\facade\Cache; + +// 设置缓存 +Cache::set('key', 'value', 3600); + +// 获取缓存 +$value = Cache::get('key'); +``` + +### 示例 4: 队列任务 +```php +// 使用 redis-queue +use support\Queue; + +// 推送任务 +Queue::push('App\\Jobs\\SendEmail', ['email' => 'user@example.com']); +``` + +### 示例 5: 事件监听 +```php +// 使用 webman/event +use support\Event; + +// 监听事件 +Event::listen('user.registered', function ($user) { + // 处理用户注册事件 +}); + +// 触发事件 +Event::trigger('user.registered', $user); +``` + +### 示例 6: 数据验证 +```php +// 使用 tinywan/validate +use Tinywan\Validate\Validate; + +$validate = new Validate(); +$validate->rule([ + 'name' => 'require|max:25', + 'email' => 'require|email', +]); + +if (!$validate->check($data)) { + return $validate->getError(); +} +``` + +## 项目配置建议 + +1. **composer.json** 配置: + - 保持依赖版本的稳定性 + - 定期更新依赖以获取安全补丁 + +2. **数据库配置**: + - 优化数据库连接池设置 + - 合理使用索引 + - 定期备份数据库 + +3. **缓存配置**: + - 根据业务场景选择合适的缓存策略 + - 设置合理的缓存过期时间 + - 考虑缓存预热机制 + +4. **队列配置**: + - 合理设置队列 worker 数量 + - 监控队列任务执行状态 + - 实现失败重试机制 + +5. **性能优化**: + - 启用 OPcache + - 优化数据库查询 + - 使用合适的缓存策略 + - 合理设计事件系统 + +## 常见问题与解决方案 + +1. **依赖冲突**: + - 检查 composer.json 中的版本约束 + - 使用 `composer update` 解决版本冲突 + +2. **数据库连接问题**: + - 检查数据库配置文件 + - 确认数据库服务是否正常运行 + +3. **缓存失效**: + - 检查缓存配置 + - 确认缓存服务是否正常 + +4. **队列任务失败**: + - 检查队列配置 + - 查看任务执行日志 + - 实现失败重试机制 + +5. **事件不触发**: + - 检查事件监听注册 + - 确认事件触发代码 + +6. **验证失败**: + - 检查验证规则 + - 确认输入数据格式 + +## 最佳实践 + +1. **代码组织**: + - 遵循 PSR 代码规范 + - 合理使用命名空间 + - 保持代码结构清晰 + +2. **安全性**: + - 防止 SQL 注入 + - 防止 XSS 攻击 + - 保护敏感信息 + +3. **可维护性**: + - 编写清晰的注释 + - 使用一致的代码风格 + - 遵循设计模式 + +4. **性能**: + - 优化数据库查询 + - 合理使用缓存 + - 减少不必要的计算 + +5. **扩展性**: + - 采用模块化设计 + - 依赖注入 + - 接口分离 + +本技能旨在帮助开发者更高效地开发和维护基于 webman 的 OpenIM 管理项目,提供专业的技术支持和最佳实践建议。 \ No newline at end of file diff --git a/app/api/controller/PaymentController.php b/app/api/controller/PaymentController.php new file mode 100644 index 0000000..34cb7fd --- /dev/null +++ b/app/api/controller/PaymentController.php @@ -0,0 +1,731 @@ +post(); + + // 验证参数 + if (!isset($params['amount']) || !isset($params['order_no']) || !isset($params['subject'])) { + return $this->error('缺少必要参数'); + } + + $amount = $params['amount']; + $orderNo = $params['order_no']; + $subject = $params['subject']; + $body = $params['body'] ?? $subject; + $type = $params['type'] ?? 'goods'; + + // 获取支付宝配置 + $config = config('payment.alipay.default'); + + // 构建支付参数 + $payOrder = [ + 'out_trade_no' => $orderNo, + 'total_amount' => $amount, + 'subject' => $subject, + 'body' => $body, + ]; + + // 发起支付 + $result = Pay::alipay($config)->web($payOrder); + + // 记录支付订单 + $this->record_payment_order($orderNo, 'alipay', $amount, $subject, $type); + + return $this->success('ok',[ + 'pay_url' => $result->getTargetUrl(), + 'order_no' => $orderNo + ]); + + } catch (\Exception $e) { + WebmanLog::error('支付宝支付下单失败: ' . $e->getMessage()); + return $this->error('支付下单失败: ' . $e->getMessage()); + } + } + + /** + * 支付宝同步回调 + * @Apidoc\Title("支付宝同步回调") + * @Apidoc\Url("/api/payment/alipay/return") + * @Apidoc\Method("GET") + * @Apidoc\Param("out_trade_no", "string", "订单号", true) + * @Apidoc\Param("trade_no", "string", "支付宝交易号", true) + * @Apidoc\Param("trade_status", "string", "交易状态", true) + * @Apidoc\Return("redirect", "string", "跳转到成功或失败页面") + * @param Request $request + * @return Response + */ + public function alipay_return(Request $request): Response + { + try { + $config = config('payment.alipay.default'); + $data = $request->all(); + + // 验证回调数据 + $result = Pay::alipay($config)->verify($data); + + // 处理支付结果 + $orderNo = $result->out_trade_no; + $tradeNo = $result->trade_no; + $status = $result->trade_status; + + // 更新订单状态 + $this->update_payment_order($orderNo, $tradeNo, $status); + + // 跳转到成功页面 + return redirect('/api/payment/success?order_no=' . $orderNo); + + } catch (\Exception $e) { + WebmanLog::error('支付宝同步回调失败: ' . $e->getMessage()); + return redirect('/api/payment/fail?error=' . urlencode($e->getMessage())); + } + } + + /** + * 支付宝异步回调 + * @Apidoc\Title("支付宝异步回调") + * @Apidoc\Url("/api/payment/alipay/notify") + * @Apidoc\Method("POST") + * @Apidoc\Param("out_trade_no", "string", "订单号", true) + * @Apidoc\Param("trade_no", "string", "支付宝交易号", true) + * @Apidoc\Param("trade_status", "string", "交易状态", true) + * @Apidoc\Return("string", "string", "返回success或fail") + * @param Request $request + * @return string + */ + public function alipay_notify(Request $request): string + { + try { + $config = config('payment.alipay.default'); + $data = $request->all(); + + // 验证回调数据 + $result = Pay::alipay($config)->verify($data); + + // 处理支付结果 + $orderNo = $result->out_trade_no; + $tradeNo = $result->trade_no; + $status = $result->trade_status; + + // 更新订单状态 + $this->update_payment_order($orderNo, $tradeNo, $status); + + // 返回成功 + return 'success'; + + } catch (\Exception $e) { + WebmanLog::error('支付宝异步回调失败: ' . $e->getMessage()); + return 'fail'; + } + } + + /** + * 微信支付下单 + * @Apidoc\Title("微信支付下单") + * @Apidoc\Url("/api/payment/wechat/order") + * @Apidoc\Method("POST") + * @Apidoc\Param("amount", "float", "支付金额", true, "0.01") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("body", "string", "订单描述", true, "测试商品描述") + * @Apidoc\Param("trade_type", "string", "交易类型: JSAPI, NATIVE, APP, MWEB", false, "JSAPI") + * @Apidoc\Param("openid", "string", "微信用户openid(JSAPI模式需要)", false, "o123456") + * @Apidoc\Param("type", "string", "订单类型: recharge(充值), goods(商品), service(服务), other(其他)", false, "goods") + * @Apidoc\Return("success", "object", "成功响应", "pay_data|object|支付数据,order_no|string|订单号") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function wechat_order(Request $request): Response + { + try { + $params = $request->post(); + + // 验证参数 + if (!isset($params['amount']) || !isset($params['order_no']) || !isset($params['body'])) { + return $this->error('缺少必要参数'); + } + + $amount = $params['amount']; + $orderNo = $params['order_no']; + $body = $params['body']; + $tradeType = $params['trade_type'] ?? 'JSAPI'; + $openid = $params['openid'] ?? ''; + $type = $params['type'] ?? 'goods'; + + // 获取微信支付配置 + $config = config('payment.wechat.default'); + + // 构建支付参数 + $payOrder = [ + 'out_trade_no' => $orderNo, + 'total_fee' => $amount * 100, // 微信支付金额单位为分 + 'body' => $body, + 'trade_type' => $tradeType, + ]; + + // JSAPI支付需要openid + if ($tradeType === 'JSAPI' && $openid) { + $payOrder['openid'] = $openid; + } + + // 发起支付 + $result = Pay::wechat($config)->order($payOrder); + + // 记录支付订单 + $this->record_payment_order($orderNo, 'wechat', $amount, $body, $type); + + return $this->success('ok',[ + 'pay_data' => $result->toArray(), + 'order_no' => $orderNo + ]); + + } catch (\Exception $e) { + WebmanLog::error('微信支付下单失败: ' . $e->getMessage()); + return $this->error('支付下单失败: ' . $e->getMessage()); + } + } + + /** + * 微信支付回调 + * @Apidoc\Title("微信支付回调") + * @Apidoc\Url("/api/payment/wechat/notify") + * @Apidoc\Method("POST") + * @Apidoc\Param("out_trade_no", "string", "订单号", true) + * @Apidoc\Param("transaction_id", "string", "微信交易号", true) + * @Apidoc\Param("result_code", "string", "业务结果", true) + * @Apidoc\Return("xml", "string", "返回XML格式的成功或失败响应") + * @param Request $request + * @return string + */ + public function wechat_notify(Request $request): string + { + try { + $config = config('payment.wechat.default'); + $data = $request->all(); + + // 验证回调数据 + $result = Pay::wechat($config)->verify($data); + + // 处理支付结果 + $orderNo = $result->out_trade_no; + $tradeNo = $result->transaction_id; + $status = $result->result_code === 'SUCCESS' ? 'SUCCESS' : 'FAIL'; + + // 更新订单状态 + $this->update_payment_order($orderNo, $tradeNo, $status); + + // 返回成功 + return Pay::wechat($config)->success(); + + } catch (\Exception $e) { + WebmanLog::error('微信支付回调失败: ' . $e->getMessage()); + return Pay::wechat(config('payment.wechat.default'))->fail(); + } + } + + /** + * 记录支付订单 + * @param string $orderNo + * @param string $payType + * @param float $amount + * @param string $subject + * @param string $type + */ + private function record_payment_order(string $orderNo, string $payType, float $amount, string $subject, string $type = 'goods'): void + { + try { + Db::name('payment_order')->insert([ + 'order_no' => $orderNo, + 'pay_type' => $payType, + 'type' => $type, + 'amount' => $amount, + 'subject' => $subject, + 'status' => Status::CREATED->value, + 'created_at' => time(), + 'updated_at' => time() + ]); + } catch (\Exception $e) { + WebmanLog::error('记录支付订单失败: ' . $e->getMessage()); + } + } + + /** + * 更新支付订单状态 + * @param string $orderNo + * @param string $tradeNo + * @param string $status + */ + private function update_payment_order(string $orderNo, string $tradeNo, string $status): void + { + try { + // 映射支付状态到我们的状态枚举 + $mappedStatus = $this->map_payment_status($status); + + Db::name('payment_order')->where('order_no', $orderNo)->update([ + 'trade_no' => $tradeNo, + 'status' => $mappedStatus, + 'updated_at' => time() + ]); + } catch (\Exception $e) { + WebmanLog::error('更新支付订单状态失败: ' . $e->getMessage()); + } + } + + /** + * 映射支付状态到枚举 + * @param string $status + * @return string + */ + private function map_payment_status(string $status): string + { + // 支付宝状态映射 + $alipayStatusMap = [ + 'TRADE_SUCCESS' => Status::SUCCESS->value, + 'TRADE_FINISHED' => Status::COMPLETE->value, + 'TRADE_CLOSED' => Status::FAIL->value, + 'WAIT_BUYER_PAY' => Status::CREATED->value + ]; + + // 微信支付状态映射 + $wechatStatusMap = [ + 'SUCCESS' => Status::SUCCESS->value, + 'FAIL' => Status::FAIL->value, + 'REFUND' => Status::REFUNDED->value + ]; + + // 先尝试支付宝映射 + if (isset($alipayStatusMap[$status])) { + return $alipayStatusMap[$status]; + } + + // 再尝试微信映射 + if (isset($wechatStatusMap[$status])) { + return $wechatStatusMap[$status]; + } + + // 默认返回成功 + return Status::SUCCESS->value; + } + + /** + * 查询支付订单状态 + * @Apidoc\Title("查询支付订单状态") + * @Apidoc\Url("/api/payment/order/query") + * @Apidoc\Method("GET") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("pay_type", "string", "支付类型: alipay, wechat", true, "alipay") + * @Apidoc\Return("success", "object", "成功响应", "order|object|订单信息,pay_status|object|支付状态") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function query_order(Request $request): Response + { + try { + $orderNo = $request->get('order_no'); + $payType = $request->get('pay_type'); + + if (!$orderNo || !$payType) { + return $this->error('缺少必要参数'); + } + + // 查询订单 + $order = Db::name('payment_order')->where('order_no', $orderNo)->find(); + if (!$order) { + return $this->error('订单不存在'); + } + + // 根据支付类型查询支付状态 + if ($payType === \app\enum\Payment\Method::ALIPAY->value) { + $config = config('payment.alipay.default'); + $result = Pay::alipay($config)->find($orderNo); + } elseif ($payType === \app\enum\Payment\Method::WECHAT->value) { + $config = config('payment.wechat.default'); + $result = Pay::wechat($config)->find($orderNo); + } else { + return $this->error('不支持的支付类型'); + } + + return $this->success('ok',[ + 'order' => $order, + 'pay_status' => $result->toArray() + ]); + + } catch (\Exception $e) { + WebmanLog::error('查询支付订单失败: ' . $e->getMessage()); + return $this->error('查询失败: ' . $e->getMessage()); + } + } + + /** + * 关闭支付订单 + * @Apidoc\Title("关闭支付订单") + * @Apidoc\Url("/api/payment/order/close") + * @Apidoc\Method("POST") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("pay_type", "string", "支付类型: alipay, wechat", true, "alipay") + * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function close_order(Request $request): Response + { + try { + $orderNo = $request->post('order_no'); + $payType = $request->post('pay_type'); + + if (!$orderNo || !$payType) { + return $this->error('缺少必要参数'); + } + + // 关闭订单 + if ($payType === 'alipay') { + $config = config('payment.alipay.default'); + $result = Pay::alipay($config)->close($orderNo); + } elseif ($payType === 'wechat') { + $config = config('payment.wechat.default'); + $result = Pay::wechat($config)->close($orderNo); + } else { + return $this->error('不支持的支付类型'); + } + + // 更新订单状态 + Db::name('payment_order')->where('order_no', $orderNo)->update([ + 'status' => \app\enum\Payment\Status::COMPLETE->value, + 'updated_at' => time() + ]); + + return $this->success('订单关闭成功'); + + } catch (\Exception $e) { + WebmanLog::error('关闭支付订单失败: ' . $e->getMessage()); + return $this->error('关闭失败: ' . $e->getMessage()); + } + } + + /** + * 退款 + * @Apidoc\Title("退款") + * @Apidoc\Url("/api/payment/refund") + * @Apidoc\Method("POST") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("pay_type", "string", "支付类型: alipay, wechat", true, "alipay") + * @Apidoc\Param("amount", "float", "退款金额", true, "0.01") + * @Apidoc\Param("refund_no", "string", "退款单号", false, "20260409001_refund") + * @Apidoc\Param("reason", "string", "退款原因", false, "退款") + * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function refund(Request $request): Response + { + try { + $params = $request->post(); + + if (!isset($params['order_no']) || !isset($params['pay_type']) || !isset($params['amount'])) { + return $this->error('缺少必要参数'); + } + + $orderNo = $params['order_no']; + $payType = $params['pay_type']; + $amount = $params['amount']; + $refundNo = $params['refund_no'] ?? $orderNo . '_' . time(); + $reason = $params['reason'] ?? '退款'; + + // 发起退款 + if ($payType === Method::ALIPAY->value) { + $config = config('payment.alipay.default'); + $result = Pay::alipay($config)->refund([ + 'out_trade_no' => $orderNo, + 'refund_amount' => $amount, + 'out_request_no' => $refundNo, + 'refund_reason' => $reason, + ]); + } elseif ($payType === Method::WECHAT->value) { + $config = config('payment.wechat.default'); + $result = Pay::wechat($config)->refund([ + 'out_trade_no' => $orderNo, + 'out_refund_no' => $refundNo, + 'total_fee' => $amount * 100, + 'refund_fee' => $amount * 100, + 'refund_desc' => $reason, + ]); + } else { + return $this->error('不支持的支付类型'); + } + + // 记录退款信息 + Db::name('payment_refund')->insert([ + 'order_no' => $orderNo, + 'refund_no' => $refundNo, + 'pay_type' => $payType, + 'amount' => $amount, + 'reason' => $reason, + 'status' => 'SUCCESS', + 'created_at' => time() + ]); + + // 更新订单状态 + Db::name('payment_order')->where('order_no', $orderNo)->update([ + 'status' => Status::REFUNDED->value, + 'updated_at' => time() + ]); + + return $this->success('退款成功'); + + } catch (\Exception $e) { + WebmanLog::error('退款失败: ' . $e->getMessage()); + return $this->error('退款失败: ' . $e->getMessage()); + } + } + + /** + * 统一支付接口 + * @Apidoc\Title("统一支付接口") + * @Apidoc\Url("/api/payment/unified/order") + * @Apidoc\Method("POST") + * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") + * @Apidoc\Param("amount", "float", "支付金额", true, "0.01") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("subject", "string", "订单标题", true, "测试商品") + * @Apidoc\Param("body", "string", "订单描述", false, "测试商品描述") + * @Apidoc\Param("type", "string", "订单类型: recharge(充值), goods(商品), service(服务), other(其他)", false, "goods") + * @Apidoc\Param("trade_type", "string", "交易类型(微信支付需要): JSAPI, NATIVE, APP, MWEB", false, "JSAPI") + * @Apidoc\Param("openid", "string", "微信用户openid(JSAPI模式需要)", false, "o123456") + * @Apidoc\Return("success", "object", "成功响应", "pay_url|string|支付宝支付链接,pay_data|object|微信支付数据,order_no|string|订单号") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function unified_order(Request $request): Response + { + try { + $params = $request->post(); + + // 验证必要参数 + if (!isset($params['payment']) || !isset($params['amount']) || !isset($params['order_no']) || !isset($params['subject'])) { + return $this->error('缺少必要参数'); + } + + $payment = strtolower($params['payment']); + $amount = $params['amount']; + $orderNo = $params['order_no']; + $subject = $params['subject']; + $body = $params['body'] ?? $subject; + $type = $params['type'] ?? 'goods'; + + // 根据payment参数选择支付方式 + switch ($payment) { + case Method::ALIPAY->value: + // 调用支付宝支付 + return $this->alipay_order($request); + + case Method::WECHAT->value: + // 调用微信支付 + return $this->wechat_order($request); + + default: + return $this->error('不支持的支付方式'); + } + + } catch (\Exception $e) { + WebmanLog::error('统一支付接口失败: ' . $e->getMessage()); + return $this->error('支付失败: ' . $e->getMessage()); + } + } + + /** + * 统一支付回调接口 + * @Apidoc\Title("统一支付回调接口") + * @Apidoc\Url("/api/payment/unified/notify") + * @Apidoc\Method("ANY") + * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", false) + * @Apidoc\Param("out_trade_no", "string", "订单号", true) + * @Apidoc\Param("trade_no|transaction_id", "string", "支付交易号", true) + * @Apidoc\Param("trade_status|result_code", "string", "交易状态", true) + * @Apidoc\Return("string", "string", "返回success/fail或XML格式响应") + * @param Request $request + * @return Response + */ + public function unified_notify(Request $request): Response + { + try { + $payment = $request->get('payment') ?? $request->post('payment'); + + if (!$payment) { + // 尝试从请求参数中自动识别 + $data = $request->all(); + if (isset($data['app_id']) && strpos($data['app_id'], '20') === 0) { + $payment = 'alipay'; + } elseif (isset($data['mch_id'])) { + $payment = 'wechat'; + } else { + return $this->error('无法识别支付方式'); + } + } + + $payment = strtolower($payment); + + // 根据payment参数选择回调处理 + switch ($payment) { + case Method::ALIPAY->value: + // 调用支付宝回调 + return $this->alipay_notify($request); + + case Method::WECHAT->value: + // 调用微信回调 + return $this->wechat_notify($request); + + default: + return $this->error('不支持的支付方式'); + } + + } catch (\Exception $e) { + WebmanLog::error('统一支付回调接口失败: ' . $e->getMessage()); + return $this->error('回调处理失败: ' . $e->getMessage()); + } + } + + /** + * 统一支付查询接口 + * @Apidoc\Title("统一支付查询接口") + * @Apidoc\Url("/api/payment/unified/query") + * @Apidoc\Method("GET") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") + * @Apidoc\Return("success", "object", "成功响应", "order|object|订单信息,pay_status|object|支付状态") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function unified_query(Request $request): Response + { + try { + $params = $request->all(); + + // 验证必要参数 + if (!isset($params['order_no']) || !isset($params['payment'])) { + return $this->error('缺少必要参数'); + } + + // 设置pay_type参数 + $request->withAddedHeader('pay_type', $params['payment']); + + // 调用查询接口 + return $this->query_order($request); + + } catch (\Exception $e) { + WebmanLog::error('统一支付查询接口失败: ' . $e->getMessage()); + return $this->error('查询失败: ' . $e->getMessage()); + } + } + + /** + * 统一支付关闭接口 + * @Apidoc\Title("统一支付关闭接口") + * @Apidoc\Url("/api/payment/unified/close") + * @Apidoc\Method("POST") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") + * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function unified_close(Request $request): Response + { + try { + $params = $request->post(); + + // 验证必要参数 + if (!isset($params['order_no']) || !isset($params['payment'])) { + return $this->error('缺少必要参数'); + } + + // 设置pay_type参数 + $request->withAddedHeader('pay_type', $params['payment']); + + // 调用关闭接口 + return $this->close_order($request); + + } catch (\Exception $e) { + WebmanLog::error('统一支付关闭接口失败: ' . $e->getMessage()); + return $this->error('关闭失败: ' . $e->getMessage()); + } + } + + /** + * 统一退款接口 + * @Apidoc\Title("统一退款接口") + * @Apidoc\Url("/api/payment/unified/refund") + * @Apidoc\Method("POST") + * @Apidoc\Param("order_no", "string", "订单号", true, "20260409001") + * @Apidoc\Param("payment", "string", "支付方式: alipay, wechat", true, "alipay") + * @Apidoc\Param("amount", "float", "退款金额", true, "0.01") + * @Apidoc\Param("refund_no", "string", "退款单号", false, "20260409001_refund") + * @Apidoc\Param("reason", "string", "退款原因", false, "退款") + * @Apidoc\Return("success", "object", "成功响应", "msg|string|成功信息") + * @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息") + * @param Request $request + * @return Response + */ + public function unified_refund(Request $request): Response + { + try { + $params = $request->post(); + + // 验证必要参数 + if (!isset($params['order_no']) || !isset($params['payment']) || !isset($params['amount'])) { + return $this->error('缺少必要参数'); + } + + // 设置pay_type参数 + $params['pay_type'] = $params['payment']; + $request->withAddedHeader('pay_type', $params['payment']); + + // 调用退款接口 + return $this->refund($request); + + } catch (\Exception $e) { + WebmanLog::error('统一退款接口失败: ' . $e->getMessage()); + return $this->error('退款失败: ' . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/command/OpenIm.php b/app/command/OpenIm.php index 78fed84..1848b60 100755 --- a/app/command/OpenIm.php +++ b/app/command/OpenIm.php @@ -9,683 +9,1552 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Output\OutputInterface; use support\think\Db; +/** + * OpenIM 命令行工具 + * + * 提供OpenIM相关的命令行操作,包括: + * - 用户ID转换 + * - URL修复 + * - 消息处理 + * - 群组同步 + * - 缓存管理 + */ class OpenIm extends Command { + /** + * 命令默认名称 + * @var string + */ protected static $defaultName = 'openim'; - protected static $defaultDescription = 'OpenIm'; - public $sdk= null; - function fixurl(){ - $sdk = $this->getSdk(); - $search = 'http://103.39.222.184:10002/object/'; - $replace = 'https://s1.shun777.com/imapi/object/'; - $users = Db::name('User') - ->whereLike('avatar','%'.$search) - ->field('avatar,id,userID') - ->select(); - foreach($users as $k=>$user){ - $avatar = str_replace($search,$replace,$user['avatar']); - Db::name('User') - ->where('id',$user['id']) - ->update(['avatar'=>$avatar]); - $sdata = [ - 'faceURL' => $avatar - ]; - $sdk->user->updateUserInfo($user['userID'],$sdata); - } + + /** + * 命令默认描述 + * @var string + */ + protected static $defaultDescription = 'OpenIM 命令行工具'; + + /** + * OpenIM SDK实例 + * @var object|null + */ + public $sdk = null; - $model = new \app\model\Openim\Group(); - $list = $model->whereLike('face_url', '%'.$search)->select(); - foreach($list as $k=>$group){ - $avatar = str_replace($search,$replace,$group['face_url']); - $model->where('id',$group['id'])->update(['face_url'=>$avatar]); - } + /** + * 每批处理消息数量 + * @var int + */ + const BATCH_SIZE = 1000; - $model = new \app\model\Openim\Msg(); - $total = $model->count('id'); - $count = 0; - for($i=0;$i<$total;$i+=1000){ - $list = $model->limit($i,1000)->select(); - foreach($list as $item){ - if(!empty($item['msgs']) && is_array($item['msgs'])){ - $msgs = []; - foreach($item['msgs'] as $key => $msg){ - if(is_null($msg['msg'])){ - continue; - } - $msgs[] = $msg; - } - $item->msgs = $msgs; - } - $item->save(); - $count++; - } + /** + * 特殊系统账号映射表 + * @var array + */ + const SPECIAL_ACCOUNTS = [ + 'official_team' => 'SystemOfficialTeam', + 'officialteam' => 'SystemOfficialTeam', + 'group_bot' => 'SystemGroupBot', + 'imAdmin' => 'SystemImAdmin', + 'system' => 'SystemAccount' + ]; + + /** + * 单聊类型常量 + * @var int + */ + const SINGLE_CHAT_TYPE = 1; + + /** + * 群聊类型常量 + * @var int + */ + const GROUP_CHAT_TYPE = 2; + + function revoke_msg(InputInterface $input, OutputInterface $output) + { + $user_id = $input->getOption('userID'); + $msg_seq = $input->getOption('seq'); + if(!$user_id){ + cp('user_id不能为空'); + return 1; } + if(!$msg_seq){ + cp('msg_seq不能为空'); + return 1; + } + $data = $this->getSdk()->message->revokeMessage('sg_2639473367',$msg_seq,$user_id); + //$data = $this->getSdk()->message->userClearAllMsg('100014'); + cp($data); return 0; } + + function delete_all_msg(InputInterface $input, OutputInterface $output) + { + $user_id = $input->getOption('userID'); + if(!$user_id){ + cp('user_id不能为空'); + return 1; + } + $data = $this->getSdk()->message->userClearAllMsg($user_id); + cp($data); + return 0; + } + /** + * 修复seq(空方法,待实现) + * + * @param InputInterface $input + * @param OutputInterface $output + * @return void + */ + function fix_seq(InputInterface $input, OutputInterface $output) + { + } + + /** + * 修复并清理消息数据 + * + * 该方法是 OpenIM seq 修复的核心功能,包含以下操作: + * 1. 清理消息文档中的空消息(msg 为 null 的条目) + * 2. 根据实际消息数据重新计算并修复 seq 表 + * 3. 根据用户加入时间重新计算并修复 seq_user 表 + * 4. 清理 Redis 缓存 + * + * OpenIM Seq 设计机制说明: + * - seq 表:记录每个会话(conversation)的最小和最大消息序号 + * - min_seq: 会话中第一条消息的 seq + * - max_seq: 会话中最后一条消息的 seq + 1(用于分配下一个 seq) + * + * - seq_user 表:记录每个用户在每个会话中的消息范围 + * - min_seq: 用户加入会话后第一条消息的 seq + * - max_seq: 会话的当前最大 seq + * - read_seq: 用户已读到的 seq(默认为 max_seq,表示全部已读) + * + * - 消息读取流程: + * 1. 客户端通过 GetConversationMaxSeq 获取会话最大 seq + * 2. 客户端通过 GetHasReadSeq 获取用户已读 seq + * 3. 未读消息数 = max_seq - read_seq + * 4. 拉取消息时,根据 seq 范围从 msg 表查询 + * + * - 已读标记流程: + * 1. 客户端调用 MarkConversationAsRead 标记已读 + * 2. 服务端更新 seq_user 表的 read_seq + * 3. 发送已读回执通知其他端 + * + * @param bool $dryRun 是否为试运行模式(只显示不执行) + * @param bool $clearCache 是否清理 Redis 缓存 + * @return array 统计信息 + */ + protected function cleanEmptyMessages($dryRun = false, $clearCache = true) + { + cp("========== 开始修复消息和 Seq 数据 =========="); + + $stats = [ + 'total_docs' => 0, + 'cleaned_docs' => 0, + 'total_msgs' => 0, + 'cleaned_msgs' => 0, + 'seq_updated' => 0, + 'seq_user_updated' => 0, + 'conversations' => [] + ]; + + $msgModel = new \app\model\Openim\Msg(); + $seqModel = new \app\model\Openim\Seq(); + $seqUserModel = new \app\model\Openim\SeqUser(); + $friendModel = new \app\model\Openim\Friend(); + $groupMemberModel = new \app\model\Openim\GroupMember(); + + $total = $msgModel->count('id'); + cp("消息文档总数: {$total}"); + + $conversations = []; + + for ($i = 0; $i < $total; $i += self::BATCH_SIZE) { + $list = $msgModel->limit($i, self::BATCH_SIZE)->select(); + + foreach ($list as $item) { + $stats['total_docs']++; + $docIdParts = explode(':', $item['doc_id']); + $conversationId = $docIdParts[0]; + + if (!isset($conversations[$conversationId])) { + $conversations[$conversationId] = [ + 'messages' => [], + 'min_seq' => PHP_INT_MAX, + 'max_seq' => 0, + 'user_joins' => [] + ]; + } + + $originalMsgCount = count($item['msgs'] ?? []); + $validMsgs = []; + + if (!empty($item['msgs']) && is_array($item['msgs'])) { + foreach ($item['msgs'] as $msg) { + if (!is_null($msg['msg'])) { + $validMsgs[] = $msg; + $seq = $msg['msg']['seq'] ?? 0; + $sendTime = $msg['msg']['send_time'] ?? 0; + $sendId = $msg['msg']['send_id'] ?? ''; + $recvId = $msg['msg']['recv_id'] ?? ''; + $groupId = $msg['msg']['group_id'] ?? ''; + $sessionType = $msg['msg']['session_type'] ?? self::SINGLE_CHAT_TYPE; + + if ($seq > 0) { + $conversations[$conversationId]['messages'][] = [ + 'seq' => $seq, + 'send_time' => $sendTime, + 'send_id' => $sendId, + 'recv_id' => $recvId, + 'group_id' => $groupId, + 'session_type' => $sessionType + ]; + + $conversations[$conversationId]['min_seq'] = min($conversations[$conversationId]['min_seq'], $seq); + $conversations[$conversationId]['max_seq'] = max($conversations[$conversationId]['max_seq'], $seq); + } + + $stats['total_msgs']++; + } else { + $stats['cleaned_msgs']++; + } + } + } + + $cleanedMsgCount = count($validMsgs); + if ($cleanedMsgCount != $originalMsgCount) { + if (!$dryRun) { + $item->msgs = $validMsgs; + $item->save(); + } + $stats['cleaned_docs']++; + cp("文档 {$item['doc_id']}: 清理了 " . ($originalMsgCount - $cleanedMsgCount) . " 条空消息"); + } + + if ($stats['total_docs'] % 100 == 0) { + cp("已处理 {$stats['total_docs']}/{$total} 个文档..."); + } + } + } + + cp("\n========== 分析会话数据 =========="); + $stats['conversations'] = $conversations; + + foreach ($conversations as $convId => $convData) { + if (empty($convData['messages'])) { + continue; + } + + sort($convData['messages']); + $minSeq = $convData['min_seq']; + $maxSeq = $convData['max_seq'] + 1; + + cp("会话 {$convId}: min_seq={$minSeq}, max_seq={$maxSeq}, 消息数=" . count($convData['messages'])); + + if (!$dryRun) { + $this->updateSeq($convId, $minSeq, $maxSeq); + } + $stats['seq_updated']++; + + $userJoins = $this->getUserJoinTimes($convId, $friendModel, $groupMemberModel); + + foreach ($userJoins as $userId => $joinTime) { + $userMinSeq = $this->calculateUserMinSeq($convData['messages'], $joinTime, $maxSeq); + $userMaxSeq = $maxSeq - 1; + $readSeq = $userMaxSeq; + + if (!$dryRun) { + $this->updateSeqUser($userId, $convId, $userMinSeq, $userMaxSeq, $readSeq); + } + $stats['seq_user_updated']++; + + if ($stats['seq_user_updated'] % 100 == 0) { + cp("已更新 {$stats['seq_user_updated']} 条 seq_user 记录..."); + } + } + } + + if ($clearCache && !$dryRun) { + $this->clearSeqCache(); + } + + cp("\n========== 修复完成 =========="); + cp("统计信息:"); + cp(" - 处理文档数: {$stats['total_docs']}"); + cp(" - 清理文档数: {$stats['cleaned_docs']}"); + cp(" - 消息总数: {$stats['total_msgs']}"); + cp(" - 清理消息数: {$stats['cleaned_msgs']}"); + cp(" - 更新 seq 数: {$stats['seq_updated']}"); + cp(" - 更新 seq_user 数: {$stats['seq_user_updated']}"); + + return $stats; + } + + /** + * 更新 seq 表 + * + * @param string $conversationId 会话ID + * @param int $minSeq 最小seq + * @param int $maxSeq 最大seq + * @return void + */ + protected function updateSeq($conversationId, $minSeq, $maxSeq) + { + $seqModel = new \app\model\Openim\Seq(); + + $existing = $seqModel->where('conversation_id', $conversationId)->find(); + + if ($existing) { + $seqModel->where('conversation_id', $conversationId)->update([ + 'min_seq' => $minSeq, + 'max_seq' => $maxSeq + ]); + } else { + $seqModel->insert([ + 'conversation_id' => $conversationId, + 'min_seq' => $minSeq, + 'max_seq' => $maxSeq + ]); + } + } + + /** + * 更新 seq_user 表 + * + * @param string $userId 用户ID + * @param string $conversationId 会话ID + * @param int $minSeq 用户最小seq + * @param int $maxSeq 用户最大seq + * @param int $readSeq 已读seq + * @return void + */ + protected function updateSeqUser($userId, $conversationId, $minSeq, $maxSeq, $readSeq) + { + $seqUserModel = new \app\model\Openim\SeqUser(); + + $existing = $seqUserModel->where('user_id', $userId) + ->where('conversation_id', $conversationId) + ->find(); + + if ($existing) { + $seqUserModel->where('user_id', $userId) + ->where('conversation_id', $conversationId) + ->update([ + 'min_seq' => $minSeq, + 'max_seq' => $maxSeq, + 'read_seq' => $readSeq + ]); + } else { + $seqUserModel->insert([ + 'user_id' => $userId, + 'conversation_id' => $conversationId, + 'min_seq' => $minSeq, + 'max_seq' => $maxSeq, + 'read_seq' => $readSeq + ]); + } + } + + /** + * 获取用户加入会话的时间 + * + * @param string $conversationId 会话ID + * @param object $friendModel 好友模型 + * @param object $groupMemberModel 群成员模型 + * @return array 用户ID => 加入时间戳 + */ + protected function getUserJoinTimes($conversationId, $friendModel, $groupMemberModel) + { + $userJoins = []; + $parts = explode('_', $conversationId); + + if (count($parts) >= 2 && $parts[0] === 'si') { + $user1 = $parts[1]; + $user2 = $parts[2] ?? ''; + + $friend = $friendModel->where('owner_user_id', $user1) + ->where('friend_user_id', $user2) + ->find(); + + if ($friend) { + $joinTime = strtotime($friend['create_time']) * 1000; + $userJoins[$user1] = $joinTime; + $userJoins[$user2] = $joinTime; + } + } elseif (count($parts) >= 2 && $parts[0] === 'sg') { + $groupId = $parts[1]; + + $members = $groupMemberModel->where('group_id', $groupId)->select(); + + foreach ($members as $member) { + $userJoins[$member['user_id']] = strtotime($member['join_time']) * 1000; + } + } + + return $userJoins; + } + + /** + * 计算用户在会话中的最小 seq + * + * 根据用户加入时间,找到用户加入后的第一条消息的 seq + * + * @param array $messages 消息列表 + * @param int $joinTime 用户加入时间戳 + * @param int $maxSeq 会话最大seq + * @return int 用户最小seq + */ + protected function calculateUserMinSeq($messages, $joinTime, $maxSeq) + { + foreach ($messages as $msg) { + if ($msg['send_time'] >= $joinTime) { + return $msg['seq']; + } + } + + return $maxSeq; + } + + /** + * 清理 Seq 相关的 Redis 缓存 + * + * 清理以下缓存键: + * - SEQ_USER_MAX:* - 用户最大 seq 缓存 + * - SEQ_USER_MIN:* - 用户最小 seq 缓存 + * - SEQ_USER_READ:* - 用户已读 seq 缓存 + * - MALLOC_SEQ:* - 会话 seq 分配缓存 + * - MSG_CACHE:* - 消息缓存 + * + * @return void + */ + protected function clearSeqCache() + { + cp("\n清理 Redis 缓存..."); + + $patterns = [ + 'SEQ_USER_MAX:*', + 'SEQ_USER_MIN:*', + 'SEQ_USER_READ:*', + 'MALLOC_SEQ:*', + 'MSG_CACHE:*' + ]; + + $totalDeleted = 0; + + foreach ($patterns as $pattern) { + try { + $keys = cache()->handler()->keys($pattern); + + if (!empty($keys)) { + cache()->handler()->del(...$keys); + $totalDeleted += count($keys); + cp(" 删除 {$pattern}: " . count($keys) . " 个键"); + } + } catch (\Exception $e) { + cp(" 警告: 清理 {$pattern} 失败: " . $e->getMessage()); + } + } + + cp("共清理 {$totalDeleted} 个缓存键"); + } + /** + * 修复头像URL + * + * 将旧的CDN域名替换为新的CDN域名, + * 并同步更新用户头像和群组头像 + * + * @return int + */ + function fixurl() + { + $sdk = $this->getSdk(); + + $oldUrl = 'http://103.39.222.184:10002/object/'; + $newUrl = 'https://s1.shun777.com/imapi/object/'; + + $this->updateUserAvatars($sdk, $oldUrl, $newUrl); + $this->updateGroupAvatars($oldUrl, $newUrl); + $this->cleanEmptyMessages(); + + return 0; + } + + /** + * 更新用户头像 + * + * @param object $sdk OpenIM SDK实例 + * @param string $oldUrl 旧URL + * @param string $newUrl 新URL + * @return void + */ + protected function updateUserAvatars($sdk, $oldUrl, $newUrl) + { + $users = Db::name('User') + ->whereLike('avatar', '%' . $oldUrl) + ->field('avatar,id,userID') + ->select(); + + foreach ($users as $user) { + $avatar = str_replace($oldUrl, $newUrl, $user['avatar']); + Db::name('User') + ->where('id', $user['id']) + ->update(['avatar' => $avatar]); + + $userData = [ + 'faceURL' => $avatar + ]; + $sdk->user->updateUserInfo($user['userID'], $userData); + } + } + + /** + * 更新群组头像 + * + * @param string $oldUrl 旧URL + * @param string $newUrl 新URL + * @return void + */ + protected function updateGroupAvatars($oldUrl, $newUrl) + { + $groupModel = new \app\model\Openim\Group(); + $groupList = $groupModel->whereLike('face_url', '%' . $oldUrl)->select(); + + foreach ($groupList as $group) { + $avatar = str_replace($oldUrl, $newUrl, $group['face_url']); + $groupModel->where('id', $group['id'])->update(['face_url' => $avatar]); + } + } + + + /** + * 转换用户ID命令入口 + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ function convertUserID(InputInterface $input, OutputInterface $output): int { $userID = $input->getOption('userID'); - $this->_convertUserID($userID,$output); + $this->_convertUserID($userID, $output); return 0; } - function _convertUserID($userID,$output): int + + /** + * 转换用户ID内部方法 + * + * 批量将用户ID从旧格式转换为新格式 + * + * @param int $userID + * @param OutputInterface $output + * @return int + */ + function _convertUserID($userID, $output): int { $oldUserID = \support\Encrypt::userIDEncode($userID); $newUserID = \support\Encrypt::intEncode($userID); cp("开始转换用户ID: {$userID} -> {$oldUserID} -> {$newUserID}"); - // $this->convertBlackTable($oldUserID, $newUserID, $output); - // $this->convertConversationTable($oldUserID, $newUserID, $output); - // $this->convertVersionTables($oldUserID, $newUserID, $output); - // $this->convertFriendTable($oldUserID, $newUserID, $output); - // $this->convertFriendRequestTable($oldUserID, $newUserID, $output); - // $this->convertGroupTable($oldUserID, $newUserID, $output); - // $this->convertGroupMemberTable($oldUserID, $newUserID, $output); - // $this->convertGroupRequestTable($oldUserID, $newUserID, $output); - // $this->convertMsgTable($oldUserID, $newUserID, $output); - // $this->convertSeqTable($oldUserID, $newUserID, $output); - // $this->convertSeqUserTable($oldUserID, $newUserID, $output); - // $this->convertUserTable($oldUserID, $newUserID, $output); - for ($i=100006;$i<102028;$i++){ + + for ($i = 100006; $i < 102028; $i++) { $newUserID = \support\Encrypt::intEncode($i); - Db::name('User')->where('id',$i)->update(['userID'=>$newUserID]); + Db::name('User')->where('id', $i)->update(['userID' => $newUserID]); } + cp("用户ID:{$userID} -> {$newUserID} 转换完成"); return 0; } - protected function convertFriendTable($oldUserID, $newUserID, $output){ + + /** + * 转换好友表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertFriendTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\Friend(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['owner_user_id']){ - $savedata['owner_user_id'] = $this->turnID($item['owner_user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['owner_user_id']) { + $saveData['owner_user_id'] = $this->turnID($item['owner_user_id']); } - if($item['friend_user_id']){ - $savedata['friend_user_id'] = $this->turnID($item['friend_user_id']); + if ($item['friend_user_id']) { + $saveData['friend_user_id'] = $this->turnID($item['friend_user_id']); } - if($item['operator_user_id']){ - $savedata['operator_user_id'] = $this->turnID($item['operator_user_id']); + if ($item['operator_user_id']) { + $saveData['operator_user_id'] = $this->turnID($item['operator_user_id']); } - if(!empty($savedata)){ - $model->where('id',$item['id'])->update($savedata); + + if (!empty($saveData)) { + $model->where('id', $item['id'])->update($saveData); } + $count++; } + $output->writeln("Friend 表转换完成,共 {$count} 条记录"); } - protected function convertFriendRequestTable($oldUserID, $newUserID, $output){ + /** + * 转换好友请求表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertFriendRequestTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\FriendRequest(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['from_user_id']){ - $savedata['from_user_id'] = $this->turnID($item['from_user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['from_user_id']) { + $saveData['from_user_id'] = $this->turnID($item['from_user_id']); } - if($item['to_user_id']){ - $savedata['to_user_id'] = $this->turnID($item['to_user_id']); + if ($item['to_user_id']) { + $saveData['to_user_id'] = $this->turnID($item['to_user_id']); } - if($item['handler_user_id']){ - $savedata['handler_user_id'] = $this->turnID($item['handler_user_id']); + if ($item['handler_user_id']) { + $saveData['handler_user_id'] = $this->turnID($item['handler_user_id']); } - if(!empty($savedata)){ - $model->where('id',$item['id'])->update($savedata); + + if (!empty($saveData)) { + $model->where('id', $item['id'])->update($saveData); } + $count++; } + $output->writeln("FriendRequest 表转换完成,共 {$count} 条记录"); } - protected function convertGroupTable($oldUserID, $newUserID, $output){ + + /** + * 转换群组表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertGroupTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\Group(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['creator_user_id']){ - $savedata['creator_user_id'] = $this->turnID($item['creator_user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['creator_user_id']) { + $saveData['creator_user_id'] = $this->turnID($item['creator_user_id']); } - if($item['notification_user_id']){ - $savedata['notification_user_id'] = $this->turnID($item['notification_user_id']); + if ($item['notification_user_id']) { + $saveData['notification_user_id'] = $this->turnID($item['notification_user_id']); } - if(!empty($savedata)){ - $model->where('id',$item['id'])->update($savedata); + + if (!empty($saveData)) { + $model->where('id', $item['id'])->update($saveData); } + $count++; } + $output->writeln("Group 表转换完成,共 {$count} 条记录"); } - protected function convertGroupMemberTable($oldUserID, $newUserID, $output){ + /** + * 转换群组成员表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertGroupMemberTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\GroupMember(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['user_id']){ - $savedata['user_id'] = $this->turnID($item['user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['user_id']) { + $saveData['user_id'] = $this->turnID($item['user_id']); } - if($item['inviter_user_id']){ - $savedata['inviter_user_id'] = $this->turnID($item['inviter_user_id']); + if ($item['inviter_user_id']) { + $saveData['inviter_user_id'] = $this->turnID($item['inviter_user_id']); } - if($item['operator_user_id']){ - $savedata['operator_user_id'] = $this->turnID($item['operator_user_id']); + if ($item['operator_user_id']) { + $saveData['operator_user_id'] = $this->turnID($item['operator_user_id']); } - if(!empty($savedata)){ - $model->where('id',$item['id'])->update($savedata); + + if (!empty($saveData)) { + $model->where('id', $item['id'])->update($saveData); } + $count++; } + $output->writeln("GroupMember 表转换完成,共 {$count} 条记录"); } - - protected function convertBlackTable($oldUserID, $newUserID, $output){ + /** + * 转换黑名单表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertBlackTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\Black(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['block_user_id']){ - $savedata['block_user_id'] = $this->turnID($item['block_user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['block_user_id']) { + $saveData['block_user_id'] = $this->turnID($item['block_user_id']); } - if($item['owner_user_id']){ - $savedata['owner_user_id'] = $this->turnID($item['owner_user_id']); + if ($item['owner_user_id']) { + $saveData['owner_user_id'] = $this->turnID($item['owner_user_id']); } - if($item['operator_user_id']){ - $savedata['operator_user_id'] = $this->turnID($item['operator_user_id']); + if ($item['operator_user_id']) { + $saveData['operator_user_id'] = $this->turnID($item['operator_user_id']); } - if(!empty($savedata)){ - $model->where('id',$item['id'])->update($savedata); + + if (!empty($saveData)) { + $model->where('id', $item['id'])->update($saveData); } + $count++; } + $output->writeln("Black 表转换完成,共 {$count} 条记录"); } - protected function convertConversationTable($oldUserID, $newUserID, $output){ + + /** + * 转换会话表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertConversationTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\Conversation(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['user_id']){ - $savedata['user_id'] = $this->turnID($item['user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['user_id']) { + $saveData['user_id'] = $this->turnID($item['user_id']); } - if($item['owner_user_id']){ - $savedata['owner_user_id'] = $this->turnID($item['owner_user_id']); + if ($item['owner_user_id']) { + $saveData['owner_user_id'] = $this->turnID($item['owner_user_id']); } - if($item['conversation_id']){ - $savedata['conversation_id'] = $this->handlerConversationID($item['conversation_id']); + if ($item['conversation_id']) { + $saveData['conversation_id'] = $this->handlerConversationID($item['conversation_id']); } - $model->where('id',$item['id'])->update($savedata); - //$item->save(); + + $model->where('id', $item['id'])->update($saveData); $count++; } + $output->writeln("Conversation 表转换完成,共 {$count} 条记录"); } - protected function convertUserTable($oldUserID, $newUserID, $output){ + /** + * 转换用户表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertUserTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\User(); $list = $model->select(); - foreach($list as $k=>$user){ - $model->where('id',$user['id'])->update([ - 'user_id' => $this->turnID($user['user_id']) + + foreach ($list as $user) { + $model->where('id', $user['id'])->update([ + 'user_id' => $this->turnID($user['user_id']) ]); } } - - protected function convertGroupRequestTable($oldUserID, $newUserID, $output){ + /** + * 转换群组请求表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertGroupRequestTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\GroupRequest(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - if($item['user_id']){ - $savedata['user_id'] = $this->turnID($item['user_id']); + + foreach ($list as $item) { + $saveData = []; + + if ($item['user_id']) { + $saveData['user_id'] = $this->turnID($item['user_id']); } - if($item['handle_user_id']){ - $savedata['handle_user_id'] = $this->turnID($item['handle_user_id']); + if ($item['handle_user_id']) { + $saveData['handle_user_id'] = $this->turnID($item['handle_user_id']); } - if($item['inviter_user_id']){ - $savedata['inviter_user_id'] = $this->turnID($item['inviter_user_id']); + if ($item['inviter_user_id']) { + $saveData['inviter_user_id'] = $this->turnID($item['inviter_user_id']); } - $model->where('id',$item['id'])->update($savedata); + + $model->where('id', $item['id'])->update($saveData); $count++; } + $output->writeln("GroupRequest 表转换完成,共 {$count} 条记录"); } - protected function convertSeqTable($oldUserID, $newUserID, $output){ + /** + * 转换seq表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertSeqTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\Seq(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $model->where('id',$item['id'])->update([ + + foreach ($list as $item) { + $model->where('id', $item['id'])->update([ 'conversation_id' => $this->handlerConversationID($item['conversation_id']) ]); $count++; } + $output->writeln("Seq 表转换完成,共 {$count} 条记录"); } - - protected function convertSeqUserTable($oldUserID, $newUserID, $output){ + /** + * 转换seq_user表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertSeqUserTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\SeqUser(); $list = $model->select(); - //cp($model->getLastSql()); $count = 0; - foreach($list as $item){ - $model->where('id',$item['id'])->update([ - 'user_id' => $this->turnID($item->user_id), - 'conversation_id' => $this->handlerConversationID($item['conversation_id']) + + foreach ($list as $item) { + $model->where('id', $item['id'])->update([ + 'user_id' => $this->turnID($item->user_id), + 'conversation_id' => $this->handlerConversationID($item['conversation_id']) ]); $count++; } - if($count){ + + if ($count) { $output->writeln("SeqUser 表转换完成,共 {$count} 条记录"); } } - protected function convertVersionTables($oldUserID, $newUserID, $output){ + /** + * 转换所有版本表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertVersionTables($oldUserID, $newUserID, $output) + { $this->convertConversationVersionTable($oldUserID, $newUserID, $output); $this->convertFriendVersionTable($oldUserID, $newUserID, $output); $this->convertGroupMemberVersionTable($oldUserID, $newUserID, $output); $this->convertGroupJoinVersionTable($oldUserID, $newUserID, $output); } - protected function convertConversationVersionTable($oldUserID, $newUserID, $output){ + /** + * 转换会话版本表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertConversationVersionTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\ConversationVersion(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - $savedata['d_id'] = $this->turnID($item['d_id']); - if(!empty($item['logs']) && is_array($item['logs'])){ + + foreach ($list as $item) { + $saveData = []; + $saveData['d_id'] = $this->turnID($item['d_id']); + + if (!empty($item['logs']) && is_array($item['logs'])) { $logs = $item['logs']; - foreach($logs as $key => $log){ - if(isset($log['e_id'])){ + foreach ($logs as $key => $log) { + if (isset($log['e_id'])) { $logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']); } } - $savedata['logs'] = $logs; + $saveData['logs'] = $logs; } - $model->where('id',$item['id'])->update($savedata); + + $model->where('id', $item['id'])->update($saveData); $count++; } + $output->writeln("ConversationVersion 表转换完成,共 {$count} 条记录"); } - protected function convertFriendVersionTable($oldUserID, $newUserID, $output){ + /** + * 转换好友版本表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertFriendVersionTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\FriendVersion(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - $savedata['d_id'] = $this->turnID($item['d_id']); - if(!empty($item['logs']) && is_array($item['logs'])){ + + foreach ($list as $item) { + $saveData = []; + $saveData['d_id'] = $this->turnID($item['d_id']); + + if (!empty($item['logs']) && is_array($item['logs'])) { $logs = $item['logs']; - foreach($logs as $key => $log){ - if(isset($log['e_id'])){ + foreach ($logs as $key => $log) { + if (isset($log['e_id'])) { $logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']); } } - $savedata['logs'] = $logs; + $saveData['logs'] = $logs; } - $model->where('id',$item['id'])->update($savedata); + + $model->where('id', $item['id'])->update($saveData); $count++; } + $output->writeln("FriendVersion 表转换完成,共 {$count} 条记录"); } - protected function convertGroupMemberVersionTable($oldUserID, $newUserID, $output){ + /** + * 转换群组成员版本表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertGroupMemberVersionTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\GroupMemberVersion(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - $savedata['d_id'] = $this->turnID($item['d_id']); - if(!empty($item['logs']) && is_array($item['logs'])){ + + foreach ($list as $item) { + $saveData = []; + $saveData['d_id'] = $this->turnID($item['d_id']); + + if (!empty($item['logs']) && is_array($item['logs'])) { $logs = $item['logs']; - foreach($logs as $key => $log){ - if(isset($log['e_id'])){ + foreach ($logs as $key => $log) { + if (isset($log['e_id'])) { $logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']); } } - $savedata['logs'] = $logs; + $saveData['logs'] = $logs; } - $model->where('id',$item['id'])->update($savedata); + + $model->where('id', $item['id'])->update($saveData); $count++; } + $output->writeln("GroupMemberVersion 表转换完成,共 {$count} 条记录"); } - protected function convertGroupJoinVersionTable($oldUserID, $newUserID, $output){ + /** + * 转换群组加入版本表 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertGroupJoinVersionTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\GroupJoinVersion(); $list = $model->select(); $count = 0; - foreach($list as $item){ - $savedata = []; - $savedata['d_id'] = $this->turnID($item['d_id']); - if(!empty($item['logs']) && is_array($item['logs'])){ + + foreach ($list as $item) { + $saveData = []; + $saveData['d_id'] = $this->turnID($item['d_id']); + + if (!empty($item['logs']) && is_array($item['logs'])) { $logs = $item['logs']; - foreach($logs as $key => $log){ - if(isset($log['e_id'])){ + foreach ($logs as $key => $log) { + if (isset($log['e_id'])) { $logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']); } } - $savedata['logs'] = $logs; + $saveData['logs'] = $logs; } - $model->where('id',$item['id'])->update($savedata); + + $model->where('id', $item['id'])->update($saveData); $count++; } + $output->writeln("GroupJoinVersion 表转换完成,共 {$count} 条记录"); } - function turnID($id){ - if($id == 'official_team' || $id == 'officialteam'){ - return 'SystemOfficialTeam'; + + /** + * 转换用户ID + * + * 将用户ID从旧格式转换为新格式 + * + * @param string $id + * @return string + */ + function turnID($id) + { + if (isset(self::SPECIAL_ACCOUNTS[$id])) { + return self::SPECIAL_ACCOUNTS[$id]; } - if($id == 'group_bot'){ - return 'SystemGroupBot'; - } - if($id == 'imAdmin'){ - return 'SystemImAdmin'; - } - if($id == 'system'){ - return 'SystemAccount'; - } - try{ + + try { return \support\Encrypt::intEncode(\support\Encrypt::userIDDecode($id)); - }catch(\Exception $e){ + } catch (\Exception $e) { return $id; } } - protected function convertMsgTable($oldUserID, $newUserID, $output){ + + /** + * 转换消息表 + * + * 处理消息内容中的用户ID、URL等 + * + * @param string $oldUserID + * @param string $newUserID + * @param OutputInterface $output + * @return void + */ + protected function convertMsgTable($oldUserID, $newUserID, $output) + { $model = new \app\model\Openim\Msg(); $total = $model->count('id'); $count = 0; - for($i=0;$i<$total;$i+=1000){ - $list = $model->limit($i,1000)->select(); - foreach($list as $item){ - $savedata = []; - $doc_id = explode(':',$item['doc_id']); - $doc_id[0] = $this->handlerConversationID($doc_id[0]); - $savedata['doc_id'] = $doc_id[0].':'.$doc_id[1]; - foreach($item['msgs'] as $key => $msg){ - if(isset($msg['msg']['send_id'])){ - $savedata['msgs'][$key]['msg']['send_id'] = $this->turnID($msg['msg']['send_id']); - } - if(isset($msg['msg']['recv_id'])){ - $savedata['msgs'][$key]['msg']['recv_id'] = $this->turnID($msg['msg']['recv_id']); - } - $content = []; - if($msg['msg']['content']){ - $content = json_decode($msg['msg']['content'],true); - } - if($msg['msg']['content_type'] == '101'){ - //cp($content); - //break 2; - } - if($msg['msg']['content_type'] == '102'){ - $urlFields = ['sourcePath']; - $this->handlerUrl($content,$urlFields); - if(isset($content['sourcePicture'])){ - $urlFields = ['url']; - $this->handlerUrl($content['sourcePicture'],$urlFields); - } - if(isset($content['bigPicture'])){ - $urlFields = ['url']; - $this->handlerUrl($content['bigPicture'],$urlFields); - } - if(isset($content['snapshotPicture'])){ - $urlFields = ['url']; - $this->handlerUrl($content['snapshotPicture'],$urlFields); - } - //cp($content); - //break 2; - } - if($msg['msg']['content_type'] == '103'){ - $urlFields = ['soundPath','sourceUrl']; - $this->handlerUrl($content,$urlFields); - } - if($msg['msg']['content_type'] == '104'){ - $urlFields = ['videoUrl','snapshotUrl','snapshotPath','videoPath']; - $this->handlerUrl($content,$urlFields); - //cp($content); - //break 2; - } - if($msg['msg']['content_type'] == '106'){ - if(isset($content['atUserList'])){ - foreach($content['atUserList'] as $k=>$userID){ - $content['atUserList'][$k] = $this->turnID($userID); - } - } - if(isset($content['atUsersInfo'])){ - foreach($content['atUsersInfo'] as $k=>$v){ - $content['atUsersInfo'][$k]['atUserID'] = $this->turnID($v['atUserID']); - } - } - } - if($msg['msg']['content_type'] == '108'){ - $this->handlerUserId($content,['userID']); - $this->handlerUrl($content,['faceURL']); - } - if($msg['msg']['content_type'] == '109'){ - //cp($content); - //break 2; - } - if($msg['msg']['content_type'] == '114'){ - if(isset($content['quoteMessage'])){ - $userIDFields = ['sendID','recvID']; - $this->handlerUserId($detail['quoteMessage'],$userIDFields); - $urlFields = ['senderFaceUrl']; - $this->handlerUrl($detail['quoteMessage'],$urlFields); - } - } - if($msg['msg']['content_type'] == '1201'){ - //cp($content['detail']); - $detail = json_decode($content['detail'],true); - if(isset($detail['fromToUserID'])){ - $userIDFields = ['fromUserID','toUserID']; - $this->handlerUserId($detail['fromToUserID'],$userIDFields); - } - if(isset($detail['request'])){ - $userIDFields = ['handlerUserID','fromUserID','toUserID']; - $this->handlerUserId($detail['request'],$userIDFields); - $urlFields = ['fromFaceURL','toFaceURL']; - $this->handlerUrl($detail['request'],$urlFields); - - } - $content['detail'] = json_encode($detail,JSON_UNESCAPED_UNICODE); - //cp($detail); - //break 2; - } - if(in_array($msg['msg']['content_type'],['1501','1504','1507','1508','1509','1510','1512','1513','1514','1519','1515','1520'])){ - $detail = json_decode($content['detail'],true); - - if(isset($detail['memberList'])){ - foreach($detail['memberList'] as $memberIndex => $member){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($member,$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($member,$urlFields); - $detail['memberList'][$memberIndex] = $member; - } - } - if(isset($detail['invitedUserList'])){ - foreach($detail['invitedUserList'] as $memberIndex => $member){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($member,$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($member,$urlFields); - $detail['invitedUserList'][$memberIndex] = $member; - } - } - if(isset($detail['groupOwnerUser'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['groupOwnerUser'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['groupOwnerUser'],$urlFields); - } - if(isset($detail['inviterUser'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['inviterUser'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['inviterUser'],$urlFields); - } - if(isset($detail['oldGroupOwnerInfo'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['oldGroupOwnerInfo'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['oldGroupOwnerInfo'],$urlFields); - } - if(isset($detail['newGroupOwner'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['newGroupOwner'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['newGroupOwner'],$urlFields); - } - if(isset($detail['entrantUser'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['entrantUser'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['entrantUser'],$urlFields); - } - if(isset($detail['mutedUser'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['mutedUser'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['mutedUser'],$urlFields); - } - if(isset($detail['group'])){ - $userIDFields = ['ownerUserID','creatorUserID']; - $this->handlerUserId($detail['group'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['group'],$urlFields); - } - if(isset($detail['opUser'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['opUser'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['opUser'],$urlFields); - } - if(isset($detail['quitUser'])){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($detail['quitUser'],$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($detail['quitUser'],$urlFields); - } - if(isset($detail['kickedUserList'])){ - foreach($detail['kickedUserList'] as $memberIndex => $member){ - $userIDFields = ['userID','operatorUserID','inviterUserID']; - $this->handlerUserId($member,$userIDFields); - $urlFields = ['faceURL']; - $this->handlerUrl($member,$urlFields); - $detail['kickedUserList'][$memberIndex] = $member; - } - } - $content['detail'] = json_encode($detail,JSON_UNESCAPED_UNICODE); - //break 2; - } - if($msg['msg']['content_type'] == '1701'){ - $detail = json_decode($content['detail'],true); - $userIDFields = ['recvID','sendID']; - $this->handlerUserId($detail,$userIDFields); - if(isset($detail['conversationID'])){ - $detail['conversationID'] = $this->handlerConversationID($detail['conversationID']); - } - $content['detail'] = json_encode($detail,JSON_UNESCAPED_UNICODE); - } - if($msg['msg']['content_type'] == '2001'){ - //cp($content); - //break 2; - } - if($msg['msg']['content_type'] == '2200'){ - $detail = json_decode($content['detail'],true); - $userIDFields = ['markAsReadUserID']; - $this->handlerUserId($detail,$userIDFields); - if(isset($detail['conversationID'])){ - $detail['conversationID'] = $this->handlerConversationID($detail['conversationID']); - } - //cp($detail); - $content['detail'] = json_encode($detail,JSON_UNESCAPED_UNICODE); - //break 2; - } - if($msg['msg']['content_type'] == '2101'){ - $detail = json_decode($content['detail'],true); - $userIDFields = ['revokerUserID']; - $this->handlerUserId($detail,$userIDFields); - $detail['conversationID'] = $this->handlerConversationID($detail['conversationID']); - $content['detail'] = json_encode($detail,JSON_UNESCAPED_UNICODE); - } - $msg['msg']['content'] = json_encode($content,JSON_UNESCAPED_UNICODE); - $savedata['msgs'][$key] = $msg; - } - if(!empty($savedata)){ - $model->where('id',$item['id'])->update($savedata); + + for ($i = 0; $i < $total; $i += self::BATCH_SIZE) { + $list = $model->limit($i, self::BATCH_SIZE)->select(); + + foreach ($list as $item) { + $saveData = $this->processMessageItem($item); + + if (!empty($saveData)) { + $model->where('id', $item['id'])->update($saveData); $count++; } } } - //log_alert($contentTypes,'cansnow'); - //$output->writeln("Msg 表转换完成,共 {$count} 条记录"); + } + + /** + * 处理单条消息记录 + * + * @param array $item + * @return array + */ + protected function processMessageItem($item) + { + $saveData = []; + + $docIdParts = explode(':', $item['doc_id']); + $docIdParts[0] = $this->handlerConversationID($docIdParts[0]); + $saveData['doc_id'] = $docIdParts[0] . ':' . $docIdParts[1]; + + foreach ($item['msgs'] as $key => $msg) { + $saveData['msgs'][$key] = $this->processSingleMessage($msg); + } + + return $saveData; + } + + /** + * 处理单条消息 + * + * @param array $msg + * @return array + */ + protected function processSingleMessage($msg) + { + if (isset($msg['msg']['send_id'])) { + $msg['msg']['send_id'] = $this->turnID($msg['msg']['send_id']); + } + if (isset($msg['msg']['recv_id'])) { + $msg['msg']['recv_id'] = $this->turnID($msg['msg']['recv_id']); + } + + $content = []; + if ($msg['msg']['content']) { + $content = json_decode($msg['msg']['content'], true); + } + + $content = $this->processMessageContent($content, $msg['msg']['content_type']); + + $msg['msg']['content'] = json_encode($content, JSON_UNESCAPED_UNICODE); + + return $msg; + } + + /** + * 处理消息内容 + * + * @param array $content + * @param string $contentType + * @return array + */ + protected function processMessageContent($content, $contentType) + { + switch ($contentType) { + case '101': + break; + + case '102': + $urlFields = ['sourcePath']; + $this->handlerUrl($content, $urlFields); + + if (isset($content['sourcePicture'])) { + $this->handlerUrl($content['sourcePicture'], ['url']); + } + if (isset($content['bigPicture'])) { + $this->handlerUrl($content['bigPicture'], ['url']); + } + if (isset($content['snapshotPicture'])) { + $this->handlerUrl($content['snapshotPicture'], ['url']); + } + break; + + case '103': + $this->handlerUrl($content, ['soundPath', 'sourceUrl']); + break; + + case '104': + $this->handlerUrl($content, ['videoUrl', 'snapshotUrl', 'snapshotPath', 'videoPath']); + break; + + case '106': + if (isset($content['atUserList'])) { + foreach ($content['atUserList'] as $k => $userID) { + $content['atUserList'][$k] = $this->turnID($userID); + } + } + if (isset($content['atUsersInfo'])) { + foreach ($content['atUsersInfo'] as $k => $v) { + $content['atUsersInfo'][$k]['atUserID'] = $this->turnID($v['atUserID']); + } + } + break; + + case '108': + $this->handlerUserId($content, ['userID']); + $this->handlerUrl($content, ['faceURL']); + break; + + case '114': + if (isset($content['quoteMessage'])) { + $this->handlerUserId($content['quoteMessage'], ['sendID', 'recvID']); + $this->handlerUrl($content['quoteMessage'], ['senderFaceUrl']); + } + break; + + case '1201': + $content = $this->processFriendNotification($content); + break; + + case '1501': + case '1504': + case '1507': + case '1508': + case '1509': + case '1510': + case '1512': + case '1513': + case '1514': + case '1519': + case '1515': + case '1520': + $content = $this->processGroupNotification($content); + break; + + case '1701': + $content = $this->processReadNotification($content); + break; + + case '2001': + break; + + case '2200': + $content = $this->processReadReceipt($content); + break; + + case '2101': + $content = $this->processMessageRecall($content); + break; + } + + return $content; + } + + /** + * 处理好友通知消息 + * + * @param array $content + * @return array + */ + protected function processFriendNotification($content) + { + $detail = json_decode($content['detail'], true); + if (isset($detail['fromToUserID'])) { + $this->handlerUserId($detail['fromToUserID'], ['fromUserID', 'toUserID']); + } + if (isset($detail['request'])) { + $this->handlerUserId($detail['request'], ['handlerUserID', 'fromUserID', 'toUserID']); + $this->handlerUrl($detail['request'], ['fromFaceURL', 'toFaceURL']); + } + $content['detail'] = json_encode($detail, JSON_UNESCAPED_UNICODE); + return $content; + } + + /** + * 处理群组通知消息 + * + * @param array $content + * @return array + */ + protected function processGroupNotification($content) + { + $detail = json_decode($content['detail'], true); + + if (isset($detail['memberList'])) { + foreach ($detail['memberList'] as $memberIndex => $member) { + $this->handlerUserId($member, ['userID', 'operatorUserID', 'inviterUserID']); + $this->handlerUrl($member, ['faceURL']); + $detail['memberList'][$memberIndex] = $member; + } + } + + if (isset($detail['invitedUserList'])) { + foreach ($detail['invitedUserList'] as $memberIndex => $member) { + $this->handlerUserId($member, ['userID', 'operatorUserID', 'inviterUserID']); + $this->handlerUrl($member, ['faceURL']); + $detail['invitedUserList'][$memberIndex] = $member; + } + } + + $userFields = [ + 'groupOwnerUser', 'inviterUser', 'oldGroupOwnerInfo', + 'newGroupOwner', 'entrantUser', 'mutedUser', 'opUser', 'quitUser' + ]; + + foreach ($userFields as $field) { + if (isset($detail[$field])) { + $this->handlerUserId($detail[$field], ['userID', 'operatorUserID', 'inviterUserID']); + $this->handlerUrl($detail[$field], ['faceURL']); + } + } + + if (isset($detail['group'])) { + $this->handlerUserId($detail['group'], ['ownerUserID', 'creatorUserID']); + $this->handlerUrl($detail['group'], ['faceURL']); + } + + if (isset($detail['kickedUserList'])) { + foreach ($detail['kickedUserList'] as $memberIndex => $member) { + $this->handlerUserId($member, ['userID', 'operatorUserID', 'inviterUserID']); + $this->handlerUrl($member, ['faceURL']); + $detail['kickedUserList'][$memberIndex] = $member; + } + } + + $content['detail'] = json_encode($detail, JSON_UNESCAPED_UNICODE); + return $content; + } + + /** + * 处理消息已读通知 + * + * @param array $content + * @return array + */ + protected function processReadNotification($content) + { + $detail = json_decode($content['detail'], true); + $this->handlerUserId($detail, ['recvID', 'sendID']); + if (isset($detail['conversationID'])) { + $detail['conversationID'] = $this->handlerConversationID($detail['conversationID']); + } + $content['detail'] = json_encode($detail, JSON_UNESCAPED_UNICODE); + return $content; + } + + /** + * 处理消息已读回执 + * + * @param array $content + * @return array + */ + protected function processReadReceipt($content) + { + $detail = json_decode($content['detail'], true); + $this->handlerUserId($detail, ['markAsReadUserID']); + if (isset($detail['conversationID'])) { + $detail['conversationID'] = $this->handlerConversationID($detail['conversationID']); + } + $content['detail'] = json_encode($detail, JSON_UNESCAPED_UNICODE); + return $content; + } + + /** + * 处理消息撤回 + * + * @param array $content + * @return array + */ + protected function processMessageRecall($content) + { + $detail = json_decode($content['detail'], true); + $this->handlerUserId($detail, ['revokerUserID']); + $detail['conversationID'] = $this->handlerConversationID($detail['conversationID']); + $content['detail'] = json_encode($detail, JSON_UNESCAPED_UNICODE); + return $content; } - function handlerConversationID($data){ - $data = str_replace('official_team','officialteam',$data); - $cids = explode('_',$data); + /** + * 处理会话ID + * + * 将会话ID中的用户ID转换为新格式 + * + * @param string $data + * @return string + */ + function handlerConversationID($data) + { + $data = str_replace('official_team', 'officialteam', $data); + $cids = explode('_', $data); $cids[1] = $this->turnID($cids[1]); - if(count($cids) >2){ + + if (count($cids) > 2) { $cids[2] = $this->turnID($cids[2]); } - $data = implode('_',$cids); - return $data; + + return implode('_', $cids); } - function handlerUserId(&$data,$userIDFields){ - foreach($userIDFields as $userIDField){ - if(isset($data[$userIDField]) && $data[$userIDField]){ + + /** + * 处理用户ID字段 + * + * @param array $data + * @param array $userIDFields + * @return void + */ + function handlerUserId(&$data, $userIDFields) + { + foreach ($userIDFields as $userIDField) { + if (isset($data[$userIDField]) && $data[$userIDField]) { $data[$userIDField] = $this->turnID($data[$userIDField]); } } } - function handlerUrl(&$data,$urlFields){ - foreach($urlFields as $urlField){ - if(isset($data[$urlField])){ + + /** + * 处理URL字段 + * + * @param array $data + * @param array $urlFields + * @return void + */ + function handlerUrl(&$data, $urlFields) + { + foreach ($urlFields as $urlField) { + if (isset($data[$urlField])) { $data[$urlField] = $this->cdn($data[$urlField]); } } } - function cdn($url){ - if(substr(0,2) =='//'){ - $url = 'https:'.$url; + /** + * CDN URL处理 + * + * 将HTTP协议转换为HTTPS + * + * @param string $url + * @return string + */ + function cdn($url) + { + if (substr($url, 0, 2) == '//') { + $url = 'https:' . $url; } - if(substr(0,7) =='http://'){ - $url = 'https://'.substr($url,7); + + if (substr($url, 0, 7) == 'http://') { + $url = 'https://' . substr($url, 7); } + return $url; } - function getUser(){ + + /** + * 获取用户(测试方法) + * + * @return int + */ + function getUser() + { $im = $this->getSdk(); $data = $im->user->searchNotificationAccount(''); cp($data); return 0; } + + /** + * 修改用户(测试方法) + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + private function change_user(InputInterface $input, OutputInterface $output): int + { + $im = $this->getSdk(); + $data = $im->user->updateUserInfo( + \support\Encrypt::userIDencode('100006'), + ['userInfo' => ['userId' => 'wx100001']] + ); + cp($data); + return self::SUCCESS; + } + + /** + * 同步用户(未完成) + * + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + private function sync_users(InputInterface $input, OutputInterface $output): int + { + $im = $this->getSdk(); + $data = $im->user->getAllUsersUid(1, 1000); + cp($data); + return self::SUCCESS; + } + + /** + * 同步缓存 + * + * 同步群组相关数据到缓存 + * + * @return int + */ + function sync_cache() + { + $res = \app\model\Openim\Group::field('group_id,creator_user_id')->select(); + + $groupCreate = []; + foreach ($res as $v) { + if (!isset($groupCreate[$v['creator_user_id']])) { + $groupCreate[$v['creator_user_id']] = 0; + } + if ($v['status'] != 2) { + $groupCreate[$v['creator_user_id']] += 1; + } + + cache('group_owner_' . $v['group_id'], $v['creator_user_id']); + + $groupUserCount = \app\model\Openim\GroupMember::field('group_id,count(*) as count') + ->where('group_id', $v['group_id']) + ->count('user_id'); + cp('群组数量', $v['group_id'], '成员数量:', $groupUserCount); + cache('group_' . $v['group_id'] . '_user_count', $groupUserCount); + } + + foreach ($groupCreate as $userId => $count) { + cp('用户:', $userId, '创建群组数量:', $count); + cache('user_' . $userId . '_create_group_count', $count); + } + + return 0; + } + + /** + * 获取OpenIM SDK实例 + * + * @return object + */ + function getSdk() + { + if ($this->sdk) { + return $this->sdk; + } + + $this->sdk = new \support\OpenImSdk\Client([ + 'host' => 'http://127.0.0.1:10002', + 'secret' => 'n1e5a6s6m7', + ]); + + return $this->sdk; + } + + /** + * 配置命令参数 + * * @return void */ protected function configure() { - $this->addOption('action','a', InputArgument::OPTIONAL, '操作类型'); - $this->addOption('conversationID','c', InputArgument::OPTIONAL, '会话ID'); - $this->addOption('userID','u', InputArgument::OPTIONAL, '用户ID'); + $this->addOption('action', 'a', InputOption::VALUE_OPTIONAL, '操作类型'); + $this->addOption('conversationID', 'c', InputOption::VALUE_OPTIONAL, '会话ID'); + $this->addOption('userID', 'u', InputOption::VALUE_OPTIONAL, '用户ID'); + $this->addOption('seq', 's', InputOption::VALUE_OPTIONAL, '消息序列'); } - - - /** + * 执行命令 + * * @param InputInterface $input * @param OutputInterface $output * @return int @@ -693,88 +1562,17 @@ class OpenIm extends Command protected function execute(InputInterface $input, OutputInterface $output): int { $action = $input->getOption('action'); - if(!$action){ + + if (!$action) { $output->writeln('空操作'); return self::FAILURE; } - if(method_exists($this, $action)){ + + if (method_exists($this, $action)) { return $this->$action($input, $output); } - $output->writeln($action.'不存在'); + + $output->writeln($action . '不存在'); return self::FAILURE; } - private function change_user(InputInterface $input, OutputInterface $output):int{ - - $im = $this->getSdk(); - $data = $im->user->updateUserInfo(\support\Encrypt::userIDencode('100006'),['userInfo'=>['userId'=>'wx100001']]); - cp($data); - return self::SUCCESS; - } - private function sync_users(InputInterface $input, OutputInterface $output):int{ - $im = $this->getSdk(); - $data = $im->user->getAllUsersUid(1,1000); - cp($data); - return self::SUCCESS; - $exsit_user_ids = Db::name('user')->whereIn('id',$data['userIDs'])->column('id'); - $not_exsit_user_ids =array_diff($data['userIDs'],$exsit_user_ids); - if(count($not_exsit_user_ids)> 0){ - //同步用户 - $res = $im->user->getUsersInfo($not_exsit_user_ids); - $save_data = []; - foreach($res['usersInfo'] as $k=>$_user){ - array_push($save_data,[ - 'id' => $_user['userID'], - 'nickname' => $_user['nickname'], - 'password' => '123456', - 'avatar' => $_user['faceURL'] - ]); - - // "ex": "", - // "createTime": 1688454168302, - // "appMangerLevel": 18, - // "globalRecvMsgOpt": 0 - } - if(!empty($save_data)){ - Db::name('user')->insertAll($save_data); - } - } - return self::SUCCESS; - } - - function sync_cache(){ - $res = \app\model\Openim\Group::field('group_id,creator_user_id')->select(); - - $group_create = []; - foreach($res as $v){ - if(!isset($group_create[$v['creator_user_id']])){ - $group_create[$v['creator_user_id']] = 0; - } - if($v['status'] != 2){ - $group_create[$v['creator_user_id']]+=1; - } - cache('group_owner_'.$v['group_id'],$v['creator_user_id']); - $group_user_count = \app\model\Openim\GroupMember::field('group_id,count(*) as count') - ->where('group_id',$v['group_id']) - ->count('user_id'); - cp('群组数量',$v['group_id'],'成员数量:',$group_user_count); - cache('group_'.$v['group_id'].'_user_count',$group_user_count); - - } - //cp($group_create); - foreach($group_create as $userId =>$count){ - cp('用户:',$userId,'创建群组数量:',$count); - cache('user_'.$userId.'_create_group_count',$count); - } - return 0; - } - function getSdk(){ - if($this->sdk){ - return $this->sdk; - } - $this->sdk = new \support\OpenImSdk\Client([ - 'host' => 'http://127.0.0.1:10002', // OpenIM API地址 - 'secret' => 'n1e5a6s6m7', // OpenIM密钥 - ]); - return $this->sdk; - } } diff --git a/app/command/User.php b/app/command/User.php index 535b5bd..2e97d6b 100755 --- a/app/command/User.php +++ b/app/command/User.php @@ -40,26 +40,12 @@ class User extends Command cp('操作不存在:'.$action); return 0; } - function test(InputInterface $input, OutputInterface $output) - { - $user_id = 104864; - $_user = Db::name('user')->where('id',$user_id)->find(); - Db::query('delete FROM `wa_user_team` WHERE descendant_id='.$user_id.' or ancestor_id='.$user_id.';'); - Hook('user.register_successed',$_user); - //管理团队人数 - // $team_user_ids = Db::name('user_team')->where('descendant_id',$_user['id']) - // ->where('depth','>',0) - // ->order('depth','ASC') - // ->column('ancestor_id'); - - // Db::name('user_extend')->whereIn('user_id',$team_user_ids)->data([ - // 'team_total'=> Db::raw('team_total+1') - // ])->save(); - // $list = Db::name('user_extend')->whereIn('user_id',$team_user_ids)->field('user_id,team_total')->select(); - // foreach($list as $v){ - // cache('team_user_count_'.$v['user_id'],$v['team_total']); - // } - // cp($team_user_ids); + function update_password(InputInterface $input, OutputInterface $output){ + $newpassword = \plugin\admin\app\common\Util::passwordHash(MD5('qwe123')); + Db::name('User')->where('id','>',0)->save([ + 'loginfailure' => 0, + 'password' => $newpassword + ]); return 0; } function login(InputInterface $input, OutputInterface $output){ diff --git a/app/enum/BaseEnum.php b/app/enum/BaseEnum.php new file mode 100644 index 0000000..c1fea7c --- /dev/null +++ b/app/enum/BaseEnum.php @@ -0,0 +1,24 @@ +value]; + } + + /** + * 安全地从值创建枚举实例 + */ + public static function tryFromValue(int $value): ?self + { + return self::tryFrom($value); + } +} \ No newline at end of file diff --git a/app/enum/Payment/Method.php b/app/enum/Payment/Method.php new file mode 100644 index 0000000..f66f2d3 --- /dev/null +++ b/app/enum/Payment/Method.php @@ -0,0 +1,32 @@ +value => __('微信'), + self::ALIPAY->value => __('支付宝') + ]; + } + +} \ No newline at end of file diff --git a/app/enum/Payment/Status.php b/app/enum/Payment/Status.php new file mode 100644 index 0000000..4d3a60b --- /dev/null +++ b/app/enum/Payment/Status.php @@ -0,0 +1,61 @@ +value => __('等待支付'), + self::SUCCESS->value => __('成功'), + self::FAIL->value => __('失败'), + self::COMPLETE->value => __('完成'), + self::REFUNDED->value => __('退款'), + ]; + } + + /** + * 获取当前状态的描述文本 + */ + public function getDescription(): string + { + return self::toArray()[$this->value]; + } + + /** + * 安全地从值创建枚举实例 + */ + public static function tryFromValue(int $value): ?self + { + return self::tryFrom($value); + } +} \ No newline at end of file diff --git a/app/enum/Payment/Type.php b/app/enum/Payment/Type.php new file mode 100644 index 0000000..8e5bb78 --- /dev/null +++ b/app/enum/Payment/Type.php @@ -0,0 +1,42 @@ +value => __('充值'), + self::GOODS->value => __('商品'), + self::SERVICE->value => __('服务'), + self::OTHER->value => __('其他') + ]; + } + +} \ No newline at end of file diff --git a/app/mcp/McpService.php b/app/mcp/McpService.php index ef3f1b4..cd18347 100755 --- a/app/mcp/McpService.php +++ b/app/mcp/McpService.php @@ -2,13 +2,14 @@ namespace app\mcp; use PhpMcp\Server\Server; -use PhpMcp\Server\ServerBuilder; use PhpMcp\Server\Transports\StdioServerTransport; +use PhpMcp\Server\Transports\StreamableHttpServerTransport; +use PhpMcp\Server\Transports\HttpServerTransport; +use PhpMcp\Server\Attributes\McpTool; +use PhpMcp\Server\Attributes\McpResource; +use PhpMcp\Server\Attributes\McpPrompt; +use PhpMcp\Server\Attributes\Schema; use PhpMcp\Server\Defaults\BasicContainer; -use PhpMcp\Schema\Tool; -use PhpMcp\Schema\Resource; -use PhpMcp\Schema\ToolAnnotations; -use PhpMcp\Schema\Annotations; use think\facade\Db; use support\Log; @@ -116,14 +117,21 @@ class McpService { // 确保 logger 不为 null try { + // 尝试获取 mcp 通道 $this->logger = Log::channel('mcp'); - if (!$this->logger) { - // 如果 mcp 通道不存在,使用默认通道 - $this->logger = Log::channel('default'); - } } catch (\Throwable $e) { - // 如果所有通道都失败,创建一个简单的 logger - $this->logger = Log::channel('default'); + try { + // 如果 mcp 通道不存在,尝试使用默认通道 + $this->logger = Log::channel('default'); + } catch (\Throwable $e) { + // 如果所有通道都失败,创建一个简单的 logger + $this->logger = new class { + public function info($message, $context = []) {} + public function error($message, $context = []) {} + public function warning($message, $context = []) {} + public function addRecord($level, $message, $context = []) {} + }; + } } // 读取MCP配置文件 @@ -193,18 +201,24 @@ class McpService $this->heartbeatInterval = $mcpConfig['heartbeat_interval']; } - Log::channel('mcp')->info('MCP配置加载成功', [ - 'timeout' => $this->timeout, - 'connect_timeout' => $this->connectTimeout, - 'read_timeout' => $this->readTimeout, - 'retry_attempts' => $this->retryAttempts, - 'retry_delay' => $this->retryDelay, - 'heartbeat_enabled' => $this->heartbeatEnabled ?? false, - 'heartbeat_interval' => $this->heartbeatInterval ?? 30 - ]); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info('MCP配置加载成功', [ + 'timeout' => $this->timeout, + 'connect_timeout' => $this->connectTimeout, + 'read_timeout' => $this->readTimeout, + 'retry_attempts' => $this->retryAttempts, + 'retry_delay' => $this->retryDelay, + 'heartbeat_enabled' => $this->heartbeatEnabled ?? false, + 'heartbeat_interval' => $this->heartbeatInterval ?? 30 + ]); + } } catch (\Exception $e) { - Log::warning('MCP配置加载失败,使用默认配置: ' . $e->getMessage()); + // 安全地记录警告 + if (method_exists($this->logger, 'warning')) { + $this->logger->warning('MCP配置加载失败,使用默认配置: ' . $e->getMessage()); + } } } @@ -291,29 +305,44 @@ class McpService while ($attempts < $this->retryAttempts) { try { $attempts++; - Log::channel('mcp')->info("执行{$operationName},第{$attempts}次尝试"); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info("执行{$operationName},第{$attempts}次尝试"); + } $result = $operation(); if ($attempts > 1) { - Log::channel('mcp')->info("{$operationName}在第{$attempts}次尝试后成功"); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info("{$operationName}在第{$attempts}次尝试后成功"); + } } return $result; } catch (\Exception $e) { $lastException = $e; - Log::warning("{$operationName}第{$attempts}次尝试失败: " . $e->getMessage()); + // 安全地记录警告 + if (method_exists($this->logger, 'warning')) { + $this->logger->warning("{$operationName}第{$attempts}次尝试失败: " . $e->getMessage()); + } if ($attempts < $this->retryAttempts) { $delay = $this->retryDelay * pow(1.5, $attempts - 1); // 指数退避 - Log::channel('mcp')->info("等待{$delay}ms后重试"); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info("等待{$delay}ms后重试"); + } usleep($delay * 1000); } } } - Log::channel('mcp')->error("{$operationName}在{$this->retryAttempts}次尝试后仍然失败"); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error("{$operationName}在{$this->retryAttempts}次尝试后仍然失败"); + } throw $lastException; } @@ -343,27 +372,11 @@ class McpService } $this->server = $builder->withContainer($container) - ->withTool([self::class, 'handleDbQuery'], 'db-query', '执行数据库查询操作(仅支持SELECT语句)') - ->withTool([self::class, 'handleSysConfig'], 'sys-config', '获取系统配置信息') - ->withTool([self::class, 'handleWriteLog'], 'write-log', '写入系统日志') - ->withTool([self::class, 'handleFileOperation'], 'file-operation', '文件读写操作') - ->withTool([self::class, 'handleUserManagement'], 'user-management', '用户管理相关操作') - ->withTool([self::class, 'handleSystemInfo'], 'system-info', '获取系统运行信息') - ->withTool([self::class, 'handleCreateController'], 'controller', '生成webman控制器文件') - ->withTool([self::class, 'handleCreateModel'], 'model', '生成webman模型文件') - ->withTool([self::class, 'handleCreateView'], 'view', '生成webman视图文件') - ->withTool([self::class, 'handleCreateJs'], 'js', '生成webman JS文件') - ->withTool([self::class, 'handleCreateApi'], 'api', '生成webman API接口文件') - ->withTool([self::class, 'handleCurd'], 'curd', '生成webman CURD模块') - ->withTool([self::class, 'handleAddon'], 'addon', '生成webman 插件模块') - ->withTool([self::class, 'handleMenu'], 'menu', '生成webman 菜单模块') - ->withTool([self::class, 'handleCreateTable'], 'table', '创建数据库表格, 支持字段信息、类型、注释等') - ->withTool([self::class, 'handleThinkCommand'], 'think-command', '执行webman框架命令') - ->withTool([self::class, 'handleMcpCommand'], 'mcp-command', '执行MCP专用命令') - ->withTool([self::class, 'handleWebmanCommand'], 'webman-command', '执行webman框架命令') - ->withPrompt([self::class, 'handleWithPrompt'], 'with-prompt', '通过自然语言描述生成数据库表、控制器、模型等') - ->withResource([self::class, 'handleConfigResource'], 'config://system', 'config-system', '系统配置信息资源', 'application/json') - ->withResource([self::class, 'handleSchemaResource'], 'schema://database', 'schema-database', '数据库表结构信息资源', 'application/json') + // 使用属性发现自动注册工具、资源和提示 + ->discover( + basePath: __DIR__ . '/..', + scanDirs: ['mcp'] + ) ->build(); return $this->server; } @@ -376,7 +389,13 @@ class McpService * @param array $params 查询参数 * @return array */ - public function handleDbQuery(string $query, array $params = []): array + #[McpTool(name: 'db-query', description: '执行数据库查询操作(仅支持SELECT语句)')] + public function handleDbQuery( + #[Schema(type: 'string', description: 'SQL查询语句')] + string $query, + #[Schema(type: 'array', description: '查询参数')] + array $params = [] + ): array { try { if (empty($query)) { @@ -400,7 +419,10 @@ class McpService ]; } catch (\Exception $e) { - Log::channel('mcp')->error('MCP数据库查询错误: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP数据库查询错误: ' . $e->getMessage()); + } return [ 'success' => false, 'error' => $e->getMessage() @@ -413,7 +435,11 @@ class McpService * @param string $key 配置键名(可选) * @return array */ - public function handleSysConfig(string $key = ''): array + #[McpTool(name: 'sys-config', description: '获取系统配置信息')] + public function handleSysConfig( + #[Schema(type: 'string', description: '配置键名(可选)')] + string $key = '' + ): array { try { if (empty($key)) { @@ -451,7 +477,15 @@ class McpService * @param array $context 上下文数据 * @return string */ - public function handleWriteLog(string $message, string $level = 'info', array $context = []): string + #[McpTool(name: 'write-log', description: '写入系统日志')] + public function handleWriteLog( + #[Schema(type: 'string', description: '日志消息')] + string $message, + #[Schema(type: 'string', description: '日志级别')] + string $level = 'info', + #[Schema(type: 'array', description: '上下文数据')] + array $context = [] + ): string { try { if (empty($message)) { @@ -469,7 +503,10 @@ class McpService return "日志记录成功 [级别: {$level}]"; } catch (\Exception $e) { - Log::channel('mcp')->error('MCP日志写入错误: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP日志写入错误: ' . $e->getMessage()); + } throw $e; } } @@ -480,7 +517,13 @@ class McpService * @param string $filepath 文件路径 * @return array */ - public function handleFileOperation(string $operation, string $filepath): array + #[McpTool(name: 'file-operation', description: '文件读写操作')] + public function handleFileOperation( + #[Schema(type: 'string', description: '操作类型')] + string $operation, + #[Schema(type: 'string', description: '文件路径')] + string $filepath + ): array { try { if (empty($operation) || empty($filepath)) { @@ -541,7 +584,10 @@ class McpService } } catch (\Exception $e) { - Log::channel('mcp')->error('MCP文件操作错误: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP文件操作错误: ' . $e->getMessage()); + } throw $e; } } @@ -553,7 +599,15 @@ class McpService * @param int $limit 返回数量限制(可选) * @return array */ - public function handleUserManagement(string $action, int $userId = 0, int $limit = 10): array + #[McpTool(name: 'user-management', description: '用户管理相关操作')] + public function handleUserManagement( + #[Schema(type: 'string', description: '操作类型')] + string $action, + #[Schema(type: 'integer', description: '用户ID(可选)')] + int $userId = 0, + #[Schema(type: 'integer', description: '返回数量限制(可选)')] + int $limit = 10 + ): array { try { switch ($action) { @@ -599,7 +653,10 @@ class McpService } } catch (\Exception $e) { - Log::channel('mcp')->error('MCP用户管理错误: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP用户管理错误: ' . $e->getMessage()); + } throw $e; } } @@ -609,7 +666,11 @@ class McpService * @param string $type 信息类型 * @return array */ - public function handleSystemInfo(string $type = 'general'): array + #[McpTool(name: 'system-info', description: '获取系统运行信息')] + public function handleSystemInfo( + #[Schema(type: 'string', description: '信息类型')] + string $type = 'general' + ): array { try { switch ($type) { @@ -661,7 +722,10 @@ class McpService } } catch (\Exception $e) { - Log::channel('mcp')->error('MCP系统信息错误: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP系统信息错误: ' . $e->getMessage()); + } throw $e; } } @@ -688,6 +752,7 @@ class McpService * 处理配置资源 * @return string */ + #[McpResource(uri: 'config://system', name: 'config-system', description: '系统配置信息资源', contentType: 'application/json')] public function handleConfigResource(): string { $configs = [ @@ -709,6 +774,7 @@ class McpService * 处理数据库模式资源 * @return string */ + #[McpResource(uri: 'schema://database', name: 'schema-database', description: '数据库表结构信息资源', contentType: 'application/json')] public function handleSchemaResource(): string { try { @@ -727,7 +793,10 @@ class McpService return json_encode($schema, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); } catch (\Exception $e) { - Log::channel('mcp')->error('MCP数据库模式获取错误: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP数据库模式获取错误: ' . $e->getMessage()); + } return json_encode(['error' => $e->getMessage()], JSON_UNESCAPED_UNICODE); } } @@ -772,11 +841,17 @@ class McpService $server = $this->buildServer(); $transport = new StdioServerTransport(); - Log::channel('mcp')->info('MCP STDIO服务器启动成功'); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info('MCP STDIO服务器启动成功'); + } $server->listen($transport); } catch (\Exception $e) { - Log::channel('mcp')->error('MCP STDIO服务器启动失败: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP STDIO服务器启动失败: ' . $e->getMessage()); + } throw $e; } } @@ -793,10 +868,16 @@ class McpService $server = $this->buildServer(); $server->listen($transport); - Log::channel('mcp')->info('MCP服务器启动成功'); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info('MCP服务器启动成功'); + } } catch (\Exception $e) { - Log::channel('mcp')->error('MCP服务器启动失败: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP服务器启动失败: ' . $e->getMessage()); + } throw $e; } } @@ -811,13 +892,19 @@ class McpService $this->startHeartbeat(); $server = $this->buildServer(); - $transport = new \PhpMcp\Server\Transports\StreamableHttpServerTransport($host, $port, $mcpPath); + $transport = new StreamableHttpServerTransport($host, $port, $mcpPath); - Log::channel('mcp')->info("MCP SSE服务器启动成功,监听地址: http://{$host}:{$port}/{$mcpPath}"); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info("MCP SSE服务器启动成功,监听地址: http://{$host}:{$port}/{$mcpPath}"); + } $server->listen($transport); } catch (\Exception $e) { - Log::channel('mcp')->error('MCP SSE服务器启动失败: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP SSE服务器启动失败: ' . $e->getMessage()); + } throw $e; } } @@ -832,13 +919,19 @@ class McpService $this->startHeartbeat(); $server = $this->buildServer(); - $transport = new \PhpMcp\Server\Transports\HttpServerTransport($host, $port, $mcpPath); + $transport = new HttpServerTransport($host, $port, $mcpPath); - Log::channel('mcp')->info("MCP HTTP服务器启动成功,监听地址: http://{$host}:{$port}/{$mcpPath}"); + // 安全地记录日志 + if (method_exists($this->logger, 'info')) { + $this->logger->info("MCP HTTP服务器启动成功,监听地址: http://{$host}:{$port}/{$mcpPath}"); + } $server->listen($transport); } catch (\Exception $e) { - Log::channel('mcp')->error('MCP HTTP服务器启动失败: ' . $e->getMessage()); + // 安全地记录错误 + if (method_exists($this->logger, 'error')) { + $this->logger->error('MCP HTTP服务器启动失败: ' . $e->getMessage()); + } throw $e; } } @@ -877,7 +970,17 @@ class McpService * @param string $description 控制器描述 (可选) * @return array */ - public function handleCreateController(string $module, string $controller, array $fields = [], string $description = ''): array + #[McpTool(name: 'controller', description: '生成webman控制器文件')] + public function handleCreateController( + #[Schema(type: 'string', description: '模块名称 (admin/api/frontend等)')] + string $module, + #[Schema(type: 'string', description: '控制器名称')] + string $controller, + #[Schema(type: 'array', description: '字段信息 (可选)')] + array $fields = [], + #[Schema(type: 'string', description: '控制器描述 (可选)')] + string $description = '' + ): array { try { // 生成控制器类名 @@ -934,7 +1037,17 @@ class McpService * @param string $description 模型描述 (可选) * @return array */ - public function handleCreateModel(string $modelName, array $fields = [], string $tableName = '', string $description = ''): array + #[McpTool(name: 'model', description: '生成webman模型文件')] + public function handleCreateModel( + #[Schema(type: 'string', description: '模型名称')] + string $modelName, + #[Schema(type: 'array', description: '字段信息 (可选)')] + array $fields = [], + #[Schema(type: 'string', description: '表名 (可选,默认使用模型名)')] + string $tableName = '', + #[Schema(type: 'string', description: '模型描述 (可选)')] + string $description = '' + ): array { try { // 生成模型类名 @@ -1078,7 +1191,19 @@ class {$modelClass} extends Base * @param string $charset 字符集 (默认 utf8mb4) * @return array */ - public function handleCreateTable(string $tableName, array $fields, string $tableComment = '', string $engine = 'InnoDB', string $charset = 'utf8mb4'): array + #[McpTool(name: 'table', description: '创建数据库表格,支持字段信息、类型、注释等')] + public function handleCreateTable( + #[Schema(type: 'string', description: '表名')] + string $tableName, + #[Schema(type: 'array', description: '字段信息数组')] + array $fields, + #[Schema(type: 'string', description: '表注释')] + string $tableComment = '', + #[Schema(type: 'string', description: '存储引擎 (默认 InnoDB)')] + string $engine = 'InnoDB', + #[Schema(type: 'string', description: '字符集 (默认 utf8mb4)')] + string $charset = 'utf8mb4' + ): array { try { // 验证表名 @@ -1293,7 +1418,19 @@ class {$modelClass} extends Base * @param array $options 其他选项 * @return array */ - public function handleCurd(string $tableName, string $module = 'admin', array $fields = [], string $description = '', array $options = []): array + #[McpTool(name: 'curd', description: '生成webman CURD模块')] + public function handleCurd( + #[Schema(type: 'string', description: '表名')] + string $tableName, + #[Schema(type: 'string', description: '模块名称 (默认 admin)')] + string $module = 'admin', + #[Schema(type: 'array', description: '字段信息 (可选)')] + array $fields = [], + #[Schema(type: 'string', description: '描述 (可选)')] + string $description = '', + #[Schema(type: 'array', description: '选项 (可选)')] + array $options = [] + ): array { try { // 构建命令行参数 @@ -1359,7 +1496,15 @@ class {$modelClass} extends Base * @param array $options 其他选项 * @return array */ - public function handleAddon(string $action, string $addonName = '', array $options = []): array + #[McpTool(name: 'addon', description: '生成webman 插件模块')] + public function handleAddon( + #[Schema(type: 'string', description: '操作类型')] + string $action, + #[Schema(type: 'string', description: '插件名称 (可选)')] + string $addonName = '', + #[Schema(type: 'array', description: '选项 (可选)')] + array $options = [] + ): array { try { // 构建命令行参数 @@ -1459,7 +1604,15 @@ class {$modelClass} extends Base * @param array $options 其他选项 * @return array */ - public function handleMenu(string $action, array $menuData = [], array $options = []): array + #[McpTool(name: 'menu', description: '生成webman 菜单模块')] + public function handleMenu( + #[Schema(type: 'string', description: '操作类型')] + string $action, + #[Schema(type: 'array', description: '菜单数据 (可选)')] + array $menuData = [], + #[Schema(type: 'array', description: '选项 (可选)')] + array $options = [] + ): array { try { // 构建命令行参数 @@ -1533,7 +1686,13 @@ class {$modelClass} extends Base * @param string $type 生成类型 (table/controller/model/js/api/view/all) * @return array */ - public function handleWithPrompt(string $prompt, string $type = 'all'): array + #[McpPrompt(name: 'with-prompt', description: '通过自然语言描述生成数据库表、控制器、模型等')] + public function handleWithPrompt( + #[Schema(type: 'string', description: '自然语言描述')] + string $prompt, + #[Schema(type: 'string', description: '生成类型')] + string $type = 'all' + ): array { try { if (empty($prompt)) { @@ -2181,11 +2340,21 @@ class {$modelClass} extends Base * @param string $description 描述 (可选) * @return array */ - public function handleCreateJs(string $module, string $controller, array $fields = [], string $description = ''): array + #[McpTool(name: 'js', description: '生成webman JS文件')] + public function handleCreateJs( + #[Schema(type: 'string', description: '模块名称')] + string $module, + #[Schema(type: 'string', description: 'JS文件名')] + string $name, + #[Schema(type: 'array', description: 'JS数据 (可选)')] + array $data = [], + #[Schema(type: 'string', description: 'JS描述 (可选)')] + string $description = '' + ): array { try { // 生成JS文件名 - $jsFileName = strtolower($controller); + $jsFileName = strtolower($name); $jsPath = "plugin/admin/public/js/{$jsFileName}.js"; if ($module == 'frontend') { @@ -2200,7 +2369,7 @@ class {$modelClass} extends Base } // 生成JS内容 - $jsContent = $this->generateJsContent($module, $controller, $fields, $description); + $jsContent = $this->generateJsContent($module, $name, $data, $description); // 确保目录存在 $dir = dirname($jsPath); @@ -2238,7 +2407,17 @@ class {$modelClass} extends Base * @param string $description 描述 (可选) * @return array */ - public function handleCreateApi(string $controller, string $module = 'api', array $fields = [], string $description = ''): array + #[McpTool(name: 'api', description: '生成webman API接口文件')] + public function handleCreateApi( + #[Schema(type: 'string', description: '控制器名称')] + string $controller, + #[Schema(type: 'string', description: '模块名称 (默认 api)')] + string $module = 'api', + #[Schema(type: 'array', description: '字段信息 (可选)')] + array $fields = [], + #[Schema(type: 'string', description: 'API描述 (可选)')] + string $description = '' + ): array { try { // 生成API控制器类名 @@ -2455,7 +2634,17 @@ class {$modelClass} extends Base * @param string $description 描述 (可选) * @return array */ - public function handleCreateView(string $module, string $controller, array $fields = [], string $description = ''): array + #[McpTool(name: 'view', description: '生成webman视图文件')] + public function handleCreateView( + #[Schema(type: 'string', description: '模块名称')] + string $module, + #[Schema(type: 'string', description: '控制器名称')] + string $controller, + #[Schema(type: 'array', description: '字段信息 (可选)')] + array $fields = [], + #[Schema(type: 'string', description: '视图描述 (可选)')] + string $description = '' + ): array { try { // 生成视图文件名 @@ -3143,7 +3332,15 @@ class {$modelClass} extends Base * @param array $options 命令选项 (可选) * @return array */ - public function handleThinkCommand(string $command, array $params = [], array $options = []): array + #[McpTool(name: 'think-command', description: '执行webman框架命令')] + public function handleThinkCommand( + #[Schema(type: 'string', description: '命令名称')] + string $command, + #[Schema(type: 'array', description: '命令参数 (可选)')] + array $params = [], + #[Schema(type: 'array', description: '命令选项 (可选)')] + array $options = [] + ): array { try { // 验证命令是否为安全的内置命令 @@ -3283,7 +3480,15 @@ class {$modelClass} extends Base * @param array $options 选项 * @return array */ - public function handleMcpCommand(string $command, array $params = [], array $options = []): array + #[McpTool(name: 'mcp-command', description: '执行MCP专用命令')] + public function handleMcpCommand( + #[Schema(type: 'string', description: '命令名称')] + string $command, + #[Schema(type: 'array', description: '命令参数 (可选)')] + array $params = [], + #[Schema(type: 'array', description: '命令选项 (可选)')] + array $options = [] + ): array { try { // MCP 专用命令列表 @@ -3374,7 +3579,15 @@ class {$modelClass} extends Base * @param array $options 选项 * @return array */ - public function handleWebmanCommand(string $command, array $params = [], array $options = []): array + #[McpTool(name: 'webman-command', description: '执行webman框架命令')] + public function handleWebmanCommand( + #[Schema(type: 'string', description: '命令名称')] + string $command, + #[Schema(type: 'array', description: '命令参数 (可选)')] + array $params = [], + #[Schema(type: 'array', description: '命令选项 (可选)')] + array $options = [] + ): array { try { // webman 框架命令列表 diff --git a/app/model/PaymentOrder.php b/app/model/PaymentOrder.php new file mode 100644 index 0000000..e93db1c --- /dev/null +++ b/app/model/PaymentOrder.php @@ -0,0 +1,102 @@ +find(); + } + + /** + * 更新订单状态 + * @param string $orderNo + * @param string $status + * @param array $data + * @return bool + */ + public static function updateStatus(string $orderNo, string $status, array $data = []): bool + { + $updateData = array_merge([ + 'status' => $status, + 'updated_at' => time(), + ], $data); + + return self::where('order_no', $orderNo)->update($updateData); + } + + /** + * 获取状态文本 + * @param string $status + * @return string + */ + public static function getStatusText(string $status): string + { + $list = self::getStatusList(); + return $list[$status] ?? $status; + } + + /** + * 获取类型文本 + * @param string $type + * @return string + */ + public static function getTypeText(string $type): string + { + $list = self::getTypeList(); + return $list[$type] ?? $type; + } + + /** + * 获取支付类型文本 + * @param string $payType + * @return string + */ + public static function getPayTypeText(string $payType): string + { + $list = self::getPayTypeList(); + return $list[$payType] ?? $payType; + } +} diff --git a/app/model/PaymentRefund.php b/app/model/PaymentRefund.php new file mode 100644 index 0000000..e84194d --- /dev/null +++ b/app/model/PaymentRefund.php @@ -0,0 +1,72 @@ + '退款成功', + 'fail' => '退款失败', + ]; + } + + /** + * 获取支付类型列表 + * @return array + */ + public static function getPayTypeList(): array + { + return \app\enum\Payment\Method::toArray(); + } + + /** + * 根据订单号查询 + * @param string $orderNo + * @return array + */ + public static function findByOrderNo(string $orderNo): array + { + return self::where('order_no', $orderNo)->select()->toArray(); + } + + /** + * 根据退款单号查询 + * @param string $refundNo + * @return array|null + */ + public static function findByRefundNo(string $refundNo): ?array + { + return self::where('refund_no', $refundNo)->find(); + } + + /** + * 获取状态文本 + * @param string $status + * @return string + */ + public static function getStatusText(string $status): string + { + $list = self::getStatusList(); + return $list[$status] ?? $status; + } + + /** + * 获取支付类型文本 + * @param string $payType + * @return string + */ + public static function getPayTypeText(string $payType): string + { + $list = self::getPayTypeList(); + return $list[$payType] ?? $payType; + } +} diff --git a/check_conversation.php b/check_conversation.php deleted file mode 100644 index d01e208..0000000 --- a/check_conversation.php +++ /dev/null @@ -1,52 +0,0 @@ -selectDatabase($mongoConfig['database']); - -$conversationId = 'sg_2639473367'; -$ownerUserId = '83484627'; - -echo "=== 检查 conversation 表记录 ===\n"; -$convCollection = $db->selectCollection('conversation'); -$conv = $convCollection->findOne([ - 'conversation_id' => $conversationId, - 'owner_user_id' => $ownerUserId -]); - -if ($conv) { - echo "找到记录:\n"; - print_r($conv); - echo "\n"; - if (isset($conv['max_seq'])) { - echo "max_seq: " . $conv['max_seq'] . "\n"; - } else { - echo "max_seq 不存在\n"; - } - if (isset($conv['min_seq'])) { - echo "min_seq: " . $conv['min_seq'] . "\n"; - } else { - echo "min_seq 不存在\n"; - } -} else { - echo "未找到记录\n"; - echo "\n查找同一 conversation_id 的其他记录:\n"; - $allConvs = $convCollection->find(['conversation_id' => $conversationId])->toArray(); - foreach ($allConvs as $c) { - echo "owner_user_id: " . ($c['owner_user_id'] ?? 'null') . "\n"; - echo " max_seq: " . ($c['max_seq'] ?? 'null') . "\n"; - echo " min_seq: " . ($c['min_seq'] ?? 'null') . "\n"; - } -} - -echo "\n=== 检查 seq 表 ===\n"; -$seqCollection = $db->selectCollection('seq'); -$seq = $seqCollection->findOne(['conversation_id' => $conversationId]); -if ($seq) { - echo "conversation_id: " . $seq['conversation_id'] . "\n"; - echo "max_seq: " . $seq['max_seq'] . "\n"; - echo "min_seq: " . $seq['min_seq'] . "\n"; -} diff --git a/composer.json b/composer.json index bf6b0bf..27b174d 100755 --- a/composer.json +++ b/composer.json @@ -48,7 +48,8 @@ "league/flysystem-aws-s3-v3": "^3.0", "webman/think-cache": "^2.1", "symfony/translation": "^7.4", - "tinywan/validate": "^1.0" + "tinywan/validate": "^1.0", + "yansongda/pay": "^3.7" }, "suggest": { "ext-event": "For better performance. " diff --git a/config/payment.php b/config/payment.php new file mode 100644 index 0000000..d1cef75 --- /dev/null +++ b/config/payment.php @@ -0,0 +1,81 @@ + true, // 注意,这个必须是true + 'alipay' => [ + 'default' => [ + // 必填-支付宝分配的 app_id + 'app_id' => '2021006117606688', + // 必填-应用私钥 字符串或路径 + 'app_secret_cert' => 'tzNKV16DeoakZbzyN8Wy2g==', + // 必填-应用公钥证书 路径 + 'app_public_cert_path' => base_path().'/payment/appCertPublicKey_2021006117606688.crt', + // 必填-支付宝公钥证书 路径 + 'alipay_public_cert_path' => base_path().'/payment/alipayCertPublicKey_RSA2.crt', + // 必填-支付宝根证书 路径 + 'alipay_root_cert_path' => base_path().'/payment/alipayRootCert.crt', + // 选填-同步回调地址 + 'return_url' => 'https://test.shunlkj.cn/api/payment/alipay-return', + // 选填-异步回调地址 + 'notify_url' => 'https://test.shunlkj.cn/api/payment/alipay-notify', + // 选填-服务商模式下的服务商 id,当 mode 为 Pay::MODE_SERVICE 时使用该参数 + 'service_provider_id' => '', + // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SANDBOX, MODE_SERVICE + 'mode' => \Yansongda\Pay\Pay::MODE_SANDBOX, + ] + ], + 'wechat' => [ + 'default' => [ + // 必填-商户号,服务商模式下为服务商商户号 + 'mch_id' => '', + // 必填-商户秘钥 + 'mch_secret_key' => '', + // 必填-商户私钥 字符串或路径 + 'mch_secret_cert' => '', + // 必填-商户公钥证书路径 + 'mch_public_cert_path' => '', + // 必填 + 'notify_url' => 'https://test.shunlkj.cn/wechat/notify', + // 选填-公众号 的 app_id + 'mp_app_id' => '2016082000291234', + // 选填-小程序 的 app_id + 'mini_app_id' => '', + // 选填-app 的 app_id + 'app_id' => '', + // 选填-合单 app_id + 'combine_app_id' => '', + // 选填-合单商户号 + 'combine_mch_id' => '', + // 选填-服务商模式下,子公众号 的 app_id + 'sub_mp_app_id' => '', + // 选填-服务商模式下,子 app 的 app_id + 'sub_app_id' => '', + // 选填-服务商模式下,子小程序 的 app_id + 'sub_mini_app_id' => '', + // 选填-服务商模式下,子商户id + 'sub_mch_id' => '', + // 选填-微信公钥证书路径, optional,强烈建议 php-fpm 模式下配置此参数 + 'wechat_public_cert_path' => [ + '45F59D4DABF31918AFCEC556D5D2C6E376675D57' => __DIR__.'/Cert/wechatPublicKey.crt', + ], + // 选填-默认为正常模式。可选为: MODE_NORMAL, MODE_SERVICE + 'mode' => \Yansongda\Pay\Pay::MODE_SANDBOX, + ] + ], + 'logger' => [ + 'enable' => false, + 'file' => runtime_path().'/logs/alipay.log', + 'level' => 'debug', // 建议生产环境等级调整为 info,开发环境为 debug + 'type' => 'single', // optional, 可选 daily. + 'max_file' => 30, // optional, 当 type 为 daily 时有效,默认 30 天 + ], + 'http' => [ // optional + 'timeout' => 5.0, + 'connect_timeout' => 5.0, + // 更多配置项请参考 [Guzzle](https://guzzle-cn.readthedocs.io/zh_CN/latest/request-options.html) + ] +]; \ No newline at end of file diff --git a/database/full_schema.sql b/database/full_schema.sql new file mode 100644 index 0000000..905be43 --- /dev/null +++ b/database/full_schema.sql @@ -0,0 +1,495 @@ +-- 数据库结构生成时间: 2026-04-09 14:03:43 + +CREATE TABLE `wa_address` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `title` varchar(100) DEFAULT NULL, + `network` varchar(10) DEFAULT 'BEP-20', + `address` varchar(80) NOT NULL, + `status` tinyint(1) DEFAULT '1', + `created_at` int NOT NULL DEFAULT '0', + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `network` (`network`), + KEY `status` (`status`) +) ENGINE=InnoDB AUTO_INCREMENT=770 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `wa_admin` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `username` varchar(32) NOT NULL COMMENT '用户名', + `nickname` varchar(40) NOT NULL COMMENT '昵称', + `password` varchar(255) NOT NULL COMMENT '密码', + `avatar` varchar(255) DEFAULT '/app/admin/avatar.png' COMMENT '头像', + `email` varchar(100) DEFAULT NULL COMMENT '邮箱', + `mobile` varchar(16) DEFAULT NULL COMMENT '手机', + `totp_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `login_at` int DEFAULT NULL, + `status` tinyint DEFAULT NULL COMMENT '禁用', + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员表'; + +CREATE TABLE `wa_admin_access` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `role_id` int NOT NULL COMMENT '角色id', + `admin_id` int NOT NULL COMMENT '管理员id', + PRIMARY KEY (`id`), + UNIQUE KEY `role_admin_id` (`role_id`,`admin_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员角色表'; + +CREATE TABLE `wa_admin_role` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(80) NOT NULL COMMENT '角色组', + `rules` text COMMENT '权限', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `pid` int unsigned DEFAULT NULL COMMENT '父级', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员角色'; + +CREATE TABLE `wa_admin_rule` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `extend` varchar(255) DEFAULT NULL, + `title` varchar(255) NOT NULL COMMENT '标题', + `icon` varchar(255) DEFAULT NULL COMMENT '图标', + `key` varchar(255) NOT NULL COMMENT '标识', + `pid` int unsigned DEFAULT '0' COMMENT '上级菜单', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '更新时间', + `href` varchar(255) DEFAULT NULL COMMENT 'url', + `type` int NOT NULL DEFAULT '1' COMMENT '类型', + `weight` int DEFAULT '0' COMMENT '排序', + `status` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=293 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限规则'; + +CREATE TABLE `wa_album` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `groupID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '群ID', + `userID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题', + `image` int NOT NULL COMMENT '封面ID', + `total` int NOT NULL COMMENT '相片总数', + `status` tinyint(1) NOT NULL COMMENT '状态', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + `weigh` int NOT NULL COMMENT '排序权重', + PRIMARY KEY (`id`), + KEY `groupID` (`groupID`,`userID`) +) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_archives` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(10) NOT NULL DEFAULT 'article', + `user_id` int unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `category_id` int DEFAULT NULL COMMENT '分类ID', + `title` varchar(255) DEFAULT '' COMMENT '文章标题', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '标识', + `flag` varchar(100) DEFAULT '' COMMENT '标志', + `style` varchar(100) DEFAULT '' COMMENT '样式', + `image` varchar(255) DEFAULT '' COMMENT '缩略图', + `images` varchar(1500) DEFAULT '' COMMENT '组图', + `tags` varchar(255) DEFAULT '' COMMENT 'TAG', + `intro` text, + `weigh` int NOT NULL DEFAULT '0' COMMENT '权重', + `views` int unsigned NOT NULL DEFAULT '0' COMMENT '浏览次数', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `publishtime` int DEFAULT NULL COMMENT '发布时间', + `deleted_at` int DEFAULT NULL COMMENT '删除时间', + `memo` varchar(100) DEFAULT '' COMMENT '备注', + `status` enum('normal','hidden','rejected','pulloff') NOT NULL DEFAULT 'normal' COMMENT '状态', + PRIMARY KEY (`id`), + KEY `weigh` (`weigh`,`publishtime`) +) ENGINE=InnoDB AUTO_INCREMENT=618 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='内容表'; + +CREATE TABLE `wa_archives_read` ( + `user_id` int NOT NULL, + `source_id` int DEFAULT NULL, + `value` tinyint(1) DEFAULT '1', + KEY `user_id` (`user_id`) USING BTREE, + KEY `source_id` (`source_id`) USING BTREE, + KEY `value` (`value`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_card` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` int DEFAULT NULL, + `title` varchar(64) NOT NULL, + `total` int NOT NULL DEFAULT '0', + `used` int DEFAULT '0', + `expires` int NOT NULL, + `days` int NOT NULL DEFAULT '0', + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int NOT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `wa_category` ( + `id` int NOT NULL AUTO_INCREMENT, + `title` varchar(64) NOT NULL, + `type` varchar(10) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `wa_cdkey` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` int DEFAULT NULL, + `category_id` int DEFAULT NULL, + `account` varchar(64) NOT NULL, + `passworrd` varchar(64) DEFAULT NULL, + `days` int DEFAULT '0', + `expires` int NOT NULL, + `is_used` tinyint(1) NOT NULL DEFAULT '0', + `record_id` int DEFAULT NULL, + `use_time` int DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int NOT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `account` (`account`) +) ENGINE=InnoDB AUTO_INCREMENT=11314 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `wa_collection` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL DEFAULT '0', + `content_type` enum('text','image','file','video','link','audio') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'text' COMMENT '内容类型', + `content` json NOT NULL COMMENT '收藏内容本体', + `tags` varchar(255) DEFAULT NULL COMMENT '用户自定义标签', + `is_pinned` tinyint(1) DEFAULT '0' COMMENT '是否置顶', + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `wa_config` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) DEFAULT '' COMMENT '变量名', + `group` varchar(30) DEFAULT '' COMMENT '分组', + `title` varchar(100) DEFAULT '' COMMENT '变量标题', + `tip` varchar(100) DEFAULT '' COMMENT '变量描述', + `type` varchar(30) DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file', + `visible` varchar(255) DEFAULT '' COMMENT '可见条件', + `is_show` tinyint NOT NULL DEFAULT '1', + `value` text COMMENT '变量值', + `content` text COMMENT '变量字典数据', + `rule` varchar(100) DEFAULT '' COMMENT '验证规则', + `extend` varchar(255) DEFAULT '' COMMENT '扩展属性', + `setting` varchar(255) DEFAULT '' COMMENT '配置', + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统配置'; + +CREATE TABLE `wa_content` ( + `id` int NOT NULL, + `content` longtext NOT NULL, + `content1` longtext, + `content2` longtext, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='内容'; + +CREATE TABLE `wa_files` ( + `id` int NOT NULL AUTO_INCREMENT, + `adapter` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `category` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `user_id` int DEFAULT NULL COMMENT '用户ID', + `admin_id` int DEFAULT NULL, + `origin_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '本地文件名', + `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '保存路径', + `file_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `size` int DEFAULT NULL COMMENT '大小', + `mime_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'mime类型', + `extension` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '扩展名', + `file_height` int DEFAULT NULL COMMENT '图片高度', + `file_width` int DEFAULT NULL COMMENT 'tup宽度', + `sha1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'sha1确定文件唯一性', + `use_count` int DEFAULT NULL COMMENT '关联次数', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `deleted_at` int DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2272 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_friend_circle` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` int NOT NULL COMMENT '用户ID', + `releaseType` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '内容', + `files` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '图片列表(JSON)', + `address` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `like_count` int NOT NULL DEFAULT '0' COMMENT '点赞数', + `comment_count` int NOT NULL DEFAULT '0' COMMENT '评论数', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:隐藏 1:正常)', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `status` (`status`), + KEY `created_at` (`created_at`) +) ENGINE=InnoDB AUTO_INCREMENT=375 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈动态表'; + +CREATE TABLE `wa_friend_circle_comment` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `circle_id` int NOT NULL COMMENT '朋友圈动态ID', + `user_id` int NOT NULL COMMENT '用户ID', + `reply_user_id` int NOT NULL DEFAULT '0' COMMENT '回复的用户ID(0表示直接评论)', + `body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '评论内容', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:隐藏 1:正常)', + PRIMARY KEY (`id`), + KEY `circle_id` (`circle_id`), + KEY `user_id` (`user_id`), + KEY `reply_user_id` (`reply_user_id`), + KEY `status` (`status`), + KEY `created_at` (`created_at`) +) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈评论表'; + +CREATE TABLE `wa_friend_circle_like` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `circle_id` int NOT NULL COMMENT '朋友圈动态ID', + `user_id` int NOT NULL COMMENT '用户ID', + `created_at` int NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `circle_user` (`circle_id`,`user_id`), + KEY `circle_id` (`circle_id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=156 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈点赞表'; + +CREATE TABLE `wa_friend_circle_setting` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` int NOT NULL COMMENT '用户ID', + `bg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '背景', + `allow_days` int NOT NULL COMMENT '允许查看最近几天的朋友圈', + `created_at` int NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=79 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈设置'; + +CREATE TABLE `wa_gallery` ( + `id` int NOT NULL AUTO_INCREMENT, + `album_id` int DEFAULT NULL COMMENT '相册ID', + `group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int NOT NULL, + `updated_at` int NOT NULL, + PRIMARY KEY (`id`), + KEY `album_id` (`album_id`), + KEY `group_id` (`group_id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=379 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_payment_order` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `order_no` varchar(50) NOT NULL COMMENT '订单号', + `trade_no` varchar(100) DEFAULT NULL COMMENT '支付交易号', + `pay_type` varchar(20) NOT NULL COMMENT '支付类型: alipay, wechat', + `type` varchar(20) NOT NULL DEFAULT 'goods' COMMENT '订单类型: recharge(充值), goods(商品), service(服务), other(其他)', + `amount` decimal(10,2) NOT NULL COMMENT '支付金额', + `subject` varchar(255) NOT NULL COMMENT '订单标题', + `status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '订单状态: PENDING, SUCCESS, FAIL, CLOSED, REFUNDED', + `extra` text COMMENT '额外信息', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `order_no` (`order_no`), + KEY `pay_type` (`pay_type`), + KEY `type` (`type`), + KEY `status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='支付订单表'; + +CREATE TABLE `wa_payment_refund` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `order_no` varchar(50) NOT NULL COMMENT '原订单号', + `refund_no` varchar(50) NOT NULL COMMENT '退款单号', + `pay_type` varchar(20) NOT NULL COMMENT '支付类型: alipay, wechat', + `amount` decimal(10,2) NOT NULL COMMENT '退款金额', + `reason` varchar(255) NOT NULL COMMENT '退款原因', + `status` varchar(20) NOT NULL DEFAULT 'SUCCESS' COMMENT '退款状态: SUCCESS, FAIL', + `extra` text COMMENT '额外信息', + `created_at` int NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `refund_no` (`refund_no`), + KEY `order_no` (`order_no`), + KEY `pay_type` (`pay_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退款表'; + +CREATE TABLE `wa_recharge` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `amount` int NOT NULL, + `created_at` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_team` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `parent_user_id` int DEFAULT NULL, + `members` text NOT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +CREATE TABLE `wa_thali` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID', + `role_id` int DEFAULT NULL COMMENT '关联角色', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名称', + `price` decimal(10,2) DEFAULT NULL COMMENT '价格', + `month_discount` decimal(6,4) DEFAULT '1.0000' COMMENT '月折扣', + `quarter_discount` decimal(6,4) DEFAULT '1.0000' COMMENT '季折扣', + `year_discount` decimal(6,4) NOT NULL DEFAULT '1.0000' COMMENT '年折扣', + `label` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标签', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `status` tinyint DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_user` ( + `id` mediumint NOT NULL AUTO_INCREMENT COMMENT '主键', + `userID` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `role_id` int DEFAULT NULL, + `parent_id` int DEFAULT NULL, + `group_id` tinyint NOT NULL DEFAULT '0' COMMENT '用戶分組', + `username` varchar(32) NOT NULL COMMENT '用户名', + `nickname` varchar(40) NOT NULL COMMENT '昵称', + `password` varchar(255) NOT NULL COMMENT '密码', + `trade_password` varchar(64) DEFAULT NULL, + `empty_password` varchar(255) DEFAULT NULL COMMENT '清空密码', + `sex` enum('0','1','2') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '性别', + `avatar` varchar(255) DEFAULT NULL COMMENT '头像', + `email` varchar(128) DEFAULT NULL COMMENT '邮箱', + `region` varchar(6) DEFAULT NULL COMMENT '国家', + `mobile` varchar(16) DEFAULT NULL COMMENT '手机', + `level` tinyint DEFAULT '0' COMMENT '等级', + `birthday` date DEFAULT NULL COMMENT '生日', + `bio` varchar(255) DEFAULT NULL, + `money` decimal(20,10) DEFAULT '0.0000000000' COMMENT '余额(元)', + `score` int DEFAULT '0' COMMENT '积分', + `currency1` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency2` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency3` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency4` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency5` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency6` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency7` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency8` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency9` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `maxsuccessions` tinyint NOT NULL DEFAULT '0', + `successions` tinyint NOT NULL DEFAULT '0', + `loginfailure` tinyint NOT NULL DEFAULT '0', + `prev_time` int DEFAULT NULL, + `last_time` int DEFAULT NULL COMMENT '登录时间', + `last_ip` varchar(50) DEFAULT NULL COMMENT '登录ip', + `join_time` int DEFAULT NULL COMMENT '注册时间', + `join_ip` varchar(50) DEFAULT NULL COMMENT '注册ip', + `token` varchar(50) DEFAULT NULL COMMENT 'token', + `invite_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `expire_at` int DEFAULT NULL COMMENT '过期时间', + `status` tinyint DEFAULT '0' COMMENT '禁用', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `deleted_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`), + UNIQUE KEY `userID` (`userID`), + KEY `join_time` (`join_time`), + KEY `mobile` (`mobile`), + KEY `email` (`email`), + KEY `region` (`region`) +) ENGINE=InnoDB AUTO_INCREMENT=105244 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'; + +CREATE TABLE `wa_user_extend` ( + `user_id` int NOT NULL, + `team_total` int DEFAULT '0' COMMENT '团队总人数', + `direct_total` int DEFAULT '0' COMMENT '直属团队人数', + `vip_total` int NOT NULL DEFAULT '0' COMMENT '旗下VIP总数', + `consume` int DEFAULT '0', + `sales` int DEFAULT '0', + `profile_banner` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '个人信息背景', + `moments_banner` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '朋友圈背景', + `moments_allow_view_days` tinyint NOT NULL DEFAULT '0' COMMENT '允许查看朋友圈的天数,0不限制,-1:不允许查看,>0,具体的天数', + PRIMARY KEY (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_user_role` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `pid` int unsigned DEFAULT NULL COMMENT '父级', + `name` varchar(80) NOT NULL COMMENT '角色组', + `rules` text COMMENT '权限', + `right` json DEFAULT NULL COMMENT '权益', + `max_send_msg_count` int DEFAULT '0' COMMENT '最大消息数量', + `max_friend_count` int DEFAULT '0' COMMENT '最大好友数量', + `max_group_join_count` int DEFAULT '0' COMMENT '最大加入的群组数量', + `max_gourp_create_count` int DEFAULT NULL COMMENT '最大创建的群组数量', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `status` tinyint DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员角色'; + +CREATE TABLE `wa_user_rule` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `title` varchar(255) NOT NULL COMMENT '标题', + `icon` varchar(255) DEFAULT NULL COMMENT '图标', + `key` varchar(255) NOT NULL COMMENT '标识', + `pid` int unsigned DEFAULT '0' COMMENT '上级菜单', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `href` varchar(255) DEFAULT NULL COMMENT 'url', + `type` int NOT NULL DEFAULT '1' COMMENT '类型', + `weight` int DEFAULT '0' COMMENT '排序', + `status` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限规则'; + +CREATE TABLE `wa_user_team` ( + `descendant_id` int NOT NULL, + `ancestor_id` int NOT NULL, + `depth` int DEFAULT NULL, + `status` tinyint DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_version` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` tinyint DEFAULT '2', + `platform` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `version_wgt` int DEFAULT NULL, + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `force` tinyint DEFAULT NULL, + `source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` tinyint DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + +CREATE TABLE `wa_withdrawl` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL DEFAULT '0', + `deduction_amount` decimal(20,10) DEFAULT '0.0000000000', + `recive_amount` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `fee` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `network` varchar(10) DEFAULT NULL, + `address` varchar(80) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `memo` varchar(200) DEFAULT NULL, + `transfer_at` int DEFAULT NULL, + `txid` varchar(100) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1120 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + diff --git a/database/index_optimization.sql b/database/index_optimization.sql new file mode 100644 index 0000000..0b6aeb3 --- /dev/null +++ b/database/index_optimization.sql @@ -0,0 +1,204 @@ +-- 数据库索引优化 +-- 生成时间: 2026-04-09 14:09:58 + +CREATE INDEX idx_created_at ON `wa_address` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_address` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_address` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_address` (`updated_at`); +CREATE INDEX idx_user_id_created_at ON `wa_address` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_address` (`status`, `created_at`); +CREATE INDEX idx_status ON `wa_admin` (`status`); +CREATE INDEX idx_created_at ON `wa_admin` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_admin` (`updated_at`); +CREATE INDEX idx_email ON `wa_admin` (`email`); +CREATE INDEX idx_created_at ON `wa_admin` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_admin` (`updated_at`); +CREATE INDEX idx_status ON `wa_admin` (`status`); +CREATE INDEX idx_email ON `wa_admin` (`email`); +CREATE INDEX idx_status_created_at ON `wa_admin` (`status`, `created_at`); +CREATE INDEX idx_role_id ON `wa_admin_access` (`role_id`); +CREATE INDEX idx_admin_id ON `wa_admin_access` (`admin_id`); +CREATE INDEX idx_admin_id ON `wa_admin_access` (`admin_id`); +CREATE INDEX idx_created_at ON `wa_admin_role` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_admin_role` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_admin_role` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_admin_role` (`updated_at`); +CREATE INDEX idx_status ON `wa_admin_rule` (`status`); +CREATE INDEX idx_type ON `wa_admin_rule` (`type`); +CREATE INDEX idx_created_at ON `wa_admin_rule` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_admin_rule` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_admin_rule` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_admin_rule` (`updated_at`); +CREATE INDEX idx_status ON `wa_admin_rule` (`status`); +CREATE INDEX idx_type ON `wa_admin_rule` (`type`); +CREATE INDEX idx_status_created_at ON `wa_admin_rule` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_admin_rule` (`type`, `created_at`); +CREATE INDEX idx_status ON `wa_album` (`status`); +CREATE INDEX idx_created_at ON `wa_album` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_album` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_album` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_album` (`updated_at`); +CREATE INDEX idx_status ON `wa_album` (`status`); +CREATE INDEX idx_status_created_at ON `wa_album` (`status`, `created_at`); +CREATE INDEX idx_user_id ON `wa_archives` (`user_id`); +CREATE INDEX idx_category_id ON `wa_archives` (`category_id`); +CREATE INDEX idx_user_id ON `wa_archives` (`user_id`); +CREATE INDEX idx_status ON `wa_archives` (`status`); +CREATE INDEX idx_type ON `wa_archives` (`type`); +CREATE INDEX idx_created_at ON `wa_archives` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_archives` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_archives` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_archives` (`updated_at`); +CREATE INDEX idx_status ON `wa_archives` (`status`); +CREATE INDEX idx_type ON `wa_archives` (`type`); +CREATE INDEX idx_user_id_created_at ON `wa_archives` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_archives` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_archives` (`type`, `created_at`); +CREATE INDEX idx_status ON `wa_card` (`status`); +CREATE INDEX idx_type ON `wa_card` (`type`); +CREATE INDEX idx_created_at ON `wa_card` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_card` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_card` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_card` (`updated_at`); +CREATE INDEX idx_status ON `wa_card` (`status`); +CREATE INDEX idx_type ON `wa_card` (`type`); +CREATE INDEX idx_status_created_at ON `wa_card` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_card` (`type`, `created_at`); +CREATE INDEX idx_status ON `wa_category` (`status`); +CREATE INDEX idx_type ON `wa_category` (`type`); +CREATE INDEX idx_created_at ON `wa_category` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_category` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_category` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_category` (`updated_at`); +CREATE INDEX idx_status ON `wa_category` (`status`); +CREATE INDEX idx_type ON `wa_category` (`type`); +CREATE INDEX idx_status_created_at ON `wa_category` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_category` (`type`, `created_at`); +CREATE INDEX idx_category_id ON `wa_cdkey` (`category_id`); +CREATE INDEX idx_record_id ON `wa_cdkey` (`record_id`); +CREATE INDEX idx_status ON `wa_cdkey` (`status`); +CREATE INDEX idx_type ON `wa_cdkey` (`type`); +CREATE INDEX idx_created_at ON `wa_cdkey` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_cdkey` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_cdkey` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_cdkey` (`updated_at`); +CREATE INDEX idx_status ON `wa_cdkey` (`status`); +CREATE INDEX idx_type ON `wa_cdkey` (`type`); +CREATE INDEX idx_status_created_at ON `wa_cdkey` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_cdkey` (`type`, `created_at`); +CREATE INDEX idx_status ON `wa_collection` (`status`); +CREATE INDEX idx_created_at ON `wa_collection` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_collection` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_collection` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_collection` (`updated_at`); +CREATE INDEX idx_status ON `wa_collection` (`status`); +CREATE INDEX idx_user_id_created_at ON `wa_collection` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_collection` (`status`, `created_at`); +CREATE INDEX idx_type ON `wa_config` (`type`); +CREATE INDEX idx_type ON `wa_config` (`type`); +CREATE INDEX idx_user_id ON `wa_files` (`user_id`); +CREATE INDEX idx_admin_id ON `wa_files` (`admin_id`); +CREATE INDEX idx_user_id ON `wa_files` (`user_id`); +CREATE INDEX idx_admin_id ON `wa_files` (`admin_id`); +CREATE INDEX idx_created_at ON `wa_files` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_files` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_files` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_files` (`updated_at`); +CREATE INDEX idx_user_id_created_at ON `wa_files` (`user_id`, `created_at`); +CREATE INDEX idx_admin_id_created_at ON `wa_files` (`admin_id`, `created_at`); +CREATE INDEX idx_updated_at ON `wa_friend_circle` (`updated_at`); +CREATE INDEX idx_updated_at ON `wa_friend_circle` (`updated_at`); +CREATE INDEX idx_user_id_created_at ON `wa_friend_circle` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_friend_circle` (`status`, `created_at`); +CREATE INDEX idx_updated_at ON `wa_friend_circle_comment` (`updated_at`); +CREATE INDEX idx_updated_at ON `wa_friend_circle_comment` (`updated_at`); +CREATE INDEX idx_user_id_created_at ON `wa_friend_circle_comment` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_friend_circle_comment` (`status`, `created_at`); +CREATE INDEX idx_created_at ON `wa_friend_circle_like` (`created_at`); +CREATE INDEX idx_created_at ON `wa_friend_circle_like` (`created_at`); +CREATE INDEX idx_user_id_created_at ON `wa_friend_circle_like` (`user_id`, `created_at`); +CREATE INDEX idx_user_id ON `wa_friend_circle_setting` (`user_id`); +CREATE INDEX idx_user_id ON `wa_friend_circle_setting` (`user_id`); +CREATE INDEX idx_created_at ON `wa_friend_circle_setting` (`created_at`); +CREATE INDEX idx_created_at ON `wa_friend_circle_setting` (`created_at`); +CREATE INDEX idx_user_id_created_at ON `wa_friend_circle_setting` (`user_id`, `created_at`); +CREATE INDEX idx_status ON `wa_gallery` (`status`); +CREATE INDEX idx_created_at ON `wa_gallery` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_gallery` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_gallery` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_gallery` (`updated_at`); +CREATE INDEX idx_status ON `wa_gallery` (`status`); +CREATE INDEX idx_user_id_created_at ON `wa_gallery` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_gallery` (`status`, `created_at`); +CREATE INDEX idx_created_at ON `wa_payment_order` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_payment_order` (`updated_at`); +CREATE INDEX idx_trade_no ON `wa_payment_order` (`trade_no`); +CREATE INDEX idx_created_at ON `wa_payment_order` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_payment_order` (`updated_at`); +CREATE INDEX idx_trade_no ON `wa_payment_order` (`trade_no`); +CREATE INDEX idx_status_created_at ON `wa_payment_order` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_payment_order` (`type`, `created_at`); +CREATE INDEX idx_status ON `wa_payment_refund` (`status`); +CREATE INDEX idx_created_at ON `wa_payment_refund` (`created_at`); +CREATE INDEX idx_created_at ON `wa_payment_refund` (`created_at`); +CREATE INDEX idx_status ON `wa_payment_refund` (`status`); +CREATE INDEX idx_status_created_at ON `wa_payment_refund` (`status`, `created_at`); +CREATE INDEX idx_user_id ON `wa_recharge` (`user_id`); +CREATE INDEX idx_user_id ON `wa_recharge` (`user_id`); +CREATE INDEX idx_created_at ON `wa_recharge` (`created_at`); +CREATE INDEX idx_created_at ON `wa_recharge` (`created_at`); +CREATE INDEX idx_user_id_created_at ON `wa_recharge` (`user_id`, `created_at`); +CREATE INDEX idx_parent_user_id ON `wa_team` (`parent_user_id`); +CREATE INDEX idx_role_id ON `wa_thali` (`role_id`); +CREATE INDEX idx_status ON `wa_thali` (`status`); +CREATE INDEX idx_created_at ON `wa_thali` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_thali` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_thali` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_thali` (`updated_at`); +CREATE INDEX idx_status ON `wa_thali` (`status`); +CREATE INDEX idx_status_created_at ON `wa_thali` (`status`, `created_at`); +CREATE INDEX idx_role_id ON `wa_user` (`role_id`); +CREATE INDEX idx_parent_id ON `wa_user` (`parent_id`); +CREATE INDEX idx_group_id ON `wa_user` (`group_id`); +CREATE INDEX idx_status ON `wa_user` (`status`); +CREATE INDEX idx_created_at ON `wa_user` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_user` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_user` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_user` (`updated_at`); +CREATE INDEX idx_status ON `wa_user` (`status`); +CREATE INDEX idx_status_created_at ON `wa_user` (`status`, `created_at`); +CREATE INDEX idx_user_id ON `wa_user_extend` (`user_id`); +CREATE INDEX idx_user_id ON `wa_user_extend` (`user_id`); +CREATE INDEX idx_status ON `wa_user_role` (`status`); +CREATE INDEX idx_created_at ON `wa_user_role` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_user_role` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_user_role` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_user_role` (`updated_at`); +CREATE INDEX idx_status ON `wa_user_role` (`status`); +CREATE INDEX idx_status_created_at ON `wa_user_role` (`status`, `created_at`); +CREATE INDEX idx_status ON `wa_user_rule` (`status`); +CREATE INDEX idx_type ON `wa_user_rule` (`type`); +CREATE INDEX idx_created_at ON `wa_user_rule` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_user_rule` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_user_rule` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_user_rule` (`updated_at`); +CREATE INDEX idx_status ON `wa_user_rule` (`status`); +CREATE INDEX idx_type ON `wa_user_rule` (`type`); +CREATE INDEX idx_status_created_at ON `wa_user_rule` (`status`, `created_at`); +CREATE INDEX idx_type_created_at ON `wa_user_rule` (`type`, `created_at`); +CREATE INDEX idx_descendant_id ON `wa_user_team` (`descendant_id`); +CREATE INDEX idx_ancestor_id ON `wa_user_team` (`ancestor_id`); +CREATE INDEX idx_status ON `wa_user_team` (`status`); +CREATE INDEX idx_status ON `wa_user_team` (`status`); +CREATE INDEX idx_status ON `wa_version` (`status`); +CREATE INDEX idx_type ON `wa_version` (`type`); +CREATE INDEX idx_status ON `wa_version` (`status`); +CREATE INDEX idx_type ON `wa_version` (`type`); +CREATE INDEX idx_status ON `wa_withdrawl` (`status`); +CREATE INDEX idx_created_at ON `wa_withdrawl` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_withdrawl` (`updated_at`); +CREATE INDEX idx_created_at ON `wa_withdrawl` (`created_at`); +CREATE INDEX idx_updated_at ON `wa_withdrawl` (`updated_at`); +CREATE INDEX idx_status ON `wa_withdrawl` (`status`); +CREATE INDEX idx_user_id_created_at ON `wa_withdrawl` (`user_id`, `created_at`); +CREATE INDEX idx_status_created_at ON `wa_withdrawl` (`status`, `created_at`); diff --git a/database/wa_address.sql b/database/wa_address.sql new file mode 100644 index 0000000..1e2261c --- /dev/null +++ b/database/wa_address.sql @@ -0,0 +1,14 @@ +CREATE TABLE `wa_address` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `title` varchar(100) DEFAULT NULL, + `network` varchar(10) DEFAULT 'BEP-20', + `address` varchar(80) NOT NULL, + `status` tinyint(1) DEFAULT '1', + `created_at` int NOT NULL DEFAULT '0', + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `network` (`network`), + KEY `status` (`status`) +) ENGINE=InnoDB AUTO_INCREMENT=770 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/wa_admin.sql b/database/wa_admin.sql new file mode 100644 index 0000000..c00e8c9 --- /dev/null +++ b/database/wa_admin.sql @@ -0,0 +1,16 @@ +CREATE TABLE `wa_admin` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', + `username` varchar(32) NOT NULL COMMENT '用户名', + `nickname` varchar(40) NOT NULL COMMENT '昵称', + `password` varchar(255) NOT NULL COMMENT '密码', + `avatar` varchar(255) DEFAULT '/app/admin/avatar.png' COMMENT '头像', + `email` varchar(100) DEFAULT NULL COMMENT '邮箱', + `mobile` varchar(16) DEFAULT NULL COMMENT '手机', + `totp_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `login_at` int DEFAULT NULL, + `status` tinyint DEFAULT NULL COMMENT '禁用', + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员表'; diff --git a/database/wa_admin_access.sql b/database/wa_admin_access.sql new file mode 100644 index 0000000..fefc431 --- /dev/null +++ b/database/wa_admin_access.sql @@ -0,0 +1,7 @@ +CREATE TABLE `wa_admin_access` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `role_id` int NOT NULL COMMENT '角色id', + `admin_id` int NOT NULL COMMENT '管理员id', + PRIMARY KEY (`id`), + UNIQUE KEY `role_admin_id` (`role_id`,`admin_id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员角色表'; diff --git a/database/wa_admin_role.sql b/database/wa_admin_role.sql new file mode 100644 index 0000000..047a287 --- /dev/null +++ b/database/wa_admin_role.sql @@ -0,0 +1,9 @@ +CREATE TABLE `wa_admin_role` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `name` varchar(80) NOT NULL COMMENT '角色组', + `rules` text COMMENT '权限', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `pid` int unsigned DEFAULT NULL COMMENT '父级', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员角色'; diff --git a/database/wa_admin_rule.sql b/database/wa_admin_rule.sql new file mode 100644 index 0000000..c44a652 --- /dev/null +++ b/database/wa_admin_rule.sql @@ -0,0 +1,15 @@ +CREATE TABLE `wa_admin_rule` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `extend` varchar(255) DEFAULT NULL, + `title` varchar(255) NOT NULL COMMENT '标题', + `icon` varchar(255) DEFAULT NULL COMMENT '图标', + `key` varchar(255) NOT NULL COMMENT '标识', + `pid` int unsigned DEFAULT '0' COMMENT '上级菜单', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '更新时间', + `href` varchar(255) DEFAULT NULL COMMENT 'url', + `type` int NOT NULL DEFAULT '1' COMMENT '类型', + `weight` int DEFAULT '0' COMMENT '排序', + `status` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=293 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限规则'; diff --git a/database/wa_album.sql b/database/wa_album.sql new file mode 100644 index 0000000..33bbd0f --- /dev/null +++ b/database/wa_album.sql @@ -0,0 +1,14 @@ +CREATE TABLE `wa_album` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', + `groupID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '群ID', + `userID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户ID', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标题', + `image` int NOT NULL COMMENT '封面ID', + `total` int NOT NULL COMMENT '相片总数', + `status` tinyint(1) NOT NULL COMMENT '状态', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + `weigh` int NOT NULL COMMENT '排序权重', + PRIMARY KEY (`id`), + KEY `groupID` (`groupID`,`userID`) +) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_archives.sql b/database/wa_archives.sql new file mode 100644 index 0000000..5c96e94 --- /dev/null +++ b/database/wa_archives.sql @@ -0,0 +1,24 @@ +CREATE TABLE `wa_archives` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(10) NOT NULL DEFAULT 'article', + `user_id` int unsigned NOT NULL DEFAULT '0' COMMENT '会员ID', + `category_id` int DEFAULT NULL COMMENT '分类ID', + `title` varchar(255) DEFAULT '' COMMENT '文章标题', + `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '标识', + `flag` varchar(100) DEFAULT '' COMMENT '标志', + `style` varchar(100) DEFAULT '' COMMENT '样式', + `image` varchar(255) DEFAULT '' COMMENT '缩略图', + `images` varchar(1500) DEFAULT '' COMMENT '组图', + `tags` varchar(255) DEFAULT '' COMMENT 'TAG', + `intro` text, + `weigh` int NOT NULL DEFAULT '0' COMMENT '权重', + `views` int unsigned NOT NULL DEFAULT '0' COMMENT '浏览次数', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `publishtime` int DEFAULT NULL COMMENT '发布时间', + `deleted_at` int DEFAULT NULL COMMENT '删除时间', + `memo` varchar(100) DEFAULT '' COMMENT '备注', + `status` enum('normal','hidden','rejected','pulloff') NOT NULL DEFAULT 'normal' COMMENT '状态', + PRIMARY KEY (`id`), + KEY `weigh` (`weigh`,`publishtime`) +) ENGINE=InnoDB AUTO_INCREMENT=618 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='内容表'; diff --git a/database/wa_archives_read.sql b/database/wa_archives_read.sql new file mode 100644 index 0000000..219ebca --- /dev/null +++ b/database/wa_archives_read.sql @@ -0,0 +1,8 @@ +CREATE TABLE `wa_archives_read` ( + `user_id` int NOT NULL, + `source_id` int DEFAULT NULL, + `value` tinyint(1) DEFAULT '1', + KEY `user_id` (`user_id`) USING BTREE, + KEY `source_id` (`source_id`) USING BTREE, + KEY `value` (`value`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_card.sql b/database/wa_card.sql new file mode 100644 index 0000000..2b4733f --- /dev/null +++ b/database/wa_card.sql @@ -0,0 +1,13 @@ +CREATE TABLE `wa_card` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` int DEFAULT NULL, + `title` varchar(64) NOT NULL, + `total` int NOT NULL DEFAULT '0', + `used` int DEFAULT '0', + `expires` int NOT NULL, + `days` int NOT NULL DEFAULT '0', + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int NOT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/wa_category.sql b/database/wa_category.sql new file mode 100644 index 0000000..f7d0796 --- /dev/null +++ b/database/wa_category.sql @@ -0,0 +1,9 @@ +CREATE TABLE `wa_category` ( + `id` int NOT NULL AUTO_INCREMENT, + `title` varchar(64) NOT NULL, + `type` varchar(10) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/wa_cdkey.sql b/database/wa_cdkey.sql new file mode 100644 index 0000000..e0c1536 --- /dev/null +++ b/database/wa_cdkey.sql @@ -0,0 +1,17 @@ +CREATE TABLE `wa_cdkey` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` int DEFAULT NULL, + `category_id` int DEFAULT NULL, + `account` varchar(64) NOT NULL, + `passworrd` varchar(64) DEFAULT NULL, + `days` int DEFAULT '0', + `expires` int NOT NULL, + `is_used` tinyint(1) NOT NULL DEFAULT '0', + `record_id` int DEFAULT NULL, + `use_time` int DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int NOT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `account` (`account`) +) ENGINE=InnoDB AUTO_INCREMENT=11314 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/wa_collection.sql b/database/wa_collection.sql new file mode 100644 index 0000000..8784587 --- /dev/null +++ b/database/wa_collection.sql @@ -0,0 +1,13 @@ +CREATE TABLE `wa_collection` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL DEFAULT '0', + `content_type` enum('text','image','file','video','link','audio') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'text' COMMENT '内容类型', + `content` json NOT NULL COMMENT '收藏内容本体', + `tags` varchar(255) DEFAULT NULL COMMENT '用户自定义标签', + `is_pinned` tinyint(1) DEFAULT '0' COMMENT '是否置顶', + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/wa_config.sql b/database/wa_config.sql new file mode 100644 index 0000000..0142c56 --- /dev/null +++ b/database/wa_config.sql @@ -0,0 +1,17 @@ +CREATE TABLE `wa_config` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) DEFAULT '' COMMENT '变量名', + `group` varchar(30) DEFAULT '' COMMENT '分组', + `title` varchar(100) DEFAULT '' COMMENT '变量标题', + `tip` varchar(100) DEFAULT '' COMMENT '变量描述', + `type` varchar(30) DEFAULT '' COMMENT '类型:string,text,int,bool,array,datetime,date,file', + `visible` varchar(255) DEFAULT '' COMMENT '可见条件', + `is_show` tinyint NOT NULL DEFAULT '1', + `value` text COMMENT '变量值', + `content` text COMMENT '变量字典数据', + `rule` varchar(100) DEFAULT '' COMMENT '验证规则', + `extend` varchar(255) DEFAULT '' COMMENT '扩展属性', + `setting` varchar(255) DEFAULT '' COMMENT '配置', + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统配置'; diff --git a/database/wa_content.sql b/database/wa_content.sql new file mode 100644 index 0000000..3c0ff76 --- /dev/null +++ b/database/wa_content.sql @@ -0,0 +1,7 @@ +CREATE TABLE `wa_content` ( + `id` int NOT NULL, + `content` longtext NOT NULL, + `content1` longtext, + `content2` longtext, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='内容'; diff --git a/database/wa_files.sql b/database/wa_files.sql new file mode 100644 index 0000000..4081135 --- /dev/null +++ b/database/wa_files.sql @@ -0,0 +1,21 @@ +CREATE TABLE `wa_files` ( + `id` int NOT NULL AUTO_INCREMENT, + `adapter` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `category` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `user_id` int DEFAULT NULL COMMENT '用户ID', + `admin_id` int DEFAULT NULL, + `origin_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '本地文件名', + `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '保存路径', + `file_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `size` int DEFAULT NULL COMMENT '大小', + `mime_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'mime类型', + `extension` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '扩展名', + `file_height` int DEFAULT NULL COMMENT '图片高度', + `file_width` int DEFAULT NULL COMMENT 'tup宽度', + `sha1` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'sha1确定文件唯一性', + `use_count` int DEFAULT NULL COMMENT '关联次数', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `deleted_at` int DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2272 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_friend_circle.sql b/database/wa_friend_circle.sql new file mode 100644 index 0000000..168497f --- /dev/null +++ b/database/wa_friend_circle.sql @@ -0,0 +1,17 @@ +CREATE TABLE `wa_friend_circle` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` int NOT NULL COMMENT '用户ID', + `releaseType` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '内容', + `files` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '图片列表(JSON)', + `address` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, + `like_count` int NOT NULL DEFAULT '0' COMMENT '点赞数', + `comment_count` int NOT NULL DEFAULT '0' COMMENT '评论数', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:隐藏 1:正常)', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `status` (`status`), + KEY `created_at` (`created_at`) +) ENGINE=InnoDB AUTO_INCREMENT=375 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈动态表'; diff --git a/database/wa_friend_circle_comment.sql b/database/wa_friend_circle_comment.sql new file mode 100644 index 0000000..63d8476 --- /dev/null +++ b/database/wa_friend_circle_comment.sql @@ -0,0 +1,16 @@ +CREATE TABLE `wa_friend_circle_comment` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `circle_id` int NOT NULL COMMENT '朋友圈动态ID', + `user_id` int NOT NULL COMMENT '用户ID', + `reply_user_id` int NOT NULL DEFAULT '0' COMMENT '回复的用户ID(0表示直接评论)', + `body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '评论内容', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态(0:隐藏 1:正常)', + PRIMARY KEY (`id`), + KEY `circle_id` (`circle_id`), + KEY `user_id` (`user_id`), + KEY `reply_user_id` (`reply_user_id`), + KEY `status` (`status`), + KEY `created_at` (`created_at`) +) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈评论表'; diff --git a/database/wa_friend_circle_like.sql b/database/wa_friend_circle_like.sql new file mode 100644 index 0000000..ce4c9c4 --- /dev/null +++ b/database/wa_friend_circle_like.sql @@ -0,0 +1,10 @@ +CREATE TABLE `wa_friend_circle_like` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `circle_id` int NOT NULL COMMENT '朋友圈动态ID', + `user_id` int NOT NULL COMMENT '用户ID', + `created_at` int NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `circle_user` (`circle_id`,`user_id`), + KEY `circle_id` (`circle_id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=156 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈点赞表'; diff --git a/database/wa_friend_circle_setting.sql b/database/wa_friend_circle_setting.sql new file mode 100644 index 0000000..601b1f2 --- /dev/null +++ b/database/wa_friend_circle_setting.sql @@ -0,0 +1,8 @@ +CREATE TABLE `wa_friend_circle_setting` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` int NOT NULL COMMENT '用户ID', + `bg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '背景', + `allow_days` int NOT NULL COMMENT '允许查看最近几天的朋友圈', + `created_at` int NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=79 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='朋友圈设置'; diff --git a/database/wa_gallery.sql b/database/wa_gallery.sql new file mode 100644 index 0000000..8aa4b17 --- /dev/null +++ b/database/wa_gallery.sql @@ -0,0 +1,15 @@ +CREATE TABLE `wa_gallery` ( + `id` int NOT NULL AUTO_INCREMENT, + `album_id` int DEFAULT NULL COMMENT '相册ID', + `group_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT '1', + `created_at` int NOT NULL, + `updated_at` int NOT NULL, + PRIMARY KEY (`id`), + KEY `album_id` (`album_id`), + KEY `group_id` (`group_id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=379 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_payment_order.sql b/database/wa_payment_order.sql new file mode 100644 index 0000000..32a0d1a --- /dev/null +++ b/database/wa_payment_order.sql @@ -0,0 +1,18 @@ +CREATE TABLE `wa_payment_order` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `order_no` varchar(50) NOT NULL COMMENT '订单号', + `trade_no` varchar(100) DEFAULT NULL COMMENT '支付交易号', + `pay_type` varchar(20) NOT NULL COMMENT '支付类型: alipay, wechat', + `type` varchar(20) NOT NULL DEFAULT 'goods' COMMENT '订单类型: recharge(充值), goods(商品), service(服务), other(其他)', + `amount` decimal(10,2) NOT NULL COMMENT '支付金额', + `subject` varchar(255) NOT NULL COMMENT '订单标题', + `status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '订单状态: PENDING, SUCCESS, FAIL, CLOSED, REFUNDED', + `extra` text COMMENT '额外信息', + `created_at` int NOT NULL COMMENT '创建时间', + `updated_at` int NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `order_no` (`order_no`), + KEY `pay_type` (`pay_type`), + KEY `type` (`type`), + KEY `status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='支付订单表'; diff --git a/database/wa_payment_refund.sql b/database/wa_payment_refund.sql new file mode 100644 index 0000000..da468af --- /dev/null +++ b/database/wa_payment_refund.sql @@ -0,0 +1,15 @@ +CREATE TABLE `wa_payment_refund` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `order_no` varchar(50) NOT NULL COMMENT '原订单号', + `refund_no` varchar(50) NOT NULL COMMENT '退款单号', + `pay_type` varchar(20) NOT NULL COMMENT '支付类型: alipay, wechat', + `amount` decimal(10,2) NOT NULL COMMENT '退款金额', + `reason` varchar(255) NOT NULL COMMENT '退款原因', + `status` varchar(20) NOT NULL DEFAULT 'SUCCESS' COMMENT '退款状态: SUCCESS, FAIL', + `extra` text COMMENT '额外信息', + `created_at` int NOT NULL COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `refund_no` (`refund_no`), + KEY `order_no` (`order_no`), + KEY `pay_type` (`pay_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退款表'; diff --git a/database/wa_recharge.sql b/database/wa_recharge.sql new file mode 100644 index 0000000..c06c098 --- /dev/null +++ b/database/wa_recharge.sql @@ -0,0 +1,7 @@ +CREATE TABLE `wa_recharge` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL, + `amount` int NOT NULL, + `created_at` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_team.sql b/database/wa_team.sql new file mode 100644 index 0000000..34e7e66 --- /dev/null +++ b/database/wa_team.sql @@ -0,0 +1,8 @@ +CREATE TABLE `wa_team` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int DEFAULT NULL, + `parent_user_id` int DEFAULT NULL, + `members` text NOT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/database/wa_thali.sql b/database/wa_thali.sql new file mode 100644 index 0000000..5914a29 --- /dev/null +++ b/database/wa_thali.sql @@ -0,0 +1,14 @@ +CREATE TABLE `wa_thali` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID', + `role_id` int DEFAULT NULL COMMENT '关联角色', + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名称', + `price` decimal(10,2) DEFAULT NULL COMMENT '价格', + `month_discount` decimal(6,4) DEFAULT '1.0000' COMMENT '月折扣', + `quarter_discount` decimal(6,4) DEFAULT '1.0000' COMMENT '季折扣', + `year_discount` decimal(6,4) NOT NULL DEFAULT '1.0000' COMMENT '年折扣', + `label` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '标签', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `status` tinyint DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_user.sql b/database/wa_user.sql new file mode 100644 index 0000000..88da65d --- /dev/null +++ b/database/wa_user.sql @@ -0,0 +1,53 @@ +CREATE TABLE `wa_user` ( + `id` mediumint NOT NULL AUTO_INCREMENT COMMENT '主键', + `userID` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `role_id` int DEFAULT NULL, + `parent_id` int DEFAULT NULL, + `group_id` tinyint NOT NULL DEFAULT '0' COMMENT '用戶分組', + `username` varchar(32) NOT NULL COMMENT '用户名', + `nickname` varchar(40) NOT NULL COMMENT '昵称', + `password` varchar(255) NOT NULL COMMENT '密码', + `trade_password` varchar(64) DEFAULT NULL, + `empty_password` varchar(255) DEFAULT NULL COMMENT '清空密码', + `sex` enum('0','1','2') CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '性别', + `avatar` varchar(255) DEFAULT NULL COMMENT '头像', + `email` varchar(128) DEFAULT NULL COMMENT '邮箱', + `region` varchar(6) DEFAULT NULL COMMENT '国家', + `mobile` varchar(16) DEFAULT NULL COMMENT '手机', + `level` tinyint DEFAULT '0' COMMENT '等级', + `birthday` date DEFAULT NULL COMMENT '生日', + `bio` varchar(255) DEFAULT NULL, + `money` decimal(20,10) DEFAULT '0.0000000000' COMMENT '余额(元)', + `score` int DEFAULT '0' COMMENT '积分', + `currency1` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency2` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency3` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency4` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency5` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency6` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency7` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency8` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `currency9` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `maxsuccessions` tinyint NOT NULL DEFAULT '0', + `successions` tinyint NOT NULL DEFAULT '0', + `loginfailure` tinyint NOT NULL DEFAULT '0', + `prev_time` int DEFAULT NULL, + `last_time` int DEFAULT NULL COMMENT '登录时间', + `last_ip` varchar(50) DEFAULT NULL COMMENT '登录ip', + `join_time` int DEFAULT NULL COMMENT '注册时间', + `join_ip` varchar(50) DEFAULT NULL COMMENT '注册ip', + `token` varchar(50) DEFAULT NULL COMMENT 'token', + `invite_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, + `expire_at` int DEFAULT NULL COMMENT '过期时间', + `status` tinyint DEFAULT '0' COMMENT '禁用', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `deleted_at` int DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`), + UNIQUE KEY `userID` (`userID`), + KEY `join_time` (`join_time`), + KEY `mobile` (`mobile`), + KEY `email` (`email`), + KEY `region` (`region`) +) ENGINE=InnoDB AUTO_INCREMENT=105244 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表'; diff --git a/database/wa_user_extend.sql b/database/wa_user_extend.sql new file mode 100644 index 0000000..b486777 --- /dev/null +++ b/database/wa_user_extend.sql @@ -0,0 +1,12 @@ +CREATE TABLE `wa_user_extend` ( + `user_id` int NOT NULL, + `team_total` int DEFAULT '0' COMMENT '团队总人数', + `direct_total` int DEFAULT '0' COMMENT '直属团队人数', + `vip_total` int NOT NULL DEFAULT '0' COMMENT '旗下VIP总数', + `consume` int DEFAULT '0', + `sales` int DEFAULT '0', + `profile_banner` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '个人信息背景', + `moments_banner` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '朋友圈背景', + `moments_allow_view_days` tinyint NOT NULL DEFAULT '0' COMMENT '允许查看朋友圈的天数,0不限制,-1:不允许查看,>0,具体的天数', + PRIMARY KEY (`user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_user_role.sql b/database/wa_user_role.sql new file mode 100644 index 0000000..2a28166 --- /dev/null +++ b/database/wa_user_role.sql @@ -0,0 +1,15 @@ +CREATE TABLE `wa_user_role` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `pid` int unsigned DEFAULT NULL COMMENT '父级', + `name` varchar(80) NOT NULL COMMENT '角色组', + `rules` text COMMENT '权限', + `right` json DEFAULT NULL COMMENT '权益', + `max_send_msg_count` int DEFAULT '0' COMMENT '最大消息数量', + `max_friend_count` int DEFAULT '0' COMMENT '最大好友数量', + `max_group_join_count` int DEFAULT '0' COMMENT '最大加入的群组数量', + `max_gourp_create_count` int DEFAULT NULL COMMENT '最大创建的群组数量', + `created_at` int DEFAULT NULL COMMENT '创建时间', + `updated_at` int DEFAULT NULL COMMENT '更新时间', + `status` tinyint DEFAULT NULL COMMENT '状态', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='管理员角色'; diff --git a/database/wa_user_rule.sql b/database/wa_user_rule.sql new file mode 100644 index 0000000..ac6046d --- /dev/null +++ b/database/wa_user_rule.sql @@ -0,0 +1,14 @@ +CREATE TABLE `wa_user_rule` ( + `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', + `title` varchar(255) NOT NULL COMMENT '标题', + `icon` varchar(255) DEFAULT NULL COMMENT '图标', + `key` varchar(255) NOT NULL COMMENT '标识', + `pid` int unsigned DEFAULT '0' COMMENT '上级菜单', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `href` varchar(255) DEFAULT NULL COMMENT 'url', + `type` int NOT NULL DEFAULT '1' COMMENT '类型', + `weight` int DEFAULT '0' COMMENT '排序', + `status` tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限规则'; diff --git a/database/wa_user_team.sql b/database/wa_user_team.sql new file mode 100644 index 0000000..24bfb0b --- /dev/null +++ b/database/wa_user_team.sql @@ -0,0 +1,6 @@ +CREATE TABLE `wa_user_team` ( + `descendant_id` int NOT NULL, + `ancestor_id` int NOT NULL, + `depth` int DEFAULT NULL, + `status` tinyint DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_version.sql b/database/wa_version.sql new file mode 100644 index 0000000..3a0e368 --- /dev/null +++ b/database/wa_version.sql @@ -0,0 +1,12 @@ +CREATE TABLE `wa_version` ( + `id` int NOT NULL AUTO_INCREMENT, + `type` tinyint DEFAULT '2', + `platform` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `version` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `version_wgt` int DEFAULT NULL, + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, + `force` tinyint DEFAULT NULL, + `source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, + `status` tinyint DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/database/wa_withdrawl.sql b/database/wa_withdrawl.sql new file mode 100644 index 0000000..b063bc3 --- /dev/null +++ b/database/wa_withdrawl.sql @@ -0,0 +1,17 @@ +CREATE TABLE `wa_withdrawl` ( + `id` int NOT NULL AUTO_INCREMENT, + `user_id` int NOT NULL DEFAULT '0', + `deduction_amount` decimal(20,10) DEFAULT '0.0000000000', + `recive_amount` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `fee` decimal(20,10) NOT NULL DEFAULT '0.0000000000', + `created_at` int DEFAULT NULL, + `updated_at` int DEFAULT NULL, + `network` varchar(10) DEFAULT NULL, + `address` varchar(80) DEFAULT NULL, + `status` tinyint(1) NOT NULL DEFAULT '0', + `memo` varchar(200) DEFAULT NULL, + `transfer_at` int DEFAULT NULL, + `txid` varchar(100) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1120 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; diff --git a/payment/CSR文件.csr b/payment/CSR文件.csr new file mode 100644 index 0000000..58b0a22 --- /dev/null +++ b/payment/CSR文件.csr @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICpTCCAY0CAQAwYDEJMAcGA1UEBhMAMQkwBwYDVQQIEwAxCTAHBgNVBAcTADEn +MCUGA1UECgwe5q2m5rGJ556s6IGK56eR5oqA5pyJ6ZmQ5YWs5Y+4MQkwBwYDVQQL +EwAxCTAHBgNVBAMTADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIxK +/oKEMjkTy+V2NiYOWHuEQqXnwkRn/gapEaLYafABeYKKxnHDmOgJ5FxJKafsWu06 +59cTkxahylnLls+4h8bWYyWKcXvxry6fkd6HOnsVfZUSki6BM7JwiZqRv9FWxKlo +FIL+AoJk+3xIWv2im2kX60/ml/Xk6QuwR6FhZ1SdTJoOuNhmfvyy7hIEnfhn6ONg +DNmIX9eaI4dSw7CUa5veKzKyej8nYpCQUtL1nSbPuDB8kZBchz0A7cREmq8sYLsX +Zu6DNI8Sp7AvZrNh+v0hE3Aky7HlDPph28XypenEXIUO7ugYlJGOaN55UeNLjajo ++ANfNSM5NKWz7V0Hmd8CAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4IBAQByjR0sW/hG +bQLskqzCU5t19GM15OJHBFmMw0cwdjgHBt5lLcK9g1V7dH6G+FNoL01vPu93xKVb +19vsPfZbO6wBaRDDgapiwxqY3T8gRrdrF/WuQGNlYyc+TjrSOLqflYgG59emDHa0 +Xtzax7zYF2Jdrm20q88GmcdniMOL21gt1eA1pJqNbds0ubdMCiPnQujssZZEz7s8 +hJcvLcuRLiEmEpUtEzGsQT4PZcn1XC+3crX5xSSstIh6EMBoCg9zbcrHEVuB55dT +1levUqJxA0LyvrXykc9Bc4R4dRHTuRY8q6kBGbZ9lk1YTlqxC8VSVS+VyLGXeVF+ +sPlrtL1mA3XN +-----END CERTIFICATE REQUEST----- diff --git a/payment/alipayCertPublicKey_RSA2.crt b/payment/alipayCertPublicKey_RSA2.crt new file mode 100644 index 0000000..db07694 --- /dev/null +++ b/payment/alipayCertPublicKey_RSA2.crt @@ -0,0 +1,43 @@ +-----BEGIN CERTIFICATE----- +MIIDrDCCApSgAwIBAgIQICYECaP9tRQa/eSkTdu5nzANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs +YXNzIDIgUjEwHhcNMjYwNDA5MDMxNDE0WhcNMzEwNDA4MDMxNDE0WjCBjDELMAkGA1UEBhMCQ04x +JzAlBgNVBAoMHuatpuaxieeerOiBiuenkeaKgOaciemZkOWFrOWPuDEPMA0GA1UECwwGQWxpcGF5 +MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWPuC0yMDg4 +MTgwNTY2NTE1MjEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk+xgjwYWwD8tkJzS +bGH/hydQ5D7q3vsfa2h5wZKfbq9IPDvFKYcWLQ07/4h/jV8H+s3esfvFiNNz4NISeawRbE6iXnmd +QFJSbLaGsIynv5A5ZLrE4IeWBqtcmV3TQgf/W+9mwEDzp3T0I7yomekeL+f7r5QaEie8JDEZ0xJp +ba6z/cu/JtILsdoGjikUFz7duNfzYwW541Rmo219CJrryiw1ZxItC3my4dvqMvIINBrF1yZ8EmAz +Lw1mrsF9wMJhusaf/tVmlNta/xxJvvCDWYR6o/2pTDFeKcTOSEkr6JHln5o/xWK2CGtt9H+yCUH1 +KDu8cqK8dhkoj+2Pf8puHwIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcNAQELBQAD +ggEBAI63S9yfw+VJ8iJzDC4s1tjbq9PoU1/Ryf8Jd+YBY7uT83Iol/VtURxdV8vW3y1SlZtvtDUG +X3ng/wSyrs2WnL6bDYM6sbz/SVrVZiYzfGJDLWTnujFhP1Uoa4OnycqlTfUsSJrT0FCt39lZpo5p +l9ImJF6mX4UU4jv/7G/DOtAlAmKmtCRynLE8/UZgAd0cefaLAVycXXzjGaXI4JNpfwBIK0IwSUXz +K5QLisG+m3cGYpgjw4Ale1EZ5BNRrvESJHHfDxIEzlVeeR16s689EUlvPH/40+S9IYFftpQy3snw +AMalJ/8dgpNKdzI+K/BRdVtm2+5HTNYWkEjpnPG6RXg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIE4jCCAsqgAwIBAgIIYsSr5bKAMl8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UEBhMCQ04xFjAU +BgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEw +LwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMy +MjE0MzQxNVoXDTM3MTEyNjE0MzQxNVowgYIxCzAJBgNVBAYTAkNOMRYwFAYDVQQKDA1BbnQgRmlu +YW5jaWFsMSAwHgYDVQQLDBdDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTE5MDcGA1UEAwwwQW50IEZp +bmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBDbGFzcyAyIFIxMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAsLMfYaoRoPRbmDcAfXPCmKf43pWRN5yTXa/KJWO0l+mrgQvs89bA +NEvbDUxlkGwycwtwi5DgBuBgVhLliXu+R9CYgr2dXs8D8Hx/gsggDcyGPLmVrDOnL+dyeauheARZ +fA3du60fwEwwbGcVIpIxPa/4n3IS/ElxQa6DNgqxh8J9Xwh7qMGl0JK9+bALuxf7B541Gr4p0WEN +G8fhgjBV4w4ut9eQLOoa1eddOUSZcy46Z7allwowwgt7b5VFfx/P1iKJ3LzBMgkCK7GZ2kiLrL7R +iqV+h482J7hkJD+ardoc6LnrHO/hIZymDxok+VH9fVeUdQa29IZKrIDVj65THQIDAQABo2MwYTAf +BgNVHSMEGDAWgBRfdLQEwE8HWurlsdsio4dBspzhATAdBgNVHQ4EFgQUSqHkYINtUSAtDPnS8Xoy +oP9p7qEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIB +AIQ8TzFy4bVIVb8+WhHKCkKNPcJe2EZuIcqvRoi727lZTJOfYy/JzLtckyZYfEI8J0lasZ29wkTt +a1IjSo+a6XdhudU4ONVBrL70U8Kzntplw/6TBNbLFpp7taRALjUgbCOk4EoBMbeCL0GiYYsTS0mw +7xdySzmGQku4GTyqutIGPQwKxSj9iSFw1FCZqr4VP4tyXzMUgc52SzagA6i7AyLedd3tbS6lnR5B +L+W9Kx9hwT8L7WANAxQzv/jGldeuSLN8bsTxlOYlsdjmIGu/C9OWblPYGpjQQIRyvs4Cc/mNhrh+ +14EQgwuemIIFDLOgcD+iISoN8CqegelNcJndFw1PDN6LkVoiHz9p7jzsge8RKay/QW6C03KNDpWZ +EUCgCUdfHfo8xKeR+LL1cfn24HKJmZt8L/aeRZwZ1jwePXFRVtiXELvgJuM/tJDIFj2KD337iV64 +fWcKQ/ydDVGqfDZAdcU4hQdsrPWENwPTQPfVPq2NNLMyIH9+WKx9Ed6/WzeZmIy5ZWpX1TtTolo6 +OJXQFeItMAjHxW/ZSZTok5IS3FuRhExturaInnzjYpx50a6kS34c5+c8hYq7sAtZ/CNLZmBnBCFD +aMQqT8xFZJ5uolUaSeXxg7JFY1QsYp5RKvj4SjFwCGKJ2+hPPe9UyyltxOidNtxjaknOCeBHytOr +-----END CERTIFICATE----- diff --git a/payment/alipayRootCert.crt b/payment/alipayRootCert.crt new file mode 100644 index 0000000..76417c5 --- /dev/null +++ b/payment/alipayRootCert.crt @@ -0,0 +1,88 @@ +-----BEGIN CERTIFICATE----- +MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG +EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw +MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO +UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE +MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT +V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti +W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ +MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b +53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI +pDoiVhsLwg== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj +YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0 +MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV +BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk +rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2 +xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp +dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6 +vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl +YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1 +Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H +DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98 +SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG +PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe +9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC +AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90 +tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy +nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf +tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq +JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3 +IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW +05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41 +T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI +kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop +PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N +1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y +jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02 +77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi +kT9qhqn+lw== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG +EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0 +WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE +CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp +YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU +WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt +rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ +4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2 +zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg +wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH +Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA== +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF +BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM +E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg +MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq +MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp +bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv +b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV +nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5 +4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg +wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw +WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN +z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g +KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA +uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF +MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp +emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3 +U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I +UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn +DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU +1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX +Yf4Zr0fJsGuv +-----END CERTIFICATE----- \ No newline at end of file diff --git a/payment/appCertPublicKey_2021006117606688.crt b/payment/appCertPublicKey_2021006117606688.crt new file mode 100644 index 0000000..781076c --- /dev/null +++ b/payment/appCertPublicKey_2021006117606688.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIEmzCCA4OgAwIBAgIQICYECU3bDxwuKLGefTr80jANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE +BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs +YXNzIDEgUjEwHhcNMjYwNDA5MDMxNDEzWhcNMzEwNDA4MDMxNDEzWjBiMQswCQYDVQQGEwJDTjEn +MCUGA1UECgwe5q2m5rGJ556s6IGK56eR5oqA5pyJ6ZmQ5YWs5Y+4MQ8wDQYDVQQLDAZBbGlwYXkx +GTAXBgNVBAMMEDIwODgxODA1NjY1MTUyMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCMSv6ChDI5E8vldjYmDlh7hEKl58JEZ/4GqRGi2GnwAXmCisZxw5joCeRcSSmn7FrtOufXE5MW +ocpZy5bPuIfG1mMlinF78a8un5Hehzp7FX2VEpIugTOycImakb/RVsSpaBSC/gKCZPt8SFr9optp +F+tP5pf15OkLsEehYWdUnUyaDrjYZn78su4SBJ34Z+jjYAzZiF/XmiOHUsOwlGub3isysno/J2KQ +kFLS9Z0mz7gwfJGQXIc9AO3ERJqvLGC7F2bugzSPEqewL2azYfr9IRNwJMux5Qz6YdvF8qXpxFyF +Du7oGJSRjmjeeVHjS42o6PgDXzUjOTSls+1dB5nfAgMBAAGjggEqMIIBJjAfBgNVHSMEGDAWgBRx +B+IEYRbk5fJl6zEPyeD0PJrVkTAdBgNVHQ4EFgQUhPteDOBc22TNznF/i0XAUH3F3fowQAYDVR0g +BDkwNzA1BgdggRwBbgEBMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9jYS5hbGlwYXkuY29tL2Nwcy5w +ZGYwDgYDVR0PAQH/BAQDAgbAMDAGA1UdHwQpMCcwJaAjoCGGH2h0dHA6Ly9jYS5hbGlwYXkuY29t +L2NybDExMi5jcmwwYAYIKwYBBQUHAQEEVDBSMCgGCCsGAQUFBzAChhxodHRwOi8vY2EuYWxpcGF5 +LmNvbS9jYTYuY2VyMCYGCCsGAQUFBzABhhpodHRwOi8vY2EuYWxpcGF5LmNvbTo4MzQwLzANBgkq +hkiG9w0BAQsFAAOCAQEATLlnzYUaypwqzxtHLpcO7B8ay1Rs3/eL35XhWTX5ZLxTnDDpTxSr5jLZ +yoDyIZx7k0WhY6IfUcCsQRgokp8GABaSXezzVyhOK5YZ5J50dty8Jeduvx6V02AFCXs1yQnfX+QY +utQ5uUHCKP3tWC4Qhi+8e+DAFN6OjnPSQbTSBSWpA4+ML4xwLzOtv2U/SoaO8S+JBK1GU69ukKht +qcsFMxgP6kT2tV/OR0patMoWv6ONRuiNexoZtH68uh6hE3lSGfEkUzEvKYeYIV4npe2Uzc+RwqVa +4wp6eFu1TPWRvC+UBuuEcipDl0dmA2P8NWox68+SwfC+CMavoYITFfj7qA== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/payment/应用公钥RSA2048.txt b/payment/应用公钥RSA2048.txt new file mode 100644 index 0000000..e1aca57 --- /dev/null +++ b/payment/应用公钥RSA2048.txt @@ -0,0 +1 @@ +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjEr+goQyORPL5XY2Jg5Ye4RCpefCRGf+BqkRothp8AF5gorGccOY6AnkXEkpp+xa7Trn1xOTFqHKWcuWz7iHxtZjJYpxe/GvLp+R3oc6exV9lRKSLoEzsnCJmpG/0VbEqWgUgv4CgmT7fEha/aKbaRfrT+aX9eTpC7BHoWFnVJ1Mmg642GZ+/LLuEgSd+Gfo42AM2Yhf15ojh1LDsJRrm94rMrJ6PydikJBS0vWdJs+4MHyRkFyHPQDtxESaryxguxdm7oM0jxKnsC9ms2H6/SETcCTLseUM+mHbxfKl6cRchQ7u6BiUkY5o3nlR40uNqOj4A181Izk0pbPtXQeZ3wIDAQAB \ No newline at end of file diff --git a/payment/应用私钥RSA2048-敏感数据,请妥善保管.txt b/payment/应用私钥RSA2048-敏感数据,请妥善保管.txt new file mode 100644 index 0000000..b5a6643 --- /dev/null +++ b/payment/应用私钥RSA2048-敏感数据,请妥善保管.txt @@ -0,0 +1 @@ +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCMSv6ChDI5E8vldjYmDlh7hEKl58JEZ/4GqRGi2GnwAXmCisZxw5joCeRcSSmn7FrtOufXE5MWocpZy5bPuIfG1mMlinF78a8un5Hehzp7FX2VEpIugTOycImakb/RVsSpaBSC/gKCZPt8SFr9optpF+tP5pf15OkLsEehYWdUnUyaDrjYZn78su4SBJ34Z+jjYAzZiF/XmiOHUsOwlGub3isysno/J2KQkFLS9Z0mz7gwfJGQXIc9AO3ERJqvLGC7F2bugzSPEqewL2azYfr9IRNwJMux5Qz6YdvF8qXpxFyFDu7oGJSRjmjeeVHjS42o6PgDXzUjOTSls+1dB5nfAgMBAAECggEAMVrwztnTEWF1ifRceGbzmgCdseAm6AntxEoWekPIgTcDmHlZ9wvo7zkLravPi9QK6QqgJXotdPjWR2DoXYt9uOYTW1GpOwF7r9tTZgYM/OBCnf86jkHF/jC5ODeuU+dCqJaFINBb8aO1FbQezrw2EHLpxeJJ86fklMgfaxbpU0q+DL+wVcKnQr2AQU/3uWvzdqXPPdIhf816qdAbpzlKkyS4GUjdJbw6atDkPBCsFI7aMCa60CWtVO4bxsAKgyZpr+SUH7DFLcw5KYCfMOnO7R7GRtTwStIegOfURzh5pAgbTavo2ONlbzhlk3oot7Bd2vjxykCnZd4rIQqGSXd3AQKBgQDEB6kh5Lxn1Pqr1LdaRZhqZaopYii0irz0wPazzn130ChCzwnw6x5nm++gTCfjBSyJF2E25RLJ06ajPl2dBxZ2ORiHP7iLA3xYeAN9Etnmpe/Kyu9zaGHpA/30d0wyp5ovDSXzNVWNQnbe9y+XMBwueOXZZNXl6aGgQvCLIIn3hwKBgQC3Njgke1v0oVwzVGT4RM04HG46egX2Ej/XpKnQYQ7CoAPzbRA3Fv7aMF8nR4ERTTgSgbwv9KdkSfVgJJgZKFeBcauv2KnreJADpxjOm0eXulllXPTG9/EuxAsySbZAfPLvOaalThLQYnr4zj3eY1E+BJbJT0Z+OyEStPRVwZQw6QKBgGeoYWBB0vrY3WDFj9sVWB5S+iL9++ZUQsztyMaV1zTnBpMlZe1WdvJ0u8a43pKmZsFrcoWTplbBK6nAzcG+QLttP7ItGMmxoywo8jdOI5laf61tmCcfuD5+VOjTuKBEjjx5KGst9bpHvhHzBeBdmVldprVJI+56l3PmUcpP/4S7AoGBAJpVcy/QNuM5NnkK48977cARuw1Jk1H5h4w5as9do1hyAUIDIsC1f1P/oXhuNJyFhvGYFhXzqzrBTpGB4hp3MFebkLwd8O2p7u8UyMHqboGy+fF4P76ISMRTYvcmvv9OAjthPi9MOG8QNnT3D4kR8vMiXH8SZ9iYKMEgbB79Zb9ZAoGBAJp9vJDL9Z16u505On/GywcfjjoE0BkeAmnfA9C4AkQ8XAAZhJgizxqYqo7jrp+6bdPIBP5sG+eUL8Zi2gGvEZBR337fYs+c2wQ5U1o8NwOPi6eOWZiH6113dfh09qLViwrcfEBqw7Nx8Aco/LZTe/U2rcHOHsphSTcCnw5wLChx \ No newline at end of file diff --git a/support/OpenImSdk/Api/Message.php b/support/OpenImSdk/Api/Message.php index 3a89ccc..2f31594 100755 --- a/support/OpenImSdk/Api/Message.php +++ b/support/OpenImSdk/Api/Message.php @@ -173,7 +173,7 @@ class Message * @param string $userID 用户ID * @return array */ - public function revokeMessage(string $conversationID, string $seq, string $userID): array + public function revokeMessage(string $conversationID, string $seq, string $userID): array|bool { // 获取管理员token $adminToken = Utils::getAdminToken(); @@ -185,6 +185,27 @@ class Message return Utils::send(Url::$revokeMessage, $data, '撤回消息失败', $adminToken); } + /** + * 删除用户所有消息 + * @desc 从服务端删除用户所有消息,不影响客户端已同步消息。 + * @param string $userID 用户ID + * @param bool $isSyncSelf 是否同步自身消息,默认true + * @param bool $isSyncOther 是否同步对方消息,默认true + * @return array + */ + public function userClearAllMsg(string $userID,bool $isSyncSelf=true, bool $isSyncOther=true): array + { + // 获取管理员token + $data = [ + 'deleteSyncOpt' => [ + 'isSyncSelf' => (bool)$isSyncSelf, + 'isSyncOther' => (bool)$isSyncOther, + ], + 'userID' => $userID + ]; + return Utils::send(Url::$userClearAllMsg, $data); + } + /** * 发送业务通知 * @param string $sendID 发送者ID diff --git a/support/OpenImSdk/Core/Url.php b/support/OpenImSdk/Core/Url.php index a0ce0b2..475d649 100755 --- a/support/OpenImSdk/Core/Url.php +++ b/support/OpenImSdk/Core/Url.php @@ -74,6 +74,7 @@ class Url static $delMsg = '/msg/del_msg'; static $manageSendMsg = '/msg/manage_send_msg'; static $revokeMessage = '/msg/revoke_msg'; + static $userClearAllMsg = '/msg/user_clear_all_msg'; static $sendBusinessNotification = '/msg/send_business_notification'; static $getAllConversations = '/msg/get_all_conversations'; static $getConversation = '/msg/get_conversation';