# 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
<?php
// app/plugins/vr_ticket/hook/AppInit.php
namespace app\plugins\vr_ticket\hook;

class AppInit
{
    public function handle($params = [])
    {
        // 处理逻辑
        return ['code' => 0, 'msg' => 'success'];
    }
}
```

---

## 四、插件生命周期事件（Event）

插件可以通过 `Event.php` 响应系统生命周期回调。

### 生命周期事件列表

| 事件方法 | 触发时机 | 返回值意义 |
|---------|---------|-----------|
| `Upload` | 上传插件包时 | |
| `BeginInstall` | 安装前（验证） | 非0 → 阻止安装 |
| `Install` | 执行安装 | |
| `Uninstall` | 卸载插件 | |
| `Download` | 下载插件 | |
| `BeginUpgrade` | 更新前（验证） | 非0 → 阻止更新 |
| `Upgrade` | 执行更新 | |
| `Delete` | 删除插件 | |

### Event.php 示例

```php
<?php
// app/plugins/vr_ticket/Event.php
namespace app\plugins\vr_ticket;

class Event
{
    // 安装前验证
    public function BeginInstall($params = [])
    {
        // 检查依赖等
        return DataReturn('可以安装', 0);
    }

    // 执行安装
    public function Install($params = [])
    {
        // 创建数据库表等
        return DataReturn('安装成功', 0);
    }

    // 卸载
    public function Uninstall($params = [])
    {
        // 清理数据等
        return DataReturn('卸载成功', 0);
    }

    // 支付回调事件（需配合支付插件使用）
    public function PaySuccess($params = [])
    {
        // $params 包含 order_id, pay_price, payment 等
        return DataReturn('处理成功', 0);
    }
}
```

---

## 五、控制器与路由

### URL 路由格式

```
/plugins/{group}?pluginsname={plugins}&pluginscontrol={control}&pluginsaction={action}
```

示例：
```
/plugins/index?pluginsname=vr_ticket&pluginscontrol=index&pluginsaction=index
/plugins/admin?pluginsname=vr_ticket&pluginscontrol=admin&pluginsaction=index
/plugins/api?pluginsname=vr_ticket&pluginscontrol=api&pluginsaction=notify
```

### 控制器命名规范

```
app/plugins/{name}/{group}/controller/{Control}.php
```

- `group`: `index`（前台）| `admin`（后台）| `api`（API）
- `Control`: 首字母大写，如 `Index`, `Admin`, `Order`

### 控制器基类结构

```php
<?php
// app/plugins/vr_ticket/index/controller/Index.php
namespace app\plugins\vr_ticket\index\controller;

class Index
{
    // 前台首页
    public function index($params = [])
    {
        return MyView('', ['data' => $data]);
    }

    // JSON API
    public function detail($params = [])
    {
        return DataReturn('success', 0, $data);
    }
}
```

```php
<?php
// app/plugins/vr_ticket/admin/controller/Index.php
namespace app\plugins\vr_ticket\admin\controller;

class Index
{
    public function index($params = [])
    {
        IsLogin();  // 后台需登录校验
        return MyView('', []);
    }
}
```

---

## 六、BaseService — 基础服务类

```php
<?php
// app/plugins/vr_ticket/service/BaseService.php
namespace app\plugins\vr_ticket\service;

use think\facade\Db;

class BaseService
{
    // 附件字段（配置中涉及文件上传的字段名）
    public static $base_config_attachment_field = [];

    // 私有字段（不返回给前端的敏感字段）
    public static $base_config_private_field = ['secret_key'];

    // 后台权限菜单（可选）
    public static function AdminPowerMenu()
    {
        return [
            ['name' => '场次管理', '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
<img src="{{ \app\common.phpStaticAttachmentUrl('logo.png', 'images', 'vr_ticket') }}" />

<!-- 或直接写死路径 -->
<link href="/static/plugins/vr_ticket/index/css/style.css" rel="stylesheet">
<script src="/static/plugins/vr_ticket/index/js/app.js"></script>
```

### 视图渲染

```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/ |
