XShop 二次开发文档

新彦达 · ThinkPHP 8 后端 + Vite/Vue3 管理后台 + uni-app C 端 · 版本 2026-09-10 · 内容均来自当前代码实测

0. 阅读对象与约定

平台开发 改 backend/(TP8) + admin-web/(Vite/Vue3),对应「总平台管理后台」。

商家开发 改 backend/route/merchant.php + admin-web/src/router/index.js 的 /merchant 段。

C 端开发 改 uni-app/,需 HBuilderX 重新打包,不在本仓库热更新。

金额单位:数据库与接口内部一律「分」(int),对外/前端展示换算「元」。
项目没有自定义 Translator/Adapter 类(仅 vendor league/flysystem)。分↔元与字段映射在 service 层按约定处理,新增表/接口请沿用在 service 层统一换算,不要散落各处 *100/100。

每次本地改完后端 controller/路由后需重启 8000 服务才生效;前端改完需重新 vite build 并 cp 到 backend/public/admin/。

1. 本地工程与启动

模块目录启动 / 调试端口
后端 APIbackend/php think run -H 127.0.0.1 -p 80008000
管理后台前端admin-web/npm run dev(Vite)9999
C 端(uni-app)uni-app/HBuilderX 编译—
  • 后端路由前缀:/api/admin、/api/merchant、/api/member、/api/pay(见 backend/route/*.php)。
  • 本地库:xshop_admin(root/空)。测试线库 xshop,正式线库 xydshop。
  • 前端请求封装:admin-web/src/utils/request.js,统一信封 {code, msg, data};code===0 为成功。

2. 新增一个管理后台 API

入口文件:backend/route/admin.php。统一前缀 /admin,响应 {code,msg,data}。

关键语法:ThinkPHP 8 路由中间件用变参语法 ->middleware('别名','参数1','参数2'),不支持 '别名:参数' 冒号写法(冒号会被当成类名一部分)。
// backend/route/admin.php (在 Route::group('admin', ...) 内合适分组下追加)
Route::get('demo/list', 'admin/Demo/list')
    ->middleware('PermissionMiddleware', 'demo:view')        // 权限点
    ->middleware('LogMiddleware', 'demo', 'demo_list', 1);   // 模块,动作,operation_level
  • 权限中间件 PermissionMiddleware:参数 perm_code(如 demo:view),未配置该权限点的角色返回 403。
  • 日志中间件 LogMiddleware:参数 模块, 动作, operation_level: 1 查看2 操作3 高危·动资金。
  • 二次密码(高危操作):当权限点 operation_level = 3,PermissionMiddleware 校验请求头 X-Verify-Password(明文,bcrypt 比对 admin_user.password)。前端目前尚未实现该输入框,故所有 level=3 按钮在未补前端前会一律 403——新增 level=3 接口务必同步补前端弹窗(见 §7)。

3. 新增一个权限点(permCode)

权限点存于 admin_permission 表:perm_code、perm_name、perm_type(menu/button)、parent_id、operation_level、sort_order、status。

  • 菜单类:perm_code 形如 menu:xxx,前端路由 meta.permCode 与之对应(见 §6)。
  • 操作类:perm_code 形如 demo:edit,在路由挂到 PermissionMiddleware。
  • 增删建议走「系统管理 → 权限管理」后台或幂等 SQL;SUPER_ADMIN 拥有全部权限(PermissionService::hasPerm)。
  • 权限树接口:GET /admin/permission/perm/tree(返回扁平树,供角色授权编辑)。

4. 新增一个支付渠道(核心扩展点)

所有第三方支付实现统一接口 app\service\gateway\PaymentGatewayInterface。上层 PaymentGatewayManager 按 channel_code 取实例,对上层零感知。

接口五个方法

interface PaymentGatewayInterface
{
    public function getChannelCode(): string;              // 与 pay_channel.channel_code 一致
    public function createPayment(array $trade): array;    // 发起支付
    public function verifyNotify(array $payload): array;  // 校验异步回调
    public function query(string $outTradeNo): array;      // 查单(可选)
    public function refund(array $params): array;         // 退款(可选)
}

createPayment 返回(pay_type 决定前端调起方式):

[ 'pay_type' => 'redirect'|'qr'|'form'|'none', // 跳转/扫码/表单/无
  'pay_url'=>'', 'qr_code'=>'', 'form_html'=>'',
  'channel_trade_no'=>'', 'expire_at'=>'', 'raw'=>[] ]

verifyNotify 返回(控制器据此写支付成功并原样输出 respond 给支付商确认):

[ 'valid'=>true, 'out_trade_no'=>'', 'channel_trade_no'=>'',
  'amount'=>0.0, 'paid'=>true, 'paid_at'=>'',
  'respond'=>'',   // 支付商期望回执,如微信 "SUCCESS"/支付宝 "success"
  'raw'=>[] ]

新增渠道四步

  1. 在 backend/app/service/gateway/ 新建 XxxGateway.php,implements PaymentGatewayInterface。
  2. 在 pay_channel 表插入一行:channel_code(如 xxx)、channel_name、gateway_class(app\service\gateway\XxxGateway)、mode(1=实时分账 / 2=平台代收)、status=1、priority、min_amount/max_amount(max=0 不限)、config_json(密钥 JSON)。
  3. (可选)在 PaymentGatewayManager::FALLBACK 补充 channel_code => 类名,保证缺 gateway_class 时也能实例化。
  4. 密钥字段在 PayChannel 控制器有脱敏白名单(api_key/api_v3_key/private_key/sec_key/sign_key/md5_key/plat_key/mp_secret/mini_secret/ali_private_key/ali_public_key 等),列表接口回显自动打码为 ******。
FALLBACK 现有映射(已实现):joinpay、kuaifu(快付/Gdkf)、wechat、alipay、ecny(数币)、unionpay、mock。
C 端收银台 xshop-multipay 四渠道路由:xpay(微信/支付宝) / joinpay(汇聚) / gdkf(快付,渠道码 kuaifu) / ecny(数币)。
注意 UnionPay/余额/Wallet/Mock 为后端可配置渠道,但不在 C 端四渠道收银台内;要让 C 端用户看到新渠道,还须改 C 端 xshop-multipay 组件(需 HBuilderX 重新打包)。

渠道路由优先级(PaymentGatewayManager::resolve): ① 订单指定渠道(preferred) → ② 商户绑定优先级(merchant_pay_channel.priority) → ③ 金额区间 → ④ 平台默认优先级。实时分账渠道子商户未入驻会自动跳过降级。

5. 商家维度数据隔离

商家体系(非平台租户 tid 方案)通过 merchant_id 隔离:

  • 表:merchant(组织根)、merchant_staff(员工)、merchant_role(角色)。
  • 鉴权:MerchantAuthMiddleware 校验商家员工 token,注入 request->merchantInfo。
  • 路由:backend/route/merchant.php,统一前缀 /merchant,与平台后台平行独立登录态。
  • 数据隔离:所有商家查询必须带 merchant_id 条件(门店/供应商/团队均归属商家)。

6. 新增后台菜单 + 页面

文件:admin-web/src/router/index.js。

本项目一级菜单是分组容器(无对应页面),用 redirect 指到默认子页;子页面 meta.permCode 与后端 admin_permission.perm_code 一一对应,前端按权限点控制左侧菜单显隐。

// 1) 一级分组里加一个重定向容器(若该分组已存在则跳过)
{ path: 'operation', redirect: '/operation/announcement' },

// 2) 加子页面
{
  path: 'operation/demo', name: 'OperationDemo',
  component: () => import('@/views/demo/index.vue'),
  meta: { title: '示例模块', permCode: 'menu:demo' },  // ← 与后端 perm_code 对齐
}
  • 视图文件放 admin-web/src/views/<模块>/index.vue。
  • 旧路径用 redirect 做兼容,避免历史书签 404。
  • V6 专属模块:/v6/:section(.*)*(权限点 v6:dashboard)。

7. 高危操作的二次密码弹窗(待补)

当前前端未实现 operation_level=3 的二次密码输入,导致所有 level=3 接口在前端会 403。

  • 修复点:调用 level=3 接口前,弹窗收集管理员密码,塞入请求头 X-Verify-Password。
  • 后端校验见 §2(PermissionMiddleware)。新增 level=3 接口必须同步补此弹窗,否则按钮不可用。

8. 数据库与迁移

  • 迁移顺序(幂等,可生产安全重跑):install → modules → business → migrate → seed,用 INSERT IGNORE / CREATE TABLE IF NOT EXISTS。
  • 字段命名:沿用 snake_case;金额类字段存储「分」(int),展示在 service 层转元。
  • 索引长度:生产 MySQL 5.7,utf8mb4 InnoDB 单列索引上限约 3072 字节(≈ varchar(767)),长字符串唯一键注意前缀或改用哈希列。
  • 敏感配置不入 git/migration:密钥、DB 密码放环境变量/配置表 config_param。

9. 部署

测试线(xyd.findhere.cn)

outputs/prod-tool/prod.sh 已写死 BACKEND=/www/wwwroot/•••/•••/backend(仅测试线)。常用:

bash outputs/prod-tool/prod.sh upload 本地文件 相对路径   # 上传到测试线 backend/
bash outputs/prod-tool/prod.sh sql   xxx.sql              # 执行 SQL
bash outputs/prod-tool/prod.sh run                            # 重启后端
bash outputs/prod-tool/prod.sh backup / dump                 # 备份/导出
  • 改后端 controller/路由后须重启 php-fpm-82。
  • 含反引号/引号的 SQL 走 sqlf 文件管道,sql 走 shell 易丢转义报 1064。

正式线(xyd.xyanda.cn)

prod.sh 默认指向测试线,发正式线须换路径:web 根 /www/wwwroot/•••/•••/backend/public/,库 xydshop。用原始 scp/ssh:

KEY=<项目>/.workbuddy/ssh/xshop_prod_key
scp -i $KEY backend/app/... root@8.166.•••.133:/www/wwwroot/•••/•••/backend/app/...
ssh  -i $KEY root@8.166.•••.133 "cd /www/wwwroot/•••/•••/backend && php think clear"
  • .user.ini 不可变:宝塔 open_basedir 限制,.user.ini 为 immutable,禁止 sed/> 直接改;确需改须 chattr -i 解锁→改→chattr +i 复原。
  • 每轮收尾必须 diff 正式线 vs 本地,曾两次抓到正式线落后,发现落后直接传本地完整版。
  • 公开文档目录:backend/public/docs/(线上 https://xyd.xyanda.cn/docs/ 可访问)。

10. 常见坑(务必先看)

坑1TP8 中间件变参语法:->middleware('Auth','a','b'),别写 ->middleware('Auth:a:b')。
坑2二次密码未实现:所有 operation_level=3 接口前端先 403,需补 X-Verify-Password 弹窗。
坑3金额单位:库内「分」,service 层转「元」,勿在 controller/前端散算。
坑4等级 V0 合法:会员等级 level() 下限 0,取值用 ?? 兜底,禁止 level || 默认(V0 会被当 falsy 吞掉)。
坑5C 端改动要打包:uni-app/ 与 admin-web 的 uni_modules/ 改完需 HBuilderX 重新发行。
坑6渠道密钥脱敏:PayChannel 列表对白名单字段打码,保存时 ****** 表示未改、保留原值。
坑7幂等 SQL:迁移脚本必须可重复执行,生产重跑不报错。

11. 扩展清单速查

扩展场景改哪里注意
新后台 APIroute/admin.php + app/controller/admin/Xxx.php挂 Permission/Log 中间件
新权限点admin_permission 表/后台menu:* 关联前端 permCode
新支付渠道app/service/gateway/XxxGateway.php + pay_channel 表实现 5 方法;C 端需另改 xshop-multipay
新后台菜单页admin-web/src/router/index.js + views/xxx一级分组用 redirect 容器
新商家接口route/merchant.php + MerchantXxx 控制器带 merchant_id 隔离
新迁移xxx.sql(幂等)install→modules→business→migrate→seed
上线正式线原始 scp/ssh 到 xyanda.cn勿用写死测试线的 prod.sh;重启 php-fpm-82