xiuno-bbs-plugin-generator
---
name: "xiuno-bbs-plugin-generator"
description: "根据用户自然语言需求,自动生成符合Xiuno BBS规范的完整插件代码。当用户需要创建Xiuno BBS插件、开发论坛功能扩展、或提到'写插件'、'开发插件'、'生成插件'时调用。"
---
# Xiuno BBS 插件自动生成技能
## 技能定位
本技能使AI能够根据用户的自然语言需求描述,按照标准化流程自动生成符合Xiuno BBS 4.0规范的完整插件代码。AI将执行:需求分析 → 结构规划 → 代码生成 → 校验输出的完整流程。
## 技术栈约束
- **后端**: PHP 7.2+(推荐8.0+),面向过程风格
- **数据库**: MySQL 5.7+ / MariaDB 10.2+,InnoDB引擎
- **前端**: Bootstrap 5 + HTMX + 原生JS/jQuery
- **模板**: PHP原生模板语法(.htm文件)
- **编码**: UTF-8 无BOM,LF换行符
## 核心机制速查
### Hook机制(编译时合并)
- PHP标记: `// hook 文件名.php`
- HTM标记: ``
- PHP Hook文件以 `" />`
- 权限: `user_login_check()` 登录检查,`$gid` 权限检查
---
## 模块一:需求分析
当用户描述插件需求时,AI必须先完成以下分析,并向用户确认后再生成代码:
### 1.1 功能分类判定
将用户需求归入以下类别之一:
| 类别 | 特征 | 典型示例 |
|------|------|---------|
| **数据展示型** | 读取现有数据并展示 | 热帖排行、用户排行、统计面板 |
| **用户交互型** | 用户可操作并产生数据 | 点赞、收藏、签到、评分 |
| **内容增强型** | 增强帖子/版块展示 | 标签、图章、编辑器、代码高亮 |
| **系统扩展型** | 扩展系统功能 | 积分、VIP、权限、搜索 |
| **页面新增型** | 添加全新页面 | 自定义页面、工具页、数据看板 |
| **后台管理型** | 后台管理功能 | 批量操作、数据导入导出 |
### 1.2 必须确认的信息
AI必须明确以下信息(可从用户描述中推断,无法推断时需询问):
1. **插件名称和ID**: 遵循 `作者标识_功能名称` 格式
2. **核心功能列表**: 插件需要实现的每个功能点
3. **数据存储需求**: 是否需要新建数据库表?需要哪些字段?
4. **UI展示位置**: 在哪些页面展示?使用哪些Hook点?
5. **是否需要设置页面**: 用户可配置哪些选项?
6. **是否需要后台管理**: 管理员需要哪些管理功能?
7. **依赖插件**: 是否依赖其他插件(如积分插件)?
8. **权限需求**: 哪些用户组可以使用?是否需要登录?
### 1.3 Hook点选择指南
根据功能需求选择合适的Hook点:
| 需求场景 | 推荐Hook点 | 说明 |
|---------|-----------|------|
| 首页添加面板 | `index_site_brief_after.htm` | 站点简介后 |
| 首页注入JS | `index_js.htm` | 首页JS区域 |
| 帖子底部按钮 | `thread_plugin_body.htm` | 首帖底部操作区 |
| 帖子标题后标记 | `thread_subject_end.htm` | 标题区域 |
| 帖子列表标记 | `thread_list_inc_subject_after.htm` | 列表标题后 |
| 发帖表单增强 | `post_message_after.htm` | 发帖内容区后 |
| 帖子页JS | `thread_js.htm` | 帖子页JS区域 |
| 发帖页JS | `post_js.htm` | 发帖页JS区域 |
| 导航栏添加项 | `header_nav_user_start.htm` | 导航栏用户区前 |
| 全局JS | `footer_js_after.htm` | 页面底部JS后 |
| 注册Model | `model_inc_file.php` | 注入Model文件(末尾需逗号) |
| 注册前台路由 | `index_route_case_end.php` | switch-case内添加case |
| 注册个人中心路由 | `my_end.php` | elseif分支 |
| 注册后台路由 | `admin_index_route_case_end.php` | switch-case内添加case |
| 帖子创建后 | `model_thread_create_end.php` | 可用$tid, $arr |
| 帖子删除前 | `model_thread_delete_start.php` | 可用$tid |
| 回帖创建后 | `model_post_create_end.php` | 可用$pid, $arr |
| 回帖删除前 | `model_post_delete_start.php` | 可用$pid |
| 回帖更新后 | `model_post_update_end.php` | 可用$pid, $arr |
| 用户登录后 | `user_login_post_end.php` | 可用$uid |
| 版块格式化 | `model_forum_end.php` | 注入forum_format扩展 |
| 主题格式化 | `model_thread_format_end.php` | 注入thread_format扩展 |
---
## 模块二:插件结构生成
### 2.1 目录结构决策树
```
需要新建数据库表?
├─ 是 → 需要 install.php + unstall.php + model/
└─ 否 → 不需要
需要独立页面?
├─ 是 → 需要 route/ + view/htm/ + hook/index_route_case_end.php
└─ 否 → 仅通过hook注入UI
需要用户配置?
├─ 是 → 需要 setting.php + setting.htm
└─ 否 → 不需要
需要后台管理?
├─ 是 → 需要 admin_*.php + hook/admin_index_route_case_end.php
└─ 否 → 不需要
需要静态资源?
├─ 是 → 需要 static/ + static/index.htm
└─ 否 → 不需要
```
### 2.2 标准目录结构模板
```
{plugin_id}/
├── conf.json # 必须
├── icon.png # 推荐(64x64)
├── install.php # 需要建表时
├── unstall.php # 需要建表时
├── upgrade.php # 有版本升级需求时
├── setting.php # 需要设置页面时
├── setting.htm # 需要设置页面时
├── hook/ # 必须
│ └── (根据需求选择)
├── model/ # 需要自定义数据模型时
│ └── {plugin_id}.func.php
├── route/ # 需要独立页面时
│ └── {page_name}.php
├── view/ # 需要独立页面时
│ └── htm/
│ └── {page_name}.htm
└── static/ # 需要静态资源时
├── index.htm
└── (资源文件)
```
---
## 模块三:代码模板调用
### 3.1 conf.json 模板
```json
{
"name": "{插件中文名}",
"brief": "{一句话描述}",
"version": "1.0.0",
"bbs_version": "4.0",
"installed": 0,
"enable": 0,
"hooks_rank": {},
"overwrites_rank": {},
"dependencies": {}
}
```
**hooks_rank填充规则**:
- 默认不填(权重0)
- 需要优先执行时填负值(如积分扣除: `-10`)
- 需要延后执行时填正值(如VIP标记: `100`)
- 多插件竞争同一hook时明确设置
### 3.2 install.php 模板
```php
tablepre;
// === 创建数据表 ===
$sql = "CREATE TABLE IF NOT EXISTS {$tablepre}{表名} (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) unsigned NOT NULL default '0',
`tid` int(11) unsigned NOT NULL default '0',
`pid` int(11) unsigned NOT NULL default '0',
`create_date` int(10) unsigned NOT NULL default '0',
`create_ip` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (id),
KEY (uid),
KEY (tid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
db_exec($sql);
// === 给核心表添加字段(安全方式) ===
$col_exists = db_find_one("SHOW COLUMNS FROM {$tablepre}post LIKE '{字段名}'");
if (empty($col_exists)) {
db_exec("ALTER TABLE {$tablepre}post ADD COLUMN {字段名} int(11) DEFAULT '0';");
}
// === 初始化配置 ===
setting_set('{plugin_id}', array(
'{option_key}' => '{default_value}',
));
```
### 3.3 unstall.php 模板
```php
tablepre;
// 删除数据表
db_exec("DROP TABLE IF EXISTS {$tablepre}{表名};");
// 删除添加的字段
$col_exists = db_find_one("SHOW COLUMNS FROM {$tablepre}post LIKE '{字段名}'");
if (!empty($col_exists)) {
db_exec("ALTER TABLE {$tablepre}post DROP COLUMN {字段名};");
}
// 清理配置和缓存
setting_delete('{plugin_id}');
cache_delete('{plugin_id}_cache');
```
### 3.4 Model文件模板
```php
$id), $arr);
}
function {table}__read($id) {
return db_find_one('{table}', array('id'=>$id));
}
function {table}__delete($id) {
return db_delete('{table}', array('id'=>$id));
}
function {table}__find($cond = array(), $orderby = array(), $page = 1, $pagesize = 20) {
return db_find('{table}', $cond, $orderby, $page, $pagesize);
}
function {table}__count($cond = array()) {
return db_count('{table}', $cond);
}
// === 关联CRUD(含缓存清理和数据格式化) ===
function {table}_create($arr) {
$r = {table}__create($arr);
cache_delete('{table}_list');
return $r;
}
function {table}_read($id) {
$data = {table}__read($id);
if ($data) {table}_format($data);
return $data;
}
function {table}_read_cache($id) {
$key = '{table}_' . $id;
$data = cache_get($key);
if ($data === NULL) {
$data = {table}_read($id);
cache_set($key, $data, 3600);
}
return $data;
}
function {table}_update($id, $arr) {
$r = {table}__update($id, $arr);
cache_delete('{table}_' . $id);
cache_delete('{table}_list');
return $r;
}
function {table}_delete($id) {
$r = {table}__delete($id);
cache_delete('{table}_' . $id);
cache_delete('{table}_list');
return $r;
}
function {table}_find($cond = array(), $orderby = array('id'=>-1), $page = 1, $pagesize = 20) {
$datalist = {table}__find($cond, $orderby, $page, $pagesize);
if ($datalist) foreach ($datalist as &$data) {
{table}_format($data);
}
return $datalist;
}
function {table}_format(&$data) {
if (empty($data)) return;
$data['create_date_fmt'] = date('Y-m-d H:i', $data['create_date']);
}
function {table}_count($cond = array()) {
return {table}__count($cond);
}
function {table}_maxid() {
return db_maxid('{table}');
}
```
### 3.5 Hook文件模板
**model_inc_file.php**(注册Model):
```php
**注意**: 末尾必须有逗号!
**index_route_case_end.php**(注册前台路由):
```php
```
**JS Hook文件**(注入交互):
```html
```
**model_xxx_delete_start.php**(级联删除):
```php
$tid));
```
### 3.6 路由文件模板
```php
-1), $page, $pagesize);
$total = {table}_count();
$pagination = pagination(url('{route}-list-{page}'), $total, $page, $pagesize);
include _include(APP_PATH.'plugin/{plugin_id}/view/htm/{page}_list.htm');
} elseif ($action == 'create') {
user_login_check();
if ($method == 'GET') {
include _include(APP_PATH.'plugin/{plugin_id}/view/htm/{page}_create.htm');
} elseif ($method == 'POST') {
$field = param('field');
if (empty($field)) message(-1, '字段不能为空');
{table}_create(array(
'uid' => $uid,
'field' => $field,
'create_date' => time(),
'create_ip' => ip2long($ip),
));
message(0, '创建成功');
}
} elseif ($action == 'delete') {
user_login_check();
$id = param(2, 0);
{table}_delete($id);
message(0, '删除成功');
} else {
include _include(APP_PATH.'plugin/{plugin_id}/view/htm/{page}.htm');
}
```
### 3.7 视图文件模板
**标准页面**:
```php
```
**设置页面**:
```php
```
### 3.8 setting.php 模板
```php
'{default_value}',
);
include _include(APP_PATH.'plugin/{plugin_id}/setting.htm');
} elseif ($method == 'POST') {
$config = setting_get('{plugin_id}');
$config['{option_key}'] = param('{option_key}');
setting_set('{plugin_id}', $config);
message(0, '设置成功');
}
}
```
---
## 模块四:功能实现指引
### 4.1 常见功能实现模式
#### 点赞/收藏/关注模式
```
数据: 独立表(uid+pid/tid, UNIQUE KEY防重复) + 核心表字段(count缓存)
操作: toggle(存在则删除+count-1,不存在则创建+count+1)
UI: 按钮+数字,AJAX交互
Hook: thread_plugin_body.htm(按钮) + thread_js.htm(JS) + model_post_delete_start.php(级联删除)
```
#### 签到/打卡模式
```
数据: 独立表(uid+日期, UNIQUE KEY防重复) + setting(奖励配置)
操作: 每日一次,检查今日是否已签到,计算连续签到/首签奖励
UI: 首页面板 + 个人中心记录
Hook: index_site_brief_after.htm(面板) + my_end.php(个人中心) + index_js.htm(JS)
依赖: 常依赖积分插件(tt_credits)
```
#### 积分/虚拟货币模式
```
数据: ALTER TABLE user ADD COLUMN(积分字段) + setting(规则配置) + 独立表(交易记录)
操作: 各场景hook中扣除/奖励积分,增量更新(user_update array('credits+'=>1))
Hook: model_thread_create_end.php(发帖奖励) + model_post_create_end.php(回帖奖励) + model_xxx_delete_start.php(删帖扣除)
权限: forum_access_user()检查 + 自定义权限检查
```
#### 标签/分类模式
```
数据: 标签表 + 关联表(tagid+tid) + ALTER TABLE thread ADD COLUMN tagids(缓存)
操作: 发帖时选择标签(写入关联表+缓存tagids),查询时按标签筛选
Hook: model_thread_format_end.php(格式化时加载标签) + post_js.htm(发帖表单) + thread_subject_end.htm(显示标签)
```
#### 编辑器增强模式
```
数据: 无需建表,纯前端替换
操作: 通过hook注入编辑器JS,替换默认textarea
Hook: post_js.htm(注入编辑器) + index_route_case_end.php(图片上传路由)
```
#### 排行榜/统计面板模式
```
数据: 读取现有数据 + cache缓存结果
操作: 定期从核心表聚合数据,缓存到cache
UI: 首页面板或独立页面
Hook: index_site_brief_after.htm(面板) + index_js.htm(异步加载)
```
### 4.2 数据库设计规范
| 字段类型 | 推荐定义 | 说明 |
|---------|---------|------|
| 主键 | `id int(11) unsigned NOT NULL AUTO_INCREMENT` | 自增主键 |
| 用户ID | `uid int(11) unsigned NOT NULL default '0'` | 关联bbs_user |
| 主题ID | `tid int(11) unsigned NOT NULL default '0'` | 关联bbs_thread |
| 帖子ID | `pid int(11) unsigned NOT NULL default '0'` | 关联bbs_post |
| 版块ID | `fid int(6) NOT NULL default '0'` | 关联bbs_forum |
| 时间戳 | `create_date int(10) unsigned NOT NULL default '0'` | Unix时间戳 |
| IP地址 | `create_ip int(10) unsigned NOT NULL default '0'` | ip2long()转换 |
| 文本 | `message longtext NOT NULL` | 长文本 |
| 短文本 | `title varchar(255) DEFAULT ''` | 短文本 |
| 整数 | `count int(11) DEFAULT '0'` | 计数类 |
| 开关 | `status tinyint(1) DEFAULT '0'` | 0/1开关 |
### 4.3 安全检查清单
生成代码时必须确保:
- [ ] 所有用户输入通过 `param()` 获取
- [ ] 表单包含 `form_hash` CSRF防护
- [ ] 需要登录的操作调用 `user_login_check()`
- [ ] 需要权限的操作检查 `$gid`
- [ ] PHP Hook文件以 `tablepre`获取) |
| `$form_hash` | string | CSRF令牌 |
| `$header['title']` | string | 页面标题 |
## 附录C:常见错误与修复
| 错误现象 | 可能原因 | 修复方法 |
|---------|---------|---------|
| 白屏/500错误 | hook文件PHP语法错误 | 检查tmp/编译文件,修复语法 |
| hook不生效 | DEBUG不是2 / 文件名不匹配 | 设DEBUG=2,核对hook文件名 |
| 安装报错 | ALTER TABLE字段已存在 | 添加字段前先检查 |
| 数据不更新 | 缓存未清理 | 更新后cache_delete() |
| AJAX返回HTML | 请求未带正确头 | 使用$.xpost() |
| URL 404 | 路由未注册 | 检查index_route_case_end.php |
| 权限不足 | 未检查gid | 添加权限检查代码 |
| 重复提交 | 缺少form_hash | 表单添加CSRF令牌 |