diff --git a/docs/07_SHOPXO_PLUGIN_MECHANISM.md b/docs/07_SHOPXO_PLUGIN_MECHANISM.md new file mode 100644 index 0000000..59b76d6 --- /dev/null +++ b/docs/07_SHOPXO_PLUGIN_MECHANISM.md @@ -0,0 +1,488 @@ +# ShopXO 插件开发文档 + +> 来源:doc.shopxo.net 官方文档 + PluginsService.php 源码 + CSDN/搜索补充 +> 版本:ShopXO v2.2.0+(基于 ThinkPHP 8) +> 整理:西莉雅 + +--- + +## 一、目录结构 + +### 系统结构(v2.2.0+) + +``` +www/ # WEB部署目录(或子目录) +├─ app/ # 应用目录 +│ ├─ plugins/ # 插件目录 +│ │ ├─ {plugins_name}/ # 插件目录(目录名 = config.json 的 plugins 字段) +│ │ │ ├─ config.json # ★ 插件配置(核心文件) +│ │ │ ├─ admin/ # 后台管理 +│ │ │ │ ├─ controller/ # 控制器 +│ │ │ │ └─ view/ # Blade 视图(app/plugins/{name}/view/admin/) +│ │ │ ├─ index/ # 前台用户端 +│ │ │ │ ├─ controller/ +│ │ │ │ └─ view/ +│ │ │ ├─ api/ # API接口端 +│ │ │ │ └─ controller/ +│ │ │ ├─ service/ +│ │ │ │ └─ BaseService.php # ★ 基础服务类(配置处理、菜单等) +│ │ │ ├─ Event.php # ★ 生命周期事件类 +│ │ │ ├─ common.php # 公共函数 +│ │ │ └─ hook/ # 钩子类目录(可选) +│ │ └─ ... +│ ├─ admin/ # 后台管理模块 +│ ├─ index/ # 前台模块 +│ ├─ api/ # API接口模块 +│ ├─ common.php # 公共函数文件 +│ └─ event.php # 事件定义文件 +├─ config/ # 全局配置 +├─ public/ # WEB目录(对外访问) +│ ├─ index.php # 前端入口 +│ ├─ admin.php # 后台入口 +│ └─ api.php # API入口 +└─ vendor/ # Composer依赖 +``` + +### 插件静态资源路径 + +``` +public/static/plugins/{plugins_name}/ # JS/CSS/图片 + ├─ index/ # 前台静态资源 + └─ admin/ # 后台静态资源 +``` + +--- + +## 二、config.json — 插件配置文件(核心) + +### 标准结构 + +```json +{ + "base": { + "name": "插件显示名称", + "version": "1.0.0", + "author": "作者名", + "desc": "插件描述", + "plugins": "vr_ticket", // ★ 必须与目录名一致(唯一标识) + "is_apps_ten_pay": 0 + }, + "element": [ + { + "element": "input", + "type": "text", + "name": "api_key", + "title": "API密钥", + "value": "", + "message": "请输入API密钥" + } + ], + "hook": { + "app_init": ["\\app\\plugins\\vr_ticket\\hook\\AppInit"] + }, + "backend_hook": { + "admin_user_view_right": ["\\app\\plugins\\vr_ticket\\hook\\AdminUserViewRight"] + } +} +``` + +### element 支持的表单类型 + +| element | 说明 | 关键参数 | +|---------|------|---------| +| `input` | 单行文本 | `type`: text/password/number | +| `textarea` | 多行文本 | | +| `radio` | 单选 | `data`: `[{name, value}]` | +| `checkbox` | 多选 | `data`: 同上 | +| `select` | 下拉选择 | 同上 | +| `upload` | 文件上传 | | + +### hook vs backend_hook + +- `hook`:前端/全局钩子,在用户端触发 +- `backend_hook`:后台管理钩子,在 admin 模块触发 + +--- + +## 三、钩子系统(Hook) + +ShopXO 基于 ThinkPHP 6 的 Event/Facade 机制实现插件钩子。 + +### 已知系统钩子(从源码/文档整理) + +| 钩子名称 | 触发时机 | 常见用途 | +|---------|---------|---------| +| `app_init` | 应用初始化(最早) | 全局预处理 | +| `app_begin` | 应用开始 | 初始化扩展 | +| `module_init` | 模块初始化 | 模块级别扩展 | +| `action_begin` | 操作开始执行前 | 权限校验、前置处理 | +| `view_filter` | 视图内容过滤 | 输出内容处理 | +| `log_write` | 日志写入 | 自定义日志 | +| `app_end` | 应用结束 | 收尾处理 | +| `admin_user_view_right` | 管理员查看用户详情右侧 | 扩展用户信息面板 | +| `plugins_view_welcome` | 插件欢迎页 | 自定义插件首页 | + +> **重要**:开启开发者模式后,后台应用管理页面会显示所有可用钩子的提示名称。 +> 路径:`config/shopxo.php` → `is_develop: true` + +### 前端钩子注册方式 + +在 `config.json` 中: + +```json +"hook": { + "钩子名称": ["\\app\\plugins\\{name}\\hook\\HookClass"] +} +``` + +### 后台钩子注册方式 + +```json +"backend_hook": { + "钩子名称": ["\\app\\plugins\\{name}\\hook\\HookClass"] +} +``` + +### 钩子类示例 + +```php + 0, 'msg' => 'success']; + } +} +``` + +--- + +## 四、插件生命周期事件(Event) + +插件可以通过 `Event.php` 响应系统生命周期回调。 + +### 生命周期事件列表 + +| 事件方法 | 触发时机 | 返回值意义 | +|---------|---------|-----------| +| `Upload` | 上传插件包时 | | +| `BeginInstall` | 安装前(验证) | 非0 → 阻止安装 | +| `Install` | 执行安装 | | +| `Uninstall` | 卸载插件 | | +| `Download` | 下载插件 | | +| `BeginUpgrade` | 更新前(验证) | 非0 → 阻止更新 | +| `Upgrade` | 执行更新 | | +| `Delete` | 删除插件 | | + +### Event.php 示例 + +```php + $data]); + } + + // JSON API + public function detail($params = []) + { + return DataReturn('success', 0, $data); + } +} +``` + +```php + '场次管理', 'url' => MyUrl('plugins/admin', ['pluginsname' => 'vr_ticket', 'pluginscontrol' => 'event', 'pluginsaction' => 'index'])], + ['name' => '门票核销', 'url' => MyUrl('plugins/admin', ['pluginsname' => 'vr_ticket', 'pluginscontrol' => 'verify', 'pluginsaction' => 'index'])], + ]; + } + + // 配置数据处理(可选) + public static function BaseConfigHandle($config) + { + return $config; + } +} +``` + +### 读取插件配置 + +```php +use app\service\PluginsService; + +$data = PluginsService::PluginsData('vr_ticket'); +// 返回 ['code' => 0, 'msg' => 'success', 'data' => [...配置数据]] +``` + +### 保存插件配置 + +```php +PluginsService::PluginsDataSave([ + 'plugins' => 'vr_ticket', + 'data' => $params +], $attachment_field); +``` + +--- + +## 七、模板引擎(Blade) + +ShopXO 使用 Blade 模板(.blade.php)。 + +### 前台视图路径 + +``` +app/plugins/vr_ticket/view/index/index.blade.php # 前台 +app/plugins/vr_ticket/view/admin/index.blade.php # 后台 +``` + +### 视图中引用静态资源 + +```blade + + + + + +``` + +### 视图渲染 + +```php +// 渲染插件视图(自动读取插件view目录) +return MyView('index', ['data' => $data]); +// 路径:app/plugins/vr_ticket/view/index/index.blade.php + +// 渲染后台视图 +return MyView('admin/index', ['data' => $data]); +``` + +### 公共模板变量 + +在 `app/plugins/vr_ticket/common.php` 中定义函数,可供所有视图调用。 + +--- + +## 八、调试与开发模式 + +### 开启开发者模式 + +路径:`config/shopxo.php` + +```php +'is_develop' => true, // 从 false 改为 true +``` + +开启后后台应用管理页面会显示钩子名称提示。 + +### 日志文件 + +``` +runtime/log/{年份}/{月份}.log +例:runtime/log/2025/09.log +``` + +### 调试模式 + +在 `.env` 中设置: + +```env +APP_DEBUG=true +``` + +--- + +## 九、支付插件(extend/ 方式) + +ShopXO 的支付插件放在 `extend/payment/` 目录,与通用插件不同。 + +``` +extend/ +└─ payment/ + ├─ Alipay.php # 支付宝支付 + └─ Weixin.php # 微信支付 +``` + +支付插件实现支付接口集成,订单支付成功后会触发事件通知。 + +--- + +## 十、与 vr-shopxo-plugin 需求对应的关键点 + +### VR 票务插件需要对接的核心功能 + +#### 1. 票务场次管理(后台) + +- **路由**:`/plugins/admin?pluginsname=vr_ticket&pluginscontrol=event&pluginsaction=index` +- **数据库**:需创建 `vr_events`(场次)、`vr_sessions`(场次时间/库存)、`vr_tickets`(电子票) +- **后台菜单**:通过 `BaseService::AdminPowerMenu()` 注册 + +#### 2. 支付回调处理(API) + +- **路由**:`/plugins/api?pluginsname=vr_ticket&pluginscontrol=api&pluginsaction=notify` +- **触发方式**:支付插件在支付成功后调用此接口 +- **关键逻辑**: + 1. 验证支付签名 + 2. 创建 `vr_tickets` 记录(AES_Encrypt 防伪造 QR) + 3. 更新 `vr_sessions` 库存 + +#### 3. 观演人信息收集 + +- **触发时机**:订单 `pending` → `paid` 过渡阶段 +- **实现方式**:可在订单扩展字段或 `OrderService` 中 hook +- **字段**:姓名、手机号、身份证(可选) + +#### 4. QR 电子票展示(前台) + +- **路由**:`/plugins/index?pluginsname=vr_ticket&pluginscontrol=ticket&pluginsaction=detail` +- **数据源**:`vr_tickets` 表 +- **展示**:票面信息 + QR 码图片 + +#### 5. B 端扫码核销(后台) + +- **路由**:`/plugins/admin?pluginsname=vr_ticket&pluginscontrol=verify&pluginsaction=index` +- **方式**:微信扫码枪或管理员扫码页面 +- **逻辑**:`vr_tickets.status = 'used'` + 核销时间戳 + +### 关键开发顺序 + +``` +Phase 1: 插件骨架(config.json + BaseService + Event) + ↓ +Phase 2: 数据库迁移(vr_events, vr_sessions, vr_tickets) + ↓ +Phase 3: 后台场次管理 CRUD(admin/controller) + ↓ +Phase 4: 前台购票流程 + 支付回调(api/controller + notify) + ↓ +Phase 5: QR 票生成 + 观演人信息 + ↓ +Phase 6: B 端核销页面 +``` + +--- + +## 十一、参考资源 + +| 资源 | URL | +|------|-----| +| 官方文档站 | https://doc.shopxo.net/ | +| 插件开发索引 | https://doc.shopxo.net/article/3.html | +| 目录结构 | https://doc.shopxo.net/article/4/265292898306621440.html | +| 应用商店 | https://store.shopxo.net/ | +| GitHub 源码 | https://github.com/gongfuxiang/shopxo | +| Gitee 源码 | https://gitee.com/zongzhige/shopxo | +| ThinkPHP 8 文档 | https://www.kancloud.cn/manual/thinkphp6_0/ | diff --git a/docs/08_SHOPXO_REQUIREMENTS_MAPPING.md b/docs/08_SHOPXO_REQUIREMENTS_MAPPING.md new file mode 100644 index 0000000..3aca24a --- /dev/null +++ b/docs/08_SHOPXO_REQUIREMENTS_MAPPING.md @@ -0,0 +1,172 @@ +# vr-shopxo-plugin 需求对照分析 + +> ShopXO 插件机制 vs VR 演唱会票务需求 +> 基于 shopxo-plugin-dev.md 整理 + +--- + +## 一、票务核心功能 vs ShopXO 机制 + +### 1. 商品模型(票 = item_type='ticket') + +ShopXO 原生商品表 `item` 有 `item_type` 字段扩展能力,但票务的特殊性在于: +- 票有时间场次(session),普通商品无此概念 +- 票需要观演人信息,普通商品无此字段 +- 票需要 QR 电子票,普通商品无此交付物 + +**结论**:票务插件需创建独立表 `vr_events` + `vr_sessions` + `vr_tickets`,与 ShopXO `items` 表平行存在,通过 `item_id` 关联。 + +### 2. 库存扣减(防超卖) + +ShopXO 原生库存扣减:SELECT → UPDATE 两步,非原子操作,有超卖风险。 + +**VR 票务方案**:在支付回调 `api/notify` 中,使用 Postgres 风格 SQL(ShopXO 是 MySQL,需换为 MySQL 原子写法): + +```sql +-- MySQL 原子扣库存(FOR UPDATE SKIP LOCKED 等效写法) +START TRANSACTION; +SELECT stock FROM vr_sessions WHERE id=? AND stock>=? FOR UPDATE; +-- 如果库存足够 +UPDATE vr_sessions SET stock=stock-? WHERE id=?; +INSERT INTO vr_tickets (...) VALUES (...); +COMMIT; +``` + +**关键**:必须在支付回调的 API 控制器中完成,不能依赖 ShopXO 原有订单流程。 + +### 3. 微信支付回调 + +ShopXO 支持支付插件机制(`extend/payment/`),但票务插件需要自己的支付回调路由: + +``` +POST /plugins/api?pluginsname=vr_ticket&pluginscontrol=api&pluginsaction=notify +``` + +ShopXO 原生支付回调由支付插件处理,票务插件的回调是:**用户付款成功 → 票务插件生成电子票**。 + +**注意**:ShopXO 的微信支付插件本身已处理微信支付回调,我们需要在 `Event.php` 中监听支付成功事件,或在回调路由中自己调用微信API验证。 + +### 4. 观演人信息 + +ShopXO 订单扩展字段能力有限,建议: +- 创建 `vr_attendees` 表(姓名、手机、证件号) +- 在订单 `paid` 状态的 webhook 或手动触发时写入 +- 通过订单扩展字段 `extension`(JSON)关联 `vr_attendees` + +### 5. QR 电子票 + +ShopXO 附件上传/管理能力完善,但 QR 生成需自己实现: +- PHP QR Code:`phpqrcode/phpqrcode` Composer 包 +- 存储:`vr_tickets.qr_data` 存 QR 内容(AES_Encrypt),QR 图片可 CDN 化 + +### 6. B 端扫码核销 + +ShopXO 后台有门店核销机制(多门店插件),但票务核销是独立场景: +- 插件后台:`admin/verify` 控制器 +- 扫码枪:调用微信扫一扫或直接输入 QR code +- 核销状态更新:`vr_tickets.status = 'used'` + +--- + +## 二、ShopXO 原有功能借用分析 + +| 票务需求 | ShopXO 原生能力 | 插件扩展方式 | 优先级 | +|---------|---------------|------------|--------| +| 商品展示 | ✅ items 表 | 创建 vr_events 场次表 | P0 | +| 会员体系 | ✅ 完善 | 不需开发 | P0 | +| 钱包/余额 | ✅ 完善 | 不需开发 | P0 | +| 优惠券 | ✅ 完善 | 不需开发 | P0 | +| 微信支付 | ✅ 支付插件 | 直接用(不需改) | P0 | +| 订单管理 | ✅ orders 表 | 扩展 extension 字段 | P0 | +| 库存扣减 | ⚠️ 非原子 | 支付回调自己写 | P0 | +| 场次/SKU | ❌ 无 | 独立表 vr_sessions | P0 | +| 观演人信息 | ❌ 无 | 独立表 vr_attendees | P0 | +| QR 电子票 | ❌ 无 | phpqrcode + 插件表 | P0 | +| B端核销 | ❌ 无 | 插件后台 admin 页面 | P1 | +| 多场馆 | ❌ 无 | vr_events 已有场馆字段 | P2 | +| 周边商品 | ✅ items 原生 | 不需插件 | P0 | + +--- + +## 三、插件架构设计 + +### 数据库 E-R 图 + +``` +vr_events (场次) + └── vr_sessions (场次时间/库存) 1:N + └── vr_tickets (电子票) 1:N + └── vr_attendees (观演人) N:1 + +items (ShopXO原生) ←── item_id ──→ vr_events (通过 item_id 关联) +orders (ShopXO原生) ←─ extension ──→ vr_attendees (JSON关联) +``` + +### 插件目录 + +``` +app/plugins/vr_ticket/ +├── config.json # 插件配置(钩子:支付回调) +├── admin/ +│ ├── controller/ +│ │ ├── Event.php # 场次管理 CRUD +│ │ ├── Ticket.php # 电子票管理 +│ │ └── Verify.php # 核销管理 +│ └── view/ +│ ├── event/ # 场次管理视图 +│ ├── ticket/ +│ └── verify/ # 核销页面 +├── index/ +│ ├── controller/ +│ │ ├── Index.php # 前台插件首页 +│ │ └── Ticket.php # 我的票/QR展示 +│ └── view/ +│ ├── ticket/ +│ └── my_tickets.blade.php +├── api/ +│ └── controller/ +│ └── Notify.php # ★ 支付回调(最关键) +├── service/ +│ └── BaseService.php +├── Event.php # 生命周期事件 +├── common.php +└── hook/ + └── OrderPaid.php # 订单支付成功钩子(可选) +``` + +### 支付回调流程 + +``` +微信支付回调 → /plugins/api?pluginsname=vr_ticket&pluginscontrol=api&pluginsaction=notify + │ + ├─ 1. 验证签名(微信API) + ├─ 2. 查询 ShopXO orders 表确认支付状态 + ├─ 3. 读取订单 extension 中的 vr_session_id + vr_attendees + ├─ 4. 原子扣库存(vr_sessions) + ├─ 5. 生成 vr_tickets(AES_Encrypt QR content) + ├─ 6. 写入 vr_attendees 记录 + └─ 7. 更新 orders.extension(票ID列表) +``` + +--- + +## 四、已知限制与绕过方案 + +| 限制 | 影响 | 绕过方案 | +|------|------|---------| +| ShopXO 订单流程不可扩展 | 无法在原生下单流程中插入观演人表单 | 独立购票页,支付后写入观演人 | +| 支付回调由支付插件处理 | 票务插件无法直接拦截微信回调 | 支付插件 → Event.PaySuccess → 票务钩子 | +| 插件不能 Hook 订单状态变更 | 无法监听 paid→fulfilled | 支付回调中直接处理,或定时轮询 | +| DIY 拖拽装修不可代码化 | 票务展示页无法 AI 生成 | 票务页面全部走插件视图,不走 DIY | +| MySQL 非 Postgres | 无 FOR UPDATE SKIP LOCKED | START TRANSACTION + SELECT ... FOR UPDATE | + +--- + +## 五、下一步行动 + +1. **先确认支付插件的 PaySuccess 事件** — 从 `extend/payment/` 源码确认是否触发 Event.PaySuccess +2. **创建插件骨架** — config.json + BaseService + Event.php +3. **设计数据库迁移脚本** — vr_events, vr_sessions, vr_tickets, vr_attendees +4. **实现支付回调** — api/Notify.php(核心链路) + +---