docs: 更新API项目文档索引
This commit is contained in:
parent
0273493ab4
commit
d25ea3952f
912
API.md
912
API.md
@ -1,8 +1,19 @@
|
||||
# KT Template Online API
|
||||
|
||||
后端服务默认监听 `48085`,Swagger 地址为 `/api`,OpenAPI JSON 地址为 `/api-json`。
|
||||
本文是当前 API 的人工索引。字段细节、Swagger 示例和 DTO 以运行态 Swagger/Knife4j 为准:
|
||||
|
||||
除文件下载接口外,接口统一返回:
|
||||
- 全量 Swagger:`/api`
|
||||
- OpenAPI JSON:`/api-json`
|
||||
- Admin 分组:`/api/admin`
|
||||
- QQBot 分组:`/api/qqbot`
|
||||
- WordPress 分组:`/api/wordpress`
|
||||
- 基础能力分组:`/api/basic`
|
||||
|
||||
## 通用约定
|
||||
|
||||
后端固定监听 `48085`。根路径 `GET /` 重定向到 `/api#/`。
|
||||
|
||||
除文件下载、SSE、反向 WebSocket 等特殊接口外,业务接口统一返回:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -12,7 +23,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
失败时使用 `err` 承载错误信息,不返回成功结构里的 `data`:
|
||||
错误响应统一把 `err` 输出为字符串:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -22,600 +33,419 @@
|
||||
}
|
||||
```
|
||||
|
||||
## 功能模块
|
||||
### 认证
|
||||
|
||||
| 模块 | 说明 |
|
||||
| --------- | ----------------------------------------------------------------------------------------- |
|
||||
| Component | Admin 下受保护的组件/图表模板列表、详情、新增、编辑、逻辑删除,数据表为 `admin_component` |
|
||||
| Dict | 基于新 `admin_dict` 表的数据库字典查询,以及组件一级类型到二级类型的数据库关系映射 |
|
||||
| Admin | Vben Admin 真实接口,包含认证、用户、菜单、角色、部门、时区和上传适配 |
|
||||
| MinIO | Bucket 检查/创建、文件上传、列表、临时访问地址、下载和删除 |
|
||||
| WordPress | WordPress 文章、标签、分类管理,复用客户端 WordPress 登录态访问 REST API |
|
||||
| Common | 统一响应 Swagger 注解、字典翻译注解、`POST */save` 请求体规范化拦截器 |
|
||||
| Logging | 基于 Pino 的结构化日志、可选 Loki 直推,以及 Admin 系统日志查询代理 |
|
||||
Admin、Component、Dict、MinIO、Blog 管理、WordPress 管理和 QQBot 管理接口默认需要后台登录态。
|
||||
|
||||
## 通用规则
|
||||
支持两种 access token 传递方式:
|
||||
|
||||
### 数字 ID
|
||||
- `Authorization: Bearer <accessToken>`
|
||||
- 登录接口写入的 httpOnly `admin_access_token` cookie
|
||||
|
||||
后台主键统一使用 Snowflake 数字 ID。数据库字段使用 `BIGINT`;接口 JSON 中按字符串返回,例如 `"2041739550026043392"`,避免 JavaScript 直接用 `number` 承载 64 位长整型导致精度丢失。
|
||||
公开接口包括 `/auth/login`、`/auth/refresh`、`/auth/logout`、部分 Blog public 接口和根路径。具体以 Controller 上的 `@Public()` 为准。
|
||||
|
||||
如果旧版本曾经写入 `admin_user.id=0`,请先执行 `sql/fix-admin-user-zero-id.sql` 修复已有脏数据,再重启后端服务。
|
||||
### ID 与时间
|
||||
|
||||
### Save 请求体规范化
|
||||
- 后台主键使用 Snowflake 数字 ID,接口按字符串返回,避免 JavaScript 长整型精度丢失。
|
||||
- DTO/Entity 需要后端格式化的时间字段使用 `@FormatDateTime()`,输出格式为 `YYYY-MM-DD HH:mm:ss`。
|
||||
- `POST */save` 默认会删除请求体里的 `id`,防止新增接口误用前端主键。
|
||||
|
||||
系统全局注册 `SaveBodyInterceptor`,默认会对 `POST */save` 请求删除 `body.id`,避免新增接口因为前端误传 `id` 而走指定主键保存。
|
||||
## 环境变量分组
|
||||
|
||||
如果个别接口需要保留 `id`,可在对应 Controller 方法上使用 `@SkipSaveBodyNormalize()`。
|
||||
| 分组 | 关键变量 |
|
||||
| --- | --- |
|
||||
| MySQL | `DB_HOST`、`DB_PORT`、`DB_USERNAME`、`DB_PASSWORD`、`DB_DATABASE`、`DB_SYNC` |
|
||||
| MinIO | `MINIO_ENDPOINT`、`MINIO_PORT`、`MINIO_ACCESS_KEY`、`MINIO_SECRET_KEY`、`MINIO_BUCKET` |
|
||||
| Admin | `ADMIN_TOKEN_SECRET`、`ADMIN_COOKIE_SECURE`、`SNOWFLAKE_WORKER_ID`、`SNOWFLAKE_DATACENTER_ID` |
|
||||
| WordPress | `WORDPRESS_BASE_URL`、`WORDPRESS_HOST_HEADER`、`WORDPRESS_ADMIN_USERNAME`、`WORDPRESS_ADMIN_PASSWORD` |
|
||||
| Loki | `LOG_LEVEL`、`LOG_APP_NAME`、`LOKI_URL`、`LOKI_QUERY_HOST`、`LOKI_QUERY_SELECTOR` |
|
||||
| QQBot | `QQBOT_ENABLED`、`QQBOT_REVERSE_WS_PATH`、`QQBOT_REVERSE_WS_TOKEN`、`QQBOT_EVENT_BUS` |
|
||||
| NapCat | `NAPCAT_WEBUI_BASE_URL`、`NAPCAT_WEBUI_TOKEN`、`QQBOT_NAPCAT_*` |
|
||||
| MQTT | `MQTT_URL`、`MQTT_USERNAME`、`MQTT_PASSWORD`、`MQTT_CLIENT_ID` |
|
||||
| BangDream | `BANGDREAM_TSUGU_MAIN_SERVER`、`BANGDREAM_TSUGU_DISPLAYED_SERVERS`、`BANGDREAM_TSUGU_CACHE_ROOT` |
|
||||
| FF14 Market | `FF14_XIVAPI_BASE_URL`、`FF14_UNIVERSALIS_BASE_URL`、`FF14_DEFAULT_WORLD` |
|
||||
| FFLogs | `FFLOGS_GRAPHQL_URL`、`FFLOGS_TOKEN_URL`、`FFLOGS_CLIENT_ID`、`FFLOGS_CLIENT_SECRET` |
|
||||
|
||||
### 后台认证
|
||||
真实密码、Token、OAuth secret 和生产 env 不提交到 Git。
|
||||
|
||||
Admin、Component、Dict 与 MinIO 业务接口统一走 `JwtAuthGuard`。请求可以通过 `Authorization: Bearer <accessToken>` 传递 accessToken,也可以携带登录接口写入的 httpOnly `admin_access_token` cookie。未认证时接口返回 HTTP `401`。
|
||||
## Admin 与基础后台
|
||||
|
||||
`ADMIN_COOKIE_SECURE=false` 适用于当前内网 HTTP 访问;如果后续切到 HTTPS 域名,可以改为 `true`,cookie 会使用 `Secure + SameSite=None`。
|
||||
### Auth / User
|
||||
|
||||
`@Public()` 可用于保留不需要认证的接口口子,目前登录、刷新 token、退出登录和部分示例状态测试接口放行。
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/auth/login` | 后台登录,返回 accessToken、用户信息和 WordPress 自动登录状态,并写入 httpOnly cookie |
|
||||
| `POST` | `/auth/refresh` | 通过 refresh token cookie 刷新 accessToken |
|
||||
| `POST` | `/auth/logout` | 清理 Admin 与 WordPress 登录 cookie |
|
||||
| `GET` | `/auth/codes` | 获取当前用户按钮权限码 |
|
||||
| `GET` | `/user/info` | 获取当前用户信息 |
|
||||
|
||||
### WordPress 认证透传
|
||||
`/auth/login` 会尝试用 env 中的 WordPress 管理员账号建立 WordPress 登录态。WordPress 不可用时,Admin 主登录仍成功,返回 `wordpressAuth=null`、`wordpressAvailable=false`,菜单和权限码会过滤 Blog 管理入口。
|
||||
|
||||
WordPress 侧只使用客户端登录态,后端不走 BasicAuth。当前 WordPress 只有单管理员账号且不开放注册,管理员账号配置放在 env 中,Admin 调用 `/auth/login` 通过后,后端会在同一个登录流程里自动登录 WordPress,把 WordPress cookie 保存到本系统 httpOnly cookie,再把 REST nonce 和用户信息随 Admin 登录结果返回给前端持久化。
|
||||
### Menu / Role / Dept / User Manage
|
||||
|
||||
环境变量:
|
||||
|
||||
| 变量 | 说明 |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `WORDPRESS_BASE_URL` | WordPress 站点根地址,例如 `http://192.168.31.224:8080` |
|
||||
| `WORDPRESS_ADMIN_USERNAME` | WordPress 单管理员账号用户名 |
|
||||
| `WORDPRESS_ADMIN_PASSWORD` | WordPress 单管理员账号密码,仅放真实 env,不提交到仓库 |
|
||||
| `WORDPRESS_TIMEOUT_MS` | WordPress REST API 请求超时时间,默认 `15000` |
|
||||
| `WORDPRESS_LOGIN_TIMEOUT_MS` | Admin 登录链路里 WordPress 自动认证的短超时时间,默认 `3000`,避免远程不可用阻塞主系统登录 |
|
||||
| `WORDPRESS_AVAILABILITY_TTL_MS` | WordPress 可用性缓存时间,默认 `60000`;远程不可用时用于过滤博客菜单和按钮权限 |
|
||||
|
||||
支持的 WordPress 登录态来源:
|
||||
|
||||
| Header/Cookie | 说明 |
|
||||
| --------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `X-WordPress-Authorization` | 优先透传的 WordPress 授权头,例如客户端登录拿到的 `Bearer <token>` |
|
||||
| `Authorization` | 仅当它不是本系统 Admin access token 时才会透传,避免和后台认证冲突 |
|
||||
| `X-WP-Nonce` | WordPress REST cookie 认证 nonce |
|
||||
| `Cookie` | 只会过滤并透传 `wordpress_*`、`wordpress_logged_in_*`、`wp-settings-*` 等 WordPress 登录相关 cookie |
|
||||
| `X-WordPress-Cookie` | 显式传入 WordPress cookie,适合非浏览器客户端联调 |
|
||||
| `kt_wordpress_auth` | 后端自动认证后写入的 httpOnly cookie,前端不可读取,后端会自动转成 WordPress cookie 透传 |
|
||||
|
||||
如果 WordPress 所在 Apache/Nginx 未开启 rewrite,`/wp-json/*` 可能返回 404。后端会自动回退到 WordPress 原生 `?rest_route=/...` 形式,避免因为固定链接配置阻断文章、标签和分类管理接口。
|
||||
|
||||
Admin 主登录不依赖 WordPress 可用性:本系统账号验证通过后会先写入 Admin token;WordPress 自动认证失败时登录仍返回成功,`wordpressAuth` 为 `null`、`wordpressAvailable=false`,并清理旧 WordPress cookie。随后 `/menu/all` 与 `/auth/codes` 会基于最近一次 WordPress 可用性状态过滤 `Blog*` 菜单和 `Blog:*` 按钮权限码,避免前端展示不可用的文章、分类、标签管理入口。
|
||||
|
||||
### 系统日志
|
||||
|
||||
后端使用 `nestjs-pino` 输出结构化 JSON 日志。生产环境默认写 stdout;配置 `LOKI_URL` 或 `LOKI_HOST` 后,会同时通过 `pino-loki` 批量推送到 Loki。Admin 不直接连接 Loki,统一通过后端 `/system/logs/*` 查询代理访问,避免 Loki 地址和凭据暴露到浏览器。
|
||||
|
||||
环境变量:
|
||||
|
||||
| 变量 | 说明 |
|
||||
| ---------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `LOG_LEVEL` | 日志级别,默认生产 `info`、开发 `debug` |
|
||||
| `LOG_APP_NAME` | Loki `app` 标签,默认 `kt-template-online-api` |
|
||||
| `LOG_PRETTY` | 开发环境是否使用 `pino-pretty`,默认 `true` |
|
||||
| `LOKI_URL` / `LOKI_HOST` | Loki HTTP 地址,例如 `http://loki:3100`;为空时仅输出 stdout |
|
||||
| `LOKI_QUERY_HOST` | 查询专用 Loki 地址;为空时复用 `LOKI_HOST/LOKI_URL` |
|
||||
| `LOKI_ENV` | Loki `env` 标签,默认跟随 `NODE_ENV` |
|
||||
| `LOKI_TENANT_ID` | Loki 多租户 `X-Scope-OrgID`,没有多租户时留空 |
|
||||
| `LOKI_USERNAME` / `LOKI_PASSWORD` | Loki Basic Auth,仅放真实 env,不提交到仓库 |
|
||||
| `LOKI_PUSH_ENDPOINT` | Loki push 路径,默认 `/loki/api/v1/push` |
|
||||
| `LOKI_QUERY_ENDPOINT` | Loki query_range 路径,默认 `/loki/api/v1/query_range` |
|
||||
| `LOKI_QUERY_SELECTOR` | 查询默认 selector;为空时使用 `{app="<LOG_APP_NAME>",env="<LOKI_ENV>"}` |
|
||||
| `LOKI_BATCH_INTERVAL_SECONDS` | pino-loki 批量发送间隔,默认 `5` |
|
||||
| `LOKI_BATCH_MAX_BUFFER_SIZE` | Loki 不可用时最大内存缓冲条数,默认 `10000`,超过会丢弃旧日志避免 OOM |
|
||||
| `LOKI_QUERY_MAX_LIMIT` | Admin 单次查询最大拉取条数,默认 `1000` |
|
||||
|
||||
### 数据库字典翻译
|
||||
|
||||
组件数据维护在 `admin_component` 表中,字典数据维护在新的 `admin_dict` 表中。`Component.typeMsg`、`Component.componentTypeMsg` 会在 TypeORM `AfterLoad` 阶段根据字典缓存自动映射;旧 `/dict/*` 接口路径保持兼容,但仍需要登录态。
|
||||
|
||||
`admin_dict` 表核心字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ------------ | ------- | ------------------------------------------------------ |
|
||||
| id | string | 字典数字 ID |
|
||||
| dictCode | string | 字典分组,例如 `COMPONENT_TYPE`、`CHART`、`COMPONENT` |
|
||||
| label | string | 展示文本 |
|
||||
| value | string | 字典值 |
|
||||
| childrenCode | string | 子字典分组,例如 `COMPONENT_TYPE.value=1` 指向 `CHART` |
|
||||
| sort | number | 排序 |
|
||||
| status | number | 启停状态,`1` 启用 |
|
||||
| isDeleted | boolean | 逻辑删除标记 |
|
||||
|
||||
当前数据库示例关系:
|
||||
|
||||
| dictCode | value | label | childrenCode |
|
||||
| -------------- | ----- | ----- | ------------ |
|
||||
| COMPONENT_TYPE | 1 | 图表 | CHART |
|
||||
| COMPONENT_TYPE | 2 | 组件 | COMPONENT |
|
||||
|
||||
## 数据结构
|
||||
|
||||
### Component
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ---------------- | ------- | ---------------------------------- |
|
||||
| id | string | 组件数字 ID,新增时由后端生成 |
|
||||
| name | string | 组件名称 |
|
||||
| type | number | 一级类型,实际含义由 `dict` 表维护 |
|
||||
| componentType | number | 二级类型,实际含义由 `dict` 表维护 |
|
||||
| typeMsg | string | 一级类型文本,查询后自动映射 |
|
||||
| componentTypeMsg | string | 二级类型文本,查询后自动映射 |
|
||||
| image | string | 封面图或封面图地址 |
|
||||
| template | string | Playground 序列化模板内容 |
|
||||
| createTime | string | 创建时间 |
|
||||
| updateTime | string | 更新时间 |
|
||||
| is_deleted | boolean | 逻辑删除标记 |
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/menu/all` | 当前用户菜单 |
|
||||
| `GET` | `/system/menu/list` | 系统菜单树 |
|
||||
| `GET` | `/system/menu/name-exists` | 菜单 name 重名校验 |
|
||||
| `GET` | `/system/menu/path-exists` | 菜单 path 重名校验 |
|
||||
| `POST` | `/system/menu` | 新增菜单 |
|
||||
| `PUT` | `/system/menu/:id` | 更新菜单 |
|
||||
| `DELETE` | `/system/menu/:id` | 删除菜单及子菜单 |
|
||||
| `GET` | `/system/role/list` | 角色分页 |
|
||||
| `POST` | `/system/role` | 新增角色 |
|
||||
| `PUT` | `/system/role/:id` | 更新角色 |
|
||||
| `DELETE` | `/system/role/:id` | 删除角色 |
|
||||
| `GET` | `/system/dept/list` | 部门树 |
|
||||
| `POST` | `/system/dept` | 新增部门 |
|
||||
| `PUT` | `/system/dept/:id` | 更新部门 |
|
||||
| `DELETE` | `/system/dept/:id` | 删除部门 |
|
||||
| `GET` | `/system/user/list` | 用户分页 |
|
||||
| `POST` | `/system/user` | 新增用户 |
|
||||
| `PUT` | `/system/user/:id` | 更新用户 |
|
||||
| `DELETE` | `/system/user/:id` | 删除用户 |
|
||||
|
||||
### Dict
|
||||
|
||||
接口返回的字典项结构:
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/dict/list` | 字典项分页,支持 `dictCode`、`keyword`、`label`、`value`、`childrenCode`、`status` |
|
||||
| `GET` | `/dict/tree` | 兼容树形字典视图 |
|
||||
| `GET` | `/dict/groups` | 字典编码分组列表,适合左右表左侧分组 |
|
||||
| `GET` | `/dict/codes` | 字典编码选项 |
|
||||
| `GET` | `/dict/getDictByKey` | 按 `dictKey` 获取启用字典项 |
|
||||
| `GET` | `/dict/getComponentDictByType` | 按组件一级类型查二级类型 |
|
||||
| `POST` | `/dict/save` | 新增字典项 |
|
||||
| `POST` | `/dict/update` | 更新字典项 |
|
||||
| `DELETE` | `/dict/:id` | 物理删除字典项 |
|
||||
| `POST` | `/dict/toggle` | 启停字典项 |
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ----- | ------------- | -------- |
|
||||
| label | string | 展示文本 |
|
||||
| value | number/string | 字典值 |
|
||||
字典核心字段:
|
||||
|
||||
### MinIO
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| `dictCode` | 字典分组,例如 `COMPONENT_TYPE`、`BANGDREAM_SERVER_ALIAS` |
|
||||
| `label` | 展示文本 |
|
||||
| `value` | 字典值 |
|
||||
| `childrenCode` | 关联子分组编码 |
|
||||
| `sort` | 排序 |
|
||||
| `status` | `1` 启用 |
|
||||
|
||||
`bucketName` 未传时默认读取环境变量 `MINIO_BUCKET`,缺省值为 `kt-template-online`。
|
||||
### Component
|
||||
|
||||
## Root
|
||||
组件接口保持 `/component/*` 路径兼容,但数据表为 `admin_component`。
|
||||
|
||||
### GET `/`
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/component/allList` | 全量组件 |
|
||||
| `GET` | `/component/list` | 组件分页,支持 `pageNo`、`pageSize`、`name`、`type`、`componentType` |
|
||||
| `GET` | `/component/detail?id=` | 组件详情 |
|
||||
| `POST` | `/component/save` | 新增组件 |
|
||||
| `POST` | `/component/update` | 更新组件 |
|
||||
| `POST` | `/component/remove?id=` | 逻辑删除组件 |
|
||||
|
||||
重定向到 Swagger 文档页 `/api#/`,HTTP 状态码为 `301`。
|
||||
### Timezone / Upload / Demo
|
||||
|
||||
## Component 接口
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/timezone/getTimezoneOptions` | 时区选项 |
|
||||
| `GET` | `/timezone/getTimezone` | 当前用户时区 |
|
||||
| `POST` | `/timezone/setTimezone` | 设置当前用户时区 |
|
||||
| `POST` | `/upload` | Vben 上传适配,实际写入 MinIO |
|
||||
| `GET` | `/table/list` | Vben 示例表格 |
|
||||
| `GET` | `/status` | 状态码测试 |
|
||||
| `GET` | `/demo/bigint` | BigInt JSON 测试 |
|
||||
| `GET` | `/test` | GET 测试 |
|
||||
| `POST` | `/test` | POST 测试 |
|
||||
|
||||
组件接口仍保持 `/component/*` 路径兼容,但模块已迁入 Admin 目录并要求后台登录态。`kt-template-online-web` 和 `kt-template-online-playground` 收到 `401` 后会跳转到 `kt-template-admin` 登录页,登录完成再回到原页面。
|
||||
## 系统日志
|
||||
|
||||
### GET `/component/allList`
|
||||
后端通过 `nestjs-pino` 输出结构化日志。配置 Loki 后,Admin 日志页面通过后端代理查询,不直连 Loki。
|
||||
|
||||
获取全部组件。
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/system/logs` | 日志分页,支持 `level`、`keyword`、`context`、`path`、`requestId`、`startTime`、`endTime`、`rangeMinutes` |
|
||||
| `GET` | `/system/logs/summary` | 按级别统计 |
|
||||
| `GET` | `/system/logs/levels` | 日志级别选项 |
|
||||
| `GET` | `/system/logs/status` | Loki 查询配置状态 |
|
||||
|
||||
响应 `data`:`Component[]`。
|
||||
日志行包含 `timestamp`、`level`、`message`、`method`、`path`、`statusCode`、`durationMs`、`requestId`、`raw` 等字段。
|
||||
|
||||
示例:
|
||||
## Blog 本地内容
|
||||
|
||||
`/blog/*` 是本地博客内容能力,供 `Vue/kt-blog-web` 和 Admin 博客管理使用。
|
||||
|
||||
### Blog Article
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/blog/article/public/list` | 公开文章分页 |
|
||||
| `GET` | `/blog/article/public/detail` | 公开文章详情,支持 id/slug |
|
||||
| `GET` | `/blog/article/list` | 后台文章分页 |
|
||||
| `GET` | `/blog/article/detail` | 后台文章详情 |
|
||||
| `POST` | `/blog/article/save` | 新增文章 |
|
||||
| `POST` | `/blog/article/update` | 更新文章 |
|
||||
| `POST` | `/blog/article/remove` | 删除文章 |
|
||||
| `GET` | `/blog/article/category-options` | 文章分类选项 |
|
||||
| `GET` | `/blog/article/tag-options` | 文章标签选项 |
|
||||
| `POST` | `/blog/article/import-wordpress` | 从 WordPress 导入文章 |
|
||||
|
||||
文章 body 常用字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": [
|
||||
{
|
||||
"id": "2041739550026043392",
|
||||
"name": "基础折线图",
|
||||
"type": 1,
|
||||
"componentType": 1,
|
||||
"typeMsg": "图表",
|
||||
"componentTypeMsg": "折线图",
|
||||
"image": "",
|
||||
"template": "%7B%22version%22%3A%221.0%22%7D",
|
||||
"createTime": "2026-05-13T02:30:00.000Z",
|
||||
"updateTime": "2026-05-13T02:30:00.000Z",
|
||||
"is_deleted": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/component/list`
|
||||
|
||||
分页获取组件列表。列表默认过滤 `is_deleted=false`,并支持按名称模糊搜索。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ------------- | ------ | ---- | ---------------- |
|
||||
| pageNo | number | 是 | 页码 |
|
||||
| pageSize | number | 是 | 每页条数 |
|
||||
| name | string | 否 | 组件名称模糊搜索 |
|
||||
| type | number | 否 | 一级类型 |
|
||||
| componentType | number | 否 | 二级类型 |
|
||||
|
||||
响应 `data`:
|
||||
|
||||
```ts
|
||||
{
|
||||
list: Component[]
|
||||
total: number
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/component/detail`
|
||||
|
||||
获取组件详情。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------- |
|
||||
| id | string | 是 | 组件 ID |
|
||||
|
||||
响应 `data`:`Component`。
|
||||
|
||||
### POST `/component/save`
|
||||
|
||||
新增组件。全局 `SaveBodyInterceptor` 会删除 `body.id`,新增时不需要传 `id`。
|
||||
|
||||
Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "基础折线图",
|
||||
"type": 1,
|
||||
"componentType": 1,
|
||||
"image": "",
|
||||
"template": "%7B%22version%22%3A%221.0%22%7D"
|
||||
}
|
||||
```
|
||||
|
||||
响应 `data`:新增组件 ID。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": "2041739550026043392"
|
||||
}
|
||||
```
|
||||
|
||||
### POST `/component/update`
|
||||
|
||||
编辑组件。
|
||||
|
||||
Body:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "2041739550026043392",
|
||||
"name": "基础折线图",
|
||||
"type": 1,
|
||||
"componentType": 1,
|
||||
"image": "",
|
||||
"template": "%7B%22version%22%3A%221.0%22%7D"
|
||||
}
|
||||
```
|
||||
|
||||
响应 `data`:`true` 表示更新成功。
|
||||
|
||||
### POST `/component/remove`
|
||||
|
||||
逻辑删除组件。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | ------- |
|
||||
| id | string | 是 | 组件 ID |
|
||||
|
||||
响应 `data`:`true` 表示删除成功。
|
||||
|
||||
## Dict 接口
|
||||
|
||||
### GET `/dict/getDictByKey`
|
||||
|
||||
根据字典分组获取字典项。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ------- | ------ | ---- | ---------------------- |
|
||||
| dictKey | string | 是 | 字典分组,例如 `CHART` |
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": [
|
||||
{
|
||||
"label": "折线图",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET `/dict/getComponentDictByType`
|
||||
|
||||
根据组件一级类型获取对应的二级类型字典。
|
||||
|
||||
查询逻辑:先查 `dictCode=COMPONENT_TYPE` 且 `value=type` 的字典项,再使用该项的 `childrenCode` 查询子字典。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---- | ------ | ---- | -------- |
|
||||
| type | number | 是 | 一级类型 |
|
||||
|
||||
响应 `data`:`Array<{ label: string; value: number | string }>`。
|
||||
|
||||
## Vben Admin 真实接口
|
||||
|
||||
这些接口用于 `Vue/kt-template-admin`,响应格式与项目统一响应结构对齐:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
核心接口:
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ------ | ------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| POST | `/auth/login` | 登录,返回 `accessToken` 与 `wordpressAuth`,并写入 access token、刷新 token 和 WordPress 授权 cookie |
|
||||
| POST | `/auth/refresh` | 通过刷新 token cookie 刷新 accessToken,并更新 token cookie |
|
||||
| POST | `/auth/logout` | 退出登录并清理 access token、刷新 token 与 WordPress 授权 cookie |
|
||||
| GET | `/auth/codes` | 获取当前用户权限码 |
|
||||
| GET | `/user/info` | 获取当前用户信息 |
|
||||
| GET | `/menu/all` | 获取当前用户可访问菜单 |
|
||||
| GET | `/system/menu/list` | 获取系统菜单树 |
|
||||
| GET | `/system/menu/name-exists` | 校验菜单 name 是否重复 |
|
||||
| GET | `/system/menu/path-exists` | 校验菜单 path 是否重复 |
|
||||
| POST | `/system/menu` | 新增菜单 |
|
||||
| PUT | `/system/menu/:id` | 更新菜单 |
|
||||
| DELETE | `/system/menu/:id` | 删除菜单及子菜单 |
|
||||
| GET | `/system/role/list` | 分页查询角色 |
|
||||
| POST | `/system/role` | 新增角色 |
|
||||
| PUT | `/system/role/:id` | 更新角色 |
|
||||
| DELETE | `/system/role/:id` | 删除角色 |
|
||||
| GET | `/system/dept/list` | 获取部门树 |
|
||||
| POST | `/system/dept` | 新增部门 |
|
||||
| PUT | `/system/dept/:id` | 更新部门 |
|
||||
| DELETE | `/system/dept/:id` | 删除部门 |
|
||||
| GET | `/system/logs` | 查询 Loki 系统日志 |
|
||||
| GET | `/system/logs/summary` | 查询日志级别统计 |
|
||||
| GET | `/system/logs/levels` | 查询日志级别选项 |
|
||||
| GET | `/system/logs/status` | 查询 Loki 配置状态 |
|
||||
| GET | `/timezone/getTimezoneOptions` | 获取时区选项 |
|
||||
| GET | `/timezone/getTimezone` | 获取当前用户时区 |
|
||||
| POST | `/timezone/setTimezone` | 设置当前用户时区 |
|
||||
| POST | `/upload` | Vben Upload 适配接口,真实上传到 MinIO 并返回 `{ url }` |
|
||||
| GET | `/table/list` | Vben 示例远程表格数据 |
|
||||
| GET | `/status` | Vben 状态码测试接口 |
|
||||
| GET | `/demo/bigint` | Vben BigInt JSON 测试接口 |
|
||||
|
||||
初始化 SQL:
|
||||
|
||||
- `sql/vben-admin-init.sql`:创建 `admin_*` 表并导入基础用户、角色、菜单、部门、字典数据,同时创建空的 `admin_component` 表。
|
||||
- `sql/system-log-menu.sql`:为已有库增量补齐系统日志菜单及管理员角色授权。
|
||||
- `sql/migrate-dict-to-admin-dict.sql`:将旧 `dict` 表数据迁移到 `admin_dict`。
|
||||
- `sql/migrate-component-to-admin-component.sql`:将旧 `component` 表数据迁移到 `admin_component`,并把旧表改名为备份表。
|
||||
- `sql/fix-admin-menu-meta.sql`:修复基础后台菜单 `meta` 被旧数据或错误保存覆盖为空的问题。
|
||||
|
||||
## WordPress 接口
|
||||
|
||||
所有 `/wordpress/*` 管理接口都需要本系统后台登录态和 WordPress 客户端登录态。Admin 前端只调用现有 `/auth/login`,后端在该登录接口内部自动建立 WordPress 授权态;后端只把 WordPress cookie 保存到本系统 httpOnly cookie,不把 cookie 明文放入前端持久化。
|
||||
|
||||
### POST `/wordpress/auth/login`
|
||||
|
||||
使用 env 中的 `WORDPRESS_ADMIN_USERNAME` 和 `WORDPRESS_ADMIN_PASSWORD` 登录 WordPress,写入 `kt_wordpress_auth` httpOnly cookie,并返回前端需要持久化的 REST nonce 和 WordPress 当前用户信息。该接口主要保留为后端内部能力和调试口子,正常 Admin 登录链路不由前端主动调用它,而是通过 `/auth/login` 自动触发。
|
||||
|
||||
响应 `data`:
|
||||
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"nonce": "wordpress-rest-nonce",
|
||||
"type": "cookie"
|
||||
},
|
||||
"user": {
|
||||
"id": 1,
|
||||
"name": "admin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST `/wordpress/auth/logout`
|
||||
|
||||
清理本系统保存的 WordPress 授权 cookie。该接口用于 Admin 退出登录时同步清理 WordPress 授权态。
|
||||
|
||||
### GET `/wordpress/auth/check`
|
||||
|
||||
调用 WordPress `/wp-json/wp/v2/users/me?context=edit` 校验当前客户端 WordPress 登录态。
|
||||
|
||||
响应 `data`:WordPress 当前用户信息。
|
||||
|
||||
### WordPress Article
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ---- | ------------------------------------------- | ---------------- |
|
||||
| GET | `/wordpress/article/list` | 获取文章分页列表 |
|
||||
| GET | `/wordpress/article/detail?id=1` | 获取文章详情 |
|
||||
| POST | `/wordpress/article/save` | 新增文章 |
|
||||
| POST | `/wordpress/article/update` | 编辑文章 |
|
||||
| POST | `/wordpress/article/remove?id=1&force=true` | 删除文章 |
|
||||
|
||||
列表 Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ----------------------- |
|
||||
| pageNo | number | 否 | 页码,默认 `1` |
|
||||
| pageSize | number | 否 | 每页条数,默认 `10` |
|
||||
| search | string | 否 | 关键词搜索 |
|
||||
| status | string | 否 | 文章状态,默认 `any` |
|
||||
| categories | string | 否 | 分类 ID,多个用逗号分隔,也兼容重复传参 |
|
||||
| tags | string | 否 | 标签 ID,多个用逗号分隔,也兼容重复传参 |
|
||||
|
||||
新增/编辑 Body 常用字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"title": "文章标题",
|
||||
"content": "文章内容",
|
||||
"excerpt": "文章摘要",
|
||||
"status": "draft",
|
||||
"slug": "post-slug",
|
||||
"categories": [1],
|
||||
"tags": [2],
|
||||
"featured_media": 10,
|
||||
"sticky": false
|
||||
"status": "publish",
|
||||
"content": "Markdown 或 HTML",
|
||||
"contentFormat": "markdown",
|
||||
"cover": "",
|
||||
"categories": ["tech"],
|
||||
"tags": ["kt"]
|
||||
}
|
||||
```
|
||||
|
||||
`categories` 与 `tags` 直接对应 WordPress 文章 REST 字段,传入 ID 数组即可把文章绑定到对应分类和标签;传空数组表示清空当前文章的对应绑定。
|
||||
### Blog Category / Tag / Theme
|
||||
|
||||
### WordPress Tag
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/blog/category/list` | 本地分类分页 |
|
||||
| `GET` | `/blog/category/detail` | 本地分类详情 |
|
||||
| `POST` | `/blog/category/save` | 新增分类 |
|
||||
| `POST` | `/blog/category/update` | 更新分类 |
|
||||
| `POST` | `/blog/category/remove` | 删除分类 |
|
||||
| `GET` | `/blog/tag/list` | 本地标签分页 |
|
||||
| `GET` | `/blog/tag/detail` | 本地标签详情 |
|
||||
| `POST` | `/blog/tag/save` | 新增标签 |
|
||||
| `POST` | `/blog/tag/update` | 更新标签 |
|
||||
| `POST` | `/blog/tag/remove` | 删除标签 |
|
||||
| `GET` | `/blog/term/options` | 分类/标签选项 |
|
||||
| `GET` | `/blog/theme/config` | 获取 Argon 主题配置 |
|
||||
| `POST` | `/blog/theme/save` | 保存本地主题配置 |
|
||||
| `POST` | `/blog/theme/import-wordpress` | 从 WordPress 导入主题配置 |
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ---- | --------------------------------------- | ---------------- |
|
||||
| GET | `/wordpress/tag/list` | 获取标签分页列表 |
|
||||
| GET | `/wordpress/tag/detail?id=1` | 获取标签详情 |
|
||||
| POST | `/wordpress/tag/save` | 新增标签 |
|
||||
| POST | `/wordpress/tag/update` | 编辑标签 |
|
||||
| POST | `/wordpress/tag/remove?id=1&force=true` | 删除标签 |
|
||||
## WordPress 代理
|
||||
|
||||
WordPress 标签 term 不支持回收站,删除时必须使用 `force=true`。
|
||||
`/wordpress/*` 需要 Admin 登录态和 WordPress 登录态。后端优先使用 `kt_wordpress_auth` httpOnly cookie,也支持显式透传 WordPress 认证 header。
|
||||
|
||||
新增/编辑 Body:
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/wordpress/auth/login` | 使用 env 管理员账号登录 WordPress 并写入 cookie |
|
||||
| `POST` | `/wordpress/auth/logout` | 清理 WordPress cookie |
|
||||
| `GET` | `/wordpress/auth/check` | 校验 WordPress 登录态 |
|
||||
| `GET` | `/wordpress/theme/config` | 读取 WordPress Argon 主题配置 |
|
||||
|
||||
### WordPress Article / Tag / Category
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/wordpress/article/public/list` | 公开文章列表代理 |
|
||||
| `GET` | `/wordpress/article/public/detail` | 公开文章详情代理 |
|
||||
| `GET` | `/wordpress/article/list` | WordPress 文章分页 |
|
||||
| `GET` | `/wordpress/article/detail` | WordPress 文章详情 |
|
||||
| `POST` | `/wordpress/article/save` | 新增 WordPress 文章 |
|
||||
| `POST` | `/wordpress/article/update` | 更新 WordPress 文章 |
|
||||
| `POST` | `/wordpress/article/remove` | 删除 WordPress 文章 |
|
||||
| `GET` | `/wordpress/tag/list` | 标签分页 |
|
||||
| `GET` | `/wordpress/tag/detail` | 标签详情 |
|
||||
| `POST` | `/wordpress/tag/save` | 新增标签 |
|
||||
| `POST` | `/wordpress/tag/update` | 更新标签 |
|
||||
| `POST` | `/wordpress/tag/remove` | 删除标签 |
|
||||
| `GET` | `/wordpress/category/list` | 分类分页 |
|
||||
| `GET` | `/wordpress/category/detail` | 分类详情 |
|
||||
| `POST` | `/wordpress/category/save` | 新增分类 |
|
||||
| `POST` | `/wordpress/category/update` | 更新分类 |
|
||||
| `POST` | `/wordpress/category/remove` | 删除分类 |
|
||||
|
||||
WordPress rewrite 未开启导致 `/wp-json/*` 返回 404 时,后端会回退到 `?rest_route=/...`。
|
||||
|
||||
## MinIO
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/minio/check` | 检查连接和 bucket |
|
||||
| `POST` | `/minio/bucket` | 创建 bucket |
|
||||
| `POST` | `/minio/upload` | 上传文件,`multipart/form-data` |
|
||||
| `GET` | `/minio/list` | 文件列表 |
|
||||
| `GET` | `/minio/url` | 临时访问 URL |
|
||||
| `GET` | `/minio/resource-proxy` | 代理读取资源 |
|
||||
| `GET` | `/minio/download` | 下载文件流 |
|
||||
| `DELETE` | `/minio/remove` | 删除文件 |
|
||||
|
||||
`bucketName` 不传时使用 `MINIO_BUCKET`。
|
||||
|
||||
## QQBot 管理
|
||||
|
||||
QQBot 运行态包括 NapCat 容器登录、OneBot v11 反向 WebSocket、MQTT 事件总线、账号能力绑定、在线命令、自动回复规则、权限名单、发送/接收日志和插件生态。
|
||||
|
||||
### Account / Scan Login
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/qqbot/account/list` | QQBot 账号分页 |
|
||||
| `GET` | `/qqbot/account/enabled` | 启用账号列表 |
|
||||
| `POST` | `/qqbot/account/save` | 手动新增账号 |
|
||||
| `POST` | `/qqbot/account/update` | 更新账号 |
|
||||
| `POST` | `/qqbot/account/scan/create` | 扫码新增账号,创建登录会话 |
|
||||
| `POST` | `/qqbot/account/scan/refresh?id=` | 对已有账号刷新登录态 |
|
||||
| `GET` | `/qqbot/account/scan/status?sessionId=` | 查询扫码会话状态 |
|
||||
| `GET` | `/qqbot/account/scan/events?sessionId=` | SSE 订阅扫码进度 |
|
||||
| `POST` | `/qqbot/account/scan/qrcode/refresh?sessionId=` | 刷新当前会话二维码 |
|
||||
| `POST` | `/qqbot/account/scan/cancel?sessionId=` | 取消扫码会话 |
|
||||
| `POST` | `/qqbot/account/delete?id=` | 删除账号并断开 WS |
|
||||
| `POST` | `/qqbot/account/kick?selfId=` | 断开反向 WS 会话 |
|
||||
| `POST` | `/qqbot/account/bind/command` | 绑定账号和在线命令 |
|
||||
| `POST` | `/qqbot/account/unbind/command` | 解绑账号和在线命令 |
|
||||
| `POST` | `/qqbot/account/bind/rule` | 绑定账号和自动回复规则 |
|
||||
| `POST` | `/qqbot/account/unbind/rule` | 解绑账号和自动回复规则 |
|
||||
|
||||
扫码链路返回 `sessionId`,前端应使用 SSE 查看步骤进度,而不是等待长 HTTP 请求完成。
|
||||
|
||||
### Command / Rule / Permission
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/qqbot/command/list` | 在线命令分页,支持 `pluginKey`、`operationKey`、`selfId`、`enabled` |
|
||||
| `POST` | `/qqbot/command/save` | 新增在线命令 |
|
||||
| `POST` | `/qqbot/command/update` | 更新在线命令 |
|
||||
| `POST` | `/qqbot/command/delete?id=` | 删除在线命令 |
|
||||
| `POST` | `/qqbot/command/toggle?id=&enabled=` | 启停在线命令 |
|
||||
| `POST` | `/qqbot/command/test` | 预览测试在线命令 |
|
||||
| `GET` | `/qqbot/rule/list` | 自动回复规则分页 |
|
||||
| `POST` | `/qqbot/rule/save` | 新增自动回复规则 |
|
||||
| `POST` | `/qqbot/rule/update` | 更新自动回复规则 |
|
||||
| `POST` | `/qqbot/rule/delete?id=` | 删除自动回复规则 |
|
||||
| `POST` | `/qqbot/rule/toggle?id=&enabled=` | 启停自动回复规则 |
|
||||
| `GET` | `/qqbot/permission/config` | 权限名单配置 |
|
||||
| `POST` | `/qqbot/permission/config` | 保存权限名单配置 |
|
||||
| `GET` | `/qqbot/permission/allowlist` | 白名单分页 |
|
||||
| `POST` | `/qqbot/permission/allowlist/save` | 新增白名单 |
|
||||
| `POST` | `/qqbot/permission/allowlist/update` | 更新白名单 |
|
||||
| `POST` | `/qqbot/permission/allowlist/delete?id=` | 删除白名单 |
|
||||
| `GET` | `/qqbot/permission/blocklist` | 黑名单分页 |
|
||||
| `POST` | `/qqbot/permission/blocklist/save` | 新增黑名单 |
|
||||
| `POST` | `/qqbot/permission/blocklist/update` | 更新黑名单 |
|
||||
| `POST` | `/qqbot/permission/blocklist/delete?id=` | 删除黑名单 |
|
||||
|
||||
`/qqbot/command/test` 示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "标签名称",
|
||||
"slug": "tag-slug",
|
||||
"description": "标签描述"
|
||||
"commandId": "2041700000000000001",
|
||||
"text": "/查曲 夏祭り",
|
||||
"selfId": "10000",
|
||||
"targetType": "group",
|
||||
"targetId": "123456",
|
||||
"userId": "2354598417"
|
||||
}
|
||||
```
|
||||
|
||||
### WordPress Category
|
||||
线上 smoke 必须按 `operationKey` 查询启用命令 ID 后传入 `commandId`,避免默认 `preview` selfId 误报未匹配命令。
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
| ---- | -------------------------------------------- | ---------------- |
|
||||
| GET | `/wordpress/category/list` | 获取分类分页列表 |
|
||||
| GET | `/wordpress/category/detail?id=1` | 获取分类详情 |
|
||||
| POST | `/wordpress/category/save` | 新增分类 |
|
||||
| POST | `/wordpress/category/update` | 编辑分类 |
|
||||
| POST | `/wordpress/category/remove?id=1&force=true` | 删除分类 |
|
||||
### Plugin / Dashboard / Send / Message
|
||||
|
||||
WordPress 分类 term 不支持回收站,删除时必须使用 `force=true`;删除分类不会删除文章,文章会按 WordPress 自身规则解除或迁移分类关系。
|
||||
| 方法 | 路径 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `GET` | `/qqbot/plugin/list` | 插件列表,支持 `triggerMode=command/event` |
|
||||
| `GET` | `/qqbot/plugin/operation/list` | 插件能力列表 |
|
||||
| `GET` | `/qqbot/plugin/health` | 插件健康检查 |
|
||||
| `GET` | `/qqbot/plugin/event/list` | 事件触发插件绑定状态 |
|
||||
| `POST` | `/qqbot/plugin/event/bind` | 绑定事件触发插件 |
|
||||
| `POST` | `/qqbot/plugin/event/unbind` | 解绑事件触发插件 |
|
||||
| `GET` | `/qqbot/dashboard/summary` | QQBot 工作台汇总 |
|
||||
| `GET` | `/qqbot/send/log/list` | 发送日志分页 |
|
||||
| `POST` | `/qqbot/send/private` | 发送私聊消息 |
|
||||
| `POST` | `/qqbot/send/group` | 发送群聊消息 |
|
||||
| `GET` | `/qqbot/conversation/list` | 会话列表 |
|
||||
| `GET` | `/qqbot/message/list` | 消息列表 |
|
||||
|
||||
新增/编辑 Body:
|
||||
### OneBot Reverse WebSocket
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"name": "分类名称",
|
||||
"slug": "category-slug",
|
||||
"description": "分类描述",
|
||||
"parent": 0
|
||||
}
|
||||
`QQBOT_REVERSE_WS_PATH` 默认是 `/qqbot/onebot/reverse`。NapCat 通过反向 WS 连接 API,token 使用 `QQBOT_REVERSE_WS_TOKEN`。
|
||||
|
||||
## QQBot 插件能力
|
||||
|
||||
### BangDream
|
||||
|
||||
插件 key:`bangDream`。当前源码根目录为 `src/qqbot/plugins/bangDream`,不再使用旧 `tsugu` 子目录。
|
||||
|
||||
| operation key | 命令 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `bangdream.song.search` | `/查曲` | 查歌曲信息图片 |
|
||||
| `bangdream.song.chart` | `/查谱面` | 查谱面图片 |
|
||||
| `bangdream.song.random` | `/随机曲` | 随机歌曲 |
|
||||
| `bangdream.song.meta` | `/查询分数表` | 查歌曲分数榜 |
|
||||
| `bangdream.card.search` | `/查卡` | 查卡牌信息图片 |
|
||||
| `bangdream.card.illustration` | `/查卡面` | 查卡面插画 |
|
||||
| `bangdream.character.search` | `/查角色` | 查角色信息 |
|
||||
| `bangdream.event.search` | `/查活动` | 查活动信息 |
|
||||
| `bangdream.event.stage` | `/查试炼` | 查活动试炼,保持拆图输出 |
|
||||
| `bangdream.player.search` | `/查玩家` | 查玩家信息 |
|
||||
| `bangdream.gacha.search` | `/查卡池` | 查卡池 |
|
||||
| `bangdream.gacha.simulate` | `/抽卡模拟` | 模拟抽卡 |
|
||||
| `bangdream.cutoff.detail` | `/ycx` | 单档位预测线 |
|
||||
| `bangdream.cutoff.all` | `/ycxall` | 全档位预测线 |
|
||||
| `bangdream.cutoff.recent` | `/lsycx` | 历史/近期档线 |
|
||||
|
||||
`registry/operation-registry.ts` 是 BangDream operation、handlerName、别名、冷却和说明的单一来源。新增或调整命令必须同步在线命令 SQL,并跑 registry/command-SQL 测试。
|
||||
|
||||
### FF14 Market
|
||||
|
||||
插件 key:`ff14Market`。
|
||||
|
||||
| operation key | 说明 |
|
||||
| --- | --- |
|
||||
| `ff14.item.resolve` | 按物品名称或 ID 解析 XIVAPI 物品 |
|
||||
| `ff14.market.price` | 查询指定服务器/大区的 Universalis 市场价格 |
|
||||
|
||||
市场查价支持 `item`、`itemId`、`world`、`dataCenter`、`region`、`hq`、`language`。
|
||||
|
||||
### FFLogs
|
||||
|
||||
插件 key:`fflogs`。
|
||||
|
||||
| operation key | 说明 |
|
||||
| --- | --- |
|
||||
| `fflogs.character.summary` | 查询 FFLogs 角色公开排名;传 `encounter` 时查询指定高难最近记录 |
|
||||
|
||||
常用输入:`characterName`、`serverSlug`、`serverRegion`、`encounter`、`limit`、`metric`、`timeframe`、`zoneId`。
|
||||
|
||||
## 初始化 SQL
|
||||
|
||||
| 文件 | 用途 |
|
||||
| --- | --- |
|
||||
| `sql/vben-admin-init.sql` | 创建 Admin 基础表、用户、角色、菜单、部门、字典和空组件表 |
|
||||
| `sql/blog-init.sql` | 初始化本地 Blog 表 |
|
||||
| `sql/blog-menu.sql` | 初始化 Blog 管理菜单 |
|
||||
| `sql/qqbot-init.sql` | 初始化 QQBot 表、插件命令和字典 |
|
||||
| `sql/system-log-menu.sql` | 初始化系统日志菜单和权限 |
|
||||
| `sql/migrate-dict-to-admin-dict.sql` | 旧 `dict` 迁移到 `admin_dict` |
|
||||
| `sql/migrate-component-to-admin-component.sql` | 旧 `component` 迁移到 `admin_component` |
|
||||
| `sql/fix-admin-menu-meta.sql` | 修复菜单 meta 被覆盖为空 |
|
||||
| `sql/fix-admin-user-zero-id.sql` | 修复旧版本 `admin_user.id=0` 脏数据 |
|
||||
|
||||
## 验证入口
|
||||
|
||||
常规文档/配置检查:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## MinIO 接口
|
||||
后端代码检查:
|
||||
|
||||
### GET `/minio/check`
|
||||
|
||||
检查 MinIO 连接和 bucket 状态。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ----------- |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
|
||||
响应 `data`:`{ bucketName: string; exists: boolean }`。
|
||||
|
||||
### POST `/minio/bucket`
|
||||
|
||||
创建 bucket,已存在时跳过。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ----------- |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
|
||||
响应 `data`:bucket 名称。
|
||||
|
||||
### POST `/minio/upload`
|
||||
|
||||
上传文件,请求类型为 `multipart/form-data`。
|
||||
|
||||
Body:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ---------------------- |
|
||||
| file | File | 是 | 文件 |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
| objectName | string | 否 | 对象名,不传时自动生成 |
|
||||
|
||||
响应示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"bucketName": "kt-template-online",
|
||||
"objectName": "uploads/1715580000000-a1b2c3-demo.png",
|
||||
"etag": "9b2cf535f27731c974343645a3985328",
|
||||
"size": 2048,
|
||||
"mimeType": "image/png",
|
||||
"url": "http://127.0.0.1:9000/kt-template-online/uploads/demo.png"
|
||||
}
|
||||
}
|
||||
```bash
|
||||
pnpm run typecheck
|
||||
pnpm run lint
|
||||
pnpm test
|
||||
```
|
||||
|
||||
### GET `/minio/list`
|
||||
BangDream 图片 smoke:
|
||||
|
||||
获取文件列表。
|
||||
```powershell
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.song.search -Text "夏祭り" -OutFile ".kt-workspace/bangdream-smoke/song.jpg"
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text "310" -OutFile ".kt-workspace/bangdream-smoke/stage.jpg"
|
||||
```
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ----------------------------- |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
| prefix | string | 否 | 对象名前缀 |
|
||||
| recursive | string | 否 | 是否递归,传 `false` 时不递归 |
|
||||
|
||||
响应 `data`:MinIO 对象数组,常见字段为 `name`、`size`、`etag`、`lastModified`。
|
||||
|
||||
### GET `/minio/url`
|
||||
|
||||
获取文件临时访问地址。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | --------------------------- |
|
||||
| objectName | string | 是 | 对象名 |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
| expiry | string | 否 | 有效期秒数,默认 `86400` 秒 |
|
||||
|
||||
响应 `data`:临时访问 URL。
|
||||
|
||||
### GET `/minio/download`
|
||||
|
||||
下载文件,直接返回文件流。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ----------- |
|
||||
| objectName | string | 是 | 对象名 |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
|
||||
### DELETE `/minio/remove`
|
||||
|
||||
删除文件。
|
||||
|
||||
Query:
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
| ---------- | ------ | ---- | ----------- |
|
||||
| objectName | string | 是 | 对象名 |
|
||||
| bucketName | string | 否 | bucket 名称 |
|
||||
|
||||
响应 `data`:`true` 表示删除成功。
|
||||
Jenkins/K8s 发布后还需要观察 rollout、新 Pod 日志,并跑真实运行态 smoke;推送成功不等于发布完成。
|
||||
|
||||
184
README.md
184
README.md
@ -1,73 +1,71 @@
|
||||
# KT Template Online API
|
||||
|
||||
`kt-template-online-api` 是 KT Template Online 的后端服务,负责组件模板、数据库字典、MinIO 文件和 WordPress 内容管理能力。前台列表和 Playground 保存都通过本服务完成数据读写。
|
||||
`kt-template-online-api` 是 KT 工作区的 NestJS 后端服务,承接 Admin 后台、博客内容、组件模板、MinIO 文件、系统日志、QQBot/NapCat 和游戏查询插件能力。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Node.js + TypeScript
|
||||
- NestJS 9
|
||||
- TypeORM + MySQL
|
||||
- MinIO
|
||||
- Node.js 22 / TypeScript 5.9
|
||||
- NestJS 11 / Express 5
|
||||
- TypeORM 0.3 / MySQL
|
||||
- Swagger / Knife4j
|
||||
- pnpm
|
||||
- nestjs-pino / pino-loki / Loki
|
||||
- MinIO
|
||||
- MQTT / OneBot v11 reverse WebSocket / NapCat
|
||||
- skia-canvas / Chart.js
|
||||
- pnpm 9
|
||||
|
||||
## 功能模块
|
||||
|
||||
| 模块 | 说明 |
|
||||
| ----------- | ----------------------------------------------------------------------------------------- |
|
||||
| `component` | Admin 下受保护的组件/图表模板列表、详情、新增、编辑、逻辑删除,数据表为 `admin_component` |
|
||||
| `dict` | 基于新 `admin_dict` 表的字典查询,维护组件一级类型和二级类型关系 |
|
||||
| `admin` | Vben Admin 真实接口,包含登录、用户、菜单、角色、部门、时区、上传和示例表格 |
|
||||
| `minio` | Bucket 检查/创建、文件上传、列表、临时访问地址、下载和删除 |
|
||||
| `wordpress` | WordPress 文章、标签、分类管理接口,复用客户端 WordPress 登录态访问 REST API |
|
||||
| `common` | 响应注解、字典翻译、`POST */save` 请求体规范化等通用能力 |
|
||||
| 模块 | 说明 |
|
||||
| --- | --- |
|
||||
| `admin` | Vben Admin 认证、用户、菜单、角色、部门、时区、字典、组件模板、系统日志 |
|
||||
| `blog` | 本地博客文章、分类、标签、Argon 主题配置和 WordPress 导入 |
|
||||
| `wordpress` | WordPress REST 代理、登录态透传、文章/分类/标签/主题配置 |
|
||||
| `qqbot` | QQBot 账号、NapCat 扫码登录、OneBot 反向 WS、在线命令、规则、权限、发送/接收日志 |
|
||||
| `qqbot/plugins/bangDream` | BanG Dream 查曲、查卡、查活动、试炼、玩家、卡池、抽卡模拟、档线、谱面出图 |
|
||||
| `qqbot/plugins/ff14Market` | XIVAPI + Universalis 物品解析和 FF14 市场查价 |
|
||||
| `qqbot/plugins/fflogs` | FFLogs v2 GraphQL 角色排名和指定高难最近记录查询 |
|
||||
| `minio` | Bucket 检查、上传、列表、临时 URL、代理下载、删除 |
|
||||
| `common` | 响应封装、异常过滤、请求日志、日期格式化、字典解码、Snowflake、工具服务 |
|
||||
|
||||
## 目录结构
|
||||
|
||||
```text
|
||||
src
|
||||
common/ # 通用装饰器、拦截器、服务、Swagger 封装
|
||||
admin/ # Vben Admin 后台认证、组件、字典、菜单、角色、部门等接口
|
||||
minio/ # MinIO 文件模块
|
||||
wordpress/ # WordPress REST API 文章、标签、分类代理模块
|
||||
types/ # 全局类型声明
|
||||
app.module.ts # 全局模块、数据库、MinIO、拦截器注册
|
||||
main.ts # Swagger、Knife4j、端口启动入口
|
||||
src/
|
||||
admin/ Admin 后台接口和实体
|
||||
blog/ 本地博客内容与主题配置
|
||||
common/ 全局装饰器、过滤器、拦截器、logger、工具和类型
|
||||
minio/ MinIO 文件服务
|
||||
qqbot/ QQBot 运行态、管理接口和插件生态
|
||||
wordpress/ WordPress REST 代理
|
||||
app.module.ts
|
||||
main.ts
|
||||
test/ Jest 单元测试,统一放在 test 下
|
||||
sql/ 初始化、菜单、迁移和修复 SQL
|
||||
scripts/ smoke、husky 快速检查等脚本
|
||||
k8s/ K8s 生产部署清单
|
||||
ci/ Jenkins Agent/Docker 辅助文件
|
||||
```
|
||||
|
||||
## 环境变量
|
||||
|
||||
项目按 `NODE_ENV` 读取 `.env.${NODE_ENV}`,未指定时默认读取 `.env.development`。仓库只提交 `.env.example`,真实 `.env.development` 和 `.env.production` 保留在本地。
|
||||
项目按 `NODE_ENV` 读取 `.env.${NODE_ENV}`,未指定时默认 `.env.development`。仓库只跟踪 `.env.example`;真实 `.env.development`、`.env.production`、数据库密码、Token、OAuth secret 和 SSH key 不提交。
|
||||
|
||||
```env
|
||||
DB_HOST=localhost
|
||||
DB_PORT=3306
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
DB_DATABASE=shy_template
|
||||
DB_SYNC=true
|
||||
主要配置分组:
|
||||
|
||||
MINIO_ENDPOINT=localhost
|
||||
MINIO_PORT=9000
|
||||
MINIO_ACCESS_KEY=minioadmin
|
||||
MINIO_SECRET_KEY=minioadmin
|
||||
MINIO_BUCKET=kt-template-online
|
||||
| 分组 | 变量 |
|
||||
| --- | --- |
|
||||
| MySQL | `DB_HOST`、`DB_PORT`、`DB_USERNAME`、`DB_PASSWORD`、`DB_DATABASE`、`DB_SYNC` |
|
||||
| MinIO | `MINIO_ENDPOINT`、`MINIO_PORT`、`MINIO_ACCESS_KEY`、`MINIO_SECRET_KEY`、`MINIO_BUCKET` |
|
||||
| Admin | `ADMIN_TOKEN_SECRET`、`ADMIN_COOKIE_SECURE`、`SNOWFLAKE_WORKER_ID`、`SNOWFLAKE_DATACENTER_ID` |
|
||||
| WordPress | `WORDPRESS_BASE_URL`、`WORDPRESS_HOST_HEADER`、`WORDPRESS_ADMIN_USERNAME`、`WORDPRESS_ADMIN_PASSWORD`、`WORDPRESS_*_TIMEOUT_MS` |
|
||||
| Logging/Loki | `LOG_LEVEL`、`LOG_APP_NAME`、`LOKI_URL`、`LOKI_QUERY_HOST`、`LOKI_*` |
|
||||
| QQBot/NapCat | `QQBOT_ENABLED`、`QQBOT_REVERSE_WS_*`、`NAPCAT_*`、`QQBOT_NAPCAT_*`、`MQTT_*` |
|
||||
| BangDream | `BANGDREAM_TSUGU_MAIN_SERVER`、`BANGDREAM_TSUGU_DISPLAYED_SERVERS`、`BANGDREAM_TSUGU_CACHE_ROOT` |
|
||||
| FF14 Market | `FF14_XIVAPI_BASE_URL`、`FF14_UNIVERSALIS_BASE_URL`、`FF14_MARKET_CACHE_TTL_MS` |
|
||||
| FFLogs | `FFLOGS_BASE_URL`、`FFLOGS_GRAPHQL_URL`、`FFLOGS_TOKEN_URL`、`FFLOGS_CLIENT_ID`、`FFLOGS_CLIENT_SECRET` |
|
||||
|
||||
WORDPRESS_BASE_URL=http://localhost
|
||||
WORDPRESS_ADMIN_USERNAME=admin
|
||||
WORDPRESS_ADMIN_PASSWORD=
|
||||
WORDPRESS_TIMEOUT_MS=15000
|
||||
WORDPRESS_LOGIN_TIMEOUT_MS=3000
|
||||
WORDPRESS_AVAILABILITY_TTL_MS=60000
|
||||
|
||||
ADMIN_TOKEN_SECRET=change-me
|
||||
ADMIN_COOKIE_SECURE=false
|
||||
SNOWFLAKE_WORKER_ID=1
|
||||
SNOWFLAKE_DATACENTER_ID=1
|
||||
```
|
||||
|
||||
`DB_SYNC=true` 会让 TypeORM 根据实体同步表结构。生产环境建议关闭同步,改用迁移脚本维护表结构。
|
||||
内网 HTTP 访问时保持 `ADMIN_COOKIE_SECURE=false`;如果未来切到 HTTPS 域名,再改为 `true`。
|
||||
`DB_SYNC=true` 只适合本地开发或明确允许自动同步表结构的环境;生产应关闭并使用 SQL/迁移脚本。
|
||||
|
||||
## 启动
|
||||
|
||||
@ -76,27 +74,34 @@ pnpm install
|
||||
pnpm start:dev
|
||||
```
|
||||
|
||||
服务默认监听 `48085`。
|
||||
服务固定监听 `48085`。
|
||||
|
||||
常用命令:
|
||||
|
||||
```bash
|
||||
pnpm start # 普通启动
|
||||
pnpm start:prod # 按 production 环境运行已构建的 dist/main
|
||||
pnpm run build # Nest 构建
|
||||
pnpm run lint # ESLint 检查
|
||||
pnpm test # 单元测试
|
||||
pnpm test:e2e # e2e 测试
|
||||
pnpm start
|
||||
pnpm start:prod
|
||||
pnpm run typecheck
|
||||
pnpm run lint
|
||||
pnpm test
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
Jest 只扫描 `test/**/*.spec.ts`。如果在 Windows 下指定测试文件,使用:
|
||||
|
||||
```bash
|
||||
pnpm exec jest --runInBand --runTestsByPath test/path/to/file.spec.ts
|
||||
```
|
||||
|
||||
## 接口文档
|
||||
|
||||
- Swagger UI:`http://localhost:48085/api`
|
||||
- Swagger 全量:`http://localhost:48085/api`
|
||||
- OpenAPI JSON:`http://localhost:48085/api-json`
|
||||
- 根路径 `/` 会重定向到 Swagger 文档
|
||||
- 接口细节见 [API.md](./API.md)
|
||||
- 分组文档:`/api/admin`、`/api/qqbot`、`/api/wordpress`、`/api/basic`
|
||||
- Knife4j:服务启动后同样使用上述 OpenAPI 服务列表
|
||||
- 手工接口索引:[API.md](./API.md)
|
||||
|
||||
除文件下载接口外,业务接口统一返回:
|
||||
业务接口统一返回 Vben 结构,文件下载/流式接口除外:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -106,7 +111,7 @@ pnpm test:e2e # e2e 测试
|
||||
}
|
||||
```
|
||||
|
||||
失败时统一返回 `err` 字段,成功响应不包含 `err`:
|
||||
错误响应里的 `err` 必须是字符串,避免前端解析 JSON 对象时报错:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -118,41 +123,42 @@ pnpm test:e2e # e2e 测试
|
||||
|
||||
## 核心规则
|
||||
|
||||
- `admin_component` 表保存组件/图表模板,`admin_dict` 表是统一字典翻译数据源,`Component.typeMsg` 和 `Component.componentTypeMsg` 查询后自动映射;旧 `/dict/*` 接口路径保持兼容。
|
||||
- 业务主键统一由 Snowflake 生成数字 ID,数据库使用 `BIGINT`,接口按字符串返回以避免前端长整型精度问题。
|
||||
- 如果基础后台菜单的 `meta` 被旧数据覆盖为空,执行 `sql/fix-admin-menu-meta.sql` 可以恢复初始化菜单的 `title/icon/order` 等元数据。
|
||||
- 旧 `component` 表迁移到 `admin_component` 时,执行 `sql/migrate-component-to-admin-component.sql`,脚本会把旧表重命名为备份表。
|
||||
- 如果旧版本曾写入 `admin_user.id=0`,先执行 `sql/fix-admin-user-zero-id.sql` 修复脏数据,再重启服务。
|
||||
- Admin、Component、Dict 与 MinIO 业务接口统一走 `JwtAuthGuard`;登录、刷新 token、退出登录和部分示例状态测试接口通过 `@Public()` 放行。
|
||||
- WordPress 管理接口同样先走本系统 `JwtAuthGuard`,再透传客户端 WordPress 登录态访问 WordPress REST API;当前 WordPress 只有单管理员账号且不开放注册,账号配置放在 env 中,但不作为 BasicAuth 发送。
|
||||
- Admin 前端只调用现有 `/auth/login`;后端会在登录流程里自动尝试登录 WordPress,把 WordPress cookie 写入本系统 httpOnly cookie,前端只持久化 REST nonce 和用户信息。WordPress 远程不可用时不会阻塞 Admin 主登录,后端会返回 `wordpressAuth=null` 并在菜单和按钮权限接口中过滤博客管理相关入口。
|
||||
- WordPress 文章的 `categories` 和 `tags` 按原生 REST API 语义透传 ID 数组;分类和标签 term 支持新增、编辑、强制删除,删除 term 不会删除文章。
|
||||
- WordPress 客户端登录态优先通过 `X-WordPress-Authorization` 透传,也支持 `X-WP-Nonce` 加 WordPress 登录 cookie 的 REST cookie 认证。
|
||||
- 如果 WordPress 服务器未开启 rewrite 导致 `/wp-json/*` 返回 404,后端会自动回退到 `?rest_route=/...` 形式继续访问 REST API。
|
||||
- `kt-template-admin` 登录会写入 access token 与刷新 token cookie,`kt-template-online-web` 和 `kt-template-online-playground` 可在回跳后通过刷新 token 重新持久化登录态。
|
||||
- `kt-template-admin` 开发环境通过 `/api` 代理到本服务 `48085`,已关闭 Vben Nitro Mock。
|
||||
- `POST /component/save` 新增组件,`POST /component/update` 编辑组件。
|
||||
- 全局 `SaveBodyInterceptor` 会删除 `POST */save` 请求体里的 `id`,避免新增接口误用前端主键。
|
||||
- 如个别 `save` 接口必须保留 `id`,在 Controller 方法上使用 `@SkipSaveBodyNormalize()`。
|
||||
- MinIO 上传接口返回的 `url` 会被 Playground 写入组件 `image` 字段。
|
||||
|
||||
## 联调关系
|
||||
|
||||
- `kt-template-online-web` 读取 `/component/list`、`/component/detail`、`/dict/*` 展示组件列表,并生成 Playground 跳转链接;业务接口返回 `401` 时跳转到 `kt-template-admin` 登录。
|
||||
- `kt-template-online-playground` 读取 `/dict/*` 初始化分类,保存时上传截图到 `/minio/upload`,再调用 `/component/save` 或 `/component/update`;业务接口返回 `401` 时跳转到 `kt-template-admin` 登录并在回跳后刷新 token。
|
||||
- 前端项目通过 Vite 代理把 `/api` 转发到 `http://localhost:48085/`。
|
||||
- 后台主键使用 Snowflake 数字 ID,数据库字段为 `BIGINT`,接口按字符串返回。
|
||||
- 后端响应时间统一用 `YYYY-MM-DD HH:mm:ss`,需要格式化的 DTO/Entity 字段使用 `@FormatDateTime()`。
|
||||
- 字典维护在 `admin_dict`,Admin 字典管理按 `dictCode` 分组展示;可运营映射优先走字典或静态配置,不硬编码到业务函数。
|
||||
- 全局 `SaveBodyInterceptor` 会删除 `POST */save` 请求体里的 `id`;需要保留时使用 `@SkipSaveBodyNormalize()`。
|
||||
- Admin、Component、Dict、MinIO、Blog 管理、WordPress 管理和 QQBot 管理接口默认走 `JwtAuthGuard`;公开接口用 `@Public()`。
|
||||
- WordPress 自动登录失败不会阻断 Admin 主登录,会通过菜单和权限码过滤不可用的 Blog 管理入口。
|
||||
- 系统日志由 pino 输出,Loki 查询统一通过后端 `/system/logs/*` 代理,前端不直连 Loki。
|
||||
- QQBot 扫码登录通过 SSE `/qqbot/account/scan/events` 暴露进度,耗时链路不应阻塞普通 HTTP 响应。
|
||||
- BangDream 当前源码根目录是 `src/qqbot/plugins/bangDream`;不要恢复旧 `tsugu` 层级或旧大桶目录。
|
||||
- BangDream 在线命令以 `registry/operation-registry.ts` 为单一来源,新增命令必须同步 SQL/在线命令表并跑 registry/command-SQL 测试。
|
||||
- BangDream event stage 大图必须保持分页拆图行为,线上 smoke 关注 `imageCount=5`,避免大 canvas OOM 回归。
|
||||
|
||||
## 轻量验证
|
||||
|
||||
文档或小范围后端改动优先跑轻量命令:
|
||||
文档、小范围配置或低风险改动:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
```
|
||||
|
||||
后端代码改动:
|
||||
|
||||
```bash
|
||||
pnpm run typecheck
|
||||
pnpm run lint
|
||||
pnpm test
|
||||
```
|
||||
|
||||
完整构建只在发布前或改动影响构建链路时执行:
|
||||
BangDream 图片能力改动:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
```powershell
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.song.search -Text "夏祭り" -OutFile ".kt-workspace/bangdream-smoke/song.jpg"
|
||||
```
|
||||
|
||||
接口改动必须启动或复用本地服务,并真实调用一次对应接口。
|
||||
|
||||
## 发布
|
||||
|
||||
主线发布由 Jenkins 构建镜像、推送 NAS 本地 Registry,并滚动更新 K8s `kt-prod/kt-template-online-api`。推送后不能只看 Git push 成功,需要继续观察 Jenkins、K8s rollout、新 Pod 状态和至少一条真实运行态 smoke。
|
||||
|
||||
@ -1,447 +0,0 @@
|
||||
# QQBot BangDream 模块化文件结构重构方案
|
||||
|
||||
生成日期:2026-06-07
|
||||
|
||||
## 目标
|
||||
|
||||
基于 `docs/qqbot-bangdream-tsugu-reference.md` 的重构后现状,继续推进文件结构治理。本方案已落地为源码、测试、脚本和文档迁移记录。
|
||||
|
||||
目标是:
|
||||
|
||||
- 去掉 `src/qqbot/plugins/bangDream/tsugu` 这一层目录,BangDream 内嵌能力直接归入 `src/qqbot/plugins/bangDream`。
|
||||
- 不再按 `models`、`render-blocks`、`command-renderers`、`canvas` 这类技术能力横向堆文件。
|
||||
- 改成按 BangDream 业务模块聚合文件,例如 `song`、`card`、`event`、`gacha`、`player`、`cutoff`。
|
||||
- 对真正跨模块的大能力单独抽出,例如 `hook`、`provider`、`policy`、`registry`、`theme`。
|
||||
- 控制单个文件夹文件数,避免再次出现 `render-blocks=66`、`models=46` 的大桶目录。
|
||||
- 保持现有 15 个在线命令兼容,迁移期间不改变用户命令文本、返回图片数量和线上 smoke 方式。
|
||||
|
||||
## 迁移前问题
|
||||
|
||||
迁移前 `tsugu` 源码 168 个 TS 文件分布如下:
|
||||
|
||||
| 目录 | 文件数 | 问题 |
|
||||
| --- | ---: | --- |
|
||||
| `render-blocks` | 66 | 渲染规格、列表块、详情块、图表和资源 repository 混在一起,目录过大 |
|
||||
| `models` | 46 | 领域模型、resource repository、policy、protocol、主数据 store 混在一起 |
|
||||
| `command-renderers` | 19 | 以命令输出能力聚合,但和业务模块、模型、渲染块分离太远 |
|
||||
| `canvas` | 11 | 底层画布能力独立存在合理,但和 theme/asset manifest 分散 |
|
||||
| `data-clients` | 10 | provider 能力已经清晰,可以作为横切大能力保留 |
|
||||
| `runtime` | 9 | registry、hook、dictionary、config、asset manifest 混在同一目录 |
|
||||
| `search` | 6 | 跨模块搜索能力清晰,可以作为横切能力保留 |
|
||||
| `calculations` | 1 | 只有档线预测,适合归入 `cutoff` 或 `calculation` |
|
||||
|
||||
迁移前最大的问题不是层级太深,而是“按技术能力横切后文件越来越多”。例如歌曲相关文件散落在 `models/song.ts`、`song-resource-repository.ts`、`command-renderers/song-*`、`render-blocks/list-song-*`、`render-blocks/song-chart-preview-*`。后续维护查歌或谱面时需要跨多个目录来回跳。
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. `bangDream` 插件目录即模块根目录,不再保留 `tsugu` 子目录。
|
||||
2. 业务域模块按用户和上游数据实体命名,不按技术能力命名。
|
||||
3. 横切能力只有在多个业务模块共享时才单独成目录。
|
||||
4. 每个业务模块内文件直接平铺,默认不再建 `model/render/repository` 子目录。
|
||||
5. 单个目录建议不超过 20 个 TS 文件,超过时优先拆业务子模块,不回退到 `models` 或 `render-blocks` 大桶。
|
||||
6. 文件名用职责后缀表达角色,例如 `song.model.ts`、`song.repository.ts`、`song-search.renderer.ts`、`song-chart.layout.ts`。
|
||||
7. Jest 测试仍放在 `test/qqbot/plugins/bangDream`,按源码目标模块同步改路径,不放回源码目录。
|
||||
8. 静态资源和静态配置保留 `assets`、`static-config`,不跟 TS 源码迁移节奏绑定。
|
||||
|
||||
## 实际落地结构
|
||||
|
||||
完整文件职责以 `docs/qqbot-bangdream-tsugu-reference.md` 为准,当前源码已经去掉 `tsugu` 子目录,实际顶层结构如下:
|
||||
|
||||
```text
|
||||
src/qqbot/plugins/bangDream/
|
||||
application/ Nest 客户端、应用服务、渲染 facade、operation pipeline
|
||||
registry/ operation key、handlerName、在线命令别名、冷却和说明
|
||||
hook/ 生命周期 hook 和命令执行日志 hook
|
||||
provider/ Bestdori、HHWX、静态修正、缓存、重试和 URL 解析
|
||||
policy/ 服务器、国服活动时间、档线、抽卡等跨模块规则
|
||||
theme/ Canvas 基础能力、布局 token、资源 manifest 和视觉主题
|
||||
config/ 运行时配置、环境变量 key、服务器默认值和档线 tier
|
||||
dictionary/ 默认字典和 API 字典加载
|
||||
search/ 模糊搜索、关系表达式、搜索字典和实体列表匹配
|
||||
song/ 查曲、谱面、随机曲、分数表、歌曲资源 repository
|
||||
card/ 查卡、卡面、卡牌图标、稀有度、技能、综合力渲染
|
||||
character/ 查角色、角色详情、角色列表和角色资源 repository
|
||||
event/ 查活动、活动详情、试炼、活动时间和活动数据 repository
|
||||
gacha/ 查卡池、抽卡模拟、卡池概率、Pick Up 和卡池资源 repository
|
||||
player/ 查玩家、卡组、乐队等级、角色等级、难度完成和排名
|
||||
cutoff/ 档线、全档线、近期档线、图表和预测算法
|
||||
catalog/ 服务器、乐队、属性、区域道具、服装、称号、道具、技能、颜色
|
||||
shared/ 协议常量、主数据、通用详情块、数据块、列表框架
|
||||
commands/ QQBot 在线命令定义桥接
|
||||
assets/ 本地图片、字体和静态视觉资源
|
||||
static-config/ 搜索配置、昵称表、CN 修正表和玩家编号表
|
||||
```
|
||||
|
||||
测试目录按源码模块同步拆分:
|
||||
|
||||
```text
|
||||
test/qqbot/plugins/bangDream/
|
||||
application/ card/ catalog/ character/ cutoff/ dictionary/ event/
|
||||
gacha/ hook/ player/ policy/ provider/ registry/ search/ shared/ song/ theme/
|
||||
```
|
||||
|
||||
资源和静态配置同步从旧层级上移,Nest 构建复制路径已改为 `qqbot/plugins/bangDream/assets/**/*` 和 `qqbot/plugins/bangDream/static-config/**/*`。
|
||||
|
||||
## 横切能力说明
|
||||
|
||||
### `application`
|
||||
|
||||
承接 Nest 边界和插件内部 facade:
|
||||
|
||||
- `bangdream-client.service.ts`:替代当前根部 `qqbot-bangdream-client.service.ts`,只暴露 `execute`、`checkHealth` 和少量明确 API。
|
||||
- `bangdream-application.service.ts`:替代 `renderer/tsugu-application.service.ts`,唯一应用入口。
|
||||
- `bangdream-renderer.facade.ts`:替代 `renderer/qqbot-bangdream-renderer.service.ts`,只做 handler 分发、输入归一化、字典解析和 CQ 输出。
|
||||
|
||||
### `registry`
|
||||
|
||||
只放注册表和注册表类型:
|
||||
|
||||
- operation key
|
||||
- handlerName
|
||||
- 在线命令别名
|
||||
- 冷却
|
||||
- 命令说明
|
||||
|
||||
不放字典、hook、provider 或 renderer。
|
||||
|
||||
### `hook`
|
||||
|
||||
只放生命周期 hook:
|
||||
|
||||
- hook context
|
||||
- hook registry
|
||||
- log hook
|
||||
- 后续 metrics/output summary hook
|
||||
|
||||
业务决策不放 hook。会改变业务结果的逻辑放 `policy` 或模块 renderer。
|
||||
|
||||
### `provider`
|
||||
|
||||
只放外部数据源和缓存下载能力:
|
||||
|
||||
- Bestdori provider
|
||||
- HHWX tracker provider
|
||||
- static patch provider
|
||||
- cache path/policy/client
|
||||
- retry/cache/timing decorator
|
||||
|
||||
任何模块需要外部 JSON、asset 或 tracker 数据,都通过 provider 或本模块 repository 调用,不直接拼完整 URL。
|
||||
|
||||
### `policy`
|
||||
|
||||
只放跨模块业务规则:
|
||||
|
||||
- 服务器时区和优先级
|
||||
- 国服活动时间预估
|
||||
- 档线预测窗口和档位规则
|
||||
- 抽卡概率和卡池过滤
|
||||
|
||||
单模块私有规则可以先留在模块文件,只有跨模块复用或会独立测试时才移动到 `policy`。
|
||||
|
||||
### `theme`
|
||||
|
||||
承接视觉基础能力:
|
||||
|
||||
- 渲染主题 token
|
||||
- 本地 asset manifest
|
||||
- 通用 layout token
|
||||
- Canvas 基础工具
|
||||
|
||||
不再使用 `canvas` 和 `render-blocks` 作为顶层大桶。各业务模块自己的布局文件放回模块内部,例如 `song/song-chart-preview.layout.ts`、`card/card-stat.layout.ts`。
|
||||
|
||||
## 业务模块说明
|
||||
|
||||
### `song`
|
||||
|
||||
聚合查曲、歌曲详情、随机曲、分数表和谱面预览。现有散落来源:
|
||||
|
||||
- `models/song.ts`
|
||||
- `models/song-repository.ts`
|
||||
- `models/song-resource-repository.ts`
|
||||
- `command-renderers/song-*`
|
||||
- `render-blocks/list-song*`
|
||||
- `render-blocks/list-difficulty*`
|
||||
- `render-blocks/song-chart-preview*`
|
||||
|
||||
### `card`
|
||||
|
||||
聚合查卡、卡牌详情、卡面、卡牌图标、插画、稀有度、综合力、技能文字和 SD 缩略图。现有散落来源:
|
||||
|
||||
- `models/card.ts`
|
||||
- `models/card-repository.ts`
|
||||
- `models/card-resource-repository.ts`
|
||||
- `command-renderers/card-*`
|
||||
- `render-blocks/card-*`
|
||||
- `render-blocks/list-card-*`
|
||||
- `render-blocks/list-rarity*`
|
||||
- `render-blocks/list-stat*`
|
||||
- `render-blocks/skill-text*`
|
||||
|
||||
### `character`
|
||||
|
||||
聚合角色查询、角色详情、角色列表和玩家详情里的角色等级列表。现有散落来源:
|
||||
|
||||
- `models/character.ts`
|
||||
- `models/character-resource-repository.ts`
|
||||
- `command-renderers/character-*`
|
||||
- `render-blocks/list-character*`
|
||||
|
||||
### `event`
|
||||
|
||||
聚合查活动、活动详情、活动列表、试炼和活动时间展示。现有散落来源:
|
||||
|
||||
- `models/event.ts`
|
||||
- `models/event-repository.ts`
|
||||
- `models/event-data-repository.ts`
|
||||
- `models/event-stage.ts`
|
||||
- `models/event-stage-data-repository.ts`
|
||||
- `command-renderers/event-*`
|
||||
- `render-blocks/event-stage*`
|
||||
- `render-blocks/list-event-stage.ts`
|
||||
- `render-blocks/list-time*`
|
||||
|
||||
### `gacha`
|
||||
|
||||
聚合查卡池、抽卡模拟、卡池列表、概率和 pick up 展示。抽卡规则本身放 `policy/gacha.policy.ts`,模块只消费 policy。
|
||||
|
||||
### `player`
|
||||
|
||||
聚合玩家详情、玩家数据 repository、玩家卡组、乐队等级、角色等级、难度完成情况和排名展示。
|
||||
|
||||
### `cutoff`
|
||||
|
||||
聚合档线模型、前十榜、单档线、全档线、历史档线、档线图表、时间线图表和预测算法。档线规则本身放 `policy/cutoff.policy.ts`,模块只消费 policy。
|
||||
|
||||
### `catalog`
|
||||
|
||||
聚合不直接对应一个 QQBot 命令、但多个模块共享的静态目录实体:
|
||||
|
||||
- 服务器
|
||||
- 乐队
|
||||
- 属性
|
||||
- 区域道具
|
||||
- 服装
|
||||
- 称号
|
||||
- 道具
|
||||
- 技能
|
||||
- 颜色
|
||||
|
||||
这些不再放进 `models` 大桶。
|
||||
|
||||
### `shared`
|
||||
|
||||
只放真正跨多个模块的轻量工具、协议和通用渲染块:
|
||||
|
||||
- Bestdori 协议枚举和兼容常量
|
||||
- 主数据 store/repository
|
||||
- 模型工具函数
|
||||
- 通用详情块、数据块、列表框架
|
||||
- 图片栈工具
|
||||
|
||||
`shared` 不能成为新的大桶。超过 20 个 TS 文件时必须继续拆模块。
|
||||
|
||||
## 当前到目标迁移映射
|
||||
|
||||
| 当前路径 | 目标路径 |
|
||||
| --- | --- |
|
||||
| `tsugu/runtime/operation-registry.ts` | `registry/operation-registry.ts` |
|
||||
| `tsugu/runtime/hook-registry.ts` | `hook/hook-registry.ts`、`hook/log-hook.ts` |
|
||||
| `tsugu/runtime/config.ts` | `config/runtime-config.ts` |
|
||||
| `tsugu/runtime/runtime-options.ts` | `config/runtime-options.ts` |
|
||||
| `tsugu/runtime/default-dictionary.ts` | `dictionary/default-dictionary.ts` |
|
||||
| `tsugu/runtime/dictionary-loader.ts` | `dictionary/dictionary-loader.ts` |
|
||||
| `tsugu/runtime/asset-manifest.ts` | `theme/asset-manifest.ts` |
|
||||
| `tsugu/data-clients/*` | `provider/*` |
|
||||
| `tsugu/models/*-policy.ts` | `policy/*.policy.ts` |
|
||||
| `tsugu/search/*` | `search/*` |
|
||||
| `tsugu/canvas/*` | `theme/canvas-*.ts` |
|
||||
| `tsugu/models/song*`、`command-renderers/song-*`、`render-blocks/*song*`、`render-blocks/*difficulty*` | `song/*` |
|
||||
| `tsugu/models/card*`、`command-renderers/card-*`、`render-blocks/card-*`、`render-blocks/list-card-*`、`render-blocks/list-rarity*`、`render-blocks/list-stat*`、`render-blocks/skill-text*` | `card/*` |
|
||||
| `tsugu/models/character*`、`command-renderers/character-*`、`render-blocks/list-character*` | `character/*` |
|
||||
| `tsugu/models/event*`、`command-renderers/event-*`、`render-blocks/event-stage*`、`render-blocks/list-event-stage.ts`、`render-blocks/list-time*` | `event/*` |
|
||||
| `tsugu/models/gacha*`、`command-renderers/gacha-*`、`render-blocks/gacha-*`、`render-blocks/list-gacha-*` | `gacha/*` |
|
||||
| `tsugu/models/player*`、`command-renderers/player-detail.ts`、`render-blocks/list-player-*`、`render-blocks/deck-rank-*` | `player/*` |
|
||||
| `tsugu/models/cutoff*`、`command-renderers/cutoff-*`、`render-blocks/*cutoff*`、`render-blocks/timeline-chart*`、`calculations/cutoff-predictor.ts` | `cutoff/*` |
|
||||
| `tsugu/models/attribute*`、`band*`、`area-item*`、`costume*`、`degree*`、`item*`、`server*`、`skill*`、`color.ts` | `catalog/*` |
|
||||
| `tsugu/render-blocks/detail-block*`、`data-block*`、`list-frame*`、`list-entity*`、`image-stack.ts` | `shared/*` |
|
||||
|
||||
## 命名规则
|
||||
|
||||
| 后缀 | 含义 | 示例 |
|
||||
| --- | --- | --- |
|
||||
| `.model.ts` | 领域实体和值对象 | `song.model.ts` |
|
||||
| `.repository.ts` | 主数据、资源或外部数据读取 | `song-resource.repository.ts` |
|
||||
| `.renderer.ts` | 生成图片或组合图片区块 | `song-chart.renderer.ts` |
|
||||
| `.layout.ts` | 视觉布局规格、纯布局计算 | `song-chart-preview.layout.ts` |
|
||||
| `.policy.ts` | 跨模块业务规则 | `cutoff.policy.ts` |
|
||||
| `.provider.ts` | 外部数据源实现 | `bestdori.provider.ts` |
|
||||
| `.client.ts` | 缓存、下载或外部客户端 | `asset-cache.client.ts` |
|
||||
| `.registry.ts` | 注册表 | `operation-registry.ts` |
|
||||
| `.types.ts` | 类型集合 | `fuzzy-search.types.ts` |
|
||||
|
||||
不再新增:
|
||||
|
||||
- `*-spec.ts` 作为源码布局文件后缀。后续使用 `.layout.ts`,避免和 Jest `*.spec.ts` 语义混淆。
|
||||
- `models/`、`render-blocks/`、`command-renderers/`、`data-clients/`、`runtime/`、`canvas/` 顶层目录。
|
||||
- 巨型 `index.ts` barrel。只允许模块内少量显式 re-export,避免循环依赖。
|
||||
|
||||
## 迁移批次
|
||||
|
||||
### Phase 0:结构迁移准备
|
||||
|
||||
目标:只建立可验证的迁移边界,不移动文件。
|
||||
|
||||
任务:
|
||||
|
||||
- 用脚本生成当前文件清单和 import 图。
|
||||
- 冻结 15 个 operation 的 smoke 用例。
|
||||
- 新增结构守卫脚本:禁止新增 `tsugu/` 引用,统计目标目录文件数。
|
||||
- 在文档里确认目录预算和命名规则。
|
||||
|
||||
验证:
|
||||
|
||||
- 文件清单 168 个 TS 文件全部有目标位置。
|
||||
- operation 表 15/15 保持一致。
|
||||
- `git diff --check`、global-review。
|
||||
|
||||
### Phase 1:横切能力先迁移
|
||||
|
||||
目标:先迁移不会改变业务输出的共享能力。
|
||||
|
||||
迁移:
|
||||
|
||||
- `runtime/operation-registry.ts` -> `registry/operation-registry.ts`
|
||||
- `runtime/hook-registry.ts` -> `hook/*`
|
||||
- `runtime/config.ts`、`runtime/runtime-options.ts` -> `config/*`
|
||||
- `runtime/default-dictionary.ts`、`runtime/dictionary-loader.ts` -> `dictionary/*`
|
||||
- `runtime/asset-manifest.ts`、`canvas/*`、`render-blocks/theme.ts`、`render-blocks/layout-spec.ts` -> `theme/*`
|
||||
- `data-clients/*` -> `provider/*`
|
||||
- `models/*-policy.ts` -> `policy/*`
|
||||
- `search/*` -> `search/*`
|
||||
|
||||
验证:
|
||||
|
||||
- `pnpm run typecheck`
|
||||
- registry、hook、dictionary、provider、policy、search 相关 Jest
|
||||
- 本地 `/查谱面 136 expert` 和 `/ycx 100 50 cn` smoke
|
||||
|
||||
### Phase 2:低耦合业务模块迁移
|
||||
|
||||
目标:迁移文件数量可控、依赖较清晰的模块。
|
||||
|
||||
建议顺序:
|
||||
|
||||
1. `song`
|
||||
2. `character`
|
||||
3. `gacha`
|
||||
4. `cutoff`
|
||||
|
||||
验证:
|
||||
|
||||
- 每迁移一个模块跑对应 Jest 和一条图片 smoke。
|
||||
- 不推远程,等本批模块全部完成后一次性提交、构建、线上验证。
|
||||
|
||||
### Phase 3:高耦合业务模块迁移
|
||||
|
||||
目标:迁移涉及共享块较多的模块。
|
||||
|
||||
建议顺序:
|
||||
|
||||
1. `card`
|
||||
2. `event`
|
||||
3. `player`
|
||||
4. `catalog`
|
||||
5. `shared`
|
||||
|
||||
验证:
|
||||
|
||||
- `card`:`/查卡 472`
|
||||
- `event`:`/查活动 50`、`/查试炼 310`
|
||||
- `player`:`/查玩家 26591455 jp`
|
||||
- `catalog/shared`:跑上面三类 smoke 复验
|
||||
|
||||
### Phase 4:删除旧层级
|
||||
|
||||
目标:彻底去掉 `tsugu` 目录和旧大桶目录。
|
||||
|
||||
任务:
|
||||
|
||||
- 删除空的 `tsugu` 目录。
|
||||
- 全局替换 import。
|
||||
- 更新 `scripts/bangdream-render-smoke.ps1`。
|
||||
- 更新测试路径。
|
||||
- 更新 reference 文档。
|
||||
- 更新本方案迁移结果。
|
||||
|
||||
验收:
|
||||
|
||||
```powershell
|
||||
rg -n "plugins/bangDream/tsugu|\\.\\/tsugu|\\.\\.\\/tsugu" src test scripts docs
|
||||
```
|
||||
|
||||
源码、测试、脚本不能再依赖 `tsugu` 路径。历史文档允许在“旧路径说明”中出现,但必须明确是旧路径。
|
||||
|
||||
### Phase 5:完整发布验证
|
||||
|
||||
目标:只在批量迁移完成后做一次发布闭环。
|
||||
|
||||
必跑:
|
||||
|
||||
- `git diff --check`
|
||||
- `pnpm run typecheck`
|
||||
- BangDream scoped ESLint
|
||||
- BangDream 全量 Jest
|
||||
- `pnpm run build`
|
||||
- local smoke:查曲、查卡、查活动、查试炼、抽卡、档线、谱面
|
||||
- global-review
|
||||
- push 后 Jenkins/K8s rollout
|
||||
- 线上 `/qqbot/command/test` smoke 拉图、展示图片、查 operation 日志、清理远程临时目录
|
||||
|
||||
## 目录预算
|
||||
|
||||
| 目录 | 预算 | 超过后的处理 |
|
||||
| --- | ---: | --- |
|
||||
| `song` | 16 | 拆出 `song-chart` 作为业务子模块,不回退到 `render-blocks` |
|
||||
| `card` | 20 | 拆出 `card-art` 或 `card-detail` 作为业务子模块 |
|
||||
| `event` | 18 | 拆出 `event-stage` 作为业务子模块 |
|
||||
| `gacha` | 14 | 保持单模块 |
|
||||
| `player` | 16 | 拆出 `player-ranking` 作为业务子模块 |
|
||||
| `cutoff` | 16 | 保持单模块 |
|
||||
| `catalog` | 20 | 拆出 `catalog-degree` 或 `catalog-server` |
|
||||
| `shared` | 20 | 能归业务模块就归业务模块,不能扩成新大桶 |
|
||||
| `provider` | 14 | cache client 可拆 `provider-cache` |
|
||||
| `theme` | 18 | canvas 基础可拆 `theme-canvas` |
|
||||
|
||||
预算不是硬编译规则,但用于 review:如果一个目录继续增长,先问“这是业务模块太大,还是又按能力堆成大桶了”。
|
||||
|
||||
## 风险点
|
||||
|
||||
| 风险 | 影响 | 控制方式 |
|
||||
| --- | --- | --- |
|
||||
| import 路径大规模替换出错 | typecheck/Jest 失败 | 分批迁移,每批只移动一个边界或少数模块 |
|
||||
| 循环依赖被 barrel 放大 | 运行时 undefined | 不建巨型 `index.ts`,保留显式 import |
|
||||
| layout 文件改名导致测试误判 | Jest pattern 或 import 失败 | 源码布局后缀改 `.layout.ts`,测试仍 `.spec.ts` |
|
||||
| smoke 卡进程 | 验证耗时不可控 | 继续使用 bounded smoke 脚本 |
|
||||
| 线上命令误匹配 | preview selfId 未绑定命令 | 线上 smoke 必须按 operationKey 查 commandId |
|
||||
| 大图 OOM 回归 | `/查试炼 310` 风险最高 | 保留分页拆图测试和线上 imageCount=5 验证 |
|
||||
|
||||
## 不做的事
|
||||
|
||||
- 不拆独立 Tsugu 服务。
|
||||
- 不引入新仓库。
|
||||
- 不为了目录漂亮改业务逻辑。
|
||||
- 不把布局像素、资源文件名和协议字段放进字典表。
|
||||
- 不把 `shared`、`theme` 或 `catalog` 变成新的 `models/render-blocks` 大桶。
|
||||
- 不在结构迁移之外改 BangDream 命令文本、返回图片数量或业务结果。
|
||||
|
||||
## 完成标准
|
||||
|
||||
本次闭环完成时按以下标准验收:
|
||||
|
||||
- `src/qqbot/plugins/bangDream/tsugu` 不再存在。
|
||||
- `models`、`render-blocks`、`command-renderers`、`data-clients`、`runtime`、`canvas` 不再作为 BangDream 顶层源码目录存在。
|
||||
- 15 个 operation key、在线命令别名和 handler 绑定保持一致。
|
||||
- 每个业务模块能在一个目录内看到模型、repository、renderer 和 layout。
|
||||
- `hook`、`provider`、`policy`、`registry`、`theme` 是明确横切能力,不包含业务命令编排。
|
||||
- 没有目录明显超过预算且未解释。
|
||||
- 本地全量验证和线上 smoke 均通过。
|
||||
@ -1,512 +0,0 @@
|
||||
# QQBot BangDream Tsugu 全局重构方案
|
||||
|
||||
生成日期:2026-06-06
|
||||
|
||||
## 目标
|
||||
|
||||
基于 `docs/qqbot-bangdream-tsugu-reference.md` 的函数与变量清单,对 Tsugu 内嵌能力做一次全局重构设计。目标不是机械套用所有设计模式,而是把当前确实分散、硬编码、难验证的部分收口成稳定边界:
|
||||
|
||||
- 去掉非必要硬编码:用户可感知文案、别名、映射、阈值、数据源、渲染主题、布局规格和命令路由不能继续散落在业务函数里。
|
||||
- 保留必要硬编码:Bestdori 协议枚举、资源文件名约定、纯算法常量和极少量稳定模型字段可以保留在代码中,但必须有命名和归属。
|
||||
- 让能力可插拔:命令、数据源、搜索规则、渲染流程、输出后处理和日志观测都通过 registry/pipeline/hook 连接。
|
||||
- 让重构可验证:每个阶段保留现有命令行为,先补边界测试,再迁移实现。
|
||||
|
||||
## 当前事实
|
||||
|
||||
- Tsugu 源码目录:`src/qqbot/plugins/bangDream/tsugu`
|
||||
- TS 文件:初始基线 92;当前 `tsugu` 源码 135
|
||||
- 函数节点:481,其中稳定函数 410,匿名/内联回调 71
|
||||
- 源码 JSDoc:稳定函数 410/410 已覆盖
|
||||
- 变量声明:1896
|
||||
- class/interface/type 字段:716
|
||||
- 静态配置:`static-config` 下 9 个文件,包含昵称、CN 修正、模糊搜索、玩家编号等数据
|
||||
- 现有硬编码集中点:`models/bangdream-constants.ts` 已承接服务器、难度、活动、卡牌、卡池、档位、国服预测等一部分常量
|
||||
|
||||
## 硬编码候选扫描
|
||||
|
||||
这次只把扫描结果作为重构入口,不直接等价为“全部挪走”。布局坐标、协议路径、图片资源名和业务字典的处理方式不同。
|
||||
|
||||
| 文件 | 字符串 | 数字 | 模板字符串 | 主要问题 |
|
||||
| --- | ---: | ---: | ---: | --- |
|
||||
| `render-blocks/song-chart-preview.ts` | 112 | 158 | 7 | 谱面预览布局、颜色、轨道、音符规格混在主流程内 |
|
||||
| `models/bangdream-constants.ts` | 110 | 78 | 0 | 业务枚举已集中,但仍混合用户文案、协议值、配置阈值 |
|
||||
| `models/cutoff.ts` | 29 | 115 | 5 | 档线数据源、预测规则和请求策略耦合 |
|
||||
| `render-blocks/list-time.ts` | 29 | 108 | 0 | 时间格式、服务器时区、国服预估规则耦合 |
|
||||
| `render-blocks/card-art.ts` | 41 | 87 | 3 | 卡牌布局尺寸、素材路径、颜色散落 |
|
||||
| `render-blocks/detail-blocks.ts` | 13 | 91 | 10 | 通用详情块已收口,但尺寸/token 仍分散 |
|
||||
| `canvas/text.ts` | 32 | 67 | 2 | 字体、字号、换行、画布兜底尺寸需要统一 token |
|
||||
| `models/player.ts` | 37 | 56 | 2 | 玩家 API、模式参数、头像/称号资源规则耦合 |
|
||||
| `models/card.ts` | 53 | 32 | 5 | 卡面资源 URL、类型文案、发布逻辑耦合 |
|
||||
| `render-blocks/list-frame.ts` | 13 | 77 | 0 | 通用列表布局已有基础,但宽高、间距、颜色仍散落 |
|
||||
|
||||
## 硬编码分类
|
||||
|
||||
| 类型 | 示例 | 目标归属 | 处理原则 |
|
||||
| --- | --- | --- | --- |
|
||||
| 协议常量 | `Server.jp = 0`、Bestdori API 字段名 | enum/types | 保留代码内,但统一命名,不进入数据库 |
|
||||
| 可运营映射 | 服务器别名、难度别名、命令别名、用户可见文案 | 字典表或静态配置加载器 | 优先可维护,允许缓存,不在渲染函数里写死 |
|
||||
| 数据源地址 | `https://bestdori.com`、`https://hhwx.org` | runtime options + provider registry | 支持 env 覆盖和数据源策略 |
|
||||
| 资源路径 | `assets/Card/star.png`、谱面 note 图片名 | asset manifest | 保留文件约定,但用 manifest 管理,不散写路径 |
|
||||
| 视觉 token | 颜色、字体、间距、圆角、默认宽度 | render theme | 代码内 token 文件或主题 provider,不进字典表 |
|
||||
| 布局规格 | `800`、`30`、卡片宽高、谱面轨道尺寸 | layout spec | 从主流程抽出为命名规格,支持单元测试 |
|
||||
| 算法阈值 | 缓存过期、重试次数、数据源切换阈值 | runtime options | 默认值代码内,生产可 env 配置 |
|
||||
| 上游修正数据 | `cards-cn-fix.json`、`skills-cn-fix.json` | data patch repository | 保留静态文件或转迁移数据,统一读取入口 |
|
||||
|
||||
## 当前扁平结构
|
||||
|
||||
保持当前插件目录,不引入新顶层仓库或独立服务。Tsugu TS 源码统一压到 `tsugu/<明确分组>/<文件>.ts`,最大两级;资源目录 `assets`、`static-config` 不受 TS 源码层级限制。
|
||||
|
||||
```text
|
||||
src/qqbot/plugins/bangDream/
|
||||
commands/
|
||||
bangdream-command.definitions.ts
|
||||
renderer/
|
||||
qqbot-bangdream-renderer.service.ts
|
||||
tsugu/
|
||||
calculations/
|
||||
canvas/
|
||||
command-renderers/
|
||||
data-clients/
|
||||
models/
|
||||
render-blocks/
|
||||
runtime/
|
||||
search/
|
||||
```
|
||||
|
||||
后续新增 registry、hook、provider、policy、spec、theme 或 manifest,也只能落在这些二级目录下,例如 `runtime/operation-registry.ts`、`runtime/hook-registry.ts`、`data-clients/bestdori-provider.ts`、`models/server-policy.ts`、`render-blocks/theme.ts`。
|
||||
|
||||
## 设计模式和落点
|
||||
|
||||
| 模式/机制 | 当前问题 | 落地方式 | 首批目标 |
|
||||
| --- | --- | --- | --- |
|
||||
| Strategy | `execute` 的 switch 把 15 个 operation 堆在一个 service | 每个 operation 暴露 `execute(input, context)` | 歌曲、活动、档线、卡池分组拆出 |
|
||||
| Registry | 命令定义、operation key、在线命令 SQL 需要保持一致 | `BANGDREAM_OPERATION_REGISTRY` 单一数据源生成插件能力和 SQL 校验 | 替代手写 switch 与分散定义 |
|
||||
| Pipeline | 解析、查数、渲染、输出、错误处理流程重复 | `parse -> resolve -> render -> output` 标准步骤 | 查曲/查卡/查活动先接入 |
|
||||
| Hook | 日志、耗时、图片压缩、数据源 fallback 不应侵入业务函数 | lifecycle hook:`beforeParse`、`afterResolve`、`beforeRender`、`afterOutput`、`onError` | 日志、metrics、fallback、CQ 输出摘要 |
|
||||
| Adapter | Bestdori/HHWX/本地静态修正接口风格不同 | provider adapter 统一 `getJson`、`getAsset`、`getTracker` | 档线与素材下载 |
|
||||
| Repository | `mainAPI[...]` 到处直接取值,难 mock | `SongRepository`、`CardRepository`、`EventRepository` 包 mainAPI 和 patch | 搜索与详情渲染 |
|
||||
| Specification | 模糊搜索字段匹配和关系表达式容易继续膨胀 | 每个条件实现 `matches(target)` | `_number`、`_relationStr`、`_all`、动态字段 |
|
||||
| Factory | 输出图片和实体匹配器已有工厂雏形,仍不统一 | `createEntityMatcher`、`createImageOutput`、`createOperation` | 保留现有柯里化方向 |
|
||||
| Builder | Canvas detail block 由数组手工 push,顺序难读 | `DetailBlockBuilder` 组合标题、字段、图片区块 | 卡牌/活动详情 |
|
||||
| Template Method | 列表页、详情页、档线页流程相似 | 基类或高阶函数封装“准备数据 + 画区块 + 输出” | 列表类页面优先 |
|
||||
| Decorator | 缓存、重试、耗时统计混在下载函数 | `withCache`、`withRetry`、`withTiming` 包 provider | `downloadFile`、`getJsonAndSave` |
|
||||
| Facade | Nest service 不应知道 Tsugu 内部所有函数 | `TsuguApplicationService` 作为唯一入口 | `QqbotBangDreamRendererService` 变薄 |
|
||||
| Policy | 国服预估、服务器优先级、档位、时区是规则,不是工具函数 | `BangDreamServerPolicy`、`CutoffPolicy`、`CnEventEstimatePolicy` | `render-blocks/list-time.ts`、`models/cutoff.ts` |
|
||||
|
||||
## 场景到模式选择
|
||||
|
||||
后续实现时按场景选择封装,而不是为了使用模式而使用模式。每个改动点只选择一个主模式,确实有横切能力时再叠加 hook/decorator。
|
||||
|
||||
| 场景 | 判断条件 | 主模式 | 封装形态 | 不使用时机 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 增加或维护在线命令 | operation key、命令名、描述、SQL、执行函数必须一致 | Registry + Strategy | `TsuguOperationDefinition` 对象数组,handler 用函数或小对象 | 只有一个命令且不会扩展时,不建 registry |
|
||||
| 多个命令共用同一执行流程 | 都经历解析参数、解析实体、渲染图片、输出 CQ、错误归一 | Pipeline | `TsuguOperationPipeline.run(definition, input)` | 只有两处简单重复时,先抽公共函数 |
|
||||
| 同类命令只有中间渲染不同 | 查曲、查卡、查活动都是“搜索或 ID 详情” | Template Method | 高阶函数 `createSearchDetailOperation(options)` | 如果各命令分支差异大,不强行套模板 |
|
||||
| 外部数据源格式不同 | Bestdori、HHWX、本地 patch、缓存文件接口不一致 | Adapter | `BestdoriProvider`、`HhwxTrackerProvider`、`StaticPatchProvider` 实现统一接口 | 只是同一个 API 的不同 URL,不建 adapter |
|
||||
| 访问主数据或静态修正 | `mainAPI[...]`、`*-fix.json`、昵称表需要统一读取和 mock | Repository | `SongRepository`、`CardRepository`、`EventRepository` 封装数据入口 | 纯值对象内部字段读取不绕 repository |
|
||||
| 判断某个对象是否符合规则 | 搜索关系式、发布服务器、活动状态、卡池类型过滤 | Specification | `matches(target)` 的小规则对象或纯函数集合 | 只有一行判断且不会复用时保留内联 |
|
||||
| 业务策略会随服务器或环境变化 | 国服预估、服务器优先级、档位、时区、卡池选择 | Policy | `BangDreamServerPolicy`、`CutoffPolicy`、`GachaPolicy` | 纯格式化函数不放 policy |
|
||||
| 创建复杂图片区块 | 详情页反复 push 标题、字段、图片区、spacer | Builder | `DetailBlockBuilder.addTitle().addField().addImage()` | 简单两三个 canvas 拼接不建 builder |
|
||||
| 创建同类对象或输出收尾 | 实体 matcher、图片输出、operation 定义反复出现 | Factory | `createEntityMatcher`、`createImageOutput`、`createOperation` | 构造参数很少且只用一次时不用 factory |
|
||||
| 给数据请求加缓存、重试、耗时 | 下载函数和 provider 都需要同样外层能力 | Decorator | `withCache(provider)`、`withRetry(fn)`、`withTiming(fn)` | 业务分支逻辑不能塞进 decorator |
|
||||
| 记录日志、指标、fallback、输出摘要 | 横切多个 operation,且不改变主业务结果 | Hook | `beforeParse/afterResolve/beforeRender/afterOutput/onError` | 需要决定业务结果时不用 hook,交给 policy/strategy |
|
||||
| Nest 边界需要变薄 | controller/plugin service 不应 import 大量 Tsugu 内部函数 | Facade | `TsuguApplicationService.execute(operationKey, input)` | Tsugu 内部模块之间不互相套 facade |
|
||||
| 用户可维护的映射或文案 | 别名、标签、命令名、用户可见中文 | Dictionary Loader | 启动加载字典,内存缓存,默认值兜底 | 协议字段、布局像素、资源文件名不进字典 |
|
||||
| 颜色、尺寸、字体、资源路径 | 渲染 token 散落,改视觉需要扫很多文件 | Theme/Spec/Manifest | `BangDreamTheme`、`layoutSpec`、`assetManifest` | 单个局部坐标且无复用价值时保留命名常量 |
|
||||
| 纯计算和格式化 | 无外部依赖、无状态、无扩展点 | Pure Function | 具名函数 + 单测 | 不为了模式套 class |
|
||||
|
||||
## 选择顺序
|
||||
|
||||
1. 先判断它是不是用户可维护数据;是则走字典或 runtime options。
|
||||
2. 再判断它是不是外部系统边界;是则走 provider/adapter/repository。
|
||||
3. 再判断它是不是业务规则;是则走 policy/specification。
|
||||
4. 再判断它是不是重复流程;是则走 pipeline/template/factory。
|
||||
5. 再判断它是不是横切能力;是则走 hook/decorator。
|
||||
6. 如果只是一次性局部逻辑,保留具名纯函数,不新增模式封装。
|
||||
|
||||
封装粒度默认用函数和对象配置。只有需要 Nest 注入、运行态状态、缓存生命周期或多实现接口时,才引入 class。
|
||||
|
||||
不建议使用的模式:Singleton、Abstract Factory、Event Sourcing、复杂插件热加载。当前问题不需要它们,强行使用会增加维护成本。
|
||||
|
||||
## Hook 设计
|
||||
|
||||
Hook 只做横切能力,不承载业务决策。
|
||||
|
||||
```ts
|
||||
export interface TsuguHookContext {
|
||||
operationKey: QqbotBangDreamOperationKey;
|
||||
input: QqbotBangDreamCommandInput;
|
||||
options: TsuguRenderOptions;
|
||||
query?: string;
|
||||
entityIds?: number[];
|
||||
imageCount?: number;
|
||||
startedAt: number;
|
||||
}
|
||||
|
||||
export interface TsuguHook {
|
||||
name: string;
|
||||
order?: number;
|
||||
beforeParse?(context: TsuguHookContext): void | Promise<void>;
|
||||
afterResolve?(context: TsuguHookContext): void | Promise<void>;
|
||||
beforeRender?(context: TsuguHookContext): void | Promise<void>;
|
||||
afterOutput?(context: TsuguHookContext): void | Promise<void>;
|
||||
onError?(context: TsuguHookContext, error: unknown): void | Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
首批内置 hook:
|
||||
|
||||
- `TsuguLogHook`:记录 operation、query、耗时、图片数量、错误字符串。
|
||||
- `TsuguDataSourceHook`:记录数据源命中、fallback 次数和切换原因。
|
||||
- `TsuguOutputHook`:统一 CQ 图片输出摘要,避免超长消息污染发送日志。
|
||||
- `TsuguDictionaryHook`:启动时加载字典,运行时只读缓存,避免每次命令查 DB。
|
||||
|
||||
## 字典与配置收口
|
||||
|
||||
字典表适合承接“运营会改、用户会看到、别名会增长”的内容,不适合承接协议字段和布局像素。
|
||||
|
||||
建议字典编码:
|
||||
|
||||
| 字典编码 | 内容 | 初始来源 |
|
||||
| --- | --- | --- |
|
||||
| `BANGDREAM_SERVER_ALIAS` | 服务器别名:国服、日服、cn、jp 等 | `BANGDREAM_SERVER_ALIASES` |
|
||||
| `BANGDREAM_DIFFICULTY_ALIAS` | 难度别名:expert、ex、专家等 | `BANGDREAM_DIFFICULTY_ALIASES` |
|
||||
| `BANGDREAM_COMMAND_ALIAS` | `/bd`、`/邦邦` 下具体命令别名 | `BANGDREAM_OPERATION_REGISTRY` 和在线命令 |
|
||||
| `BANGDREAM_ENTITY_NICKNAME` | 歌曲、卡牌、活动昵称 | `nickname-*.xlsx`、`search/fuzzy-search-settings.json` |
|
||||
| `BANGDREAM_LABEL` | 活动类型、卡牌类型、卡池类型、歌曲标签中文名 | `bangdream.enum.ts` 中 label 映射 |
|
||||
|
||||
保留代码配置:
|
||||
|
||||
- `BangDreamServerCode`、`BangDreamServerId`、`BangDreamDifficultyId`
|
||||
- Bestdori API 返回字段名
|
||||
- `BANGDREAM_ITEM_TYPE_PREFIXES` 这类资源协议前缀,除非后续确实需要后台维护
|
||||
- `BANGDREAM_TIER_LIST_BY_SERVER` 可先保留 enum,确认运营维护需求后再迁字典
|
||||
|
||||
运行时 env/options:
|
||||
|
||||
- `BANGDREAM_TSUGU_CACHE_ROOT`
|
||||
- `BANGDREAM_TSUGU_COMPRESS`
|
||||
- `BANGDREAM_TSUGU_USE_EASY_BG`
|
||||
- `BANGDREAM_TSUGU_DISPLAYED_SERVERS`
|
||||
- `BANGDREAM_TSUGU_MAIN_SERVER`
|
||||
- `BANGDREAM_TSUGU_BESTDORI_BASE_URL`
|
||||
- `BANGDREAM_TSUGU_HHWX_BASE_URL`
|
||||
- `BANGDREAM_TSUGU_REQUEST_TIMEOUT_MS`
|
||||
- `BANGDREAM_TSUGU_RETRY_COUNT`
|
||||
|
||||
## 分阶段执行
|
||||
|
||||
### Phase 0:冻结行为和测试基线
|
||||
|
||||
目标:重构前先确认现有能力的稳定输出,不再靠线上报错倒逼。
|
||||
|
||||
任务:
|
||||
|
||||
- 为 15 个 operation 建立 registry 一致性测试。
|
||||
- 固定查曲、查活动、查试炼、档线、抽卡模拟的 smoke 用例。
|
||||
- 为 `fuzzySearch`、`createTsuguEntityMatcher`、服务器/难度解析补全边界测试。
|
||||
- 保存关键图片命令的“图片数量 + 非空 Buffer + 尺寸范围”断言,不做像素级强绑定。
|
||||
|
||||
验证:
|
||||
|
||||
- `pnpm exec jest --runInBand test/qqbot/plugins/bangDream/**/*.spec.ts`
|
||||
- `pnpm run typecheck`
|
||||
- BangDream scoped ESLint
|
||||
|
||||
### Phase 1:命令和 operation registry
|
||||
|
||||
目标:去掉 `QqbotBangDreamRendererService.execute` 的 switch,命令能力以 registry 为单一来源。
|
||||
|
||||
任务:
|
||||
|
||||
- 新增 `TsuguOperationDefinition`:`key`、`name`、`description`、`aliases`、`inputSchema`、`execute`。
|
||||
- 将现有 `BANGDREAM_OPERATION_DEFS` 与执行器合并为 `BANGDREAM_OPERATION_REGISTRY`。
|
||||
- SQL 初始化和测试改从 registry 校验,避免在线命令漏配或脏数据。
|
||||
- Renderer service 只保留 Nest 注入、健康检查和调用 facade。
|
||||
|
||||
验收:
|
||||
|
||||
- 15 个 operation key 不变。
|
||||
- 在线命令 SQL 与 registry 一一对应。
|
||||
- `/bd`、`/邦邦` 所有当前命令行为不变。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `runtime/operation-registry.ts`,收口 15 个 operation 的 key、name、description、handlerName、在线命令别名、冷却和备注。
|
||||
- `QqbotBangDreamRendererService.execute` 已改为按 registry 查找 handler,不再维护 operation switch。
|
||||
- `QqbotBangDreamPluginService` 和 SQL 一致性测试已改为直接读取 registry。
|
||||
|
||||
### Phase 2:配置、字典和协议常量分层
|
||||
|
||||
目标:把 `bangdream.enum.ts` 拆成协议常量、用户字典、运行策略。
|
||||
|
||||
任务:
|
||||
|
||||
- 建立 `models/bangdream-protocol.ts`:服务器 ID、难度 ID、Bestdori 字段和资源协议。
|
||||
- 建立 `runtime/default-dictionary.ts`:默认 label/alias。
|
||||
- 建立 `runtime/dictionary-loader.ts`:优先读取 API 字典缓存,失败回落默认字典。
|
||||
- `renderer` 的服务器/难度解析改走 loader,不直接 import alias object。
|
||||
- 为字典缓存加启动加载和按需刷新入口,避免每条命令查 DB。
|
||||
|
||||
验收:
|
||||
|
||||
- 删除 renderer 对 `BANGDREAM_SERVER_ALIASES`、`BANGDREAM_DIFFICULTY_ALIASES` 的直接依赖。
|
||||
- 本地无 DB 时仍能使用默认字典。
|
||||
- 字典项更新后可通过刷新入口生效。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `models/bangdream-protocol.ts` 收口服务器 ID、难度 ID、Bestdori API path、资源协议前缀和 API 枚举值。
|
||||
- 已新增 `runtime/default-dictionary.ts` 收口默认服务器/难度别名和用户可见 label,`models/bangdream-constants.ts` 改为兼容 re-export。
|
||||
- 已新增 `runtime/runtime-options.ts` 收口 env key、默认服务器策略、档线策略和布尔/list 解析工具。
|
||||
- 已新增 `runtime/dictionary-loader.ts`,启动或按需刷新时合并 API 字典项,失败时回落默认字典,不在每条命令里查 DB。
|
||||
- `QqbotBangDreamRendererService` 已移除对 server/difficulty alias object 的直接依赖,服务器、难度和 displayed server 解析统一走 loader。
|
||||
- `sql/qqbot-init.sql` 已补 `BANGDREAM_SERVER_ALIAS`、`BANGDREAM_DIFFICULTY_ALIAS` 默认字典项,后台可直接维护别名。
|
||||
|
||||
### Phase 3:数据源 provider 和 repository
|
||||
|
||||
目标:数据下载、Bestdori、HHWX、静态修正不再散落在 `models` 中。
|
||||
|
||||
任务:
|
||||
|
||||
- 定义 `BangDreamDataProvider`:`getJson`、`getAsset`、`getTracker`。
|
||||
- 在 `data-clients` 实现 `bestdori-provider.ts`、`hhwx-tracker-provider.ts`、`static-patch-provider.ts`。
|
||||
- 用 `withCache`、`withRetry`、`withTiming` 包装 provider,不在业务函数里写重试循环。
|
||||
- 在 `models` 建立 repository 文件:`song-repository.ts`、`card-repository.ts`、`event-repository.ts`、`gacha-repository.ts`、`player-repository.ts`。
|
||||
- domain model 只承载字段和轻量行为,不直接拼 URL。
|
||||
|
||||
验收:
|
||||
|
||||
- `models/song.ts`、`models/card.ts`、`models/event.ts` 中 Bestdori URL 拼接明显减少。
|
||||
- provider 可单测 mock。
|
||||
- 档线数据源 fallback 有日志 evidence。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `data-clients/data-provider.ts`,定义 `BangDreamDataProvider`、JSON/Asset/Tracker 请求参数和 provider URL 解析。
|
||||
- 已新增 `data-clients/provider-decorators.ts`,提供 `withCache`、`withRetry`、`withTiming`,缓存默认值、重试和耗时日志不再要求业务函数内手写循环。
|
||||
- 已新增 `data-clients/cache-client-policy.ts`,收口旧 cache client 的重试次数规范化、3 秒等待、HTTP 状态读取、404 不重试和缺失 URL 缓存过期时间;`api-cache-client.ts`、`asset-cache-client.ts`、`file-cache-client.ts` 开始消费统一策略,避免 JSON/API 与素材下载继续各自手写 retry/fallback。
|
||||
- 已新增 `data-clients/bestdori-provider.ts`、`data-clients/hhwx-tracker-provider.ts`、`data-clients/static-patch-provider.ts`,主数据、素材、Tracker 和本地静态修正分别走 provider。
|
||||
- `models/main-data-store.ts` 已改为通过 Bestdori provider 加载主数据,通过 static patch provider 读取 `cards-cn-fix.json`、`skills-cn-fix.json`、`area-item-fix.json` 和 `nickname-song.xlsx`。
|
||||
- 已新增 `models/main-data-repository.ts`、`song-repository.ts`、`card-repository.ts`、`event-repository.ts`、`gacha-repository.ts`、`player-repository.ts`。
|
||||
- `command-renderers/song-list.ts`、`event-list.ts`、`card-list.ts`、`gacha-detail.ts`、`player-detail.ts`、`cutoff-detail.ts` 已开始改走 repository 创建模型或读取主数据。
|
||||
- `models/song.ts`、`card.ts`、`event.ts`、`gacha.ts`、`player.ts` 的 Bestdori API/素材请求已开始改走 Bestdori provider;`models/cutoff.ts` 的 Bestdori/HHWX Tracker fallback 已改走 provider,并在 fallback 时输出数据源切换日志。
|
||||
- 已新增 `models/event-data-repository.ts` 收口活动详情、横幅、背景、轮播、Logo、奖励表情和装饰素材请求;`models/event.ts` 不再直接依赖 Bestdori provider 或 `loadImage`。
|
||||
- `drawEventDetail` 的活动真实底图改为轻量图片背景,避免长图走模糊三角纹理导致超时;活动底图由 `bg_eventtop.png` 和 `trim_eventtop.png` 合成,避免只渲染模糊背景底图;活动搜索默认遵循 `BANGDREAM_TSUGU_USE_EASY_BG=false`,不再强制简易背景。
|
||||
- 已接入 `BANGDREAM_TSUGU_REQUEST_TIMEOUT_MS` 和 `BANGDREAM_TSUGU_MAIN_DATA_READY_TIMEOUT_MS`,HTTP 请求和主数据首次 ready 等待都有硬超时,BangDream 命令执行前会等待关键主数据集合可用。
|
||||
- 已新增 `scripts/bangdream-render-smoke.ps1`,BangDream 图片 smoke 通过父进程限时、子进程 PID 清理、生成后显式退出,避免本地调试命令卡住进程。
|
||||
- 已新增 `test/qqbot/plugins/bangDream/tsugu/data-provider.spec.ts`,覆盖 provider URL 解析、Bestdori/HHWX mock 数据源、retry/cache wrapper。
|
||||
- 已新增 `cache-client-policy.spec.ts`,覆盖缓存客户端 retry/status/not-found 策略;`file-cache-client.spec.ts` 补 SVG 缺失资源 404 缓存,避免 SVG 素材缺失仍反复请求。
|
||||
- 本地图片烟测已生成查歌 `136`、查活动 `50` 简易背景和真实活动背景图片,证明 provider/repository 第一段迁移后仍能输出非空图片。
|
||||
- 已新增 `models/song-resource-repository.ts`,把 `Song` 的歌曲详情、谱面、封面路径、封面完整 URL 和封面缺失时服务器回退下载收口到不依赖 `Song` 类的资源 repository,避免 `song.ts` 继续直接拼 Bestdori API/asset 路径;`song-resource-repository.spec.ts` 覆盖 detail/chart provider 调用、13/40 旧封面批次、273 强制 CN 和封面 fallback 行为,本地 `/查曲 136`、`/查谱面 136 expert` 图片 smoke 输出非空。
|
||||
- 收尾批量把 `Song.getSongChart`、`SongResourceRepository.getChart` 和 `drawBestdoriPreview` 的谱面协议改为 `BestdoriNote[]`,移除 `drawSongChart` 的 `as any`;`EventStage.getData` 透传 `EventStageDataRows<T>`,`stageType`/`rotationMusics` 改为 repository 行类型并用一次 `Promise.all` 加载;`readExcelFile` 改为泛型 `Record<string, unknown>` 输出;`asset-cache-client` 和 `server.ts` 清理参数命名与 `Array<any>` 残留,本地 `/查谱面 136 expert`、`/查试炼 310` smoke 输出非空。
|
||||
- 已新增 `models/card-resource-repository.ts`,把 `Card` 的详情请求、资源批次目录、图标/插画/trim 图片路径和重图缓存策略收口到 provider-backed repository;`card-resource-repository.spec.ts` 覆盖 `/api/cards` 缓存参数、`9999` 后资源批次、CN 优先资源路径和非 icon 图片 `memoryCache=false`,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `models/gacha-resource-repository.ts`,把 `Gacha` 的详情请求、首页横幅、screen 背景、`bg1` fallback 和 Logo 资源路径收口到 provider-backed repository;`gacha-resource-repository.spec.ts` 覆盖 `/api/gacha` 缓存参数、CN 优先 screen 路径、横幅缺失回退 Logo 和背景 fallback,本地 `/查卡池 259`、`/抽卡模拟 10 259` 图片 smoke 输出非空。
|
||||
- 已新增 `models/event-stage-data-repository.ts`,把 `EventStage` 的 festival stages/rotationMusics 数据请求从 `bestdoriUrl + callAPIAndCacheResponse` 收口到 provider-backed repository;`event-stage-data-repository.spec.ts` 覆盖两个 festival API 路径和缓存参数,本地 `/查试炼 310` 保持拆成 5 张图片输出。
|
||||
- 已新增 `models/character-resource-repository.ts`,把 `Character` 的详情请求、角色图标、KV 立绘和名称横幅资源路径从 `bestdoriUrl + callAPIAndCacheResponse/downloadFileCache` 收口到 provider-backed repository;`character-resource-repository.spec.ts` 覆盖详情缓存策略和三类资源路径,本地 `/查角色 1` 图片 smoke 输出非空。
|
||||
- 已新增 `models/band-resource-repository.ts`,把 `Band` 的乐队 Logo 和乐队图标 SVG 资源路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository;`band-resource-repository.spec.ts` 覆盖两类资源路径和下载调用,本地 `/查角色 1` 图片 smoke 保持非空输出。
|
||||
- 已新增 `models/attribute-resource-repository.ts`,把 `Attribute` 的属性图标 SVG 资源路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository;`attribute-resource-repository.spec.ts` 覆盖属性图标路径和下载调用,本地 `/查卡 472` 图片 smoke 保持非空输出。
|
||||
- 已新增 `models/server-resource-repository.ts`,把 `Server` 的服务器图标 SVG 资源路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository,并把台服本地图标路径改走 asset manifest;`server-resource-repository.spec.ts` 覆盖服务器图标路径、台服本地图标路径和下载调用,本地 `/查卡 472` 图片 smoke 保持非空输出。
|
||||
- 已新增 `render-blocks/card-art-resource-repository.ts`,把 `card-art.ts` 的卡牌小图/插画边框资源下载从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository,`card-art-spec.ts` 改为只生成 `/res/image/...` 相对资源路径;`card-art-resource-repository.spec.ts` 覆盖边框下载调用,本地 `/查卡 472` 图片 smoke 保持非空输出。
|
||||
- 已新增 `models/costume-resource-repository.ts`,把 `Costume` 的服装详情请求和 Live2D 缩略图素材路径从 `bestdoriUrl + callAPIAndCacheResponse/downloadFile` 收口到 provider-backed repository;`costume-resource-repository.spec.ts` 覆盖详情路径、`sdchara.png` 路径和非内存缓存下载调用,本地 `/查卡 472` 图片 smoke 保持演出缩略图正常输出。
|
||||
- 已新增 `models/item-resource-repository.ts`,把 `Item` 的道具缩略图路径从 `bestdoriUrl + downloadFileCache` 收口到 provider-backed repository;`item-resource-repository.spec.ts` 覆盖 material、star 和 common item 三类路径,本地 `/查卡池 259` 图片 smoke 输出非空。
|
||||
- 已新增 `models/cutoff-event-top-repository.ts` 和 `render-blocks/player-ranking-resource-repository.ts`,把前十榜 eventtop 数据请求与排名徽章素材路径从 `bestdoriUrl + callAPIAndCacheResponse/downloadFileCache` 收口到 provider-backed repository;相关 spec 覆盖 eventtop query path、排名徽章路径和 provider 调用,本地 `/ycx 10 100 cn` 图片 smoke 输出非空。
|
||||
- 已新增 `models/degree-resource-repository.ts`,把 `Degree` 的称号缩略图、称号框、称号图标、动态称号脚本和纹理素材路径从 `bestdoriUrl + downloadFile/downloadFileCache` 收口到 provider-backed repository;`degree-resource-repository.spec.ts` 覆盖旧/新缩略图 fallback、动态称号旧纹理白名单和 provider 调用,本地 `/ycx 10 100 cn` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/deck-rank-resource-repository.ts`,把玩家详情“乐队编成等级”Rank 图片从 `list-band-detail.ts` 的本地路径和 Bestdori URL 直拼收口到 repository,渲染层只负责 `Buffer -> Image`;`deck-rank-resource-repository.spec.ts` 覆盖本地素材优先、远端 `/res/icon/*.png` 兜底和 provider 调用,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- `models/event-data-repository.ts` 已改为 provider-injected repository,并为活动背景和 topscreen trim 增加路径工厂;活动详情背景现在跟随 `displayedServerList` 选服,不再无条件按全局默认服取底图,避免 `/查活动 ... jp` 这类指定服务器查询出现横幅和背景不一致。
|
||||
- 已新增 `models/player-data-repository.ts`,把 `Player` 的 `/api/player/{server}/{playerId}` 详情请求、mode/cacheTime 策略和 mode=1 后台刷新行为从领域模型中收口到 provider-backed repository;`player-data-repository.spec.ts` 覆盖玩家详情路径、缓存参数和后台刷新调用,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- 固化:Windows 下不要用反斜杠测试路径直接调用 Jest pattern,容易出现 `Pattern ... - 0 matches`;指定文件测试统一使用 `pnpm exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangDream/tsugu/<file>.spec.ts ...`,路径用正斜杠。
|
||||
|
||||
### Phase 4:搜索 specification 和 matcher
|
||||
|
||||
目标:模糊搜索规则可以新增/禁用,不再在一个文件里继续堆 if。
|
||||
|
||||
任务:
|
||||
|
||||
- 将 keyword parse、config hit、relation match、field match 拆成 specification。
|
||||
- 建立 `FuzzySearchRuleRegistry`,每条规则只关心 `canHandle` 和 `match`。
|
||||
- `search/fuzzy-search-settings.json` 与字典昵称统一成 `SearchDictionaryRepository`。
|
||||
- `entity-list-matcher.ts` 只依赖 matcher 接口,不直接知道配置来源。
|
||||
|
||||
验收:
|
||||
|
||||
- `search/fuzzy-search.ts` 文件长度和分支继续下降。
|
||||
- 新增昵称或难度别名无需改 matcher 代码。
|
||||
- 旧 search/fuzzy-search 测试全部通过。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `search/fuzzy-search-types.ts`、`search/search-dictionary-repository.ts`、`search/relation-matcher.ts`、`search/fuzzy-search-rule-registry.ts`,把搜索类型、搜索字典读取、关系表达式和规则注册表从 `fuzzy-search.ts` 中拆出。
|
||||
- `fuzzy-search.ts` 保留关键词拆分、结果校验和目标匹配的兼容入口,关键词解析改由 `FuzzySearchRuleRegistry` 按 number、level、relation、config、fallback 顺序处理,文件长度从 516 行降到 281 行。
|
||||
- `entity-list-matcher.ts` 改为只依赖搜索结果类型和关系匹配器,并支持延迟读取 `source`,修复 mainAPI 异步加载前捕获空数据源导致查歌/查卡/查活动搜索不到的问题。
|
||||
- `song-list.ts`、`card-list.ts`、`event-list.ts` 的实体列表匹配器已改为运行时读取 repository source,查歌 `夏祭り` 和查活动 `summer` 本地图片 smoke 均生成非空图片。
|
||||
|
||||
### Phase 5:渲染 theme、layout spec 和 section builder
|
||||
|
||||
目标:Canvas 绘制仍保留函数式性能,但颜色、尺寸、字体和区块顺序收口。
|
||||
|
||||
任务:
|
||||
|
||||
- 建立 `render-blocks/theme.ts`:颜色、字体、默认宽度、列表间距、背景配置。
|
||||
- 建立 `render-blocks/*-spec.ts`:歌曲列表、活动列表、卡牌详情、谱面预览规格。
|
||||
- `render-blocks/song-chart-preview.ts` 先拆 token,再拆音符策略,不直接改视觉结果。
|
||||
- `DetailBlockBuilder` 统一 title、data block、image block、spacer。
|
||||
- 资源路径由 `asset-manifest.ts` 管理,统一 `asset('Card.star')` 形式。
|
||||
|
||||
验收:
|
||||
|
||||
- `render-blocks/song-chart-preview.ts` 字面量数量显著下降,主函数仍低复杂度。
|
||||
- 关键图片命令输出非空,尺寸在既有范围。
|
||||
- 视觉 token 修改只改 theme/spec 文件。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `render-blocks/theme.ts`,先收口公共文字颜色、分割线颜色、简易背景色、图表背景/文字色和默认字体名。
|
||||
- 已新增 `render-blocks/layout-spec.ts`,统一横向/纵向虚线分割规格,歌曲列表、活动列表、活动详情和通用列表框架不再重复维护分割线宽高/颜色。
|
||||
- 已新增 `runtime/asset-manifest.ts`,收口本地 `BG`、`Card`、`Skill`、`SongChart`、字体和标题资源路径;`canvas/text.ts`、`canvas/rect.ts`、`canvas/output.ts`、`canvas/background.ts`、`card-art.ts`、`list-rarity.ts`、`skill-text.ts`、`title.ts`、`song-chart-preview.ts` 已改走 manifest。
|
||||
- 已生成查曲、查活动和查谱面 smoke 图片,验证 theme/spec/manifest 第一段迁移后本地图片输出非空,谱面预览资源路径未断。
|
||||
- 已新增 `render-blocks/detail-block-builder.ts`,活动详情页先接入 `DetailBlockBuilder` 统一 section、spacer 和 data block 拼装,去掉局部 `pushSection` 和手动分割线维护;`detail-block-builder.spec.ts` 覆盖 section 分割线与 spacer/data block 输出。
|
||||
- 本地验证时发现 Jest 无法解析 `skia-canvas/lib/v6/index.node`,根因是 `moduleFileExtensions` 未包含 `node`;已把 `.node` 原生扩展解析固化进 Jest 配置,避免后续渲染单测重复卡在同类问题。
|
||||
- 卡牌详情页已接入 `DetailBlockBuilder`,卡牌标题、插画、基础字段、缩略图和演出缩略图不再手写数组与分割线;`/查卡 472` 本地 smoke 输出非空长图。
|
||||
- `scripts/bangdream-render-smoke.ps1` 已固化图片 smoke 完成判定:先删除旧目标图,轮询 stdout 成功 JSON 和新图片文件;如果 Tsugu 后台 handle 导致 Node 未自然退出,则在图片落盘后清理本次进程并返回成功,避免已成功出图仍卡到超时。
|
||||
- 线上 `/qqbot/command/test` smoke 已固化为先按 `operationKey` 查询启用命令、传 `commandId`,并保留完整命令文本;避免默认 `preview` selfId 未绑定命令时误报“未匹配到命令”。
|
||||
- 已新增 `render-blocks/song-chart-preview-spec.ts`,把谱面 BPM 时间计算、双押识别、滑条拆分、音符排序、展示/计数音符分类、难度颜色和布局规格从 `song-chart-preview.ts` 拆成可测纯策略;绘制文件改为消费 `createSongChartPreviewModel`,只保留资源加载和 canvas 绘制。
|
||||
- 已新增 `song-chart-preview-spec.spec.ts`,覆盖 BPM 变速时间、十六分单点、双押、滑条 bar/tick/end、布局列数和音符分类;本地生成 `song-chart-preview-spec-136-expert.jpg`,验证谱面预览重构后图片输出非空且视觉结构正常。
|
||||
- 已新增 `render-blocks/card-art-spec.ts`,收口卡牌图标/插画边框 URL 生成、icon/illustration 画布尺寸、属性/乐队/星级/突破/技能等级坐标;`card-art.ts` 改为消费 spec 和 URL factory,下载与绘制顺序保持不变。
|
||||
- 已新增 `card-art-spec.spec.ts`,覆盖 rarity=1 属性边框、其他稀有度边框和关键尺寸;本地生成 `card-art-spec-card-472.jpg`,验证卡牌详情图在规格收口后仍能正常输出。
|
||||
- 已新增 `render-blocks/detail-block-spec.ts`,收口歌曲详情、歌曲 meta、角色半身块和玩家详情头图的尺寸、间距、字号与相对分数舍入规则;`detail-blocks.ts` 改为消费 spec,数据查询和绘制顺序保持不变。
|
||||
- 已新增 `detail-block-spec.spec.ts`,覆盖歌曲详情尺寸、角色/玩家详情尺寸和 meta 相对百分比舍入;本地生成 `detail-spec-song-136.jpg`、`detail-spec-event-50.jpg`、`detail-spec-character-1.jpg`,验证详情区块规格收口后查曲/查活动/查角色图片输出正常。
|
||||
- 已新增 `render-blocks/list-frame-spec.ts`,收口通用列表行、tips、横向合并列、居中图片列表和左侧竖线的字号、间距、兜底尺寸与布局计算;`list-frame.ts` 改为消费 spec,服务器分组、换行和绘制顺序保持不变。
|
||||
- 已新增 `list-frame-spec.spec.ts`,覆盖列表正文宽度、标签/tips 偏移、合并列宽、居中图片换行和左侧竖线尺寸;本地生成 `list-frame-spec-song-136.jpg`、`list-frame-spec-event-50.jpg`、`list-frame-spec-character-1.jpg`,验证列表框架规格收口后查曲/查活动/查角色图片输出正常。
|
||||
- 已新增 `canvas/text-spec.ts`,收口文本默认字号、行高比例、baseline、空画布尺寸、混排间距和内联图片缩放计算;`canvas/text.ts` 改为消费 spec,原有文本换行和混排拆分逻辑保持不变。
|
||||
- 已新增 `text-spec.spec.ts`,覆盖行高/间距比例、baseline、内联图片缩放和空/单行/多行画布尺寸;本地生成 `text-spec-song-136.jpg`、`text-spec-event-50.jpg`、`text-spec-character-1.jpg`,验证文本规格收口后查曲/查活动/查角色图片输出正常。
|
||||
- 已新增 `render-blocks/gacha-simulate-spec.ts`,收口抽卡模拟网格宽度、单抽/汇总混排尺寸、重复卡叠层、数量右对齐和卡池横幅布局;`gacha-simulate.ts` 改为消费 spec,抽卡概率和卡池选择逻辑保持不变。
|
||||
- 已新增 `gacha-simulate-spec.spec.ts`,覆盖 10 抽网格、汇总模式、重复卡叠层、计数字样和横幅尺寸;本地生成 `gacha-simulate-spec-10-259.jpg` 和 `gacha-simulate-spec-50-259-after-cache-fix.jpg`,验证抽卡模拟两种布局模式图片输出正常。
|
||||
- 抽卡模拟 50 抽 smoke 暴露资源下载短时失败会把 URL 写入无过期错误缓存,导致后续重试直接输出 `asset error`;已改为只有 HTTP 404 进入错误缓存,超时/网络抖动不污染 URL,并新增 `file-cache-client.spec.ts` 覆盖瞬时失败后可再次下载、404 缓存缺失资源两种路径。
|
||||
- 已新增 `render-blocks/event-stage-spec.ts`,收口试炼列表最大列高、歌曲单元格/封面/ID/难度条尺寸、试炼类型顶部文字规格和换列判断;`event-stage.ts` 与 `list-event-stage.ts` 改为消费 spec,活动/歌曲选择和输出结构保持不变。
|
||||
- 线上 smoke 暴露 `/查试炼 310 -m` 将所有列横向拼成一张巨大 canvas 会触发 Pod `OOMKilled exit=137`;已改为按小批次绘制 stage、边分列边输出多张 CQ 图片,不再创建最终横向巨图,普通版和 meta 版均拆成 5 张输出,同时避免完全串行导致冷缓存首条命令耗时过长。
|
||||
- 已新增 `event-stage-spec.spec.ts`,覆盖试炼歌曲行尺寸、类型顶部文字规格、换列判断和按列高拆分算法;本地生成 `event-stage-split-310*.jpg`、`event-stage-split-310-meta*.jpg`,验证 `/查试炼 310` 与 `/查试炼 310 -m` 图片输出正常。
|
||||
- 已新增 `render-blocks/list-player-card-icon-spec.ts`,收口玩家详情主卡组展示顺序、默认行高、文本字号比例、卡牌间距比例和卡牌图标可见性标记;`list-player-card-icon-list.ts` 改为消费 spec,主卡组渲染顺序和输出结构保持不变;`list-player-card-icon-spec.spec.ts` 覆盖历史卡牌顺序、缺失条目跳过和字号/间距计算,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-card-icon-spec.ts`,收口通用卡牌图标列表默认行高、文本字号比例、卡牌间距比例、默认可见性标记和历史排序比较器;`list-card-icon-list.ts` 改为消费 spec,卡牌图标列表排序和输出结构保持不变;`list-card-icon-spec.spec.ts` 覆盖稀有度排序、优先卡牌类型排序、普通卡按 ID 排序和字号/间距计算,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-song-spec.ts`,收口歌曲列表单行封面、文本、难度块位置、列表内容宽度、分割线高度和外层行高计算;`list-song.ts` 改为消费 spec,歌曲列表绘制顺序和输出结构保持不变;`list-song-spec.spec.ts` 覆盖单行尺寸、难度块垂直居中和歌曲组列表尺寸,本地 `/查曲 136` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-difficulty-spec.ts`,收口难度列表默认高度/间距、徽章兜底色、圆形布局和等级文字比例/居中计算;`list-difficulty.ts` 改为消费 spec,难度列表绘制顺序和输出结构保持不变;`list-difficulty-spec.spec.ts` 覆盖列表宽度、条目偏移、徽章颜色、圆形布局和文字居中,本地 `/查曲 136` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-difficulty-detail-spec.ts`,收口玩家难度详情列表的徽章宽度/字号/圆角、正文宽度/行高、项画布高度和通用列表行高/间距;`list-difficulty-detail.ts` 改为消费 spec,玩家详情里的已通关/Full Combo/All Perfect 难度详情绘制顺序和输出结构保持不变;`list-difficulty-detail-spec.spec.ts` 覆盖徽章参数、正文绘制参数、项布局和列表框架参数,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-band-detail-spec.ts`,收口玩家详情乐队等级、舞台挑战达成情况和乐队编成等级列表的 Logo 缩放、正文宽度/行高、项画布、通用列表框架和 Rank 等级图片布局;`list-band-detail.ts` 改为消费 spec,玩家详情里的乐队详情绘制顺序、资源 repository 和输出结构保持不变;`list-band-detail-spec.spec.ts` 覆盖 Logo/正文参数、项布局、列表框架、Rank 画布/图片位置和等级图片 Rank ID 封顶,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-character-detail-spec.ts`,收口玩家详情角色等级列表的头像缩放、正文宽度/行高、项画布和通用列表框架;`list-character-detail.ts` 改为消费 spec,玩家详情角色等级绘制顺序、角色数据读取和输出结构保持不变;`list-character-detail-spec.spec.ts` 覆盖头像/正文参数、项布局和列表框架参数,本地 `/查玩家 26591455 jp` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-card-sd-character-spec.ts`,收口卡牌详情演出缩略图的 SD 角色 sprite 裁切网格、列表缩放行高、字号和间距;`list-card-sd-character.ts` 改为消费 spec,素材加载、四帧裁切和列表绘制顺序保持不变;`list-card-sd-character-spec.spec.ts` 覆盖四帧裁切坐标、单帧裁切计算和列表规格,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-card-prefix-spec.ts`,收口卡牌详情顶部标题块的画布、背景、乐队 Logo 等比缩放、卡牌标题和角色名文字布局;`list-card-prefix.ts` 改为消费 spec,乐队/角色/服务器优先级读取和绘制顺序保持不变;`list-card-prefix-spec.spec.ts` 覆盖标题块尺寸、背景、文字坐标和 Logo 缩放,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-stat-spec.ts`,收口卡牌/玩家综合力列表的间隔、数值行画布、文字规格、进度条布局和条宽算法;`list-stat.ts` 改为消费 spec,综合力计算、突破加成和绘制顺序保持不变;`list-stat-spec.spec.ts` 覆盖间隔/画布/文字/进度条规格、文本生成和条宽布局,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/title-spec.ts`,收口通用标题条的背景偏移、两行文字字号/行高/颜色/字体/坐标;`title.ts` 改为消费 spec,标题底图加载、标题文案和绘制顺序保持不变;`title-spec.spec.ts` 覆盖背景偏移、两行文字布局、绘制参数和坐标,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已新增 `render-blocks/list-rarity-spec.ts`,收口星级列表的文字大小、间距和特训星图使用阈值;`list-rarity.ts` 改为消费 spec,星图资源加载、星级数量和列表绘制顺序保持不变;`list-rarity-spec.spec.ts` 覆盖列表规格和 3/4/5 星及未特训边界,本地 `/查卡 472` 图片 smoke 输出非空。
|
||||
- 已批量新增 `render-blocks/skill-text-spec.ts`、`list-entity-spec.ts`、`degree-list-spec.ts`、`data-block-spec.ts`、`list-player-ranking-spec.ts`、`list-time-spec.ts`、`timeline-chart-spec.ts`、`cutoff-chart-spec.ts`、`gacha-list-spec.ts`,收口技能角标、基础实体列表、称号/活动奖励称号、数据块、玩家排名行、时间格式、时间线/档线图表和卡池列表文案/布局规格;`skill-text.ts`、`list-attribute.ts`、`list-band.ts`、`list-character.ts`、`degree-badge.ts`、`list-degree-list.ts`、`data-block.ts`、`list-player-ranking.ts`、`list-time.ts`、`timeline-chart.ts`、`cutoff-chart.ts`、`list-gacha-payment-method.ts`、`list-gacha-pick-up.ts`、`list-gacha-rate.ts` 已改为消费 spec;同步修复 `list-band.ts` 多乐队分支误用 `this.hasIcon` 导致图标判断失效的问题;新增对应 8 组 spec 单测,本地 `/查卡 472`、`/查卡池 259`、`/查活动 50`、`/ycx 100 50 cn`、`/查谱面 136 expert` 图片 smoke 输出非空。
|
||||
- 已补充 `song-chart-preview-spec.ts` 的封面信息块偏移、ID/难度面板、轨道渐变和双押线高度规格,`song-chart-preview.ts` 不再在绘制函数中散落这些视觉常量;`song-chart-preview-spec.spec.ts` 覆盖新增规格,本地 `/查谱面 136 expert` 图片 smoke 输出非空。
|
||||
- 收尾批量把谱面预览的绘图上下文改为 `CanvasRenderingContext2D`,`timeline-chart-spec.ts` 定义 `TimelineChartPoint/TimelineChartDataset`,`Cutoff.getChartData` 和 `CutoffEventTop.getChartData` 统一输出毫秒时间戳而不是 `Date` 对象;`drawTimeLineChart` 改用 typed `ChartConfiguration`,只在 skia-canvas 与 Chart.js 的 `ChartItem` 兼容边界保留显式 `unknown` 转换;同步修正 `labelName`、`cutoffEventTop`、`accuracy`、`constellationTextImage` 等拼写残留。本地 BangDream Tsugu 55 个 Jest suite / 186 个测试、`pnpm run typecheck`、scoped ESLint、`pnpm run build` 通过,`/查谱面 136 expert`、`/ycx 100 50 cn`、`/lsycx 100 50 cn`、`/查试炼 310` 图片 smoke 输出非空。
|
||||
- smoke/Jenkins/远程调试卡点继续按已固化规则处理:同一卡点第二次尝试前必须改变可验证变量;Jenkins 查询 URL 含方括号时用 `curl -g` 或改查 Jenkins home;线上图片 smoke 必须查询 `commandId`、拉回图片、展示图片、查日志并清理本轮临时目录;PowerShell 双引号 here-string 中不要写 JS `${...}` 模板字面量,避免被 PowerShell 提前插值;smoke 没有真实图片落盘时必须失败。
|
||||
|
||||
### Phase 6:策略 policy 和时间/档线规则
|
||||
|
||||
目标:服务器优先级、国服预估、档位、时区和活动状态从工具函数变成可测试规则。
|
||||
|
||||
任务:
|
||||
|
||||
- `BangDreamServerPolicy`:默认展示服、主服务器、优先级、时区。
|
||||
- `CnEventEstimatePolicy`:国服预估起始活动、跳过活动、无邦日。
|
||||
- `CutoffPolicy`:档位列表、预测线规则、近期活动选择规则。
|
||||
- `GachaPolicy`:生日卡池过滤、抽卡次数默认值、概率选择。
|
||||
|
||||
验收:
|
||||
|
||||
- `render-blocks/list-time.ts` 不再持有国服预估常量。
|
||||
- `models/cutoff.ts` 的请求、预测、展示规则分层。
|
||||
- policy 均有纯函数测试。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `models/server-policy.ts`,收口服务器 UTC 偏移、服务器时区 Date 转换、时间戳规范化和档线日增 checkpoint 服务器规则;`render-blocks/list-time.ts` 保留兼容入口但不再维护时区 switch。
|
||||
- 已新增 `models/cn-event-estimate-policy.ts`,把国服预估起始活动、跳过活动和无邦日规则从 `list-time.ts` 移出,提供 `calculateCnEventEstimateStartAt` 纯函数测试入口;`list-time.ts` 和 `event.ts` 改走 policy,不再让 Event 排序依赖渲染层预估实现。
|
||||
- 已新增 `models/cutoff-policy.ts`,收口档位列表、档位支持判断、国服缺失活动时间预估、档线活动状态、预测窗口、日增天数和最近同类型活动选择规则;`models/cutoff.ts` 和 `cutoff-all.ts` 已接入。
|
||||
- 已新增 `test/qqbot/plugins/bangDream/tsugu/policy.spec.ts`,覆盖服务器时区、国服预估纯计算、档位判断、状态/预测窗口、日增 checkpoint、活动天数和最近活动选择;policy 单测 mock `main-data-store`,避免纯测试启动主数据定时器。
|
||||
- 已生成 `phase6-policy-event-50.jpg`、`phase6-policy-cutoff-detail-100-50-cn.jpg`、`phase6-policy-cutoff-recent-100-50-cn.jpg`,验证查活动、单档线和历史档线图片输出非空。
|
||||
- 已新增 `models/gacha-policy.ts`,收口抽卡默认次数、上限、十连保底、稀有度概率、卡牌权重、生日/免费/日服常驻卡池过滤规则;`gacha-simulate.ts`、`gacha.ts`、`event-detail.ts` 和 renderer 当前卡池选择已接入。
|
||||
- `policy.spec.ts` 已补抽卡策略纯函数测试,覆盖卡池类型分类、抽卡次数上限、十连保底和权重抽取;本地生成 `gacha-policy-simulate-10-259.jpg` 和 `gacha-policy-event-50.jpg`,验证抽卡模拟与活动详情图片输出非空。
|
||||
- smoke 脚本和远程调试流程继续按已固化规则执行:图片落盘后清理本次 Node 进程并返回成功;远程命令测试必须按 `operationKey` 查询 `commandId`、传完整命令文本、外层设置 timeout,并在成功/失败分支显式退出。
|
||||
|
||||
### Phase 7:Facade 和 hook 接入
|
||||
|
||||
目标:Nest 边界稳定,Tsugu 内部可观测。
|
||||
|
||||
任务:
|
||||
|
||||
- `TsuguApplicationService` 作为唯一执行入口。
|
||||
- `TsuguOperationPipeline` 串起 registry、parser、resolver、renderer、output。
|
||||
- `TsuguHookRegistry` 注入日志、metrics、数据源 fallback、输出摘要 hook。
|
||||
- 错误统一成字符串,保持前端/QQBot 链路可解析。
|
||||
|
||||
验收:
|
||||
|
||||
- `QqbotBangDreamRendererService` 只保留薄 facade 或被替代。
|
||||
- 每次命令执行能记录 operation、耗时、图片数、错误。
|
||||
- 线上日志能定位 BangDream 命令的具体失败阶段。
|
||||
|
||||
当前进度:
|
||||
|
||||
- 已新增 `tsugu/runtime/hook-registry.ts`,定义 `TsuguHookContext`、`TsuguHook`、`TsuguHookRegistry` 和默认 `TsuguLogHook`,命令执行日志包含 `operation`、`stage`、`handler`、`query`、`imageCount`、`durationMs` 和错误字符串。
|
||||
- 已新增 `renderer/tsugu-application.service.ts` 作为 BangDream Tsugu 应用入口,统一执行主数据 ready 等待、operation registry 查找、handler 调度、hook 触发和错误字符串化。
|
||||
- `QqbotBangDreamClientService` 已改为注入 `TsuguApplicationService`,不再直接调用 renderer;`QqbotBangDreamRendererService` 移除 operation key 调度,只保留字典刷新、健康检查、渲染选项解析和 15 个具体 handler。
|
||||
- 已新增 `tsugu/runtime/operation-pipeline.ts`,把主数据 ready、operation resolve、handler render、output hook 和 error hook 固定为 `TsuguOperationPipeline.run`,`TsuguApplicationService.execute` 只负责调用 pipeline。
|
||||
- `scripts/bangdream-render-smoke.ps1` 已切到 `TsuguApplicationService`,本地 smoke 继续走真实应用入口,避免 facade 改造后验证脚本回退到旧链路。
|
||||
- 已新增 `hook-registry.spec.ts`、`operation-pipeline.spec.ts` 和 `tsugu-application.service.spec.ts`,覆盖 hook 顺序、错误 hook、pipeline 成功/未知 operation/handler 错误、应用入口执行、字典刷新和错误字符串化;本地 Phase 7 smoke 已生成 `phase7-hook-event-50.jpg`、`phase7-hook-cutoff-detail-100-50-cn.jpg`、`phase7-hook-cutoff-recent-100-50-cn.jpg`、`phase7-pipeline-event-50.jpg`、`phase7-pipeline-cutoff-detail-100-50-cn.jpg`、`phase7-pipeline-cutoff-recent-100-50-cn.jpg`。
|
||||
- 线上 `qqbot/command/test` 复测时曾出现 `bangdream.event.search` 的 `Invalid URL`,根因按风险点固化为:缓存层不能假设所有调用方都已把 `/api`、`/assets` 相对路径解析成完整 Bestdori URL;已在 `data-clients/cache-path.ts` 增加 `resolveCacheUrl` 兜底,并新增 `cache-path.spec.ts` 覆盖相对资产路径、相对 API 路径和完整 URL。线上/远程临时 Node 调试脚本必须同时设置外层 `timeout`,并在成功和失败分支显式 `process.exit(...)`,避免 SSH 会话被后台 timer 卡住。
|
||||
|
||||
## 文件迁移优先级
|
||||
|
||||
| 优先级 | 文件/目录 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| P0 | `renderer/qqbot-bangdream-renderer.service.ts` | 当前 operation 调度中心,影响在线命令一致性 |
|
||||
| P0 | `commands/qqbot-bangdream-command.definitions.ts` | 命令定义和 SQL 校验源头 |
|
||||
| P0 | `search/fuzzy-search.ts`、`search/entity-list-matcher.ts` | 搜索规则和查实体能力最容易继续膨胀 |
|
||||
| P1 | `runtime/config.ts`、`models/bangdream-constants.ts` | 硬编码分层入口 |
|
||||
| P1 | `data-clients/asset-cache-client.ts`、`data-clients/file-cache-client.ts`、`data-clients/api-cache-client.ts` | 缓存/重试/数据源切换横切能力 |
|
||||
| P1 | `models/song.ts`、`models/card.ts`、`models/event.ts`、`models/gacha.ts` | URL 拼接、mainAPI、patch、模型行为耦合 |
|
||||
| P2 | `render-blocks/song-chart-preview.ts` | 字面量最高,但视觉风险高,必须测试保护后再拆 |
|
||||
| P2 | `render-blocks/list-frame.ts`、`render-blocks/detail-blocks.ts`、`canvas/text.ts` | theme/spec 收口收益大 |
|
||||
| P3 | 其他 `command-renderers/*` | 等 builder/pipeline 稳定后逐个迁移 |
|
||||
|
||||
## 不做的事
|
||||
|
||||
- 不把所有常量都塞进数据库。布局像素、颜色 token、协议字段和资源文件名进入数据库只会降低可维护性。
|
||||
- 不做动态脚本 hook。hook 只允许代码内注册,避免线上不可控执行。
|
||||
- 不拆独立 Tsugu 服务。用户已明确不希望增加分离部署和管理成本。
|
||||
- 不一次性重命名/移动 92 个文件。先建立边界,再按风险迁移。
|
||||
- 不追求像素完全重画。当前需求是结构和硬编码治理,视觉只做等价保护。
|
||||
|
||||
## 最小可执行第一步
|
||||
|
||||
第一步建议只做三个文件级变更:
|
||||
|
||||
1. 新增 `runtime/operation-registry.ts`,把 15 个 operation 的 key/name/description/handler 统一。
|
||||
2. 修改 `QqbotBangDreamRendererService.execute`,从 registry 查 handler,不改每个 handler 内部逻辑。
|
||||
3. 新增 registry 一致性测试,保证 operation defs、SQL 初始化、TypeScript union 三者一致。
|
||||
|
||||
这一步收益明确,风险可控,也能为后续 hook/pipeline 打基础。
|
||||
|
||||
## 验证矩阵
|
||||
|
||||
| 阶段 | 必跑 |
|
||||
| --- | --- |
|
||||
| 文档/配置阶段 | `git diff --check`、global-review |
|
||||
| registry/pipeline | operation registry Jest、BangDream command SQL Jest、typecheck |
|
||||
| 字典/配置 | 字典加载单测、无 DB 回落单测、server/difficulty parse 单测 |
|
||||
| data provider | provider mock 单测、真实 Bestdori smoke、缓存命中单测 |
|
||||
| search | search/fuzzy-search 全量单测、查曲/查活动/查卡 smoke |
|
||||
| render | 非空 Buffer、图片尺寸范围、关键命令 smoke |
|
||||
| push 发布 | Jenkins 构建、K8s rollout、新 Pod 日志、真实 QQBot 命令 smoke |
|
||||
|
||||
## 成功标准
|
||||
|
||||
- `QqbotBangDreamRendererService` 不再承担 15 个 operation 的巨大 switch。
|
||||
- 用户可维护的别名、文案和命令映射从源码硬编码中移出。
|
||||
- 数据源、缓存、重试、fallback 和日志通过 provider/decorator/hook 接入。
|
||||
- 搜索规则、服务器策略、档线规则、国服时间预估都有独立可测入口。
|
||||
- 渲染层的颜色、尺寸、字体、资源路径有统一 token/spec/manifest。
|
||||
- 现有 15 个在线命令保持兼容,查曲、查活动、查试炼、档线、抽卡模拟都能输出图片。
|
||||
@ -1,147 +0,0 @@
|
||||
# QQBot BangDream 模块参考文档
|
||||
|
||||
生成日期:2026-06-07
|
||||
|
||||
## 文档定位
|
||||
|
||||
本文记录 QQBot BangDream 插件在文件结构重构完成后的稳定结构、入口链路、扩展点和验证方式。原 `tsugu` 子目录已经移除,内嵌 Tsugu 能力现在直接归入 `src/qqbot/plugins/bangDream`。
|
||||
|
||||
函数级用途以源码 JSDoc 为准;本文只维护模块边界、文件职责、命令入口、数据流、测试和发布验证入口。
|
||||
|
||||
## 覆盖范围
|
||||
|
||||
| 项 | 当前值 |
|
||||
| --- | --- |
|
||||
| 模块源码目录 | `src/qqbot/plugins/bangDream` |
|
||||
| Nest 客户端入口 | `src/qqbot/plugins/bangDream/application/bangdream-client.service.ts` |
|
||||
| 应用入口 | `src/qqbot/plugins/bangDream/application/bangdream-application.service.ts` |
|
||||
| 渲染 Facade | `src/qqbot/plugins/bangDream/application/bangdream-renderer.facade.ts` |
|
||||
| Operation 注册表 | `src/qqbot/plugins/bangDream/registry/operation-registry.ts` |
|
||||
| 测试目录 | `test/qqbot/plugins/bangDream` |
|
||||
| 资源目录 | `src/qqbot/plugins/bangDream/assets` |
|
||||
| 静态配置目录 | `src/qqbot/plugins/bangDream/static-config` |
|
||||
|
||||
## 总体链路
|
||||
|
||||
```text
|
||||
QQBot 在线命令
|
||||
-> QqbotBangDreamPluginService / command engine
|
||||
-> QqbotBangDreamClientService.execute(operationKey, input)
|
||||
-> TsuguApplicationService
|
||||
-> TsuguOperationPipeline
|
||||
-> QqbotBangDreamRendererService.executeOperationHandler(handlerName, input)
|
||||
-> song/card/event/gacha/player/cutoff 等模块 renderer
|
||||
-> provider + repository + search + theme/shared
|
||||
-> CQ image base64 reply
|
||||
```
|
||||
|
||||
核心约束:
|
||||
|
||||
- `registry/operation-registry.ts` 是 15 个 BangDream operation、handlerName、在线命令别名和冷却配置的单一来源。
|
||||
- `application/bangdream-application.service.ts` 是 Nest 边界,负责主数据 ready、字典刷新、pipeline 和错误字符串化。
|
||||
- `application/bangdream-renderer.facade.ts` 只做 handler 分发、输入归一化、字典解析和 CQ 图片输出。
|
||||
- 业务模块按领域聚合,例如歌曲文件集中在 `song`,卡牌文件集中在 `card`。
|
||||
- 横切能力只放 `hook`、`provider`、`policy`、`registry`、`theme`、`search`、`shared`。
|
||||
- 不再新增 `tsugu`、`models`、`render-blocks`、`command-renderers`、`data-clients`、`runtime`、`canvas` 顶层目录。
|
||||
|
||||
## 目录职责
|
||||
|
||||
| 目录 | 职责 |
|
||||
| --- | --- |
|
||||
| `application` | Nest 客户端、应用服务、渲染 facade、operation pipeline |
|
||||
| `registry` | operation key、handlerName、在线命令别名、冷却和说明 |
|
||||
| `hook` | 生命周期 hook 和命令执行日志 hook |
|
||||
| `provider` | Bestdori、HHWX、静态修正、缓存、重试、URL 解析和文件缓存 |
|
||||
| `policy` | 服务器策略、国服活动时间预估、档线规则、抽卡规则 |
|
||||
| `theme` | Canvas 基础能力、布局 token、本地资源 manifest 和渲染主题 |
|
||||
| `config` | 运行时配置、环境变量 key、服务器默认值和档线 tier |
|
||||
| `dictionary` | 默认字典和 API 字典加载 |
|
||||
| `search` | 模糊搜索、关系表达式、搜索字典和实体列表匹配 |
|
||||
| `song` | 查曲、歌曲详情、随机曲、分数表、谱面图片、歌曲资源 repository |
|
||||
| `card` | 查卡、卡面、卡牌图标、卡牌属性/稀有度/技能/综合力渲染 |
|
||||
| `character` | 查角色、角色详情、角色列表和角色资源 repository |
|
||||
| `event` | 查活动、活动详情、试炼、活动时间和活动数据 repository |
|
||||
| `gacha` | 查卡池、抽卡模拟、卡池概率、Pick Up 和卡池资源 repository |
|
||||
| `player` | 查玩家、玩家卡组、乐队等级、角色等级、难度完成情况和排名 |
|
||||
| `cutoff` | 档线、全档线、近期档线、档线图表、时间线图表和预测算法 |
|
||||
| `catalog` | 服务器、乐队、属性、区域道具、服装、称号、道具、技能、颜色 |
|
||||
| `shared` | 协议常量、主数据 store/repository、通用详情块、数据块、列表框架 |
|
||||
| `assets` | 本地图片、字体和静态视觉资源 |
|
||||
| `static-config` | 搜索配置、昵称表、CN 修正表和玩家编号表 |
|
||||
|
||||
## Operation 注册表
|
||||
|
||||
| operation key | handler | 命令名 | 主要别名 |
|
||||
| --- | --- | --- | --- |
|
||||
| `bangdream.song.search` | `searchSong` | 查曲 | `查曲`、`bd`、`bangdream`、`bandori`、`邦邦` |
|
||||
| `bangdream.song.chart` | `getSongChart` | 查谱面 | `查谱面`、`谱面`、`bd谱面` |
|
||||
| `bangdream.song.random` | `randomSong` | 随机曲 | `随机曲`、`随机`、`bd随机` |
|
||||
| `bangdream.song.meta` | `getSongMeta` | 查询分数表 | `查询分数表`、`查分数表`、`查询分数榜` |
|
||||
| `bangdream.card.search` | `searchCard` | 查卡 | `查卡`、`查卡牌`、`bd查卡` |
|
||||
| `bangdream.card.illustration` | `getCardIllustration` | 查卡面 | `查卡面`、`查卡插画`、`查插画` |
|
||||
| `bangdream.character.search` | `searchCharacter` | 查角色 | `查角色`、`bd角色` |
|
||||
| `bangdream.event.search` | `searchEvent` | 查活动 | `查活动`、`bd活动` |
|
||||
| `bangdream.event.stage` | `getEventStage` | 查试炼 | `查试炼`、`查stage`、`查舞台`、`查5v5` |
|
||||
| `bangdream.player.search` | `searchPlayer` | 查玩家 | `查玩家`、`查询玩家`、`bd玩家` |
|
||||
| `bangdream.gacha.search` | `searchGacha` | 查卡池 | `查卡池`、`bd卡池` |
|
||||
| `bangdream.gacha.simulate` | `simulateGacha` | 抽卡模拟 | `抽卡模拟`、`bd抽卡` |
|
||||
| `bangdream.cutoff.detail` | `getCutoffDetail` | `ycx` | `ycx`、`预测线`、`查档线`、`bd档线` |
|
||||
| `bangdream.cutoff.all` | `getCutoffAll` | `ycxall` | `ycxall`、`myycx`、`全部档线` |
|
||||
| `bangdream.cutoff.recent` | `getCutoffRecent` | `lsycx` | `lsycx`、`历史档线`、`近期档线` |
|
||||
|
||||
新增或调整在线命令时先改 `registry/operation-registry.ts`,再跑 `test/qqbot/plugins/bangDream/registry/command-sql.spec.ts` 检查数据库初始化 SQL 与注册表是否一致。
|
||||
|
||||
## 扩展规则
|
||||
|
||||
- 新增歌曲能力:优先在 `song` 增加 model/repository/renderer/layout,不要回到横切目录堆文件。
|
||||
- 新增卡牌能力:优先在 `card` 内聚合;只有静态目录实体才放 `catalog`。
|
||||
- 新增活动、卡池、玩家、档线能力:分别放 `event`、`gacha`、`player`、`cutoff`。
|
||||
- 新增外部数据源或缓存策略:放 `provider`。
|
||||
- 新增跨模块业务规则:放 `policy`。
|
||||
- 新增 Canvas 基础能力或视觉 token:放 `theme`。
|
||||
- 新增通用渲染块:先确认是否真跨多个业务模块,才放 `shared`。
|
||||
- 不建巨型 `index.ts` barrel,保持显式 import,降低循环依赖风险。
|
||||
|
||||
## 本地验证
|
||||
|
||||
常规结构或源码改动优先跑:
|
||||
|
||||
```powershell
|
||||
pnpm run typecheck
|
||||
pnpm exec jest --runInBand --runTestsByPath test/qqbot/plugins/bangDream/registry/operation-registry.spec.ts test/qqbot/plugins/bangDream/registry/command-sql.spec.ts
|
||||
pnpm exec eslint src/qqbot/plugins/bangDream test/qqbot/plugins/bangDream
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
图片 smoke 使用:
|
||||
|
||||
```powershell
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.song.search -Text "夏祭り" -OutFile ".kt-workspace/bangdream-smoke/song.jpg"
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.event.search -Text "50" -OutFile ".kt-workspace/bangdream-smoke/event.jpg"
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.event.stage -Text "310" -OutFile ".kt-workspace/bangdream-smoke/stage.jpg"
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.gacha.simulate -Text "10 259" -OutFile ".kt-workspace/bangdream-smoke/gacha.jpg"
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.cutoff.detail -Text "100 50 cn" -OutFile ".kt-workspace/bangdream-smoke/cutoff.jpg"
|
||||
.\scripts\bangdream-render-smoke.ps1 -OperationKey bangdream.song.chart -Text "136 expert" -OutFile ".kt-workspace/bangdream-smoke/chart.jpg"
|
||||
```
|
||||
|
||||
## 发布验证
|
||||
|
||||
推送 Jenkins/K8s backed 分支后必须观察发布闭环:
|
||||
|
||||
1. Jenkins 构建完成。
|
||||
2. K8s deployment rollout 成功。
|
||||
3. Pod 启动日志没有 BangDream module load error。
|
||||
4. 线上 `/qqbot/command/test` 按 operationKey 查询 commandId 后真实调用。
|
||||
5. 拉取线上 smoke 图片并目视检查。
|
||||
6. 检查操作日志中对应 command/test 调用和 BangDream hook 日志。
|
||||
7. 清理远程 smoke 临时目录。
|
||||
|
||||
## 常见风险
|
||||
|
||||
| 风险 | 处理方式 |
|
||||
| --- | --- |
|
||||
| 资源路径丢失 | 检查 `nest-cli.json` 的 `assets` include 是否指向 `qqbot/plugins/bangDream/assets` 和 `static-config` |
|
||||
| Jest pattern 匹配不到 | Windows 下指定文件统一用 `pnpm exec jest --runInBand --runTestsByPath path/with/slash.spec.ts` |
|
||||
| smoke 卡住 | 使用 `scripts/bangdream-render-smoke.ps1` 的 `TimeoutSeconds`,远程临时脚本也要有外层 timeout |
|
||||
| commandId 查错 | 线上 smoke 必须先用 operationKey 查在线命令,不使用历史脏数据 |
|
||||
| 大图 OOM 回归 | 优先验证 `bangdream.event.stage`,关注 `imageCount=5` 和图片大小 |
|
||||
Loading…
Reference in New Issue
Block a user