Agent 上下文 · 插件与主题开发手册
用途:给 Cursor / 其他 Agent 的单一入口上下文。开发「插件」或「主题」前先读本文,再按需深入分册。
代码真相源:kede-finance/(若本文与源码冲突,以源码为准)
配套分册:04/04a插件 ·04b对接 API ·05市场 ·10/10a主题 ·11钩子 ·06–08支付/实名/通知
文档版本:对齐产品 1.0.4(Build 131+)
0. Agent 必读指令(先执行)
任务类型判断:
- 改业务能力 / 钩子 / 后台菜单 / 驱动 → 插件(public/plugins)
- 改官网/会员中心/购物车外观 → 主题(frontend/themes → public/themes)
- 改开通对接(云主机等)→ 开通模块(extend/modules),不是插件
硬约束:
1. 插件目录类型用单数 addon(禁止 public/plugins/addons)
2. install()/uninstall() 必须 return true(严格 false 才失败;Magfang 短信可 return array)
3. 配置只走 config.php + getConfig()/setConfig()/configFields(),勿自建密钥存储绕过系统
4. 勿绕过授权中间件;勿在文档/示例写入授权站内部协议或密钥
5. 生产安装用「压缩包 → 本地插件 Tab」;市场一键安装仅开发环境(需 MARKETPLACE_SERVER)
完成定义(Definition of Done):
- 插件:可扫描 → 安装 → 启用 → 配置保存 → 钩子/驱动可验证 → 可卸载
- 主题:构建到 public/themes → 后台扫描 → 激活 → 前台强刷可见最短路径
| 目标 | 做这些 |
|---|---|
| 写功能插件 | §2 结构 → §4 → §6.1 测试清单 → 对照 hello_world |
| 写支付/短信/邮件/实名 | §4.4 类型契约 → wiki 06/07/08 |
| 写 SPA 主题 | §5 → §6.2 → 对照 frontend/themes/starter |
| 打包给客户 | §6.3 / §6.4 |
1. 项目上下文
1.1 系统是什么
| 项 | 说明 |
|---|---|
| 产品 | 可得财务(KeDe Finance)—— 财务/销售/会员/工单/开通 |
| 后端 | ThinkPHP 8(多应用:admin / home / api) |
| 前台 | Vue3 SPA(frontend/home) |
| 后台 | Vue3(frontend/admin) |
| 表前缀 | 默认 kd_(以 .env / 安装配置为准) |
| 仓库根 | kede-finance/(本手册相对路径均相对此根,除非写明 wiki/) |
1.2 四类扩展(勿混用)
| 扩展 | 磁盘位置 | 注册表 | 后台入口 | 用途 |
|---|---|---|---|---|
| 插件 Plugin | public/plugins/{type}/{name}/ | kd_addons | 插件管理 | 功能/支付/短信/邮件/实名等 |
| 主题 Theme | public/themes/{web|clientarea|cart}/{name}/ | kd_themes + configs.theme.* | 主题模板 | 外观 / SSR |
| 开通模块 Module | extend/modules/{Name}/ | 产品模块配置 | 产品/服务器 | 开通云资源等 |
| 魔方兼容层 | 插件命名空间别名 + 主题 mf_compat / .tpl | — | — | 兼容 ZJMF/Magfang 资产 |
插件内部的 template/ 不是站点主题。
1.3 运行时关键路径(插件)
AppInit
→ app\listener\PluginBoot
→ PluginService::boot()
· 注册自动加载:{type}\{name}\Class → public/plugins/{type}/{name}/
· status=1 的插件:校验 integrity_hash → include hooks.php → include route.php
· server/reserver 类型始终尝试加载 hooks/route
→ NotifyService 等核心钩子1.4 关键文件(Agent 优先打开)
| 角色 | 路径 |
|---|---|
| 插件基类 | app/common/lib/Plugin.php |
| 生命周期 | app/common/service/PluginService.php |
| 钩子 | app/common/service/HookService.php + app/common.php(add_hook) |
| 后台菜单/页 | app/common/service/PluginMenuService.php |
| 后台 API | app/admin/controller/Addon.php / Marketplace.php |
| 主题服务 | app/common/service/ThemeService.php |
| 主题后台 | app/admin/controller/Theme.php |
| 前台应用主题 | frontend/home/src/utils/theme.ts |
| 主题构建 | frontend/theme-sdk/scripts/build-themes.mjs |
| 打包市场 | scripts/build-marketplace.php |
| 诊断 | scripts/test-siyun-plugin.php · scripts/diagnose-theme.php |
2. 目录与命名结构(清晰标注)
2.1 插件目录树
public/plugins/
├── addon/ ← 功能扩展(最常见)
│ └── {snake_name}/
│ ├── {CamelName}.php 【必须】主类;或 {CamelName}Plugin.php
│ ├── hooks.php 【可选】add_hook(...)
│ ├── config.php 【可选】默认配置 array
│ ├── route.php 【可选】启用后加载;server/reserver 始终尝试
│ ├── admin_menu.php 【可选】后台侧栏
│ ├── admin/page.php 【可选】后台宿主页 HTML
│ ├── template/ 【可选】插件自有视图(非站点主题)
│ └── vendor/autoload.php 【可选】
├── gateway/ ← 支付
├── sms/ ← 短信
├── mail/ ← 邮件
├── certification/ ← 实名
├── server/ | reserver/ | oauth/ | captcha/ ← 兼容目录(可空)命名铁律
| 项 | 规则 | 示例 |
|---|---|---|
| 目录名 | snake_case | hello_world |
| 主类文件 | PascalCase.php | HelloWorld.php |
| 命名空间 | {type}\{snake_name} | addon\hello_world |
$info['name'] | PascalCase,与类名一致 | HelloWorld |
| 自建表 | {prefix}addon_{plugin}_* | kd_addon_hello_world |
2.2 主题目录树
源码(长期维护)
frontend/themes/{name}/
├── theme.json 【推荐】元数据 + config 变量
├── screenshot.svg 【可选】
├── home/theme.css → 构建到 public/themes/web/{name}/
├── clientarea/theme.css → public/themes/clientarea/{name}/
└── cart/theme.config → public/themes/cart/{name}/运行时(站点实际加载)
public/themes/
├── web/{name}/ ← 内部 type = home(官网)
├── clientarea/{name}/ ← 会员中心
└── cart/{name}/ ← 购物车| 内部 type | 磁盘目录 | 用途 |
|---|---|---|
home | public/themes/web/ | 官网 |
clientarea | public/themes/clientarea/ | 会员中心 |
cart | public/themes/cart/ | 购物车 |
2.3 参考实现索引
| 角色 | 路径 |
|---|---|
| 文档示例 addon | public/plugins/addon/hello_world/ |
| 生产向生命周期 | public/plugins/addon/login_log/ |
| 后台侧栏 + 宿主页 | public/plugins/addon/sidebar_demo/ |
| 支付示例 | public/plugins/gateway/payjs/ |
| 魔方短信 | public/plugins/sms/siyun/ |
| SPA 主题示例 | frontend/themes/starter/ |
| 魔方主题样例 | public/themes/*/mf_compat/ |
3. 开发规范(必须遵守)
3.1 通用
- PHP 8.1+,新文件建议
declare(strict_types=1); - 不提交
.env、私钥、授权站内部协议细节 - 用户输入写库前过滤;对外 HTTP 设超时
- 钩子内避免长时间阻塞;邮件用
NotifyService::sendMailQuiet()一类静默发送 - 钩子异常会被捕获记日志,不要依赖抛异常中断主流程
- 改动范围只做任务需要的文件;勿顺手重构无关模块
3.2 插件规范
| 规范 | 要求 |
|---|---|
| 基类 | addon → app\common\lib\Plugin;类型插件用对应 *Plugin |
| 生命周期 | install / uninstall / enable / disable 按需实现 |
| 返回值 | install/uninstall:true 成功;false 失败;短信 Magfang 模板数组视为成功 |
| 配置 | config.php 默认值 ∪ DB kd_addons.config;读 getConfig() |
| 表单 | configFields(): array 或 Magfang 风格 config.php 字段定义 |
| 钩子 | 仅在 hooks.php 注册;启用后才加载 |
| 菜单 | admin_menu.php 的 key 必须以 plugin: 开头 |
| 完整性 | 启用时写 integrity_hash;篡改后钩子可能被跳过,需重启用/修复 |
| 驱动键 | 支付/实名等常为 plugin:{name}(如 plugin:payjs) |
3.3 主题规范
| 规范 | 要求 |
|---|---|
| CSS 作用域 | 必须带 .kd-theme-scope-{type}[data-kd-theme-{type}="{name}"],禁止裸全局污染 |
| 变量 | theme.json → config → CSS --kd-theme-{key} |
| 维护路径 | 改 frontend/themes/ 后执行构建;勿只改 public/ 后丢源码 |
| SPA vs SSR | 默认 SPA;仅魔方旧主题需要 theme.ssr_mode=tpl |
| 分发 | 给客户的 zip 基于已构建的 public/themes/... |
3.4 Git / 发布(与仓库规则一致)
- 版本与 Build 以
app/common/Version.php为准(产品发版时) - 插件/主题独立分发时,在自身
$info['version']/theme.json.version递增 - 提交信息说明「为什么」;未经要求不要
git commit
4. 插件开发
4.1 生命周期(状态机)
磁盘文件就绪
│
▼
[安装] PluginService::install
· 校验主类存在 → new Instance → install()
· INSERT kd_addons(通常 status=1)
· 立即 include hooks.php
· trigger AfterAddonInstall
│
▼
运行中(status=1):boot 加载 hooks / route
│
├─[停用] setStatus(0) → disable() → 钩子不再加载
├─[启用] setStatus(1) → enable() → 刷新 integrity_hash
├─[配置] GET config / POST saveConfig → kd_addons.config
│
▼
[卸载] uninstall() → DELETE kd_addons → AfterAddonUninstall
(磁盘文件保留,可再次安装)4.2 最小 addon 模板
public/plugins/addon/my_addon/MyAddon.php
<?php
declare(strict_types=1);
namespace addon\my_addon;
use app\common\lib\Plugin;
class MyAddon extends Plugin
{
public $info = [
'name' => 'MyAddon',
'title' => '我的插件',
'description' => '说明',
'author' => 'YourName',
'version' => '1.0.0',
];
public function install() { return true; }
public function uninstall() { return true; }
}hooks.php
<?php
add_hook('after_user_register', function ($param) {
// ...
});config.php
<?php
return ['enabled' => '1'];完整建表 + configFields 示例:直接复制 public/plugins/addon/hello_world/。
4.3 钩子
add_hook('hook_name', callable $fn, int $priority = 50); // 数字越小越先
run_hook('hook_name', $params); // 业务侧一般用 HookService::trigger- CamelCase 与 snake_case 互通(
UserLogin↔user_login) - 常用钩子见 wiki
11-hooks.md(AfterUserRegister、AfterInvoicePaid、AdminMenu等) - 魔方别名示例:
AfterInvoicePaid→ 另触发order_paid
4.4 类型插件契约
| type | 基类 | 必须实现(要点) | 后台选用 |
|---|---|---|---|
addon | Plugin | 按需 hooks/建表 | 插件管理 |
gateway | GatewayPlugin + GatewayInterface | name/title/configFields/pay/notify/notifySuccessResponse | 支付网关 plugin:{name} |
sms | SmsPlugin + SmsDriverInterface | send(...);Magfang 可保留 sendCnSms 由桥接 | 通知设置 |
mail | MailPlugin + MailDriverInterface | send(...) | 通知设置 |
certification | CertificationPlugin + RealnameProviderInterface | initVerify / queryStatus | 实名 plugin:{name} |
接口源码:
app/common/gateway/GatewayInterface.phpapp/common/notify/SmsDriverInterface.phpapp/common/notify/MailDriverInterface.phpapp/common/realname/RealnameProviderInterface.php
4.5 后台菜单与宿主页
admin_menu.php:
return [[
'key' => 'plugin:my_addon',
'title' => '我的模块',
'addon' => 'my_addon',
'icon' => 'experiment',
'sort' => 85,
'permission' => 'plugin',
]];admin/page.php 返回 ['title' => '...', 'html' => '...'],由 PluginHost.vue 渲染。
对照:public/plugins/addon/sidebar_demo/。
4.6 配置字段形状
public function configFields(): array
{
return [
['name' => 'api_key', 'label' => '密钥', 'type' => 'text', 'default' => '', 'required' => true],
// type: text | password | textarea | select | switch …(以后台渲染支持为准)
];
}Magfang 插件常见 config.php 为带 type/value 的字段树;系统会 flatten 为键值再存库。
5. 主题开发
5.1 模式选择
| 模式 | 何时用 | 产物 |
|---|---|---|
| SPA + CSS(推荐) | 绝大多数新主题 | theme.css + 可选 theme.json |
| SPA + Theme SDK 组件 | 要改区块结构 | 需改 frontend/home(升级冲突风险高) |
| .tpl SSR | 魔方旧主题 / 要服务端 HTML | index.tpl + theme.ssr_mode=tpl |
5.2 SPA 主题最小步骤
- 复制
frontend/themes/starter→frontend/themes/{name} - 改
theme.json的name/title/config(name= 目录名) - 写带作用域的 CSS(见 §3.3)
- 构建:
cd kede-finance
node frontend/theme-sdk/scripts/build-themes.mjs- 后台 主题模板 → 扫描 → 按类型启用
- 前台 Ctrl+F5;或检查
GET /home/content/siteConfig的theme/theme_assets
5.3 CSS 作用域模板
.kd-theme-scope-home[data-kd-theme-home="mytheme"] .hero-slide {
background: linear-gradient(
125deg,
var(--kd-theme-hero-from, #0f766e),
var(--kd-theme-hero-to, #14b8a6)
);
}5.4 SSR 摘要
POST /admin/theme/ssrModebody:{ "mode": "tpl" }- 访问
/tpl/home(开发public/router.php、生产 nginx 需有/tpl规则) - 渲染器:
ThemeTplRenderer(Smarty 子集) - SPA 主题未开 SSR 时,
/tpl/*可能 302 回/,属正常
5.5 魔方主题安装
- 解压到
public/themes/{web|clientarea|cart}/{name}/ - 确保有
theme.config或theme.json - 后台扫描并启用
详见frontend/themes/README.md。
6. 安装与测试流程(一目了然)
6.1 插件:本地开发安装(最常用)
┌─────────────────────────────────────────────────────────┐
│ A. 放文件 │
│ public/plugins/{type}/{snake_name}/ 写好主类等 │
├─────────────────────────────────────────────────────────┤
│ B. 后台安装 │
│ 后台 → 插件管理 →「本地插件」Tab │
│ → 筛选 type → 找到插件 →【安装】→【启用】 │
├─────────────────────────────────────────────────────────┤
│ C. 配置(如有) │
│ →【配置】→ 填写 → 保存 │
├─────────────────────────────────────────────────────────┤
│ D. 功能验证(按下表) │
└─────────────────────────────────────────────────────────┘验收清单(addon)
| # | 步骤 | 期望 |
|---|---|---|
| 1 | 本地插件列表可见 | 标题/版本正确 |
| 2 | 安装 | 成功;kd_addons 有行;install() 建表存在 |
| 3 | 启用 | status=1 |
| 4 | 触发业务(如注册) | 钩子副作用出现(表记录/日志) |
| 5 | 改配置再触发 | 新配置生效 |
| 6 | 停用再触发 | 钩子不再执行 |
| 7 | 卸载 | kd_addons 无行;uninstall 清表(若实现) |
验收清单(gateway)
| # | 步骤 | 期望 |
|---|---|---|
| 1 | 安装启用 | 支付设置可选 plugin:{name} |
| 2 | 发起支付 | 返回跳转/二维码;失败抛可识别错误 |
| 3 | 异步回调 | 验签通过;账单变已支付 |
验收清单(sms / mail)
| # | 步骤 | 期望 |
|---|---|---|
| 1 | 安装启用并选为驱动 | 通知设置可见 |
| 2 | 后台测试发送 | 手机/邮箱收到或驱动返回明确错误 |
验收清单(certification)
| # | 步骤 | 期望 |
|---|---|---|
| 1 | 安装启用,实名驱动选 plugin:{name} | 前台可发起 |
| 2 | 完成或回调 | 用户实名状态更新 |
CLI 冒烟(魔方短信)
php scripts/test-siyun-plugin.php6.2 插件:生产压缩包安装
1. 制作 zip(推荐布局):
my_addon/
MyAddon.php
hooks.php
config.php
2. 解压到: public/plugins/addon/my_addon/
3. 后台 → 本地插件 → 安装 → 启用 → 配置不要把 zip 解压成
public/plugins/addon/public/plugins/...嵌套;系统虽有修复,但应避免。
6.3 插件:开发环境市场安装
1. .env: MARKETPLACE_SERVER = https://你的市场地址
2. 后台 → 插件市场 Tab → 安装/升级
3. 生产/内网:不要依赖此方式;改用 §6.2发布者打包(仅供市场服务器,非生产离线介质):
php scripts/build-marketplace.php
# → resources/marketplace/packages/{type}/{name}.zip + catalog.json6.4 主题:开发 → 启用 → 验收
┌─────────────────────────────────────────────────────────┐
│ 1. 改 frontend/themes/{name}/ │
│ 2. node frontend/theme-sdk/scripts/build-themes.mjs │
│ 3. 后台 → 主题模板 →【扫描】 │
│ 4. 分别启用 home / clientarea / cart(按需) │
│ 5. 前台强刷;检查配色/变量 │
└─────────────────────────────────────────────────────────┘| # | 验证 | 期望 |
|---|---|---|
| 1 | public/themes/web/{name}/theme.css 存在 | 构建成功 |
| 2 | GET /home/content/siteConfig | theme.home 等为新名 |
| 3 | 浏览器 Elements | data-kd-theme-home="{name}";CSS 已加载 |
| 4 | (可选)php scripts/diagnose-theme.php | 无发现异常 |
主题 zip 给客户
解压到站点 public/themes/ 对应子目录后 → 扫描 → 启用6.5 API 快速自测(curl)
# 健康/站点(按你的域名或本地端口调整)
curl -sS 'http://127.0.0.1:8800/home/content/siteConfig' | head
# 后台需带管理员 Cookie / Token,以下仅示意路径:
# GET /admin/addon/index?type=addon
# POST /admin/addon/install body: type, name
# POST /admin/addon/enable
# GET /admin/addon/config?type=&name=
# POST /admin/addon/saveConfig
# GET /admin/theme/index
# POST /admin/theme/sync
# POST /admin/theme/activate body: type, name
# POST /admin/theme/ssrMode body: mode=spa|tpl权限模块名:插件 UI 使用 plugin。
7. API 速查
插件对接完整说明(参数 / 响应 / 驱动 / 魔方 / 自定义 REST):
04b 插件对接 API — Agent 写对接代码时优先打开。
7.1 插件 Addon(摘要)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /admin/addon/index?type= | 扫描列表 |
| POST | /admin/addon/install | 安装 |
| POST | /admin/addon/uninstall | 卸载 |
| POST | /admin/addon/enable / disable | 启停 |
| GET | /admin/addon/config | 配置 + _fields |
| POST | /admin/addon/saveConfig | 保存 |
| GET | /admin/addon/menus | 侧栏注入 |
| GET | /admin/addon/page | 宿主页 |
7.2 市场 Marketplace(摘要)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /admin/marketplace/index | 目录 |
| GET | /admin/marketplace/detail | 详情 |
| POST | /admin/marketplace/fetch | 仅下载 |
| POST | /admin/marketplace/install | 下载+安装 |
| POST | /admin/marketplace/upgrade | 覆盖升级 |
| POST | /admin/marketplace/sync | 重扫本地 |
7.3 主题 Theme
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /admin/theme/index | 列表+当前 |
| POST | /admin/theme/sync | 扫描同步 |
| POST | /admin/theme/activate | 激活 |
| POST | /admin/theme/ssrMode | spa/tpl |
| GET | /home/content/siteConfig | 前台主题资源 |
8. 排错矩阵
| 现象 | 优先排查 |
|---|---|
| 列表看不到插件 | 路径是否 plugins/{type}/{snake};命名空间;主类文件名;PHP 语法(runtime/log) |
| 安装失败 | install() 是否 return true;建表 SQL;目录写权限 |
| 钩子不跑 | 是否启用;hooks.php 是否存在;钩子名;integrity 是否失败 |
| 配置不生效 | 是否保存;是否 getConfig();字段名是否一致 |
| 配置页「无可编辑配置」 | 缺 configFields() 且 config.php 无可识别字段;Magfang 字段需可 flatten |
| 类找不到 | 自动加载命名空间;是否误放 addons 复数目录 |
| 主题不生效 | 是否构建到 public/themes;是否扫描/激活;浏览器缓存;CSS 作用域选择器 |
/tpl 302 到首页 | 当前为 SPA 且未开 SSR,正常 |
| 市场无法安装 | 生产应改压缩包;检查 MARKETPLACE_SERVER 与网络 |
| 魔方短信异常 | php scripts/test-siyun-plugin.php;桥接类 ZjmfSmsDriverBridge |
9. Agent 任务模板(可直接套用)
9.1 新功能插件
目标:实现 addon「{标题}」,目录名 {snake_name}
步骤:
1. 创建 public/plugins/addon/{snake_name}/ 按 §2.1
2. 实现 install/uninstall/hooks/configFields(按需求)
3. 对照 hello_world 自测 §6.1
4. 若需侧栏:admin_menu.php + admin/page.php(对照 sidebar_demo)
5. 需要分发则打 zip(§6.2)
交付:路径列表 + 验收步骤结果说明9.2 新 SPA 主题
目标:主题「{title}」,name={name}
步骤:
1. 复制 frontend/themes/starter → frontend/themes/{name}
2. 改 theme.json 与 CSS 作用域
3. node frontend/theme-sdk/scripts/build-themes.mjs
4. 按 §6.4 验收
交付:源码路径 + public 产物路径 + 激活截图/siteConfig 片段10. 深入文档索引
| 文档 | 何时打开 |
|---|---|
| 01 概览 | 不熟悉产品 |
| 02 快速开始 | 搭本地环境 |
| 03 目录结构 | 找目录 |
| 04 插件系统 | 插件概览 |
| 04a 插件教程 | 手把手 addon |
| 04b 插件对接 API | HTTP/驱动/魔方全量接口 |
| 05 插件市场 | 打包/市场 |
| 06 支付 | gateway |
| 07 实名 | certification |
| 08 通知 | sms/mail |
| 09 开通模块 | 不是插件 |
| 10 主题与 SSR | 主题概览 |
| 10a 主题教程 | 手把手 SPA |
| 11 钩子 | 钩子全表 |
| 13 部署 | 宝塔/上线 |
| 29 安全 | 加固检查 |
kede-finance/AGENTS.md | 仓库内 Agent 入口 |
kede-finance/README-DEV.md | 本地启动命令 |
11. 一页纸速记
插件 = public/plugins/{type}/{snake}/ + kd_addons
主题 = frontend/themes → build → public/themes/{web|clientarea|cart}
模块 = extend/modules(开通,别写成插件)
安装插件:放文件 → 本地插件「安装+启用」→ 测钩子/驱动
装主题:build → 主题模板「扫描+启用」→ 强刷前台
必须 return true;必须 CSS 作用域;生产用 zip 本地装
参考:hello_world / sidebar_demo / starter / payjs / siyun