Compare commits
10 Commits
d0dc7930ad
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d0e34f36bd | |||
| 24273db8dd | |||
| 9393185f25 | |||
| a98d2b56ee | |||
| 4463f73efb | |||
| d75808951e | |||
| f0970446f9 | |||
| c092662ebe | |||
| 6c59e41b32 | |||
| b0e87b3fb3 |
@@ -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 管理项目,提供专业的技术支持和最佳实践建议。
|
||||
@@ -40,6 +40,12 @@ class AlbumController extends BaseController
|
||||
}else{
|
||||
$list = $query->paginate($limit);
|
||||
}
|
||||
$list->each(function($item){
|
||||
if($item->image){
|
||||
$item['image_detail'] = \support\think\Db::name('gallery')->where('id',$item->image)->find();
|
||||
}
|
||||
return $item;
|
||||
});
|
||||
return $this->success('ok',$list);
|
||||
}
|
||||
/**
|
||||
@@ -53,11 +59,16 @@ class AlbumController extends BaseController
|
||||
{
|
||||
$user_id = \support\Jwt\JwtToken::getCurrentId();
|
||||
$data = [
|
||||
'user_id' => $user_id,
|
||||
'groupID' => $request->post('groupID'),
|
||||
'userID' => $user_id,
|
||||
'groupID' => input('groupID'),
|
||||
'title' => input('title'),
|
||||
'image' => input('image'),
|
||||
];
|
||||
log_alert($data);
|
||||
|
||||
if(!$data['groupID']){
|
||||
return $this->error(__('Invalid parameters'));
|
||||
}
|
||||
$result = AlbumModel::create($data);
|
||||
return $this->success('ok',$result);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ class ArticleController extends BaseController
|
||||
{
|
||||
$id = (int)input('id');
|
||||
if (!$id) {
|
||||
return $this->error(__("Invalid parameter"));
|
||||
return $this->error(__("Invalid parameters"));
|
||||
}
|
||||
|
||||
$vo = ArchivesModel::where('id', $id)->find();
|
||||
|
||||
@@ -176,10 +176,12 @@ class CommonController extends BaseController{
|
||||
|
||||
//邀请码
|
||||
//$invite_code = 'TEAJXLEE';
|
||||
$region = Input('region','+86');
|
||||
$region = str_replace('+','',$region);
|
||||
$extends = [
|
||||
'role_id' => 1,
|
||||
'group_id' => 0,
|
||||
'region' => '86',
|
||||
'region' => $region,
|
||||
'nickname' => input('nickname'),
|
||||
'avatar' => '/static/avatar/'.rand(0,17).'.png',
|
||||
];
|
||||
@@ -331,7 +333,7 @@ class CommonController extends BaseController{
|
||||
}else{
|
||||
if ($mobile && Validate::regex($mobile, "^1\d{10}$")) {
|
||||
captcha_verify('mobile','reset_pwd',$mobile);
|
||||
$region = Input('region');
|
||||
$region = Input('region','+86');
|
||||
$region = str_replace('+','',$region);
|
||||
$user = UserModel::where('region',$region)->where('mobile',$mobile)->find();
|
||||
}else if ($email && Validate::is($email, "email")) {
|
||||
@@ -434,7 +436,7 @@ class CommonController extends BaseController{
|
||||
$user = \support\Jwt::getUser();
|
||||
$email = $user->email;
|
||||
} catch (\Exception $th) {
|
||||
return $this->error(__('Incoret param'));
|
||||
return $this->error(__('Invalid parameter'));
|
||||
}
|
||||
}
|
||||
$key = 'captcha_'.$event.'_'.$email;
|
||||
@@ -467,7 +469,7 @@ class CommonController extends BaseController{
|
||||
$user = \support\Jwt::getUser();
|
||||
$mobile = $user->mobile;
|
||||
} catch (\Exception $th) {
|
||||
return $this->error(__('Incoret param'));
|
||||
return $this->error(__('Invalid parameter'));
|
||||
}
|
||||
}
|
||||
if (!Validate::regex($mobile, "^1\d{10}$")) {
|
||||
|
||||
@@ -111,7 +111,7 @@ class GroupController extends BaseController
|
||||
//单文件上传
|
||||
$groupID = $request->post('groupID');
|
||||
if(!$groupID){
|
||||
return $this->fail(__('参数错误'));
|
||||
return $this->fail(__('Invalid parameter'));
|
||||
}
|
||||
$res = $this->_upload($request);
|
||||
if(is_string($res)){
|
||||
|
||||
@@ -48,7 +48,8 @@ class PassportController extends BaseController{
|
||||
{
|
||||
$user = \support\Jwt::getUser();
|
||||
$mobile = input('mobile');
|
||||
$region = input('region');
|
||||
$region = input('region','+86');
|
||||
$region = str_replace('+','',$region);
|
||||
|
||||
// 验证手机号格式
|
||||
if (!$mobile || !Validate::regex($mobile, "^1\d{10}$")) {
|
||||
|
||||
@@ -0,0 +1,731 @@
|
||||
<?php
|
||||
namespace app\api\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use Yansongda\Pay\Pay;
|
||||
use Yansongda\Pay\Log;
|
||||
use think\facade\Db;
|
||||
use support\Log as WebmanLog;
|
||||
use hg\apidoc\annotation as Apidoc;
|
||||
use app\enum\Payment\Method;
|
||||
use app\enum\Payment\Status;
|
||||
use app\enum\Payment\Type;
|
||||
|
||||
/**
|
||||
* 支付控制器
|
||||
* @Apidoc\Title("支付管理")
|
||||
* @Apidoc\Group("payment")
|
||||
*/
|
||||
class PaymentController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 支付宝支付下单
|
||||
* @Apidoc\Title("支付宝支付下单")
|
||||
* @Apidoc\Url("/api/payment/alipay/order")
|
||||
* @Apidoc\Method("POST")
|
||||
* @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\Return("success", "object", "成功响应", "pay_url|string|支付链接,order_no|string|订单号")
|
||||
* @Apidoc\Return("error", "object", "失败响应", "code|int|错误码,msg|string|错误信息")
|
||||
* @param Request $request
|
||||
* @return Response
|
||||
*/
|
||||
public function alipay_order(Request $request): Response
|
||||
{
|
||||
try {
|
||||
$params = $request->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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ class UserController extends BaseController{
|
||||
'user_id' => $user->id,
|
||||
];
|
||||
if(!$data['realname'] || !$data['idcard']){
|
||||
return $this->error(__('Incoret param'));
|
||||
return $this->error(__('Invalid parameter'));
|
||||
}
|
||||
if($user->realname_verify == 1){
|
||||
return $this->error(__('You have verified'));
|
||||
|
||||
@@ -122,8 +122,8 @@ class Auth implements MiddlewareInterface
|
||||
'config' => $config
|
||||
]);
|
||||
$IM = new \support\OpenImSdk\Client([
|
||||
'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
'host' => config('openim.server'), // OpenIM API地址
|
||||
'secret' => config('openim.secret'), // OpenIM密钥
|
||||
]);
|
||||
$request->IM = $IM;
|
||||
$response = $next($request);
|
||||
|
||||
@@ -0,0 +1,807 @@
|
||||
<?php
|
||||
namespace app\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class Backup extends Command
|
||||
{
|
||||
protected static $defaultName = 'backup';
|
||||
protected static $defaultDescription = '备份 MongoDB 和 MySQL 数据库';
|
||||
|
||||
// 数据源配置
|
||||
private $dataSources = [
|
||||
[
|
||||
'type' => 'mongodb',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 27017,
|
||||
'database' => 'openim_v3',
|
||||
'username' => 'openIM',
|
||||
'password' => 'n1e5a6s6m7',
|
||||
'useDocker' => true,
|
||||
'dockerContainerName' => 'mongo' // Docker 容器名称
|
||||
],
|
||||
[
|
||||
'type' => 'mysql',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 3306,
|
||||
'database' => 'imadmin',
|
||||
'username' => 'root',
|
||||
'password' => 'n1e5a6s6m7',
|
||||
'useDocker' => true,
|
||||
'dockerContainerName' => 'my_mysql' // Docker 容器名称
|
||||
],
|
||||
[
|
||||
'type' => 'redis',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 16379,
|
||||
'database' => 0,
|
||||
'username' => '',
|
||||
'password' => 'n1e5a6s6m7',
|
||||
'useDocker' => true,
|
||||
'dockerContainerName' => 'redis' // Docker 容器名称
|
||||
],
|
||||
[
|
||||
'name' => 'tettt_mongodb',
|
||||
'type' => 'mongodb',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 27017,
|
||||
'database' => 'tettt',
|
||||
'username' => 'commie',
|
||||
'password' => 'n1e5a6s6m7',
|
||||
'authSource' => 'admin',
|
||||
'useDocker' => true,
|
||||
'dockerContainerName' => 'mongo'
|
||||
],
|
||||
];
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->addOption('backup', 'b', InputOption::VALUE_NONE, '备份数据库');
|
||||
$this->addOption('restore', 'r', InputOption::VALUE_NONE, '还原数据库');
|
||||
$this->addOption('clear', 'c', InputOption::VALUE_NONE, '清空 Redis');
|
||||
$this->addOption('source', 's', InputOption::VALUE_OPTIONAL, '数据源名称 (mongodb, mysql, redis)');
|
||||
$this->addOption('output', 'o', InputOption::VALUE_OPTIONAL, '备份输出目录', '/backup');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$backup = $input->getOption('backup');
|
||||
$restore = $input->getOption('restore');
|
||||
$clear = $input->getOption('clear');
|
||||
$source = $input->getOption('source');
|
||||
$outputDir = $input->getOption('output');
|
||||
|
||||
// 确保备份目录存在
|
||||
$mongoDir = base_path($outputDir) . '/mongo';
|
||||
$mysqlDir = base_path($outputDir) . '/mysql';
|
||||
|
||||
if (!is_dir($mongoDir)) {
|
||||
mkdir($mongoDir, 0755, true);
|
||||
}
|
||||
|
||||
if (!is_dir($mysqlDir)) {
|
||||
mkdir($mysqlDir, 0755, true);
|
||||
}
|
||||
|
||||
// 显示备份目录
|
||||
$output->writeln("\n备份目录:");
|
||||
$output->writeln("- MongoDB: {$mongoDir}");
|
||||
$output->writeln("- MySQL: {$mysqlDir}");
|
||||
|
||||
// 显示环境配置
|
||||
$output->writeln("\n环境配置:");
|
||||
foreach ($this->dataSources as $_source) {
|
||||
$name = $_source['name'] ?? $_source['dockerContainerName'] ?? ucfirst($_source['type']);
|
||||
if($name == $source){
|
||||
$source = $_source;
|
||||
}
|
||||
$output->writeln("- {$name}: " . ($_source['useDocker'] ? "Docker 容器" : "本地环境"));
|
||||
}
|
||||
|
||||
// 处理命令行选项
|
||||
if ($backup) {
|
||||
if(is_array($source)){
|
||||
if ($source['type'] === 'mongodb') {
|
||||
$this->backupMongoDB($mongoDir, $output, $source);
|
||||
}
|
||||
if ($source['type'] === 'mysql') {
|
||||
$this->backupMySQL($mysqlDir, $output, $source);
|
||||
}
|
||||
} else {
|
||||
foreach ($this->dataSources as $_source) {
|
||||
if ($_source['type'] === 'mongodb') {
|
||||
$this->backupMongoDB($mongoDir, $output, $_source);
|
||||
} elseif ($_source['type'] === 'mysql') {
|
||||
$this->backupMySQL($mysqlDir, $output, $_source);
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($restore) {
|
||||
if(is_array($source)){
|
||||
if ($source['type'] === 'mongodb') {
|
||||
$this->restoreMongoDB($mongoDir, $output, $source);
|
||||
}
|
||||
if ($source['type'] === 'mysql') {
|
||||
$this->restoreMySQL($mysqlDir, $output, $source);
|
||||
}
|
||||
} else {
|
||||
$this->restoreMenu($output, $outputDir);
|
||||
}
|
||||
} elseif ($clear) {
|
||||
$this->clearRedis($output);
|
||||
} else {
|
||||
$this->mainMenu($output, $outputDir);
|
||||
}
|
||||
|
||||
$output->writeln("\n✅ 操作完成!");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主菜单
|
||||
*/
|
||||
private function mainMenu($output, $outputDir): void
|
||||
{
|
||||
while (true) {
|
||||
$output->writeln("\n================================");
|
||||
$output->writeln(" 备份工具");
|
||||
$output->writeln("================================");
|
||||
$output->writeln("1. 备份数据库");
|
||||
$output->writeln("2. 还原数据库");
|
||||
$output->writeln("3. 清空 Redis");
|
||||
$output->writeln("0. 退出");
|
||||
|
||||
$output->write("\n请选择操作 (0-3): ");
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$choice = fgets($handle);
|
||||
fclose($handle);
|
||||
$choice = trim($choice);
|
||||
|
||||
switch ($choice) {
|
||||
case '1':
|
||||
$this->backupMenu($output, $outputDir);
|
||||
break;
|
||||
case '2':
|
||||
$this->restoreMenu($output, $outputDir);
|
||||
break;
|
||||
case '3':
|
||||
$this->clearRedis($output);
|
||||
break;
|
||||
case '0':
|
||||
return;
|
||||
default:
|
||||
$output->writeln("\n无效选择,请重新输入");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份菜单
|
||||
*/
|
||||
private function backupMenu($output, $outputDir): void
|
||||
{
|
||||
$output->writeln("\n================================");
|
||||
$output->writeln(" 备份数据库");
|
||||
$output->writeln("================================");
|
||||
|
||||
foreach ($this->dataSources as $index => $source) {
|
||||
if ($source['type'] !== 'redis') {
|
||||
$name = $source['name'] ?? $source['dockerContainerName'] ?? ucfirst($source['type']);
|
||||
$output->writeln(($index + 1) . ". " . $name . " (" . ($source['useDocker'] ? "Docker 容器" : "本地环境") . ")");
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln("0. 返回上一级");
|
||||
|
||||
$output->write("\n请选择要备份的数据源 (0-" . count($this->dataSources) . "): ");
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$choice = fgets($handle);
|
||||
fclose($handle);
|
||||
$choice = trim($choice);
|
||||
|
||||
if ($choice === '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_numeric($choice) && $choice > 0 && $choice <= count($this->dataSources)) {
|
||||
$source = $this->dataSources[$choice - 1];
|
||||
|
||||
if ($source['type'] === 'mongodb') {
|
||||
$mongoDir = base_path($outputDir) . '/mongo';
|
||||
if (!is_dir($mongoDir)) {
|
||||
mkdir($mongoDir, 0755, true);
|
||||
}
|
||||
$this->backupMongoDB($mongoDir, $output, $source);
|
||||
} elseif ($source['type'] === 'mysql') {
|
||||
$mysqlDir = base_path($outputDir) . '/mysql';
|
||||
if (!is_dir($mysqlDir)) {
|
||||
mkdir($mysqlDir, 0755, true);
|
||||
}
|
||||
$this->backupMySQL($mysqlDir, $output, $source);
|
||||
}
|
||||
} else {
|
||||
$output->writeln("\n无效选择,请重新输入");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原菜单
|
||||
*/
|
||||
private function restoreMenu($output, $outputDir): void
|
||||
{
|
||||
$output->writeln("\n================================");
|
||||
$output->writeln(" 还原数据库");
|
||||
$output->writeln("================================");
|
||||
|
||||
foreach ($this->dataSources as $index => $source) {
|
||||
if ($source['type'] !== 'redis') {
|
||||
$name = $source['name'] ?? $source['dockerContainerName'] ?? ucfirst($source['type']);
|
||||
$output->writeln(($index + 1) . ". " . $name . " (" . ($source['useDocker'] ? "Docker 容器" : "本地环境") . ")");
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln("0. 返回上一级");
|
||||
|
||||
$output->write("\n请选择要还原的数据源 (0-" . count($this->dataSources) . "): ");
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$choice = fgets($handle);
|
||||
fclose($handle);
|
||||
$choice = trim($choice);
|
||||
|
||||
if ($choice === '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_numeric($choice) && $choice > 0 && $choice <= count($this->dataSources)) {
|
||||
$source = $this->dataSources[$choice - 1];
|
||||
|
||||
if ($source['type'] === 'mongodb') {
|
||||
$mongoDir = base_path($outputDir) . '/mongo';
|
||||
if (!is_dir($mongoDir)) {
|
||||
mkdir($mongoDir, 0755, true);
|
||||
}
|
||||
$this->restoreMongoDB($mongoDir, $output, $source);
|
||||
} elseif ($source['type'] === 'mysql') {
|
||||
$mysqlDir = base_path($outputDir) . '/mysql';
|
||||
if (!is_dir($mysqlDir)) {
|
||||
mkdir($mysqlDir, 0755, true);
|
||||
}
|
||||
$this->restoreMySQL($mysqlDir, $output, $source);
|
||||
}
|
||||
} else {
|
||||
$output->writeln("\n无效选择,请重新输入");
|
||||
}
|
||||
}
|
||||
|
||||
private function backupMongoDB($backupDir, $output, $dataSource = null): void
|
||||
{
|
||||
$name = $dataSource['name'] ?? $dataSource['dockerContainerName'] ?? ucfirst($dataSource['type']);
|
||||
$output->writeln("\n开始备份 {$name}...");
|
||||
|
||||
try {
|
||||
$mongoSource = $dataSource;
|
||||
if (!$mongoSource) {
|
||||
foreach ($this->dataSources as $source) {
|
||||
if ($source['type'] === 'mongodb') {
|
||||
$mongoSource = $source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mongoSource) {
|
||||
$output->writeln("❌ 未找到 MongoDB 数据源配置");
|
||||
return;
|
||||
}
|
||||
|
||||
$host = $mongoSource['host'];
|
||||
$port = $mongoSource['port'];
|
||||
$database = $mongoSource['database'];
|
||||
$useDocker = $mongoSource['useDocker'];
|
||||
$dockerContainerName = $mongoSource['dockerContainerName'];
|
||||
$username = $mongoSource['username'] ?? '';
|
||||
$password = $mongoSource['password'] ?? '';
|
||||
$authSource = $mongoSource['authSource'] ?? $database;
|
||||
$username = $mongoSource['username'] ?? '';
|
||||
$password = $mongoSource['password'] ?? '';
|
||||
$authSource = $mongoSource['authSource'] ?? $database;
|
||||
|
||||
$backupFileName = "{$database}_" . date("Y_m_d_H_i_s") . ".zip";
|
||||
$backupFilePath = "{$backupDir}/{$backupFileName}";
|
||||
|
||||
$tempDir = "/tmp/mongo_backup_" . uniqid();
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
|
||||
$cmd = $this->getMongoDumpCommand($host, $port, $database, $tempDir, $useDocker, $dockerContainerName, $username, $password, $authSource);
|
||||
$output->writeln("执行命令: {$cmd}");
|
||||
|
||||
exec($cmd, $outputLines, $returnCode);
|
||||
|
||||
if ($returnCode === 0) {
|
||||
// 保存当前工作目录
|
||||
$currentDir = getcwd();
|
||||
|
||||
// 切换到临时目录并压缩
|
||||
chdir($tempDir);
|
||||
$zipCmd = "zip -r {$backupFilePath} .";
|
||||
$output->writeln("创建压缩文件: {$backupFilePath}");
|
||||
exec($zipCmd, $zipOutput, $zipReturnCode);
|
||||
|
||||
// 切换回原来的工作目录
|
||||
chdir($currentDir);
|
||||
|
||||
if ($zipReturnCode === 0) {
|
||||
$output->writeln("✅ MongoDB 备份成功: {$backupFilePath}");
|
||||
} else {
|
||||
$output->writeln("❌ MongoDB 压缩失败");
|
||||
$output->writeln(implode("\n", $zipOutput));
|
||||
}
|
||||
|
||||
// 清理临时目录
|
||||
exec("rm -rf {$tempDir}");
|
||||
} else {
|
||||
$output->writeln("❌ MongoDB 备份失败");
|
||||
$output->writeln(implode("\n", $outputLines));
|
||||
// 清理临时目录
|
||||
exec("rm -rf {$tempDir}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("❌ MongoDB 备份失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function restoreMongoDB($backupDir, $output, $dataSource = null): void
|
||||
{
|
||||
$name = $dataSource['name'] ?? $dataSource['dockerContainerName'] ?? ucfirst($dataSource['type']);
|
||||
$output->writeln("\n开始还原 {$name}...");
|
||||
|
||||
try {
|
||||
$mongoSource = $dataSource;
|
||||
if (!$mongoSource) {
|
||||
foreach ($this->dataSources as $source) {
|
||||
if ($source['type'] === 'mongodb') {
|
||||
$mongoSource = $source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mongoSource) {
|
||||
$output->writeln("❌ 未找到 MongoDB 数据源配置");
|
||||
return;
|
||||
}
|
||||
|
||||
$host = $mongoSource['host'];
|
||||
$port = $mongoSource['port'];
|
||||
$database = $mongoSource['database'];
|
||||
$useDocker = $mongoSource['useDocker'];
|
||||
$dockerContainerName = $mongoSource['dockerContainerName'];
|
||||
$username = $mongoSource['username'] ?? '';
|
||||
$password = $mongoSource['password'] ?? '';
|
||||
$authSource = $mongoSource['authSource'] ?? $database;
|
||||
|
||||
$backupFiles = glob("{$backupDir}/*.zip");
|
||||
if (empty($backupFiles)) {
|
||||
$output->writeln("❌ 未找到备份文件");
|
||||
return;
|
||||
}
|
||||
|
||||
// 按修改时间排序
|
||||
usort($backupFiles, function ($a, $b) {
|
||||
return filemtime($b) - filemtime($a);
|
||||
});
|
||||
|
||||
// 显示备份文件列表
|
||||
$output->writeln("\n可用的备份文件:");
|
||||
foreach ($backupFiles as $index => $file) {
|
||||
$fileName = basename($file);
|
||||
$fileSize = filesize($file) / 1024 / 1024;
|
||||
$modTime = date("Y-m-d H:i:s", filemtime($file));
|
||||
$output->writeln(($index + 1) . ". {$fileName} (" . round($fileSize, 2) . " MB, {$modTime})");
|
||||
}
|
||||
|
||||
// 选择备份文件
|
||||
$output->write("\n请选择要还原的备份文件 (1-" . count($backupFiles) . "): ");
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$choice = fgets($handle);
|
||||
fclose($handle);
|
||||
$choice = trim($choice);
|
||||
|
||||
if (!is_numeric($choice) || $choice < 1 || $choice > count($backupFiles)) {
|
||||
$output->writeln("\n无效选择");
|
||||
return;
|
||||
}
|
||||
|
||||
$selectedFile = $backupFiles[$choice - 1];
|
||||
$output->writeln("\n选择的备份文件: " . basename($selectedFile));
|
||||
|
||||
// 生成临时还原目录
|
||||
$tempDir = "/tmp/mongo_restore_" . uniqid();
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
|
||||
// 解压备份文件
|
||||
$unzipCmd = "unzip {$selectedFile} -d {$tempDir}";
|
||||
$output->writeln("解压备份文件...");
|
||||
exec($unzipCmd, $unzipOutput, $unzipReturnCode);
|
||||
|
||||
if ($unzipReturnCode === 0) {
|
||||
$dbRestoreDir = $tempDir;
|
||||
$backupDbName = null;
|
||||
$subDirs = glob("{$tempDir}/*", GLOB_ONLYDIR);
|
||||
|
||||
$output->writeln("解压后的目录: " . implode(", ", array_map('basename', $subDirs)));
|
||||
|
||||
if (!empty($subDirs)) {
|
||||
foreach ($subDirs as $subDir) {
|
||||
$bsonFiles = glob("{$subDir}/*.bson");
|
||||
if (!empty($bsonFiles)) {
|
||||
$dbRestoreDir = $subDir;
|
||||
$backupDbName = basename($subDir);
|
||||
$output->writeln("找到备份目录: {$backupDbName}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bsonFiles = glob("{$dbRestoreDir}/*.bson");
|
||||
if (empty($bsonFiles)) {
|
||||
$output->writeln("❌ 备份文件中没有找到 BSON 数据文件");
|
||||
$output->writeln("目录内容: " . implode(", ", scandir($dbRestoreDir)));
|
||||
exec("rm -rf {$tempDir}");
|
||||
return;
|
||||
}
|
||||
|
||||
$output->writeln("找到 " . count($bsonFiles) . " 个 BSON 文件");
|
||||
$output->writeln("还原目录: {$dbRestoreDir}");
|
||||
$output->writeln("备份数据库: {$backupDbName} -> 目标数据库: {$database}");
|
||||
|
||||
$cmd = $this->getMongoRestoreCommand($host, $port, $database, $dbRestoreDir, $useDocker, $dockerContainerName, $username, $password, $authSource);
|
||||
$output->writeln("执行命令: {$cmd}");
|
||||
|
||||
exec($cmd, $outputLines, $returnCode);
|
||||
|
||||
if ($returnCode === 0) {
|
||||
$output->writeln("✅ MongoDB 还原成功");
|
||||
if (!empty($outputLines)) {
|
||||
$output->writeln(implode("\n", $outputLines));
|
||||
}
|
||||
} else {
|
||||
$output->writeln("❌ MongoDB 还原失败");
|
||||
$output->writeln(implode("\n", $outputLines));
|
||||
}
|
||||
|
||||
exec("rm -rf {$tempDir}");
|
||||
} else {
|
||||
$output->writeln("❌ 解压备份文件失败");
|
||||
$output->writeln(implode("\n", $unzipOutput));
|
||||
exec("rm -rf {$tempDir}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("❌ MongoDB 还原失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function backupMySQL($backupDir, $output, $dataSource = null): void
|
||||
{
|
||||
$name = $dataSource['name'] ?? $dataSource['dockerContainerName'] ?? ucfirst($dataSource['type']);
|
||||
$output->writeln("\n开始备份 {$name}...");
|
||||
|
||||
try {
|
||||
$mysqlSource = $dataSource;
|
||||
if (!$mysqlSource) {
|
||||
foreach ($this->dataSources as $source) {
|
||||
if ($source['type'] === 'mysql') {
|
||||
$mysqlSource = $source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mysqlSource) {
|
||||
$output->writeln("❌ 未找到 MySQL 数据源配置");
|
||||
return;
|
||||
}
|
||||
|
||||
$host = $mysqlSource['host'];
|
||||
$port = $mysqlSource['port'];
|
||||
$database = $mysqlSource['database'];
|
||||
$username = $mysqlSource['username'];
|
||||
$password = $mysqlSource['password'];
|
||||
$useDocker = $mysqlSource['useDocker'];
|
||||
$dockerContainerName = $mysqlSource['dockerContainerName'];
|
||||
|
||||
// 生成备份文件名
|
||||
$backupFileName = "{$database}_" . date("Y_m_d_H_i_s") . ".sql";
|
||||
$backupFilePath = "{$backupDir}/{$backupFileName}";
|
||||
|
||||
// 构建备份命令
|
||||
$cmd = $this->getMySqlDumpCommand($host, $port, $database, $username, $password, $backupFilePath, $useDocker, $dockerContainerName);
|
||||
$output->writeln("执行命令: {$cmd}");
|
||||
|
||||
// 执行备份命令
|
||||
exec($cmd, $outputLines, $returnCode);
|
||||
|
||||
if ($returnCode === 0) {
|
||||
$output->writeln("✅ MySQL 备份成功: {$backupFilePath}");
|
||||
} else {
|
||||
$output->writeln("❌ MySQL 备份失败");
|
||||
$output->writeln(implode("\n", $outputLines));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("❌ MySQL 备份失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function restoreMySQL($backupDir, $output, $dataSource = null): void
|
||||
{
|
||||
$name = $dataSource['name'] ?? $dataSource['dockerContainerName'] ?? ucfirst($dataSource['type']);
|
||||
$output->writeln("\n开始还原 {$name}...");
|
||||
|
||||
try {
|
||||
$mysqlSource = $dataSource;
|
||||
if (!$mysqlSource) {
|
||||
foreach ($this->dataSources as $source) {
|
||||
if ($source['type'] === 'mysql') {
|
||||
$mysqlSource = $source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mysqlSource) {
|
||||
$output->writeln("❌ 未找到 MySQL 数据源配置");
|
||||
return;
|
||||
}
|
||||
|
||||
$host = $mysqlSource['host'];
|
||||
$port = $mysqlSource['port'];
|
||||
$database = $mysqlSource['database'];
|
||||
$username = $mysqlSource['username'];
|
||||
$password = $mysqlSource['password'];
|
||||
$useDocker = $mysqlSource['useDocker'];
|
||||
$dockerContainerName = $mysqlSource['dockerContainerName'];
|
||||
|
||||
// 列出备份文件(支持 SQL 文件和 zip 文件)
|
||||
$backupFiles = array_merge(
|
||||
glob("{$backupDir}/*.sql"),
|
||||
glob("{$backupDir}/*.zip")
|
||||
);
|
||||
|
||||
if (empty($backupFiles)) {
|
||||
$output->writeln("❌ 未找到备份文件");
|
||||
return;
|
||||
}
|
||||
|
||||
// 按修改时间排序
|
||||
usort($backupFiles, function ($a, $b) {
|
||||
return filemtime($b) - filemtime($a);
|
||||
});
|
||||
|
||||
// 显示备份文件列表
|
||||
$output->writeln("\n可用的备份文件:");
|
||||
foreach ($backupFiles as $index => $file) {
|
||||
$fileName = basename($file);
|
||||
$fileSize = filesize($file) / 1024 / 1024;
|
||||
$modTime = date("Y-m-d H:i:s", filemtime($file));
|
||||
$output->writeln(($index + 1) . ". {$fileName} (" . round($fileSize, 2) . " MB, {$modTime})");
|
||||
}
|
||||
|
||||
// 选择备份文件
|
||||
$output->write("\n请选择要还原的备份文件 (1-" . count($backupFiles) . "): ");
|
||||
$handle = fopen("php://stdin", "r");
|
||||
$choice = fgets($handle);
|
||||
fclose($handle);
|
||||
$choice = trim($choice);
|
||||
|
||||
if (!is_numeric($choice) || $choice < 1 || $choice > count($backupFiles)) {
|
||||
$output->writeln("\n无效选择");
|
||||
return;
|
||||
}
|
||||
|
||||
$selectedFile = $backupFiles[$choice - 1];
|
||||
$output->writeln("\n选择的备份文件: " . basename($selectedFile));
|
||||
|
||||
$sqlFile = $selectedFile;
|
||||
|
||||
// 如果是 zip 文件,需要解压
|
||||
if (pathinfo($selectedFile, PATHINFO_EXTENSION) === 'zip') {
|
||||
// 生成临时还原目录
|
||||
$tempDir = "/tmp/mysql_restore_" . uniqid();
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0755, true);
|
||||
}
|
||||
|
||||
// 解压备份文件
|
||||
$unzipCmd = "unzip {$selectedFile} -d {$tempDir}";
|
||||
$output->writeln("解压备份文件...");
|
||||
exec($unzipCmd, $unzipOutput, $unzipReturnCode);
|
||||
|
||||
if ($unzipReturnCode !== 0) {
|
||||
$output->writeln("❌ 解压备份文件失败");
|
||||
$output->writeln(implode("\n", $unzipOutput));
|
||||
// 清理临时目录
|
||||
exec("rm -rf {$tempDir}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到解压后的 SQL 文件
|
||||
$sqlFiles = glob("{$tempDir}/*.sql");
|
||||
if (empty($sqlFiles)) {
|
||||
$output->writeln("❌ 未找到 SQL 文件");
|
||||
// 清理临时目录
|
||||
exec("rm -rf {$tempDir}");
|
||||
return;
|
||||
}
|
||||
|
||||
$sqlFile = $sqlFiles[0];
|
||||
}
|
||||
|
||||
// 构建还原命令
|
||||
$cmd = $this->getMySqlRestoreCommand($host, $port, $database, $username, $password, $sqlFile, $useDocker, $dockerContainerName);
|
||||
$output->writeln("执行命令: {$cmd}");
|
||||
|
||||
// 执行还原命令
|
||||
exec($cmd, $outputLines, $returnCode);
|
||||
|
||||
if ($returnCode === 0) {
|
||||
$output->writeln("✅ MySQL 还原成功");
|
||||
} else {
|
||||
$output->writeln("❌ MySQL 还原失败");
|
||||
$output->writeln(implode("\n", $outputLines));
|
||||
}
|
||||
|
||||
// 清理临时目录(如果使用了临时目录)
|
||||
if (pathinfo($selectedFile, PATHINFO_EXTENSION) === 'zip') {
|
||||
exec("rm -rf {$tempDir}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("❌ MySQL 还原失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function clearRedis($output): void
|
||||
{
|
||||
$output->writeln("\n开始清空 Redis...");
|
||||
|
||||
try {
|
||||
// 从数据源配置中获取 Redis 配置
|
||||
$redisSource = null;
|
||||
foreach ($this->dataSources as $source) {
|
||||
if ($source['type'] === 'redis') {
|
||||
$redisSource = $source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$redisSource) {
|
||||
$output->writeln("❌ 未找到 Redis 数据源配置");
|
||||
return;
|
||||
}
|
||||
|
||||
if ($redisSource['useDocker']) {
|
||||
$redisContainer = $redisSource['dockerContainerName'];
|
||||
if (!$redisContainer) {
|
||||
$output->writeln("❌ 未指定 Redis Docker 容器名称");
|
||||
return;
|
||||
}
|
||||
$output->writeln("使用 Docker 容器清空 Redis");
|
||||
$cmd = "docker exec -it {$redisContainer} redis-cli flushall";
|
||||
$output->writeln("执行命令: {$cmd}");
|
||||
|
||||
exec($cmd, $outputLines, $returnCode);
|
||||
|
||||
if ($returnCode === 0) {
|
||||
$output->writeln("✅ Redis 清空成功");
|
||||
} else {
|
||||
$output->writeln("❌ Redis 清空失败");
|
||||
$output->writeln(implode("\n", $outputLines));
|
||||
}
|
||||
} else {
|
||||
$redis = new \Redis();
|
||||
$host = $redisSource['host'] ?? '127.0.0.1';
|
||||
$port = $redisSource['port'] ?? 6379;
|
||||
$password = $redisSource['password'] ?? '';
|
||||
|
||||
$output->writeln("连接 Redis: {$host}:{$port}");
|
||||
|
||||
if ($redis->connect($host, $port)) {
|
||||
if (!empty($password)) {
|
||||
$redis->auth($password);
|
||||
}
|
||||
|
||||
$result = $redis->flushAll();
|
||||
|
||||
if ($result) {
|
||||
$output->writeln("✅ Redis 清空成功");
|
||||
} else {
|
||||
$output->writeln("❌ Redis 清空失败");
|
||||
}
|
||||
} else {
|
||||
$output->writeln("❌ 无法连接到 Redis");
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln("❌ Redis 操作失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function getMongoDumpCommand($host, $port, $database, $outputDir, $useDocker = false, $dockerContainerName = null, $username = '', $password = '', $authSource = null): string
|
||||
{
|
||||
if ($authSource === null) {
|
||||
$authSource = $database;
|
||||
}
|
||||
|
||||
$authParams = '';
|
||||
if (!empty($username) && !empty($password)) {
|
||||
$authParams = "--username {$username} --password {$password} --authenticationDatabase {$authSource}";
|
||||
}
|
||||
|
||||
if ($useDocker) {
|
||||
if (!$dockerContainerName) {
|
||||
return "echo '错误:未指定 Docker 容器名称' && exit 1";
|
||||
}
|
||||
$port = 27017;
|
||||
return "docker exec -it {$dockerContainerName} mongodump --host {$host}:{$port} {$authParams} --db {$database} --out /tmp/mongo_backup && docker cp {$dockerContainerName}:/tmp/mongo_backup/{$database} {$outputDir}/";
|
||||
} else {
|
||||
return "mongodump --host {$host}:{$port} {$authParams} --db {$database} --out {$outputDir}";
|
||||
}
|
||||
}
|
||||
|
||||
private function getMongoRestoreCommand($host, $port, $database, $restoreDir, $useDocker = false, $dockerContainerName = null, $username = '', $password = '', $authSource = null): string
|
||||
{
|
||||
if ($authSource === null) {
|
||||
$authSource = $database;
|
||||
}
|
||||
|
||||
$authParams = '';
|
||||
if (!empty($username) && !empty($password)) {
|
||||
$authParams = "--username {$username} --password {$password} --authenticationDatabase {$authSource}";
|
||||
}
|
||||
|
||||
if ($useDocker) {
|
||||
if (!$dockerContainerName) {
|
||||
return "echo '错误:未指定 Docker 容器名称' && exit 1";
|
||||
}
|
||||
$dirName = basename($restoreDir);
|
||||
return "docker cp {$restoreDir} {$dockerContainerName}:/tmp/ && docker exec -it {$dockerContainerName} mongorestore --host {$host}:{$port} {$authParams} --db {$database} /tmp/{$dirName} && docker exec -it {$dockerContainerName} rm -rf /tmp/{$dirName}";
|
||||
} else {
|
||||
return "mongorestore --host {$host}:{$port} {$authParams} --db {$database} {$restoreDir}";
|
||||
}
|
||||
}
|
||||
|
||||
private function getMySqlDumpCommand($host, $port, $database, $username, $password, $outputFile, $useDocker = false, $dockerContainerName = null): string
|
||||
{
|
||||
if ($useDocker) {
|
||||
// 使用 Docker 容器
|
||||
if (!$dockerContainerName) {
|
||||
return "echo '错误:未指定 Docker 容器名称' && exit 1";
|
||||
}
|
||||
return "docker exec -it {$dockerContainerName} mysqldump -h {$host} -P {$port} -u {$username} --password={$password} {$database} > {$outputFile}";
|
||||
} else {
|
||||
// 不使用 Docker,直接使用本地命令
|
||||
return "mysqldump -h {$host} -P {$port} -u {$username} --password={$password} {$database} > {$outputFile}";
|
||||
}
|
||||
}
|
||||
|
||||
private function getMySqlRestoreCommand($host, $port, $database, $username, $password, $sqlFile, $useDocker = false, $dockerContainerName = null): string
|
||||
{
|
||||
if ($useDocker) {
|
||||
// 使用 Docker 容器
|
||||
if (!$dockerContainerName) {
|
||||
return "echo '错误:未指定 Docker 容器名称' && exit 1";
|
||||
}
|
||||
return "docker cp {$sqlFile} {$dockerContainerName}:/tmp/mysql_restore.sql && docker exec -it {$dockerContainerName} bash -c 'mysql -h {$host} -P {$port} -u {$username} --password={$password} {$database} < /tmp/mysql_restore.sql'";
|
||||
} else {
|
||||
// 不使用 Docker,直接使用本地命令
|
||||
return "mysql -h {$host} -P {$port} -u {$username} --password={$password} {$database} < {$sqlFile}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+156
-371
@@ -12,63 +12,23 @@ class FixOpenimSeq extends Command
|
||||
protected static $defaultName = 'fix:openim:seq';
|
||||
protected static $defaultDescription = '修复 OpenIM MongoDB seq 相关字段';
|
||||
|
||||
private $dryRun = true;
|
||||
private $backupDir = '';
|
||||
private $conversationSeqMap = [];
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('execute', 'e', InputOption::VALUE_NONE, '实际执行修复(默认只预览)');
|
||||
$this->addOption('backup-dir', 'b', InputOption::VALUE_OPTIONAL, '备份目录', '/tmp/openim_seq_backup');
|
||||
$this->addOption('step', 's', InputOption::VALUE_OPTIONAL, '执行步骤: analyze, backup, fix, all', 'all');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->dryRun = !$input->getOption('execute');
|
||||
$this->backupDir = $input->getOption('backup-dir');
|
||||
$step = $input->getOption('step');
|
||||
|
||||
if ($this->dryRun) {
|
||||
$this->log("╔════════════════════════════════════════════════════════════╗");
|
||||
$this->log("║ 预览模式(不会修改数据) ║");
|
||||
$this->log("║ 使用 --execute 参数实际执行修复 ║");
|
||||
$this->log("╚════════════════════════════════════════════════════════════╝");
|
||||
} else {
|
||||
$this->log("╔════════════════════════════════════════════════════════════╗");
|
||||
$this->log("║ 执行模式 - 开始修复 ║");
|
||||
$this->log("╚════════════════════════════════════════════════════════════╝");
|
||||
}
|
||||
$this->log("╔════════════════════════════════════════════════════════════╗");
|
||||
$this->log("║ 开始修复 OpenIM Seq 数据 ║");
|
||||
$this->log("╚════════════════════════════════════════════════════════════╝");
|
||||
$this->log("");
|
||||
|
||||
$this->log("【OpenIM seq 设计原理】");
|
||||
$this->log(" - seq 表: 存储每个会话的全局 max_seq/min_seq");
|
||||
$this->log(" - seq_user 表: 存储每个用户在每个会话中的 max_seq/min_seq/read_seq");
|
||||
$this->log(" - conversation 表: 会话信息,冗余存储 max_seq/min_seq");
|
||||
$this->log(" - read_seq 不能大于 max_seq(否则未读数变负)");
|
||||
$this->log("");
|
||||
|
||||
switch ($step) {
|
||||
case 'analyze':
|
||||
$this->analyzeAll();
|
||||
break;
|
||||
case 'backup':
|
||||
$this->backupAll();
|
||||
break;
|
||||
case 'fix':
|
||||
if (empty($this->conversationSeqMap)) {
|
||||
$this->log("需要先分析消息数据...");
|
||||
$this->analyzeMsgSeq();
|
||||
}
|
||||
$this->fixAll();
|
||||
break;
|
||||
default:
|
||||
$this->analyzeAll();
|
||||
if (!$this->dryRun) {
|
||||
$this->backupAll();
|
||||
$this->fixAll();
|
||||
}
|
||||
}
|
||||
// 分析数据(获取会话 seq 信息)
|
||||
$this->analyzeMsgSeq();
|
||||
// 执行修复
|
||||
$this->fixAll();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -93,74 +53,11 @@ class FixOpenimSeq extends Command
|
||||
return null;
|
||||
}
|
||||
|
||||
private function analyzeAll(): void
|
||||
{
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log(" 第一步:分析数据 ");
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log("");
|
||||
|
||||
$this->log("【1. seq 表分析】");
|
||||
$seqCount = \app\model\Openim\Seq::count();
|
||||
$this->log(" 总记录数: {$seqCount}");
|
||||
$seqSample = \app\model\Openim\Seq::limit(5)->select()->toArray();
|
||||
foreach ($seqSample as $i => $row) {
|
||||
$this->log(" 样本[{$i}]: conversation_id={$row['conversation_id']}, max_seq={$row['max_seq']}, min_seq={$row['min_seq']}");
|
||||
}
|
||||
$this->log("");
|
||||
|
||||
$this->log("【2. seq_user 表分析】");
|
||||
$seqUserCount = \app\model\Openim\SeqUser::count();
|
||||
$this->log(" 总记录数: {$seqUserCount}");
|
||||
$seqUserSample = \app\model\Openim\SeqUser::limit(5)->select()->toArray();
|
||||
foreach ($seqUserSample as $i => $row) {
|
||||
$this->log(" 样本[{$i}]: conversation_id={$row['conversation_id']}, user_id={$row['user_id']}, max_seq={$row['max_seq']}, min_seq={$row['min_seq']}, read_seq={$row['read_seq']}");
|
||||
}
|
||||
$this->log("");
|
||||
|
||||
$this->log("【3. conversation 表分析】");
|
||||
$conversationCount = \app\model\Openim\Conversation::count();
|
||||
$this->log(" 总记录数: {$conversationCount}");
|
||||
$conversationSample = \app\model\Openim\Conversation::limit(5)->select()->toArray();
|
||||
foreach ($conversationSample as $i => $row) {
|
||||
$this->log(" 样本[{$i}]: conversation_id={$row['conversation_id']}, owner_user_id={$row['owner_user_id']}, max_seq=" . ($row['max_seq'] ?? 'null') . ", min_seq=" . ($row['min_seq'] ?? 'null'));
|
||||
}
|
||||
$this->log("");
|
||||
|
||||
$this->log("【4. msg 表分析 - 统计每个会话的 seq 范围】");
|
||||
$this->analyzeMsgSeq();
|
||||
$this->log("");
|
||||
}
|
||||
|
||||
private function analyzeMsgSeq(): void
|
||||
{
|
||||
$msgCount = \app\model\Openim\Msg::count();
|
||||
$this->log(" 消息文档数: {$msgCount}");
|
||||
$this->log(" 正在分析消息中的 seq 范围...");
|
||||
|
||||
$sampleDoc = \app\model\Openim\Msg::limit(1)->select()->first();
|
||||
if ($sampleDoc) {
|
||||
$this->log(" 样本文档 doc_id: " . ($sampleDoc['doc_id'] ?? 'null'));
|
||||
$msgsArray = $sampleDoc['msgs'];
|
||||
if ($msgsArray instanceof \think\model\Collection) {
|
||||
$msgsArray = $msgsArray->toArray();
|
||||
}
|
||||
$this->log(" 样本文档 msgs 数量: " . count($msgsArray));
|
||||
if (count($msgsArray) > 0) {
|
||||
$firstMsg = $msgsArray[0];
|
||||
if (isset($firstMsg['msg'])) {
|
||||
$msg = $firstMsg['msg'];
|
||||
$this->log(" 第一条消息 session_type: " . ($msg['session_type'] ?? 'null'));
|
||||
$this->log(" 第一条消息 send_id: " . ($msg['send_id'] ?? 'null'));
|
||||
$this->log(" 第一条消息 recv_id: " . ($msg['recv_id'] ?? 'null'));
|
||||
$this->log(" 第一条消息 group_id: " . ($msg['group_id'] ?? 'null'));
|
||||
$this->log(" 第一条消息 seq: " . ($msg['seq'] ?? 'null'));
|
||||
$conversationId = $this->getConversationId($msg);
|
||||
$this->log(" 计算得到的 conversationID: {$conversationId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->log("");
|
||||
$this->log("分析消息数据...");
|
||||
$this->log("消息文档数: {$msgCount}");
|
||||
|
||||
$processedDocs = 0;
|
||||
$totalMsgs = 0;
|
||||
@@ -197,292 +94,180 @@ class FixOpenimSeq extends Command
|
||||
}
|
||||
|
||||
if ($processedDocs % 100 == 0) {
|
||||
$this->log(" 已处理 {$processedDocs}/{$msgCount} 个文档...");
|
||||
$this->log("已处理 {$processedDocs}/{$msgCount} 个文档...");
|
||||
}
|
||||
}
|
||||
|
||||
$this->log(" 处理完成!共处理 {$processedDocs} 个文档,{$totalMsgs} 条消息");
|
||||
$this->log(" 发现 " . count($this->conversationSeqMap) . " 个会话");
|
||||
$this->log("");
|
||||
|
||||
$this->log("【5. 会话 seq 范围统计(前10个)】");
|
||||
$i = 0;
|
||||
foreach ($this->conversationSeqMap as $conversationId => $seqInfo) {
|
||||
if ($i++ >= 10) break;
|
||||
$this->log(" {$conversationId}: min_seq={$seqInfo['min_seq']}, max_seq={$seqInfo['max_seq']}, msg_count={$seqInfo['count']}");
|
||||
}
|
||||
$this->log("");
|
||||
|
||||
$this->log("【6. 检测问题数据】");
|
||||
$this->detectProblematicData();
|
||||
}
|
||||
|
||||
private function detectProblematicData(): void
|
||||
{
|
||||
$seqProblems = 0;
|
||||
$seqUserProblems = 0;
|
||||
$conversationProblems = 0;
|
||||
|
||||
$this->log(" 正在检测 seq 表...");
|
||||
$total = \app\model\Openim\Seq::count();
|
||||
$batchSize = 1000;
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$seqRecords = \app\model\Openim\Seq::field('conversation_id,max_seq,min_seq')->limit($offset, $batchSize)->select()->toArray();
|
||||
foreach ($seqRecords as $seq) {
|
||||
$conversationId = $seq['conversation_id'];
|
||||
if (isset($this->conversationSeqMap[$conversationId])) {
|
||||
$expected = $this->conversationSeqMap[$conversationId];
|
||||
if ($seq['max_seq'] != $expected['max_seq'] || $seq['min_seq'] != $expected['min_seq']) {
|
||||
$seqProblems++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$seqRecords = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
$this->log(" seq 表问题记录: {$seqProblems} 条(max_seq/min_seq 与实际消息不符)");
|
||||
|
||||
$this->log(" 正在检测 seq_user 表...");
|
||||
$total = \app\model\Openim\SeqUser::count();
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$seqUserRecords = \app\model\Openim\SeqUser::field('conversation_id,user_id,max_seq,min_seq,read_seq')->limit($offset, $batchSize)->select()->toArray();
|
||||
foreach ($seqUserRecords as $seqUser) {
|
||||
$conversationId = $seqUser['conversation_id'];
|
||||
if (isset($this->conversationSeqMap[$conversationId])) {
|
||||
$expected = $this->conversationSeqMap[$conversationId];
|
||||
if ($seqUser['max_seq'] != $expected['max_seq'] ||
|
||||
$seqUser['min_seq'] != $expected['min_seq'] ||
|
||||
$seqUser['read_seq'] > $expected['max_seq']) {
|
||||
$seqUserProblems++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$seqUserRecords = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
$this->log(" seq_user 表问题记录: {$seqUserProblems} 条(seq 不符或 read_seq > max_seq)");
|
||||
|
||||
$this->log(" 正在检测 conversation 表...");
|
||||
$total = \app\model\Openim\Conversation::count();
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$conversationRecords = \app\model\Openim\Conversation::field('conversation_id,max_seq,min_seq')->limit($offset, $batchSize)->select()->toArray();
|
||||
foreach ($conversationRecords as $conversation) {
|
||||
$conversationId = $conversation['conversation_id'];
|
||||
if (isset($this->conversationSeqMap[$conversationId])) {
|
||||
$expected = $this->conversationSeqMap[$conversationId];
|
||||
$currentMax = $conversation['max_seq'] ?? 0;
|
||||
$currentMin = $conversation['min_seq'] ?? 0;
|
||||
if ($currentMax != $expected['max_seq'] || $currentMin != $expected['min_seq']) {
|
||||
$conversationProblems++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$conversationRecords = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
$this->log(" conversation 表问题记录: {$conversationProblems} 条(max_seq/min_seq 与实际消息不符)");
|
||||
$this->log("");
|
||||
}
|
||||
|
||||
private function backupAll(): void
|
||||
{
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log(" 第二步:备份数据 ");
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log("");
|
||||
|
||||
if (!is_dir($this->backupDir)) {
|
||||
mkdir($this->backupDir, 0755, true);
|
||||
}
|
||||
|
||||
$timestamp = date('Ymd_His');
|
||||
$backupPath = $this->backupDir . '/backup_' . $timestamp;
|
||||
mkdir($backupPath, 0755, true);
|
||||
|
||||
$this->log("备份目录: {$backupPath}");
|
||||
$this->log("");
|
||||
|
||||
$this->log("【1. 备份 seq 表】");
|
||||
$total = \app\model\Openim\Seq::count();
|
||||
$batchSize = 1000;
|
||||
$seqData = [];
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$batch = \app\model\Openim\Seq::limit($offset, $batchSize)->select()->toArray();
|
||||
$seqData = array_merge($seqData, $batch);
|
||||
$batch = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
file_put_contents($backupPath . '/seq.json', json_encode($seqData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
$this->log(" 已备份 " . count($seqData) . " 条记录 -> seq.json");
|
||||
$seqData = null;
|
||||
|
||||
$this->log("【2. 备份 seq_user 表】");
|
||||
$total = \app\model\Openim\SeqUser::count();
|
||||
$seqUserData = [];
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$batch = \app\model\Openim\SeqUser::limit($offset, $batchSize)->select()->toArray();
|
||||
$seqUserData = array_merge($seqUserData, $batch);
|
||||
$batch = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
file_put_contents($backupPath . '/seq_user.json', json_encode($seqUserData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
$this->log(" 已备份 " . count($seqUserData) . " 条记录 -> seq_user.json");
|
||||
$seqUserData = null;
|
||||
|
||||
$this->log("【3. 备份 conversation 表】");
|
||||
$total = \app\model\Openim\Conversation::count();
|
||||
$conversationData = [];
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$batch = \app\model\Openim\Conversation::limit($offset, $batchSize)->select()->toArray();
|
||||
$conversationData = array_merge($conversationData, $batch);
|
||||
$batch = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
file_put_contents($backupPath . '/conversation.json', json_encode($conversationData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
$this->log(" 已备份 " . count($conversationData) . " 条记录 -> conversation.json");
|
||||
$conversationData = null;
|
||||
|
||||
$this->log("");
|
||||
$this->log("✓ 备份完成!备份文件保存在: {$backupPath}");
|
||||
$this->log("处理完成!共处理 {$processedDocs} 个文档,{$totalMsgs} 条消息");
|
||||
$this->log("发现 " . count($this->conversationSeqMap) . " 个会话");
|
||||
$this->log("");
|
||||
}
|
||||
|
||||
private function fixAll(): void
|
||||
{
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log(" 第三步:修复数据 ");
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log("开始修复数据...");
|
||||
$this->log("");
|
||||
|
||||
if (empty($this->conversationSeqMap)) {
|
||||
$this->log("错误:缺少会话 seq 数据,请先运行分析步骤");
|
||||
$this->log("错误:缺少会话 seq 数据");
|
||||
return;
|
||||
}
|
||||
|
||||
$this->log("【1. 修复 seq 表】");
|
||||
$this->log(" 原理:根据消息表中的实际 seq 更新 max_seq 和 min_seq");
|
||||
$seqFixed = 0;
|
||||
$seqCreated = 0;
|
||||
foreach ($this->conversationSeqMap as $conversationId => $seqInfo) {
|
||||
$existing = \app\model\Openim\Seq::where('conversation_id', $conversationId)->find();
|
||||
|
||||
if ($existing) {
|
||||
$oldMax = $existing['max_seq'];
|
||||
$oldMin = $existing['min_seq'];
|
||||
if ($oldMax != $seqInfo['max_seq'] || $oldMin != $seqInfo['min_seq']) {
|
||||
$existing->max_seq = $seqInfo['max_seq'];
|
||||
$existing->min_seq = $seqInfo['min_seq'];
|
||||
$existing->save();
|
||||
$this->log(" 更新 {$conversationId}: max_seq {$oldMax} -> {$seqInfo['max_seq']}, min_seq {$oldMin} -> {$seqInfo['min_seq']}");
|
||||
$seqFixed++;
|
||||
}
|
||||
} else {
|
||||
\app\model\Openim\Seq::create([
|
||||
'conversation_id' => $conversationId,
|
||||
'max_seq' => $seqInfo['max_seq'],
|
||||
'min_seq' => $seqInfo['min_seq'],
|
||||
]);
|
||||
$this->log(" 创建 {$conversationId}: max_seq={$seqInfo['max_seq']}, min_seq={$seqInfo['min_seq']}");
|
||||
$seqCreated++;
|
||||
}
|
||||
}
|
||||
$this->log(" 完成:更新 {$seqFixed} 条,新建 {$seqCreated} 条");
|
||||
$this->log("");
|
||||
|
||||
$this->log("【2. 修复 seq_user 表】");
|
||||
$this->log(" 原理:更新 max_seq/min_seq 为会话值,确保 read_seq <= max_seq");
|
||||
$seqUserFixed = 0;
|
||||
$seqUserReadFixed = 0;
|
||||
|
||||
$total = \app\model\Openim\SeqUser::count();
|
||||
$batchSize = 500;
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$seqUsers = \app\model\Openim\SeqUser::limit($offset, $batchSize)->select();
|
||||
foreach ($seqUsers as $seqUser) {
|
||||
$conversationId = $seqUser['conversation_id'];
|
||||
if (isset($this->conversationSeqMap[$conversationId])) {
|
||||
$seqInfo = $this->conversationSeqMap[$conversationId];
|
||||
$oldMax = $seqUser['max_seq'];
|
||||
$oldMin = $seqUser['min_seq'];
|
||||
$oldRead = $seqUser['read_seq'];
|
||||
$needSave = false;
|
||||
|
||||
if ($oldMax != $seqInfo['max_seq'] || $oldMin != $seqInfo['min_seq']) {
|
||||
$seqUser->max_seq = $seqInfo['max_seq'];
|
||||
$seqUser->min_seq = $seqInfo['min_seq'];
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if ($oldRead > $seqInfo['max_seq']) {
|
||||
$seqUser->read_seq = $seqInfo['max_seq'];
|
||||
$this->log(" 修正 read_seq {$conversationId}/{$seqUser['user_id']}: {$oldRead} -> {$seqInfo['max_seq']}");
|
||||
$seqUserReadFixed++;
|
||||
$needSave = true;
|
||||
}
|
||||
|
||||
if ($needSave) {
|
||||
$seqUser->save();
|
||||
$seqUserFixed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
$seqUsers = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
$this->log(" 完成:更新 {$seqUserFixed} 条,其中修正 read_seq {$seqUserReadFixed} 条");
|
||||
$this->log("");
|
||||
|
||||
$this->log("【3. 修复 conversation 表】");
|
||||
$this->log(" 原理:根据消息表中的实际 seq 更新 max_seq 和 min_seq");
|
||||
$conversationFixed = 0;
|
||||
$conversationNotInMap = 0;
|
||||
$total = \app\model\Openim\Conversation::count();
|
||||
$debugLogged = false;
|
||||
for ($offset = 0; $offset < $total; $offset += $batchSize) {
|
||||
$conversations = \app\model\Openim\Conversation::limit($offset, $batchSize)->select();
|
||||
foreach ($conversations as $conversation) {
|
||||
$conversationId = $conversation['conversation_id'];
|
||||
if (isset($this->conversationSeqMap[$conversationId])) {
|
||||
$seqInfo = $this->conversationSeqMap[$conversationId];
|
||||
$oldMax = $conversation['max_seq'] ?? 0;
|
||||
$oldMin = $conversation['min_seq'] ?? 0;
|
||||
|
||||
if ($oldMax != $seqInfo['max_seq'] || $oldMin != $seqInfo['min_seq']) {
|
||||
$data = [
|
||||
'max_seq' => (int)$seqInfo['max_seq'],
|
||||
'min_seq' => (int)$seqInfo['min_seq'],
|
||||
];
|
||||
$result = \app\model\Openim\Conversation::where('conversation_id', $conversationId)
|
||||
->where('owner_user_id', $conversation['owner_user_id'])
|
||||
->update($data);
|
||||
if ($result) {
|
||||
$conversationFixed++;
|
||||
} else {
|
||||
$this->log(" 警告: 更新失败 {$conversationId} / {$conversation['owner_user_id']}");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$conversationNotInMap++;
|
||||
foreach ($this->conversationSeqMap as $conversationId => $seqInfo) {
|
||||
// 修复 seq 表
|
||||
$existing = \app\model\Openim\Seq::where('conversation_id', $conversationId)->find();
|
||||
if ($existing) {
|
||||
$max_seq = 0;
|
||||
if(str_starts_with($conversationId,'sg_')){
|
||||
$max_seq = ceil($seqInfo['max_seq']/100)*100+1;
|
||||
}else{
|
||||
$max_seq = ceil($seqInfo['max_seq']/50)*50+1;
|
||||
}
|
||||
$existing->max_seq = $max_seq;
|
||||
$existing->min_seq = 0;
|
||||
$existing->save();
|
||||
$seqFixed++;
|
||||
} else {
|
||||
}
|
||||
$conversations = null;
|
||||
gc_collect_cycles();
|
||||
}
|
||||
$this->log(" 完成:更新 {$conversationFixed} 条,不在消息映射中 {$conversationNotInMap} 条");
|
||||
$this->log("");
|
||||
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
$this->log(" 修复完成! ");
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
// 修复 seq_user 表
|
||||
\app\model\Openim\SeqUser::where('conversation_id', $conversationId)->update([
|
||||
'max_seq' => 0,
|
||||
'min_seq' => 0,
|
||||
]);
|
||||
\app\model\Openim\SeqUser::where('conversation_id', $conversationId)
|
||||
->where('read_seq','<>',$seqInfo['max_seq'])
|
||||
->update([
|
||||
'read_seq' => $seqInfo['max_seq'],
|
||||
]);
|
||||
|
||||
// 修复 conversation 表
|
||||
\app\model\Openim\Conversation::where('min_seq', '>',0)->update([
|
||||
'max_seq' => 0,
|
||||
'min_seq' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->log("修复完成!");
|
||||
$this->log("- seq 表: 更新 {$seqFixed} 条,新建 {$seqCreated} 条");
|
||||
$this->log("- seq_user 表: 更新 {$seqUserFixed} 条");
|
||||
$this->log("- conversation 表: 更新 {$conversationFixed} 条");
|
||||
$this->log("");
|
||||
$this->log("修复统计:");
|
||||
$this->log(" - seq 表: 更新 {$seqFixed} 条,新建 {$seqCreated} 条");
|
||||
$this->log(" - seq_user 表: 更新 {$seqUserFixed} 条,修正 read_seq {$seqUserReadFixed} 条");
|
||||
$this->log(" - conversation 表: 更新 {$conversationFixed} 条");
|
||||
}
|
||||
|
||||
private function log(string $message): void
|
||||
{
|
||||
echo $message . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置 seq 相关字段并重新计算
|
||||
*/
|
||||
public function fix_seq(): void
|
||||
{
|
||||
$this->log("\n═══════════════════════════════════════════════════════════");
|
||||
$this->log(" 执行 fix_seq 方法 ");
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
|
||||
// 1. 获取所有会话ID
|
||||
$conversationIds = [];
|
||||
|
||||
// 从 seq 表获取所有会话ID
|
||||
$seqRecords = \app\model\Openim\Seq::field('conversation_id')->select()->toArray();
|
||||
foreach ($seqRecords as $record) {
|
||||
$conversationIds[] = $record['conversation_id'];
|
||||
}
|
||||
|
||||
// 去重
|
||||
$conversationIds = array_unique($conversationIds);
|
||||
$totalConversations = count($conversationIds);
|
||||
$this->log("发现 {$totalConversations} 个会话");
|
||||
|
||||
$processed = 0;
|
||||
foreach ($conversationIds as $conversationId) {
|
||||
cp('更新:'.$conversationId);
|
||||
continue;
|
||||
$processed++;
|
||||
$this->log("\n处理会话 {$conversationId} ({$processed}/{$totalConversations})");
|
||||
|
||||
// 2. 计算变量A
|
||||
$msgCount = \app\model\Openim\Msg::whereLike('doc_id', "{$conversationId}%")->count();
|
||||
$multiplier = strpos($conversationId, 'sg_') === 0 ? 100 : 50;
|
||||
$baseA = $msgCount * $multiplier + 1;
|
||||
|
||||
// 确保 A 是 1, 51, 101 等递增格式
|
||||
$remainder = $baseA % $multiplier;
|
||||
if ($remainder != 1) {
|
||||
$baseA = $baseA - $remainder + 1;
|
||||
}
|
||||
|
||||
// 3. 获取最后一条消息的 seq
|
||||
$lastSeq = 0;
|
||||
$msgDocs = \app\model\Openim\Msg::whereLike('doc_id', "{$conversationId}%")->select();
|
||||
foreach ($msgDocs as $doc) {
|
||||
$msgsArray = $doc['msgs'];
|
||||
if ($msgsArray instanceof \think\model\Collection) {
|
||||
$msgsArray = $msgsArray->toArray();
|
||||
}
|
||||
if (!empty($msgsArray) && is_array($msgsArray)) {
|
||||
foreach ($msgsArray as $msgItem) {
|
||||
if (isset($msgItem['msg']['seq'])) {
|
||||
$lastSeq = max($lastSeq, (int)$msgItem['msg']['seq']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 确保 A 大于最后一条消息的 seq
|
||||
if ($baseA <= $lastSeq) {
|
||||
$baseA = $lastSeq + 50 - ($lastSeq % 50) + 1;
|
||||
if ($baseA % 50 != 1) {
|
||||
$baseA += 1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->log(" - 消息记录数: {$msgCount}");
|
||||
$this->log(" - 乘数: {$multiplier}");
|
||||
$this->log(" - 最后消息 seq: {$lastSeq}");
|
||||
$this->log(" - 计算变量 A: {$baseA}");
|
||||
|
||||
// 4. 更新 seq 表
|
||||
$seq = \app\model\Openim\Seq::where('conversation_id', $conversationId)->find();
|
||||
if ($seq) {
|
||||
$seq->max_seq = $baseA;
|
||||
$seq->min_seq = 0;
|
||||
$seq->save();
|
||||
$this->log(" - 更新 seq 表: max_seq={$baseA}, min_seq=0");
|
||||
}
|
||||
|
||||
// 5. 更新 conversation 表
|
||||
$conversations = \app\model\Openim\Conversation::where('conversation_id', $conversationId)->select();
|
||||
foreach ($conversations as $conversation) {
|
||||
$conversation->max_seq = 0;
|
||||
$conversation->min_seq = 0;
|
||||
$conversation->save();
|
||||
}
|
||||
$this->log(" - 更新 conversation 表: max_seq=0, min_seq=0");
|
||||
|
||||
// 6. 更新 seq_user 表
|
||||
$seqUsers = \app\model\Openim\SeqUser::where('conversation_id', $conversationId)->select();
|
||||
foreach ($seqUsers as $seqUser) {
|
||||
cp('更新:'.$conversationId);
|
||||
$seqUser->max_seq = 0;
|
||||
$seqUser->min_seq = 0;
|
||||
$seqUser->read_seq = $lastSeq;
|
||||
$seqUser->save();
|
||||
}
|
||||
$this->log(" - 更新 seq_user 表: max_seq=0, min_seq=0, read_seq={$lastSeq}");
|
||||
}
|
||||
|
||||
$this->log("\n═══════════════════════════════════════════════════════════");
|
||||
$this->log(" fix_seq 方法执行完成 ");
|
||||
$this->log("═══════════════════════════════════════════════════════════");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,872 @@
|
||||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
// 引入内置进度条类
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use support\think\Db;
|
||||
|
||||
class MigrateMessages extends Command
|
||||
{
|
||||
protected static $defaultName = 'migrate:messages';
|
||||
protected static $defaultDescription = '从老数据库迁移数据到新OpenIM数据库';
|
||||
|
||||
private $sdk = null;
|
||||
private $oldManager = null;
|
||||
private $newManager = null;
|
||||
private $retry = 3;
|
||||
private $delay = 2;
|
||||
private $backupDir = '/vol3/1000/code/im/admin/backup';
|
||||
private $currentBackup = null;
|
||||
|
||||
private $skipUsers = [];
|
||||
private $skipGroups = [];
|
||||
private $stats = [
|
||||
'users' => ['total' => 0, 'success' => 0, 'failed' => 0],
|
||||
'groups' => ['total' => 0, 'success' => 0, 'failed' => 0],
|
||||
'members' => ['total' => 0, 'success' => 0, 'failed' => 0],
|
||||
'messages' => ['total' => 0, 'success' => 0, 'failed' => 0, 'skipped' => 0],
|
||||
];
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('step', 's', InputOption::VALUE_OPTIONAL, '执行步骤: users/groups/members/messages/all', 'all');
|
||||
$this->addOption('skip-users', null, InputOption::VALUE_OPTIONAL, '跳过的用户ID(逗号分隔)');
|
||||
$this->addOption('skip-groups', null, InputOption::VALUE_OPTIONAL, '跳过的群ID(逗号分隔)');
|
||||
$this->addOption('clean', null, InputOption::VALUE_NONE, '清空现有数据后再迁移');
|
||||
$this->addOption('retry', 'r', InputOption::VALUE_OPTIONAL, '失败重试次数', 3);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$step = $input->getOption('step');
|
||||
$skipUsers = $input->getOption('skip-users') ? explode(',', $input->getOption('skip-users')) : [];
|
||||
$skipGroups = $input->getOption('skip-groups') ? explode(',', $input->getOption('skip-groups')) : [];
|
||||
$clean = $input->getOption('clean');
|
||||
$retry = (int)$input->getOption('retry');
|
||||
|
||||
// 自动忽略特殊用户
|
||||
$defaultSkipUsers = ['group_bot', 'official_team', 'system','imAdmin'];
|
||||
$skipUsers = array_merge($skipUsers, $defaultSkipUsers);
|
||||
$this->skipUsers = array_unique($skipUsers);
|
||||
$this->skipGroups = array_unique($skipGroups);
|
||||
$this->retry = $retry;
|
||||
|
||||
$this->log($output, "╔════════════════════════════════════════════════════════════╗");
|
||||
$this->log($output, "║ OpenIM 数据迁移工具 v2.0 ║");
|
||||
$this->log($output, "╚════════════════════════════════════════════════════════════╝");
|
||||
$this->log($output, "");
|
||||
|
||||
if ($clean) {
|
||||
$this->log($output, "🗑️ 清理模式:会清空现有数据");
|
||||
}
|
||||
$this->log($output, "📍 执行步骤: {$step}");
|
||||
$this->log($output, "");
|
||||
if($step == 'restore'){
|
||||
$this->restoreMongoDB($output, '/vol3/1000/code/im/admin/backup/openim_v3_groups_20260413141105.json');
|
||||
return 0;
|
||||
}
|
||||
$this->cleanExistingData($output,[]);
|
||||
try {
|
||||
$this->initConnections($output);
|
||||
//return 0 ;
|
||||
if ($clean) {
|
||||
$this->cleanExistingData($output,[
|
||||
'conversation', 'conversation_version', // 会话相关集合
|
||||
'data_version', // 数据版本集合
|
||||
'friend', 'friend_request', 'friend_version', // 好友关系相关集合
|
||||
'group', 'group_join_version','group_member','group_member_version','group_request', // 群组相关集合
|
||||
'msg','seq','seq_user' // 消息和序列号相关集合
|
||||
]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cache('admin_token_imAdmin',null);
|
||||
$steps = $step === 'all' ? [
|
||||
'users',
|
||||
'friends',
|
||||
'groups',
|
||||
//'members',
|
||||
'messages'
|
||||
] : [$step];
|
||||
|
||||
foreach ($steps as $s) {
|
||||
// 备份数据
|
||||
$backupFile = $this->backupMongoDB($output, $s);
|
||||
|
||||
try {
|
||||
switch ($s) {
|
||||
case 'users':
|
||||
$this->migrateUsers($output);
|
||||
break;
|
||||
case 'friends':
|
||||
$this->migrateFriends($output);
|
||||
break;
|
||||
case 'groups':
|
||||
$this->migrateGroups($output);
|
||||
break;
|
||||
case 'members':
|
||||
$this->migrateGroupMembers($output);
|
||||
break;
|
||||
case 'messages':
|
||||
$this->migrateMessages($output);
|
||||
break;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 遇到错误,回滚数据
|
||||
if (!empty($backupFile)) {
|
||||
$this->restoreMongoDB($output, $backupFile);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$this->printStats($output);
|
||||
return self::SUCCESS;
|
||||
} catch (\Exception $e) {
|
||||
$this->log($output, "❌ 错误: " . $e->getMessage());
|
||||
//$this->log($output, $e->getTraceAsString());
|
||||
return self::FAILURE;
|
||||
}
|
||||
}
|
||||
private function migrateUsers(OutputInterface $output): void
|
||||
{
|
||||
//之前残留了一部分数据,是单向好友,这里没做删除,所以数据大小和之前的不一样,用户重新删除一次就好了
|
||||
$this->log($output, "");
|
||||
$this->log($output, "═════════════════ 步骤1: 迁移用户 ═════════════════");
|
||||
$this->log($output, "清理旧的数据");
|
||||
|
||||
$this->cleanExistingData($output,[
|
||||
'user'
|
||||
]);
|
||||
$user_list = (new \app\model\Openim\User())->setOption('connection','tettt')
|
||||
->whereNotIn('user_id',$this->skipUsers)
|
||||
->field('user_id,nickname,face_url')
|
||||
->select();
|
||||
$user_list = $user_list->toArray();
|
||||
|
||||
// 1. 创建进度条(内置核心方法)
|
||||
$progressBar = new ProgressBar($output, count($user_list));
|
||||
|
||||
// 可选:设置进度条样式(字符、长度等)
|
||||
$progressBar->setBarCharacter('█');
|
||||
$progressBar->setEmptyBarCharacter('░');
|
||||
$progressBar->setProgressCharacter('▶');
|
||||
$progressBar->setBarWidth(400);
|
||||
|
||||
// 2. 开始显示
|
||||
$progressBar->start();
|
||||
echo sprintf("\r");
|
||||
while(count($user_list) > 0){
|
||||
$step = 100;
|
||||
$user = array_slice($user_list,0,$step);
|
||||
$user_list = array_slice($user_list,$step);
|
||||
$this->sdk->user->userRegister($user);
|
||||
$progressBar->advance($step);
|
||||
}
|
||||
// 4. 结束进度条
|
||||
$progressBar->finish();
|
||||
|
||||
}
|
||||
private function migrateFriends(OutputInterface $output): void
|
||||
{
|
||||
//之前残留了一部分数据,是单向好友,这里没做删除,所以数据大小和之前的不一样,用户重新删除一次就好了
|
||||
$this->log($output, "");
|
||||
$this->log($output, "═════════════════ 步骤3: 迁移好友 ═════════════════");
|
||||
$this->log($output, "清理旧的数据");
|
||||
|
||||
// $this->cleanExistingData($output,[
|
||||
// 'conversation', 'conversation_version', // 会话相关集合
|
||||
// 'data_version', // 数据版本集合
|
||||
// 'friend', 'friend_request', 'friend_version', // 好友关系相关集合
|
||||
// 'group', 'group_join_version','group_member','group_member_version','group_request', // 群组相关集合
|
||||
// 'msg','seq','seq_user' // 消息和序列号相关集合
|
||||
// ]);
|
||||
$user_list = (new \app\model\Openim\User())->setOption('connection','tettt')
|
||||
->whereNotNull('user_id')
|
||||
->column('user_id');
|
||||
|
||||
// 1. 创建进度条(内置核心方法)
|
||||
$progressBar = new ProgressBar($output, count($user_list));
|
||||
|
||||
// 可选:设置进度条样式(字符、长度等)
|
||||
$progressBar->setBarCharacter('█');
|
||||
$progressBar->setEmptyBarCharacter('░');
|
||||
$progressBar->setProgressCharacter('▶');
|
||||
$progressBar->setBarWidth(400);
|
||||
|
||||
// 2. 开始显示
|
||||
$progressBar->start();
|
||||
foreach($user_list as $userID){
|
||||
$friend_list = (new \app\model\Openim\Friend())->setOption('connection','tettt')
|
||||
->where('owner_user_id',$userID)
|
||||
->column('friend_user_id');
|
||||
if(count($friend_list)){
|
||||
while(count($friend_list)){
|
||||
$_friend_list = array_slice($friend_list, 0, 500);
|
||||
$friend_list = array_slice($friend_list, 500);
|
||||
$this->sdk->friend->importFriend($userID,$_friend_list);
|
||||
}
|
||||
}
|
||||
$progressBar->advance();
|
||||
}
|
||||
// 4. 结束进度条
|
||||
$progressBar->finish();
|
||||
|
||||
}
|
||||
private function migrateGroups(OutputInterface $output): void
|
||||
{
|
||||
$this->log($output, "");
|
||||
$this->log($output, "═════════════════ 步骤2: 迁移群组 ═════════════════");
|
||||
$this->log($output, "");
|
||||
|
||||
$options = [];
|
||||
|
||||
$groups = $this->queryOldDb('group', [], $options);
|
||||
$this->stats['groups']['total'] = count($groups);
|
||||
$this->log($output, "📊 找到 {$this->stats['groups']['total']} 个群组");
|
||||
|
||||
$processed = 0;
|
||||
// 1. 创建进度条(内置核心方法)
|
||||
$progressBar = new ProgressBar($output, count($groups));
|
||||
|
||||
// 可选:设置进度条样式(字符、长度等)
|
||||
$progressBar->setBarCharacter('█');
|
||||
$progressBar->setEmptyBarCharacter('░');
|
||||
$progressBar->setProgressCharacter('▶');
|
||||
$progressBar->setBarWidth(400);
|
||||
|
||||
// 2. 开始显示
|
||||
$progressBar->start();
|
||||
foreach ($groups as $group) {
|
||||
$processed++;
|
||||
$groupID = (string)($group['group_id'] ?? $group['groupID'] ?? '');
|
||||
|
||||
if (empty($groupID) || in_array($groupID, $this->skipGroups)) {
|
||||
$this->stats['groups']['failed']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$ownerUserID = (string)($group['owner_user_id'] ?? $group['ownerUserID'] ?? $group['creator_user_id'] ?? $group['creatorUserID'] ?? '');
|
||||
if (empty($ownerUserID)) {
|
||||
$this->stats['groups']['failed']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$groupName = (string)($group['group_name'] ?? $group['groupName'] ?? '');
|
||||
$faceURL = (string)($group['face_url'] ?? $group['faceURL'] ?? '');
|
||||
$introduction = (string)($group['introduction'] ?? '');
|
||||
$notification = (string)($group['notification'] ?? '');
|
||||
$ex = (string)($group['ex'] ?? '');
|
||||
|
||||
// 群组设置字段
|
||||
$groupType = (int)($group['group_type'] ?? $group['groupType'] ?? 2);
|
||||
$needVerification = (int)($group['need_verification'] ?? $group['needVerification'] ?? 0);
|
||||
$lookMemberInfo = (int)($group['look_member_info'] ?? $group['lookMemberInfo'] ?? 0);
|
||||
$applyMemberFriend = (int)($group['apply_member_friend'] ?? $group['applyMemberFriend'] ?? 0);
|
||||
|
||||
$progress = sprintf("[群组 %d/%d]", $processed, $this->stats['groups']['total']);
|
||||
|
||||
if ($processed % 20 == 0 || $processed == 1) {
|
||||
$this->log($output, "{$progress} 处理中...");
|
||||
}
|
||||
$this->log($output, "{$progress} 尝试创建群组: {$groupID}, 群主: {$ownerUserID}");
|
||||
// 管理员信息
|
||||
$adminUserIDs = (new \app\model\Openim\GroupMember())->setOption('connection','tettt')
|
||||
->where('group_id',$groupID)
|
||||
->where('role_level',60)
|
||||
->column('user_id');
|
||||
//cp($adminUserIDs );
|
||||
// 成员信息
|
||||
$memberUserIDs = (new \app\model\Openim\GroupMember())->setOption('connection','tettt')
|
||||
->where('group_id',$groupID)
|
||||
->where('role_level',20)
|
||||
->column('user_id');
|
||||
//cp($memberUserIDs );
|
||||
$memberUserIDs = array_unique($memberUserIDs);
|
||||
$_memberUserIDs = array_slice($memberUserIDs, 0, 10);
|
||||
$memberUserIDs = array_slice($memberUserIDs, 10);
|
||||
try {
|
||||
$this->sdk->group->createGroup(
|
||||
$ownerUserID,
|
||||
$_memberUserIDs,
|
||||
$adminUserIDs,
|
||||
$groupName,
|
||||
$groupID,
|
||||
$faceURL,
|
||||
$introduction,
|
||||
$notification,
|
||||
$ex,
|
||||
$groupType,
|
||||
$needVerification,
|
||||
$lookMemberInfo,
|
||||
$applyMemberFriend
|
||||
);
|
||||
while(count($memberUserIDs)){
|
||||
$_memberUserIDs = array_slice($memberUserIDs, 0, 10);
|
||||
$memberUserIDs = array_slice($memberUserIDs, 10);
|
||||
try{
|
||||
$this->sdk->group->inviteUserToGroup($groupID, $_memberUserIDs);
|
||||
} catch (\Exception $e) {
|
||||
$this->log($output, "{$progress} ❌ 邀请成员失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->stats['groups']['success']++;
|
||||
//$this->log($output, "{$progress} ✅ 创建成功");
|
||||
} catch (\Exception $e) {
|
||||
$this->stats['groups']['failed']++;
|
||||
if ($e->getCode() == 1202 || strpos($e->getMessage(), 'GroupIDExisted') !== false) {
|
||||
$this->log($output, "{$progress} ℹ️ 群组已存在,跳过创建");
|
||||
$this->stats['groups']['success']++;
|
||||
continue;
|
||||
} else {
|
||||
$this->log($output, "{$progress} ❌ 创建失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$progressBar->advance();
|
||||
}
|
||||
// 4. 结束进度条
|
||||
$progressBar->finish();
|
||||
}
|
||||
|
||||
private function migrateGroupMembers(OutputInterface $output): void
|
||||
{
|
||||
$this->log($output, "");
|
||||
$this->log($output, "═════════════════ 步骤3: 迁移群成员 ═════════════════");
|
||||
$this->log($output, "");
|
||||
|
||||
$groups = $this->queryOldDb('group', [], ['projection' => ['group_id' => 1, 'groupID' => 1]]);
|
||||
$groupIDs = [];
|
||||
foreach ($groups as $g) {
|
||||
$gid = (string)($g['group_id'] ?? $g['groupID'] ?? '');
|
||||
if (!empty($gid) && !in_array($gid, $this->skipGroups)) {
|
||||
$groupIDs[] = $gid;
|
||||
}
|
||||
}
|
||||
|
||||
$totalMembers = 0;
|
||||
foreach ($groupIDs as $groupID) {
|
||||
$members = $this->queryOldDb('group_member', ['group_id' => $groupID]);
|
||||
$ownerUserID = null;
|
||||
$adminUserIDs = [];
|
||||
$memberUserIDs = [];
|
||||
|
||||
foreach ($members as $member) {
|
||||
$userID = (string)($member['user_id'] ?? $member['userID'] ?? '');
|
||||
if (empty($userID)) continue;
|
||||
|
||||
$roleLevel = (int)($member['role_level'] ?? $member['roleLevel'] ?? 0);
|
||||
if ($roleLevel == 100) {
|
||||
$ownerUserID = $userID;
|
||||
} elseif ($roleLevel == 60) {
|
||||
$adminUserIDs[] = $userID;
|
||||
} else {
|
||||
$memberUserIDs[] = $userID;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($memberUserIDs) && empty($adminUserIDs)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$totalMembers += count($memberUserIDs);
|
||||
$this->stats['members']['total'] += count($memberUserIDs);
|
||||
|
||||
$progress = sprintf("[群 %s 成员 %d]", $groupID, count($memberUserIDs));
|
||||
$this->log($output, "{$progress} 处理中...");
|
||||
|
||||
// 分批邀请,每批最多50人
|
||||
$batches = array_chunk($memberUserIDs, 50);
|
||||
foreach ($batches as $batch) {
|
||||
if (empty($batch)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attempts = 0;
|
||||
while ($attempts < $this->retry) {
|
||||
try {
|
||||
$this->log($output, "{$progress} 邀请成员: " . implode(', ', array_slice($batch, 0, 5)) . (count($batch) > 5 ? '...' : ''));
|
||||
$result = $this->sdk->group->inviteUserToGroup($groupID, $ownerUserID ?? 'admin', $batch);
|
||||
$this->log($output, "{$progress} API返回: " . json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
if (isset($result['errCode']) && $result['errCode'] != 0) {
|
||||
// 检查是否是重复键错误
|
||||
if (strpos($result['errMsg'] ?? '', 'duplicate key') !== false || strpos($result['errMsg'] ?? '', 'DuplicateKey') !== false) {
|
||||
$this->log($output, "{$progress} ℹ️ 部分成员已存在,跳过");
|
||||
$this->stats['members']['success'] += count($batch);
|
||||
} else {
|
||||
$this->stats['members']['failed'] += count($batch);
|
||||
$this->log($output, "{$progress} ❌ 邀请失败: " . ($result['errMsg'] ?? '未知错误'));
|
||||
}
|
||||
} else {
|
||||
$this->stats['members']['success'] += count($batch);
|
||||
$this->log($output, "{$progress} ✅ 邀请成功");
|
||||
}
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
$attempts++;
|
||||
// 检查是否是重复键错误
|
||||
if (strpos($e->getMessage(), 'duplicate key') !== false || strpos($e->getMessage(), 'DuplicateKey') !== false) {
|
||||
$this->log($output, "{$progress} ℹ️ 部分成员已存在,跳过");
|
||||
$this->stats['members']['success'] += count($batch);
|
||||
break;
|
||||
} elseif ($attempts >= $this->retry) {
|
||||
$this->stats['members']['failed'] += count($batch);
|
||||
$this->log($output, "{$progress} ❌ 邀请异常: " . $e->getMessage());
|
||||
} else {
|
||||
$this->log($output, "{$progress} ⚠️ 邀请失败,第 {$attempts}/{$this->retry} 次重试...");
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
usleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
$this->log($output, "📊 共处理 {$totalMembers} 个群成员");
|
||||
}
|
||||
|
||||
private function migrateMessages(OutputInterface $output): void
|
||||
{
|
||||
$this->log($output, "");
|
||||
$this->log($output, "═════════════════ 步骤4: 迁移消息 ═════════════════");
|
||||
$this->log($output, "");
|
||||
|
||||
$pipeline = [
|
||||
['$unwind' => '$msgs'],
|
||||
['$match' => ['msgs.msg' => ['$ne' => null]]],
|
||||
['$sort' => ['msgs.msg.send_time' => 1]],
|
||||
];
|
||||
|
||||
$pipeline[] = ['$project' => ['doc_id' => 1, 'msg' => '$msgs.msg']];
|
||||
|
||||
$command = new \MongoDB\Driver\Command([
|
||||
'aggregate' => 'msg',
|
||||
'pipeline' => $pipeline,
|
||||
'cursor' => new \stdClass
|
||||
]);
|
||||
$cursor = $this->oldManager->executeCommand('tettt', $command);
|
||||
|
||||
$messages = [];
|
||||
foreach ($cursor as $doc) {
|
||||
$messages[] = $this->bsonToArray($doc);
|
||||
}
|
||||
|
||||
$this->stats['messages']['total'] = count($messages);
|
||||
$this->log($output, "📊 找到 {$this->stats['messages']['total']} 条消息");
|
||||
|
||||
$processed = 0;
|
||||
foreach ($messages as $doc) {
|
||||
$processed++;
|
||||
$msg = $doc['msg'] ?? [];
|
||||
|
||||
if (empty($msg)) {
|
||||
$this->stats['messages']['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$sendID = (string)($msg['send_id'] ?? $msg['sendID'] ?? '');
|
||||
$recvID = (string)($msg['recv_id'] ?? $msg['recvID'] ?? '');
|
||||
$groupID = (string)($msg['group_id'] ?? $msg['groupID'] ?? '');
|
||||
$contentType = (int)($msg['content_type'] ?? $msg['contentType'] ?? 101);
|
||||
$sessionType = (int)($msg['session_type'] ?? $msg['sessionType'] ?? 1);
|
||||
|
||||
if (in_array($sendID, $this->skipUsers)) {
|
||||
$this->stats['messages']['skipped']++;
|
||||
continue;
|
||||
}
|
||||
if ($sessionType == 3 && in_array($groupID, $this->skipGroups)) {
|
||||
$this->stats['messages']['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 跳过特殊消息类型(如系统通知等)
|
||||
if (in_array($contentType, [200, 201, 202, 203, 204, 205])) {
|
||||
$this->stats['messages']['skipped']++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$progress = sprintf("[消息 %d/%d]", $processed, $this->stats['messages']['total']);
|
||||
|
||||
if ($processed % 100 == 0 || $processed == 1) {
|
||||
$this->log($output, "{$progress} 处理中...");
|
||||
}
|
||||
|
||||
try {
|
||||
$this->log($output, "{$progress} 发送消息: sendID={$sendID}, recvID={$recvID}, groupID={$groupID}, contentType={$contentType}, sessionType={$sessionType}");
|
||||
$result = $this->sendMessage($msg);
|
||||
$this->log($output, "{$progress} API返回: " . json_encode($result, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
if ($result['success'] ?? false) {
|
||||
$this->stats['messages']['success']++;
|
||||
if ($processed % 100 == 0) {
|
||||
$this->log($output, "{$progress} ✅ 发送成功");
|
||||
}
|
||||
} else {
|
||||
$this->stats['messages']['failed']++;
|
||||
$this->log($output, "{$progress} ❌ 发送失败: " . ($result['errMsg'] ?? '未知错误'));
|
||||
// 遇到NotInGroupYetError时跳过,继续迁移其他消息
|
||||
if (strpos(($result['errMsg'] ?? ''), 'NotInGroupYetError') === false) {
|
||||
// 遇到其他错误时退出
|
||||
throw new \Exception("消息发送失败: " . ($result['errMsg'] ?? '未知错误'));
|
||||
} else {
|
||||
$this->log($output, "{$progress} ℹ️ 跳过NotInGroupYetError错误,继续迁移");
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->stats['messages']['failed']++;
|
||||
$this->log($output, "{$progress} ❌ 发送异常: " . $e->getMessage());
|
||||
// 遇到NotInGroupYetError异常时跳过,继续迁移其他消息
|
||||
if (strpos($e->getMessage(), 'NotInGroupYetError') === false) {
|
||||
// 遇到其他异常时退出
|
||||
throw $e;
|
||||
} else {
|
||||
$this->log($output, "{$progress} ℹ️ 跳过NotInGroupYetError异常,继续迁移");
|
||||
}
|
||||
}
|
||||
if ($this->delay > 0) {
|
||||
usleep($this->delay * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sendMessage(array $msg): array
|
||||
{
|
||||
$sendID = (string)($msg['send_id'] ?? $msg['sendID'] ?? '');
|
||||
$recvID = (string)($msg['recv_id'] ?? $msg['recvID'] ?? '');
|
||||
$groupID = (string)($msg['group_id'] ?? $msg['groupID'] ?? '');
|
||||
$contentType = (int)($msg['content_type'] ?? $msg['contentType'] ?? 101);
|
||||
$sessionType = (int)($msg['session_type'] ?? $msg['sessionType'] ?? 1);
|
||||
$sendTime = (int)($msg['send_time'] ?? $msg['sendTime'] ?? 0);
|
||||
$content = $msg['content'] ?? '';
|
||||
$ex = (string)($msg['ex'] ?? '');
|
||||
|
||||
if (empty($sendID)) {
|
||||
return ['success' => false, 'errMsg' => 'sendID为空'];
|
||||
}
|
||||
|
||||
$contentData = $this->parseContent($content, $contentType);
|
||||
|
||||
// 构建消息数据
|
||||
$messageData = [
|
||||
'content' => $contentData,
|
||||
'contentType' => $contentType,
|
||||
'sendTime' => $sendTime,
|
||||
'ex' => $ex,
|
||||
'isOnlineOnly' => false,
|
||||
'notOfflinePush' => true
|
||||
];
|
||||
|
||||
// 根据会话类型调用不同的发送方法
|
||||
if ($sessionType == 1 && !empty($recvID)) {
|
||||
// 单聊
|
||||
$result = $this->sdk->message->sendSingleMessage($sendID, $recvID, $messageData);
|
||||
} elseif (!empty($groupID)) {
|
||||
// 群聊
|
||||
$result = $this->sdk->message->sendGroupMessage($sendID, $groupID, $messageData);
|
||||
} else {
|
||||
return ['success' => false, 'errMsg' => '缺少必要的参数'];
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => !($result['errCode'] ?? 0),
|
||||
'errMsg' => $result['errMsg'] ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
private function parseContent($content, int $contentType): array
|
||||
{
|
||||
if (is_string($content)) {
|
||||
$decoded = json_decode($content, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
return $decoded;
|
||||
}
|
||||
return ['content' => $content, 'text' => $content];
|
||||
}
|
||||
if (is_array($content)) {
|
||||
return $content;
|
||||
}
|
||||
return ['content' => '', 'text' => ''];
|
||||
}
|
||||
|
||||
private function bsonToArray($data): array
|
||||
{
|
||||
if ($data instanceof \MongoDB\Model\BSONArray) {
|
||||
return $data->getArrayCopy();
|
||||
}
|
||||
if ($data instanceof \MongoDB\Model\BSONDocument) {
|
||||
return $data->getArrayCopy();
|
||||
}
|
||||
if (is_object($data)) {
|
||||
return json_decode(json_encode($data), true);
|
||||
}
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
|
||||
private function printStats(OutputInterface $output): void
|
||||
{
|
||||
$this->log($output, "");
|
||||
$this->log($output, "╔════════════════════════════════════════════════════════════╗");
|
||||
$this->log($output, "║ 迁移统计报告 ║");
|
||||
$this->log($output, "╠════════════════════════════════════════════════════════════╣");
|
||||
$this->log($output, "║ 用户: 总数 {$this->stats['users']['total']}, 成功 {$this->stats['users']['success']}, 失败 {$this->stats['users']['failed']}");
|
||||
$this->log($output, "║ 群组: 总数 {$this->stats['groups']['total']}, 成功 {$this->stats['groups']['success']}, 失败 {$this->stats['groups']['failed']}");
|
||||
$this->log($output, "║ 成员: 总数 {$this->stats['members']['total']}, 成功 {$this->stats['members']['success']}, 失败 {$this->stats['members']['failed']}");
|
||||
$this->log($output, "║ 消息: 总数 {$this->stats['messages']['total']}, 成功 {$this->stats['messages']['success']}, 失败 {$this->stats['messages']['failed']}, 跳过 {$this->stats['messages']['skipped']}");
|
||||
$this->log($output, "╚════════════════════════════════════════════════════════════╝");
|
||||
}
|
||||
|
||||
private function log(OutputInterface $output, string $message): void
|
||||
{
|
||||
$output->writeln($message);
|
||||
}
|
||||
/**
|
||||
* 获取OpenIM SDK实例
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
function getSdk()
|
||||
{
|
||||
if ($this->sdk) {
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
$this->sdk = new \support\OpenImSdk\Client([
|
||||
'host' => config('openim.server'),
|
||||
'secret' => config('openim.secret'),
|
||||
]);
|
||||
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
private function initConnections(OutputInterface $output): void
|
||||
{
|
||||
$this->log($output, "正在初始化连接...");
|
||||
|
||||
$this->getSdk();
|
||||
|
||||
$uri = 'mongodb://commie:n1e5a6s6m7@127.0.0.1:37017/tettt?authSource=admin';
|
||||
$this->oldManager = new \MongoDB\Driver\Manager($uri);
|
||||
|
||||
$this->log($output, "✅ 连接成功");
|
||||
}
|
||||
|
||||
private function cleanExistingData(OutputInterface $output,$collections=[]): void
|
||||
{
|
||||
// 记录开始清理数据的日志信息
|
||||
$this->log($output, "\n═════════════════ 清理现有数据 ═════════════════");
|
||||
$this->log($output, "");
|
||||
|
||||
// 构建新数据库(OpenIM v3)的MongoDB连接URI
|
||||
$uri = 'mongodb://commie:n1e5a6s6m7@127.0.0.1:37017/openim_v3?authSource=admin';
|
||||
// 创建MongoDB驱动管理器实例,用于操作新数据库
|
||||
$this->newManager = new \MongoDB\Driver\Manager($uri);
|
||||
try {
|
||||
// 记录开始清理数据的状态
|
||||
$this->log($output, "正在清理mongodb数据...");
|
||||
// 定义需要清空的数据集合列表(保留user集合,清空其他所有业务数据)
|
||||
// 遍历所有需要清理的集合,逐个执行清空操作
|
||||
foreach ($collections as $collection) {
|
||||
try {
|
||||
// 创建批量写入操作对象
|
||||
$bulk = new \MongoDB\Driver\BulkWrite;
|
||||
// 添加删除所有文档的操作(空条件表示删除全部)
|
||||
$bulk->delete([]);
|
||||
// 执行批量删除操作,指定数据库和集合名称
|
||||
$this->newManager->executeBulkWrite('openim_v3.' . $collection, $bulk);
|
||||
// 记录该集合清空成功的日志
|
||||
$this->log($output, "已清空集合: {$collection}");
|
||||
} catch (\Exception $e) {
|
||||
// 单个集合清空失败时记录警告信息,不影响其他集合的清理
|
||||
$this->log($output, "⚠️ 清空集合 {$collection} 失败: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->log($output, "正在清理redis数据...");
|
||||
$redis = new \Redis();
|
||||
$host = '127.0.0.1';
|
||||
$port = 16379;
|
||||
$password = 'n1e5a6s6m7';
|
||||
|
||||
$output->writeln("连接 Redis: {$host}:{$port}");
|
||||
|
||||
if ($redis->connect($host, $port)) {
|
||||
if (!empty($password)) {
|
||||
$redis->auth($password);
|
||||
}
|
||||
|
||||
$result = $redis->flushAll();
|
||||
|
||||
if ($result) {
|
||||
$output->writeln("✅ Redis 清空成功");
|
||||
} else {
|
||||
$output->writeln("❌ Redis 清空失败");
|
||||
}
|
||||
} else {
|
||||
$output->writeln("❌ 无法连接到 Redis");
|
||||
}
|
||||
// 记录所有数据清理完成的日志
|
||||
$this->log($output, "✅ 数据清理完成");
|
||||
} catch (\Exception $e) {
|
||||
// 捕获整体清理过程中的异常,记录错误但不抛出,确保程序继续执行
|
||||
$this->log($output, "❌ 清理数据失败: " . $e->getMessage());
|
||||
// 不抛出异常,继续执行
|
||||
}
|
||||
}
|
||||
|
||||
private function queryOldDb(string $collection, array $filter = [], array $options = []): array
|
||||
{
|
||||
$query = new \MongoDB\Driver\Query($filter, $options);
|
||||
$cursor = $this->oldManager->executeQuery('tettt.' . $collection, $query);
|
||||
$result = [];
|
||||
foreach ($cursor as $doc) {
|
||||
$result[] = $this->bsonToArray($doc);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份MongoDB数据
|
||||
* @param OutputInterface $output
|
||||
* @param string $step
|
||||
* @return string
|
||||
*/
|
||||
private function backupMongoDB(OutputInterface $output, string $step): string
|
||||
{
|
||||
$this->log($output, "═════════════════ 备份MongoDB数据 ═════════════════");
|
||||
|
||||
// 确保备份目录存在
|
||||
if (!is_dir($this->backupDir)) {
|
||||
mkdir($this->backupDir, 0755, true);
|
||||
}
|
||||
|
||||
// 生成备份文件名
|
||||
$timestamp = date('YmdHis');
|
||||
$backupFile = "{$this->backupDir}/openim_v3_{$step}_{$timestamp}.json";
|
||||
|
||||
try {
|
||||
// 使用现有的新数据库连接
|
||||
if (!$this->newManager) {
|
||||
$uri = 'mongodb://commie:n1e5a6s6m7@127.0.0.1:37017/openim_v3?authSource=admin';
|
||||
$this->newManager = new \MongoDB\Driver\Manager($uri);
|
||||
}
|
||||
|
||||
// 获取所有集合
|
||||
$command = new \MongoDB\Driver\Command(['listCollections' => 1]);
|
||||
$cursor = $this->newManager->executeCommand('openim_v3', $command);
|
||||
$collections = $cursor->toArray();
|
||||
|
||||
//$this->log($output, "找到 " . count($collections) . " 个集合");
|
||||
|
||||
$backupData = [];
|
||||
|
||||
// 备份每个集合
|
||||
foreach ($collections as $collection) {
|
||||
$collectionName = $collection->name;
|
||||
if (in_array($collectionName, ['system.indexes', 'system.profile'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//$this->log($output, "备份集合: {$collectionName}");
|
||||
|
||||
$query = new \MongoDB\Driver\Query([]);
|
||||
$cursor = $this->newManager->executeQuery('openim_v3.' . $collectionName, $query);
|
||||
|
||||
$documents = [];
|
||||
foreach ($cursor as $doc) {
|
||||
$document = $this->bsonToArray($doc);
|
||||
// 处理ObjectId
|
||||
if (isset($document['_id']) && is_array($document['_id']) && isset($document['_id']['$oid'])) {
|
||||
$document['_id'] = $document['_id']['$oid'];
|
||||
}
|
||||
$documents[] = $document;
|
||||
}
|
||||
|
||||
if (!empty($documents)) {
|
||||
$backupData[$collectionName] = $documents;
|
||||
//$this->log($output, " - 备份了 " . count($documents) . " 条记录");
|
||||
}
|
||||
}
|
||||
|
||||
// 保存备份文件
|
||||
file_put_contents($backupFile, json_encode($backupData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
|
||||
$this->log($output, "✅ 备份成功: {$backupFile} (" . filesize($backupFile) . " 字节)");
|
||||
$this->currentBackup = $backupFile;
|
||||
return $backupFile;
|
||||
} catch (\Exception $e) {
|
||||
$this->log($output, "❌ 备份失败: " . $e->getMessage());
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚MongoDB数据
|
||||
* @param OutputInterface $output
|
||||
* @param string $backupFile
|
||||
* @return bool
|
||||
*/
|
||||
private function restoreMongoDB(OutputInterface $output, string $backupFile): bool
|
||||
{
|
||||
$this->log($output, "═════════════════ 回滚MongoDB数据 ═════════════════");
|
||||
|
||||
if (!file_exists($backupFile)) {
|
||||
$this->log($output, "❌ 备份文件不存在: {$backupFile}");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 读取备份文件
|
||||
$backupData = json_decode(file_get_contents($backupFile), true);
|
||||
if (empty($backupData)) {
|
||||
$this->log($output, "❌ 备份文件为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 连接到新数据库
|
||||
$uri = 'mongodb://commie:n1e5a6s6m7@127.0.0.1:37017/openim_v3?authSource=admin';
|
||||
$manager = new \MongoDB\Driver\Manager($uri);
|
||||
|
||||
// 清空所有集合
|
||||
$this->log($output, "清空现有数据...");
|
||||
foreach (array_keys($backupData) as $collectionName) {
|
||||
$bulk = new \MongoDB\Driver\BulkWrite;
|
||||
$bulk->delete([]);
|
||||
$manager->executeBulkWrite('openim_v3.' . $collectionName, $bulk);
|
||||
}
|
||||
|
||||
// 恢复数据
|
||||
foreach ($backupData as $collectionName => $documents) {
|
||||
$documentCount = count($documents);
|
||||
$this->log($output, "恢复集合: {$collectionName} ({$documentCount} 条记录)");
|
||||
|
||||
$bulk = new \MongoDB\Driver\BulkWrite;
|
||||
foreach ($documents as $document) {
|
||||
// 处理_id字段
|
||||
if (isset($document['_id']) && is_string($document['_id'])) {
|
||||
// 尝试创建ObjectId
|
||||
try {
|
||||
$document['_id'] = new \MongoDB\BSON\ObjectId($document['_id']);
|
||||
} catch (\Exception $e) {
|
||||
// 如果不是有效的ObjectId格式,保持原样
|
||||
}
|
||||
}
|
||||
$bulk->insert($document);
|
||||
}
|
||||
|
||||
$result = $manager->executeBulkWrite('openim_v3.' . $collectionName, $bulk);
|
||||
$this->log($output, "恢复成功: {$result->getInsertedCount()} 条记录");
|
||||
}
|
||||
|
||||
$this->log($output, "✅ 回滚成功");
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
$this->log($output, "❌ 回滚失败: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,780 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use support\think\Db;
|
||||
|
||||
class OpenIm extends Command
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
$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]);
|
||||
}
|
||||
|
||||
$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++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function convertUserID(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$userID = $input->getOption('userID');
|
||||
$this->_convertUserID($userID,$output);
|
||||
return 0;
|
||||
}
|
||||
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++){
|
||||
$newUserID = \support\Encrypt::intEncode($i);
|
||||
Db::name('User')->where('id',$i)->update(['userID'=>$newUserID]);
|
||||
}
|
||||
cp("用户ID:{$userID} -> {$newUserID} 转换完成");
|
||||
return 0;
|
||||
}
|
||||
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']);
|
||||
}
|
||||
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(!empty($savedata)){
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("Friend 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
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(!empty($savedata)){
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("FriendRequest 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
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']);
|
||||
}
|
||||
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);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("Group 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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']);
|
||||
}
|
||||
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(!empty($savedata)){
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("GroupMember 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
|
||||
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']);
|
||||
}
|
||||
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(!empty($savedata)){
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("Black 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
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']);
|
||||
}
|
||||
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']);
|
||||
}
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
//$item->save();
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("Conversation 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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'])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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']);
|
||||
}
|
||||
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']);
|
||||
}
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("GroupRequest 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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([
|
||||
'conversation_id' => $this->handlerConversationID($item['conversation_id'])
|
||||
]);
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("Seq 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
|
||||
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'])
|
||||
]);
|
||||
$count++;
|
||||
}
|
||||
if($count){
|
||||
$output->writeln("SeqUser 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
}
|
||||
|
||||
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){
|
||||
$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'])){
|
||||
$logs = $item['logs'];
|
||||
foreach($logs as $key => $log){
|
||||
if(isset($log['e_id'])){
|
||||
$logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']);
|
||||
}
|
||||
}
|
||||
$savedata['logs'] = $logs;
|
||||
}
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("ConversationVersion 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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'])){
|
||||
$logs = $item['logs'];
|
||||
foreach($logs as $key => $log){
|
||||
if(isset($log['e_id'])){
|
||||
$logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']);
|
||||
}
|
||||
}
|
||||
$savedata['logs'] = $logs;
|
||||
}
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("FriendVersion 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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'])){
|
||||
$logs = $item['logs'];
|
||||
foreach($logs as $key => $log){
|
||||
if(isset($log['e_id'])){
|
||||
$logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']);
|
||||
}
|
||||
}
|
||||
$savedata['logs'] = $logs;
|
||||
}
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("GroupMemberVersion 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
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'])){
|
||||
$logs = $item['logs'];
|
||||
foreach($logs as $key => $log){
|
||||
if(isset($log['e_id'])){
|
||||
$logs[$key]['e_id'] = $this->handlerConversationID($log['e_id']);
|
||||
}
|
||||
}
|
||||
$savedata['logs'] = $logs;
|
||||
}
|
||||
$model->where('id',$item['id'])->update($savedata);
|
||||
$count++;
|
||||
}
|
||||
$output->writeln("GroupJoinVersion 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
function turnID($id){
|
||||
if($id == 'official_team' || $id == 'officialteam'){
|
||||
return 'SystemOfficialTeam';
|
||||
}
|
||||
if($id == 'group_bot'){
|
||||
return 'SystemGroupBot';
|
||||
}
|
||||
if($id == 'imAdmin'){
|
||||
return 'SystemImAdmin';
|
||||
}
|
||||
if($id == 'system'){
|
||||
return 'SystemAccount';
|
||||
}
|
||||
try{
|
||||
return \support\Encrypt::intEncode(\support\Encrypt::userIDDecode($id));
|
||||
}catch(\Exception $e){
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
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);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
//log_alert($contentTypes,'cansnow');
|
||||
//$output->writeln("Msg 表转换完成,共 {$count} 条记录");
|
||||
}
|
||||
|
||||
function handlerConversationID($data){
|
||||
$data = str_replace('official_team','officialteam',$data);
|
||||
$cids = explode('_',$data);
|
||||
$cids[1] = $this->turnID($cids[1]);
|
||||
if(count($cids) >2){
|
||||
$cids[2] = $this->turnID($cids[2]);
|
||||
}
|
||||
$data = implode('_',$cids);
|
||||
return $data;
|
||||
}
|
||||
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])){
|
||||
$data[$urlField] = $this->cdn($data[$urlField]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function cdn($url){
|
||||
if(substr(0,2) =='//'){
|
||||
$url = 'https:'.$url;
|
||||
}
|
||||
if(substr(0,7) =='http://'){
|
||||
$url = 'https://'.substr($url,7);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
function getUser(){
|
||||
$im = $this->getSdk();
|
||||
$data = $im->user->searchNotificationAccount('');
|
||||
cp($data);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* @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');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param InputInterface $input
|
||||
* @param OutputInterface $output
|
||||
* @return int
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$action = $input->getOption('action');
|
||||
if(!$action){
|
||||
$output->writeln('空操作');
|
||||
return self::FAILURE;
|
||||
}
|
||||
if(method_exists($this, $action)){
|
||||
return $this->$action($input, $output);
|
||||
}
|
||||
$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;
|
||||
}
|
||||
}
|
||||
Executable
+1620
File diff suppressed because it is too large
Load Diff
+9
-27
@@ -40,33 +40,15 @@ 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){
|
||||
// $IM = new \support\OpenImSdk\Client([
|
||||
// 'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
// 'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
// ]);
|
||||
$user_id = $input->getOption('user_id');
|
||||
if(!$user_id){
|
||||
return false;
|
||||
@@ -110,7 +92,7 @@ class User extends Command
|
||||
$im = $this->getSdk();
|
||||
try {
|
||||
for($i=313;$i<333;$i++){
|
||||
$mobile = '12600000000'+$i;
|
||||
$mobile = (12600000000+$i).'';
|
||||
$password = \support\Random::build('23456789abcdefghjklmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ',8);
|
||||
$data = [
|
||||
'mobile' => $mobile,
|
||||
@@ -147,8 +129,8 @@ class User extends Command
|
||||
return $this->sdk;
|
||||
}
|
||||
$this->sdk = new \support\OpenImSdk\Client([
|
||||
'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
'host' => config('openim.server'), // OpenIM API地址
|
||||
'secret' => config('openim.secret'), // OpenIM密钥
|
||||
]);
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
@@ -20,20 +20,8 @@ class HookController{
|
||||
$userID= Input('userID');
|
||||
$nickname= Input('nickname');
|
||||
$users = Input('users');
|
||||
$im = new \support\OpenImSdk\Client([
|
||||
'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
]);
|
||||
foreach($users as $k=>$v){
|
||||
|
||||
// 'userID' => '0EO9K107ON',
|
||||
// 'nickname' => '坏蛋',
|
||||
// 'faceURL' => '/static/img/avatar.png',
|
||||
// 'ex' => '',
|
||||
// 'createTime' => 0,
|
||||
// 'appMangerLevel' => 0,
|
||||
// 'globalRecvMsgOpt' => 0,
|
||||
$im->message->sendBusinessNotification('official_team',$v['userID'],[
|
||||
$this->getSdk()->message->sendBusinessNotification('SystemOfficialTeam',$v['userID'],[
|
||||
'contentType' => 101,
|
||||
'textElem' => [
|
||||
'content' => '欢迎使用'.Config('site.name')
|
||||
@@ -374,8 +362,8 @@ class HookController{
|
||||
return $this->sdk;
|
||||
}
|
||||
$this->sdk = new \support\OpenImSdk\Client([
|
||||
'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
'host' => config('openim.server'), // OpenIM API地址
|
||||
'secret' => config('openim.secret'), // OpenIM密钥
|
||||
]);
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace app\Enum;
|
||||
trait BaseEnum {
|
||||
public static function toArray(): array
|
||||
{
|
||||
return [
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 获取当前状态的描述文本
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return self::toArray()[$this->value];
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地从值创建枚举实例
|
||||
*/
|
||||
public static function tryFromValue(int $value): ?self
|
||||
{
|
||||
return self::tryFrom($value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace app\enum\Payment;
|
||||
use app\enum\BaseEnum;
|
||||
/**
|
||||
* 支付方式
|
||||
*/
|
||||
enum Method: string
|
||||
{
|
||||
use BaseEnum;
|
||||
/**
|
||||
* 微信
|
||||
*/
|
||||
case WECHAT = 'weichat';
|
||||
|
||||
/**
|
||||
* 支付宝
|
||||
*/
|
||||
case ALIPAY = 'alipay';
|
||||
|
||||
/**
|
||||
* 获取所有状态映射数组
|
||||
*/
|
||||
public static function toArray(): array
|
||||
{
|
||||
return [
|
||||
self::WECHAT->value => __('微信'),
|
||||
self::ALIPAY->value => __('支付宝')
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace app\enum\Payment;
|
||||
use app\enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* 支付状态常量
|
||||
*/
|
||||
enum Status: string
|
||||
{
|
||||
/**
|
||||
* 等待支付
|
||||
*/
|
||||
case CREATED = 'created';
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
case SUCCESS = 'success';
|
||||
/**
|
||||
* 完成
|
||||
*/
|
||||
case COMPLETE = 'complete';
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
case FAIL = 'fail';
|
||||
/**
|
||||
* 退款
|
||||
*/
|
||||
case REFUNDED = 'refunded';
|
||||
|
||||
/**
|
||||
* 获取所有状态映射数组
|
||||
*/
|
||||
public static function toArray(): array
|
||||
{
|
||||
return [
|
||||
self::CREATED->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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace app\enum\Payment;
|
||||
use app\enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* 支付用途
|
||||
*/
|
||||
enum Type: string
|
||||
{
|
||||
use BaseEnum;
|
||||
/**
|
||||
* 充值
|
||||
*/
|
||||
case RECHARGE = 'recharge';
|
||||
/**
|
||||
* 商品
|
||||
*/
|
||||
case GOODS = 'goods';
|
||||
/**
|
||||
* 服务
|
||||
*/
|
||||
case SERVICE = 'service';
|
||||
/**
|
||||
* 其他
|
||||
*/
|
||||
case OTHER = 'other';
|
||||
|
||||
/**
|
||||
* 获取所有状态映射数组
|
||||
*/
|
||||
public static function toArray(): array
|
||||
{
|
||||
return [
|
||||
self::RECHARGE->value => __('充值'),
|
||||
self::GOODS->value => __('商品'),
|
||||
self::SERVICE->value => __('服务'),
|
||||
self::OTHER->value => __('其他')
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+297
-84
@@ -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 框架命令列表
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* 支付订单模型
|
||||
*/
|
||||
class PaymentOrder extends Base
|
||||
{
|
||||
/**
|
||||
* 数据表名
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'payment_order';
|
||||
|
||||
/**
|
||||
* 获取状态列表
|
||||
* @return array
|
||||
*/
|
||||
public static function getStatusList(): array
|
||||
{
|
||||
return \app\enum\Payment\Status::toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型列表
|
||||
* @return array
|
||||
*/
|
||||
public static function getTypeList(): array
|
||||
{
|
||||
return \app\enum\Payment\Type::toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付类型列表
|
||||
* @return array
|
||||
*/
|
||||
public static function getPayTypeList(): array
|
||||
{
|
||||
return \app\enum\Payment\Method::toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号查询
|
||||
* @param string $orderNo
|
||||
* @return array|null
|
||||
*/
|
||||
public static function findByOrderNo(string $orderNo): ?array
|
||||
{
|
||||
return self::where('order_no', $orderNo)->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
/**
|
||||
* 退款模型
|
||||
*/
|
||||
class PaymentRefund extends Base
|
||||
{
|
||||
/**
|
||||
* 获取状态列表
|
||||
* @return array
|
||||
*/
|
||||
public static function getStatusList(): array
|
||||
{
|
||||
return [
|
||||
'success' => '退款成功',
|
||||
'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;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
$app = require_once __DIR__ . '/bootstrap/app.php';
|
||||
|
||||
$mongoConfig = config('thinkorm.connections.mongo');
|
||||
$dsn = $mongoConfig['type'] . '://' . $mongoConfig['hostname'] . ':' . $mongoConfig['hostport'];
|
||||
$mongo = new MongoDB\Client($dsn);
|
||||
$db = $mongo->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";
|
||||
}
|
||||
+2
-1
@@ -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. "
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
return [
|
||||
'server' => 'http://127.0.0.1:10002',
|
||||
'admin' => 'imAdmin',
|
||||
'secret' => 'n1e5a6s6m7',
|
||||
];
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* @desc 支付配置文件
|
||||
* @author Tinywan(ShaoBo Wan)
|
||||
* @date 2022/03/11 20:15
|
||||
*/
|
||||
return [
|
||||
'_force' => 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)
|
||||
]
|
||||
];
|
||||
@@ -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;
|
||||
|
||||
@@ -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`);
|
||||
@@ -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;
|
||||
@@ -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='管理员表';
|
||||
@@ -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='管理员角色表';
|
||||
@@ -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='管理员角色';
|
||||
@@ -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='权限规则';
|
||||
@@ -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;
|
||||
@@ -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='内容表';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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='系统配置';
|
||||
@@ -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='内容';
|
||||
@@ -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;
|
||||
@@ -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='朋友圈动态表';
|
||||
@@ -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='朋友圈评论表';
|
||||
@@ -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='朋友圈点赞表';
|
||||
@@ -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='朋友圈设置';
|
||||
@@ -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;
|
||||
@@ -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='支付订单表';
|
||||
@@ -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='退款表';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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='用户表';
|
||||
@@ -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;
|
||||
@@ -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='管理员角色';
|
||||
@@ -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='权限规则';
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -0,0 +1 @@
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjEr+goQyORPL5XY2Jg5Ye4RCpefCRGf+BqkRothp8AF5gorGccOY6AnkXEkpp+xa7Trn1xOTFqHKWcuWz7iHxtZjJYpxe/GvLp+R3oc6exV9lRKSLoEzsnCJmpG/0VbEqWgUgv4CgmT7fEha/aKbaRfrT+aX9eTpC7BHoWFnVJ1Mmg642GZ+/LLuEgSd+Gfo42AM2Yhf15ojh1LDsJRrm94rMrJ6PydikJBS0vWdJs+4MHyRkFyHPQDtxESaryxguxdm7oM0jxKnsC9ms2H6/SETcCTLseUM+mHbxfKl6cRchQ7u6BiUkY5o3nlR40uNqOj4A181Izk0pbPtXQeZ3wIDAQAB
|
||||
@@ -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
|
||||
@@ -24,8 +24,8 @@ class OpenimBase extends Crud
|
||||
return $this->sdk;
|
||||
}
|
||||
$this->sdk = new \support\OpenImSdk\Client([
|
||||
'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
'host' => config('openim.server'), // OpenIM API地址
|
||||
'secret' => config('openim.secret'), // OpenIM密钥
|
||||
]);
|
||||
return $this->sdk;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ class Config implements MiddlewareInterface
|
||||
'config' => $config
|
||||
]);
|
||||
$IM = new \support\OpenImSdk\Client([
|
||||
'host' => 'http://127.0.0.1:10002', // OpenIM API地址
|
||||
'secret' => 'n1e5a6s6m7', // OpenIM密钥
|
||||
'host' => config('openim.server'), // OpenIM API地址
|
||||
'secret' => config('openim.secret'), // OpenIM密钥
|
||||
]);
|
||||
$request->IM = $IM;
|
||||
return $handler($request);
|
||||
|
||||
Executable → Regular
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
return array (
|
||||
'Article does not exist' => 'Article does not exist',
|
||||
'Invalid parameter' => 'Invalid parameter',
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return array (
|
||||
'The field %field% must be not empty. ' => 'The field %field% must be not empty. ',
|
||||
);
|
||||
@@ -13,7 +13,6 @@ return array (
|
||||
'Only one verification code can be sent within %second% seconds' => 'Only one verification code can be sent within %second% seconds',
|
||||
'Email sent successfully' => 'Email sent successfully',
|
||||
'Captcha is incorrect' => 'Captcha is incorrect',
|
||||
'Mt email code' => 'Mt email code',
|
||||
'错误的邀请码' => '错误的邀请码',
|
||||
'Incoret param' => 'Incoret param',
|
||||
'Trade password must be 6 characters' => 'Trade password must be 6 characters',
|
||||
@@ -22,4 +21,6 @@ return array (
|
||||
'Username is incorrect' => 'Username is incorrect',
|
||||
'Password must be 6 to 32 characters' => 'Password must be 6 to 32 characters',
|
||||
'Trade password must be 6-32 characters' => 'Trade password must be 6-32 characters',
|
||||
'Unknown verify type' => 'Unknown verify type',
|
||||
'Invalid parameter' => 'Invalid parameter',
|
||||
);
|
||||
Executable → Regular
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
return array (
|
||||
'参数错误' => '参数错误',
|
||||
'Invalid parameter' => 'Invalid parameter',
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
return array (
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
return array (
|
||||
'Invalid verify type' => 'Invalid verify type',
|
||||
'Security verify successfully' => 'Security verify successfully',
|
||||
'Incorrect mobile number format' => 'Incorrect mobile number format',
|
||||
'Mobile number already exists' => 'Mobile number already exists',
|
||||
'Mobile number bound successfully' => 'Mobile number bound successfully',
|
||||
'Incorrect email format' => 'Incorrect email format',
|
||||
'Email already exists' => 'Email already exists',
|
||||
'Email bound successfully' => 'Email bound successfully',
|
||||
'Username length must be between 3 and 20 characters' => 'Username length must be between 3 and 20 characters',
|
||||
'Username already exists' => 'Username already exists',
|
||||
'Username bound successfully' => 'Username bound successfully',
|
||||
'Mobile number not bound' => 'Mobile number not bound',
|
||||
'Mobile number unbound successfully' => 'Mobile number unbound successfully',
|
||||
'Email not bound' => 'Email not bound',
|
||||
'Email unbound successfully' => 'Email unbound successfully',
|
||||
);
|
||||
@@ -8,4 +8,5 @@ return array (
|
||||
'Incoret param' => 'Incoret param',
|
||||
'You have verified' => 'You have verified',
|
||||
'ID card already exists' => 'ID card already exists',
|
||||
'Invalid parameter' => 'Invalid parameter',
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
return array (
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
return array (
|
||||
'Incorrect title' => 'Incorrect title',
|
||||
'Incorrect amount' => 'Incorrect amount',
|
||||
'Incorrect quantity' => 'Incorrect quantity',
|
||||
'Insufficient balance' => 'Insufficient balance',
|
||||
'Incorrect title' => '标题不正确',
|
||||
'Incorrect amount' => '金额不正确',
|
||||
'Incorrect quantity' => '数量不正确',
|
||||
'Insufficient balance' => '余额不足',
|
||||
);
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return array (
|
||||
'The field %field% must be not empty. ' => '%field%不能为空. ',
|
||||
);
|
||||
@@ -15,12 +15,12 @@ return array (
|
||||
'Reset Trade password successful' => '重置交易密码成功',
|
||||
'Only one verification code can be sent within %second% seconds' => '在%秒%秒内只能发送一个验证码',
|
||||
'Email sent successfully' => '邮件发送成功',
|
||||
'Mt email code' => 'Mt email code',
|
||||
'错误的邀请码' => '错误的邀请码',
|
||||
'Incoret param' => 'Incoret param',
|
||||
'Unknown register way' => 'Unknown register way',
|
||||
'Username is incorrect' => 'Username is incorrect',
|
||||
'Password must be 6 to 32 characters' => 'Password must be 6 to 32 characters',
|
||||
'Trade password must be 6-32 characters' => 'Trade password must be 6-32 characters',
|
||||
'SMS sent successfully' => 'SMS sent successfully',
|
||||
'Unknown register way' => '未知注册方式',
|
||||
'Username is incorrect' => '用户名不正确',
|
||||
'Password must be 6 to 32 characters' => '密码长度必须为6到32个字符',
|
||||
'Trade password must be 6-32 characters' => '交易密码长度必须为6-32个字符',
|
||||
'SMS sent successfully' => '短信发送成功',
|
||||
'Unknown verify type' => '未知验证类型',
|
||||
'Invalid parameter' => '参数无效',
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
return array (
|
||||
);
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
return array (
|
||||
'Product does not exist' => 'Product does not exist',
|
||||
'Gift is incorrect' => 'Gift is incorrect',
|
||||
'Denomination is incorrect' => 'Denomination is incorrect',
|
||||
'Insufficient balance' => 'Insufficient balance',
|
||||
'Quantity is incorrect' => 'Quantity is incorrect',
|
||||
'Gift stock is insufficient' => 'Gift stock is insufficient',
|
||||
'Product does not exist' => '产品不存在',
|
||||
'Gift is incorrect' => '礼物不正确',
|
||||
'Denomination is incorrect' => '面额不正确',
|
||||
'Insufficient balance' => '余额不足',
|
||||
'Quantity is incorrect' => '数量不正确',
|
||||
'Gift stock is insufficient' => '礼物库存不足',
|
||||
);
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
return array (
|
||||
'参数错误' => '参数错误',
|
||||
'Invalid parameter' => '参数无效',
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
return array (
|
||||
);
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
return array (
|
||||
'Invalid verify type' => '验证类型无效',
|
||||
'Security verify successfully' => '安全验证成功',
|
||||
'Incorrect mobile number format' => '手机号码格式不正确',
|
||||
'Mobile number already exists' => '手机号码已存在',
|
||||
'Mobile number bound successfully' => '手机号码绑定成功',
|
||||
'Incorrect email format' => '邮箱格式不正确',
|
||||
'Email already exists' => '邮箱已存在',
|
||||
'Email bound successfully' => '邮箱绑定成功',
|
||||
'Username length must be between 3 and 20 characters' => '用户名长度必须在3到20个字符之间',
|
||||
'Username already exists' => '用户名已存在',
|
||||
'Username bound successfully' => '用户名绑定成功',
|
||||
'Mobile number not bound' => '手机号码未绑定',
|
||||
'Mobile number unbound successfully' => '手机号码解绑成功',
|
||||
'Email not bound' => '邮箱未绑定',
|
||||
'Email unbound successfully' => '邮箱解绑成功',
|
||||
);
|
||||
@@ -6,6 +6,6 @@ return array (
|
||||
'Out of stock product' => '产品缺货',
|
||||
'Insufficient balance' => '余额不足',
|
||||
'You can only purchase %max_quantity% copies' => '你只能购买%max_quantity%份',
|
||||
'Quantity is incorrect' => 'Quantity is incorrect',
|
||||
'Quantity is incorrect' => '数量不正确',
|
||||
'微量包每个用户只能购买一个' => '微量包每个用户只能购买一个',
|
||||
);
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
return array (
|
||||
'Product does not exist' => 'Product does not exist',
|
||||
'Questionnaire does not exist' => 'Questionnaire does not exist',
|
||||
'Incorrect parameter' => 'Incorrect parameter',
|
||||
'Server is not exist' => 'Server is not exist',
|
||||
'Server is not exist %sadds_f%' => 'Server is not exist %sadds_f%',
|
||||
'Product does not exist' => '产品不存在',
|
||||
'Questionnaire does not exist' => '问卷不存在',
|
||||
'Incorrect parameter' => '参数不正确',
|
||||
'Server is not exist' => '服务器不存在',
|
||||
'Server is not exist %sadds_f%' => '服务器不存在 %sadds_f%',
|
||||
);
|
||||
@@ -8,5 +8,5 @@ return array (
|
||||
'exist' => '已存在',
|
||||
'You have reached the limit of uncompleted orders ( %max% ). Please complete the existing orders before trying to create another one.' => '订单已达上限,请完成订单或15分钟后再试',
|
||||
'Minimum recharge of %num%' => '最低充值额 %num%',
|
||||
'Order not exsit' => 'Order not exsit',
|
||||
'Order not exsit' => '订单不存在',
|
||||
);
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
return array (
|
||||
'Please login first' => 'Please login first',
|
||||
'Please login first' => '请先登录',
|
||||
'今日已签到' => '今日已签到',
|
||||
'请上传2张图片' => '请上传2张图片',
|
||||
'请明日再来' => '请明日再来',
|
||||
@@ -9,5 +9,5 @@ return array (
|
||||
'日期格式错误' => '日期格式错误',
|
||||
'补签日期不能大于今天' => '补签日期不能大于今天',
|
||||
'该日已签到' => '该日已签到',
|
||||
'Please complete real-name verification first' => 'Please complete real-name verification first',
|
||||
'Please complete real-name verification first' => '请先完成实名认证',
|
||||
);
|
||||
@@ -3,9 +3,9 @@ return array (
|
||||
'普通用户' => '普通用户',
|
||||
'VIP' => 'VIP',
|
||||
'渠道商' => '渠道商',
|
||||
'Invalid user' => 'Invalid user',
|
||||
'Access denied' => 'Access denied',
|
||||
'It cannot be lower than the user\\\'s current level' => 'It cannot be lower than the user\\\'s current level',
|
||||
'Invalid user' => '无效用户',
|
||||
'Access denied' => '访问被拒绝',
|
||||
'It cannot be lower than the user\'s current level' => '不能低于用户当前等级',
|
||||
'V1' => 'V1',
|
||||
'V2' => 'V2',
|
||||
'V3' => 'V3',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
return array (
|
||||
'Role does not exist' => 'Role does not exist',
|
||||
'Your level is too high to purchase this character' => 'Your level is too high to purchase this character',
|
||||
'This character group is not allowed to be sold' => 'This character group is not allowed to be sold',
|
||||
'Insufficient balance' => 'Insufficient balance',
|
||||
'Role does not exist' => '角色不存在',
|
||||
'Your level is too high to purchase this character' => '您的等级过高,无法购买此角色',
|
||||
'This character group is not allowed to be sold' => '该角色组不允许出售',
|
||||
'Insufficient balance' => '余额不足',
|
||||
);
|
||||
@@ -5,7 +5,7 @@ return array (
|
||||
'The system is under maintenance, please wait...' => '系统正在维护中,请稍等…',
|
||||
'User is incorrect' => '用户名不正确',
|
||||
'Trade successful' => '交易成功',
|
||||
'Incoret param' => 'Incoret param',
|
||||
'You have verified' => 'You have verified',
|
||||
'ID card already exists' => 'ID card already exists',
|
||||
'You have verified' => '您已验证',
|
||||
'ID card already exists' => '身份证已存在',
|
||||
'Invalid parameter' => '参数无效',
|
||||
);
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
return array (
|
||||
'The system is under maintenance, please wait...' => 'The system is under maintenance, please wait...',
|
||||
'Exchange successful' => 'Exchange successful',
|
||||
'User is incorrect' => 'User is incorrect',
|
||||
'Insufficient balance' => 'Insufficient balance',
|
||||
'Transfer successful' => 'Transfer successful',
|
||||
'The system is under maintenance, please wait...' => '系统正在维护中,请稍等…',
|
||||
'Exchange successful' => '兑换成功',
|
||||
'User is incorrect' => '用户不正确',
|
||||
'Insufficient balance' => '余额不足',
|
||||
'Transfer successful' => '转账成功',
|
||||
'卡密不存在' => '卡密不存在',
|
||||
);
|
||||
@@ -1,4 +1,4 @@
|
||||
<?php
|
||||
return array (
|
||||
'Article does not exist' => 'Article does not exist',
|
||||
'Article does not exist' => '文章不存在',
|
||||
);
|
||||
@@ -12,7 +12,6 @@ return array (
|
||||
'Reset Trade password successful' => '重置交易密码成功',
|
||||
'Only one verification code can be sent within %second% seconds' => '在%秒%秒内只能发送一个验证码',
|
||||
'Email sent successfully' => '邮件发送成功',
|
||||
'Mt email code' => '邮箱验证码',
|
||||
'错误的邀请码' => '错误的邀请码',
|
||||
'Trade password must be 6 characters' => '交易密码必须为6个字符',
|
||||
'Incoret param' => '参数不正确',
|
||||
|
||||
@@ -20,7 +20,7 @@ class Auth
|
||||
'userID' => $userID,
|
||||
'secret' => Config::getSecret()
|
||||
];
|
||||
return Utils::send(Url::$getAdminToken, $data, '获取管理员token错误');
|
||||
return Utils::send(Url::$getAdminToken, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +34,7 @@ class Auth
|
||||
{
|
||||
// 获取管理员token
|
||||
$platformID = Utils::getPlatformId( $platformID );
|
||||
return Utils::send(Url::$getUserToken, ['userID' => $userID, 'platformID' => $platformID], '获取用户token错误');
|
||||
return Utils::send(Url::$getUserToken, ['userID' => $userID, 'platformID' => $platformID]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,13 +45,10 @@ class Auth
|
||||
*/
|
||||
public function forceLogout(string $userID, string $platformID = 'android'): array|bool
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
|
||||
// 清除本地缓存的用户token
|
||||
Utils::clearToken($userID);
|
||||
$platformID = Utils::getPlatformId( $platformID );
|
||||
return Utils::send(Url::$forceLogout, ['userID' => $userID, 'platformID' => $platformID], '强制登出错误', $adminToken);
|
||||
return Utils::send(Url::$forceLogout, ['userID' => $userID, 'platformID' => $platformID]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +58,7 @@ class Auth
|
||||
*/
|
||||
public function parseToken(string $token): array
|
||||
{
|
||||
return Utils::send(Url::$parseToken, [], '解析当前用户token错误', $token);
|
||||
return Utils::send(Url::$parseToken, []);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +69,6 @@ class Auth
|
||||
*/
|
||||
public function userToken(string $userID): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
return Utils::send(Url::$userToken, ['userID' => $userID], '用户登录错误', $adminToken);
|
||||
return Utils::send(Url::$userToken, ['userID' => $userID]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ class Conversation
|
||||
*/
|
||||
public function getOwnerConversation(string $userID, int $pageNumber = 1, int $showNumber = 20): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = [
|
||||
'userID' => $userID,
|
||||
'pagination' => [
|
||||
@@ -25,7 +23,7 @@ class Conversation
|
||||
'showNumber' => $showNumber
|
||||
]
|
||||
];
|
||||
return Utils::send(Url::$getOwnerConversation, $data, '获取当前用户分页会话列表失败', $adminToken);
|
||||
return Utils::send(Url::$getOwnerConversation, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,8 +33,6 @@ class Conversation
|
||||
*/
|
||||
public function getSortedConversationList(string $userID,$conversationIDs='',int $page=1,int $pagesize=10): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
return Utils::send(Url::$getSortedConversationList, [
|
||||
'userID' => $userID,
|
||||
'conversationIDs' => $conversationIDs,
|
||||
@@ -44,7 +40,7 @@ class Conversation
|
||||
'pageNumber' => $page,
|
||||
'showNumber' => $pagesize
|
||||
]
|
||||
], '获取排序的会话列表失败', $adminToken);
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,8 +59,6 @@ class Conversation
|
||||
*/
|
||||
public function setConversations(string $conversationID, array $userIDs, int $recvMsgOpt = 0, bool $isPinned = false, bool $isPrivateChat = false, int $groupAtType = 0, string $ex = '', bool $isMsgDestruct = false, int $msgDestructTime = 0, int $burnDuration = 0): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = [
|
||||
'conversationID' => $conversationID,
|
||||
'userIDs' => $userIDs,
|
||||
@@ -79,6 +73,6 @@ class Conversation
|
||||
'burnDuration' => $burnDuration
|
||||
]
|
||||
];
|
||||
return Utils::send(Url::$setConversations, $data, '为多个用户设置相同会话ID的字段失败', $adminToken);
|
||||
return Utils::send(Url::$setConversations, $data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,8 @@ class Friend
|
||||
*/
|
||||
public function addBlack(string $ownerUserID, string $blackUserID): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['ownerUserID' => $ownerUserID, 'blackUserID' => $blackUserID];
|
||||
return Utils::send(Url::$addBlack, $data, '添加黑名单错误', $adminToken);
|
||||
return Utils::send(Url::$addBlack, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,12 +28,12 @@ class Friend
|
||||
*/
|
||||
public function addFriend(string $fromUserID, string $toUserID, string $reqMsg): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['fromUserID' => $fromUserID, 'toUserID' => $toUserID, 'reqMsg' => $reqMsg];
|
||||
return Utils::send(Url::$addFriend, $data, '添加好友错误', $adminToken);
|
||||
return Utils::send(Url::$addFriend, $data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 同意/拒绝好友请求
|
||||
* @param string $ownerUserID 处理者ID
|
||||
@@ -46,15 +44,13 @@ class Friend
|
||||
*/
|
||||
public function addFriendResponse(string $ownerUserID, string $friendUserID, string $handleMsg, int $handleResult): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = [
|
||||
'ownerUserID' => $ownerUserID,
|
||||
'friendUserID' => $friendUserID,
|
||||
'handleMsg' => $handleMsg,
|
||||
'handleResult' => $handleResult
|
||||
];
|
||||
return Utils::send(Url::$addFriendResponse, $data, '同意/拒绝好友请求错误', $adminToken);
|
||||
return Utils::send(Url::$addFriendResponse, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,10 +61,8 @@ class Friend
|
||||
*/
|
||||
public function deleteFriend(string $ownerUserID, string $friendUserID): array|bool
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['ownerUserID' => $ownerUserID, 'friendUserID' => $friendUserID];
|
||||
return Utils::send(Url::$deleteFriend, $data, '删除好友错误', $adminToken);
|
||||
return Utils::send(Url::$deleteFriend, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,9 +72,7 @@ class Friend
|
||||
*/
|
||||
public function getBlackList(string $userID): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
return Utils::send(Url::$getBlackList, ['userID' => $userID], '获取黑名单列表错误', $adminToken);
|
||||
return Utils::send(Url::$getBlackList, ['userID' => $userID]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,9 +82,7 @@ class Friend
|
||||
*/
|
||||
public function getFriendApplyList(string $userID): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
return Utils::send(Url::$getFriendApplyList, ['userID' => $userID], '获取好友申请列表错误', $adminToken);
|
||||
return Utils::send(Url::$getFriendApplyList, ['userID' => $userID]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,15 +92,13 @@ class Friend
|
||||
*/
|
||||
public function getFriendList(string $userID,$page=1,$pagesize=1000): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
return Utils::send(Url::$getFriendList, [
|
||||
'userID' => $userID,
|
||||
"pagination" => [
|
||||
"pageNumber" => $page,
|
||||
"showNumber" => $pagesize
|
||||
]
|
||||
], '获取用户的好友列表错误', $adminToken);
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,9 +108,7 @@ class Friend
|
||||
*/
|
||||
public function getSelfFriendApplyList(string $userID): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
return Utils::send(Url::$getSelfFriendApplyList, ['userID' => $userID], '获取自己的好友申请列表错误', $adminToken);
|
||||
return Utils::send(Url::$getSelfFriendApplyList, ['userID' => $userID]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,12 +117,10 @@ class Friend
|
||||
* @param array $friendUserIDs 好友ID列表
|
||||
* @return array
|
||||
*/
|
||||
public function importFriend(string $ownerUserID, array $friendUserIDs = []): array
|
||||
public function importFriend(string $ownerUserID, array $friendUserIDs = []): array|bool
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['ownerUserID' => $ownerUserID, 'friendUserIDs' => $friendUserIDs];
|
||||
return Utils::send(Url::$importFriend, $data, '批量导入好友错误', $adminToken);
|
||||
return Utils::send(Url::$importFriend, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,10 +131,8 @@ class Friend
|
||||
*/
|
||||
public function isFriend(string $userID1, string $userID2): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['userID1' => $userID1, 'userID2' => $userID2];
|
||||
return Utils::send(Url::$isFriend, $data, '检查用户之间是否为好友错误', $adminToken);
|
||||
return Utils::send(Url::$isFriend, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,10 +143,8 @@ class Friend
|
||||
*/
|
||||
public function removeBlack(string $ownerUserID, string $blackUserID): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['ownerUserID' => $ownerUserID, 'blackUserID' => $blackUserID];
|
||||
return Utils::send(Url::$removeBlack, $data, '把用户移除黑名单错误', $adminToken);
|
||||
return Utils::send(Url::$removeBlack, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,10 +156,8 @@ class Friend
|
||||
*/
|
||||
public function setFriendRemark(string $fromUserID, string $toUserID, string $remark): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = ['fromUserID' => $fromUserID, 'toUserID' => $toUserID, 'remark' => $remark];
|
||||
return Utils::send(Url::$setFriendRemark, $data, '设置好友备注错误', $adminToken);
|
||||
return Utils::send(Url::$setFriendRemark, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,8 +171,6 @@ class Friend
|
||||
*/
|
||||
public function updateFriends(string $ownerUserID, string $friendUserID, string $remark = '', bool $isPinned = false, string $ex = ''): array
|
||||
{
|
||||
// 获取管理员token
|
||||
$adminToken = Utils::getAdminToken();
|
||||
$data = [
|
||||
'ownerUserID' => $ownerUserID,
|
||||
'friendUserID' => $friendUserID
|
||||
@@ -213,6 +189,6 @@ class Friend
|
||||
$data['ex'] = $ex;
|
||||
}
|
||||
|
||||
return Utils::send(Url::$updateFriends, $data, '更新好友信息失败', $adminToken);
|
||||
return Utils::send(Url::$updateFriends, $data);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user