feat: 添加字典管理功能,包括字典的增删改查接口和管理页面

- 新增字典删除接口 `/api/dictionary/delete`
- 新增字典详情接口 `/api/dictionary/detail`
- 新增字典列表接口 `/api/dictionary/list`
- 新增字典更新接口 `/api/dictionary/update`
- 新增字典服务 `dictionaryService.js`,实现字典的创建、更新、删除和查询功能
- 新增字典管理页面 `dictionary-manage.js`,支持字典的增删改查操作
- 新增管理主页 `index.js`,提供字典管理入口
- 新增示例页面 `page-b.js`,用于验证页面跳转
This commit is contained in:
2026-03-26 17:59:13 +08:00
parent 6490fc427f
commit 836e660842
27 changed files with 1797 additions and 112 deletions

View File

@@ -6,7 +6,7 @@
## 范围
本次变更覆盖 `pocket-base/` 下 PocketBase hooks 项目的微信登录、平台注册/登录、资料更新、token 刷新、认证落库、错误可观测性、索引策略与运行规范。
本次变更覆盖 `pocket-base/` 下 PocketBase hooks 项目的微信登录、平台注册/登录、资料更新、token 刷新、认证落库、错误可观测性、索引策略、字典管理、页面辅助操作、健康检查版本探针、OpenAPI 鉴权与统一响应规范。
---
@@ -15,7 +15,7 @@
### 1. 认证体系
- 保持 PocketBase 原生 auth token 作为唯一正式认证令牌。
- 登录与刷新响应统一通过 `$apis.recordAuthResponse(...)` 返回 PocketBase 原生认证结果
- 登录与刷新响应统一为项目标准结构:`code``msg``data`,认证成功时额外返回顶层 `token`
- `authMethod` 统一使用空字符串 `''`,避免触发不必要的 MFA / login alerts 校验。
### 2. Header 规则
@@ -139,6 +139,11 @@
- `POST /api/platform/login`
- `POST /api/wechat/login`
- `POST /api/wechat/profile`
- `POST /api/dictionary/list`
- `POST /api/dictionary/detail`
- `POST /api/dictionary/create`
- `POST /api/dictionary/update`
- `POST /api/dictionary/delete`
其中平台用户链路补充为:
@@ -147,14 +152,15 @@
- body 必填:`users_name``users_phone``password``passwordConfirm``users_picture`
- 自动生成 GUID 并写入统一身份字段 `openid`
- 写入 `users_idtype = ManagePlatform`
- 成功时返回 PocketBase 原生 token + auth record + meta
- 成功时返回统一结构:`code``msg``data`,并在顶层额外返回 `token`
### `POST /api/platform/login`
- body 必填:`users_phone``password`
- body 必填:`login_account``password`
- 仅允许 `users_idtype = ManagePlatform`
- 前端使用手机号+密码提交
- 服务端内部仍通过 PocketBase 原生 password auth 返回原生 token
- 前端使用邮箱或手机号 + 密码提交
- 服务端通过 PocketBase `auth-with-password` 校验身份,再由当前 hooks 进程签发正式 token
- 成功时返回统一结构:`code``msg``data`,并在顶层额外返回 `token`
其中:
@@ -164,7 +170,7 @@
- 自动以微信 code 换取微信侧 `openid` 并写入统一身份字段
- 若不存在 auth 用户则尝试创建 `tbl_auth_users` 记录
- 写入 `users_idtype = WeChat`
- 成功时返回 PocketBase 原生 token + auth record + meta
- 成功时返回统一结构:`code``msg``data`,并在顶层额外返回 `token`
### `POST /api/wechat/profile`
@@ -179,25 +185,137 @@
- 若 token 仍有效:基于当前 auth record 续签
- 若 token 已过期:回退到微信 code 重签流程
- 若 token 已过期且未提供 `users_wx_code`,返回:`token已过期请上传users_wx_code`
- 返回精简结构,仅返回新 token(不返回完整登录用户信息)
- 返回统一结构:`code``msg``data`,并在顶层额外返回新 `token`
- 属于系统级通用认证能力,不限定为微信专属接口
### 字典管理接口
新增 `dictionary` 分类接口,统一要求平台管理用户访问:
- `POST /api/dictionary/list`
- 支持按 `dict_name` 模糊搜索
- 返回字典全量信息,并将 `dict_word_enum``dict_word_description``dict_word_sort_order` 组装为 `items`
- `POST /api/dictionary/detail`
-`dict_name` 查询单条字典
- `POST /api/dictionary/create`
- 新增字典,`system_dict_id` 自动生成,`dict_name` 唯一
- `POST /api/dictionary/update`
-`original_dict_name` / `dict_name` 更新字典
- `POST /api/dictionary/delete`
-`dict_name` 真删除字典
说明:
- `dict_word_enum``dict_word_description``dict_word_sort_order` 已统一改为 JSON 字符串持久化,其中 `dict_word_sort_order` 已改为 `text` 类型。
- 查询时统一聚合为:`items: [{ enum, description, sortOrder }]`
---
## 七、当前已知边界
## 七、页面与运维辅助能力新增
### 1. PocketBase 页面
新增页面:
- `/web`
- `/web/dictionary-manage`
页面能力:
- 首页支持跳转到子页面
- 字典管理页支持:
- Bearer Token 粘贴与本地保存
- `dict_name` 模糊搜索
- 指定字典查询
- 行内编辑基础字段
- 弹窗编辑枚举项
- 新增 / 删除字典
- 返回主页
### 2. 健康检查版本探针
`POST /api/system/health` 新增:
- `data.version`
用途:
- 通过修改 `APP_VERSION` 判断 hooks 是否已成功部署并生效
配置来源:
- 进程环境变量 `APP_VERSION`
-`runtime.js`
---
## 八、OpenAPI 与 Apifox 调试策略调整
### 1. 统一返回结构
所有对外接口统一返回:
- `code`
- `msg`
- `data`
认证成功类接口额外返回:
- `token`
不再返回以下顶层字段:
- `record`
- `meta`
### 2. 鉴权文档策略
OpenAPI 文档中已取消 `bearerAuth` 鉴权组件与接口级重复 `Authorization` 参数。
统一约定:
- 在 Apifox 环境中配置全局 Header`Authorization: Bearer {{token}}`
- 不再依赖文档中的 Bearer 组件自动注入
此举目的是:
- 避免接口页重复出现局部 `Authorization`
- 统一依赖环境变量完成鉴权注入
---
## 九、当前已知边界
1. `tbl_auth_users` 为 PocketBase `auth` 集合,因此仍受 PocketBase auth 内置规则影响。
2. 自定义字段“除 openid 外均可空”已在脚本层按目标放宽,但 auth 集合结构更新仍可能触发 PocketBase 服务端限制。
3. 若线上仍返回 PocketBase 默认 400需要确保最新 hooks 已部署并重启生效。
4. 平台登录通过回源 PocketBase REST 完成密码校验,因此 `POCKETBASE_API_URL` 必须配置为 PocketBase 进程/容器内部可达地址,不应使用外部 HTTPS 域名。
5. Apifox 环境中需自行维护全局 Header`Authorization: Bearer {{token}}`,否则鉴权接口不会自动携带 token。
---
## 、归档建议
## 、归档建议
部署时至少同步以下文件:
- `pocket-base/bai-api-main.pb.js`
- `pocket-base/bai-web-main.pb.js`
- `pocket-base/bai_api_pb_hooks/`
- `script/pocketbase.newpb.js`
- `pocket-base/bai_web_pb_hooks/`
- `pocket-base/spec/openapi.yaml`
- `script/pocketbase.js`
并在 PocketBase 环境中执行 schema 同步后重启服务,再进行接口验证。
建议归档后的发布核验顺序:
1. `POST /api/system/health`:确认 `data.version`
2. `POST /api/platform/login`:确认返回统一结构与顶层 `token`
3. `POST /api/dictionary/list`:确认鉴权与字典接口可用
---
## 十一、归档状态
- 已将本轮字典管理、PocketBase 页面、统一响应、平台登录兼容修复、健康检查版本探针、OpenAPI/Apifox 鉴权策略调整并入本变更记录。
- 本记录可作为当前 PocketBase hooks 阶段性归档基线继续维护。

View File

@@ -6,6 +6,7 @@ info:
当前 `tbl_auth_users.openid` 已被定义为全平台统一身份锚点:
- 微信用户:`openid = 微信 openid`
- 平台用户:`openid = 服务端生成的 GUID`
请在 Apifox 环境中统一设置全局 Header`Authorization: Bearer {{token}}`。
version: 1.0.0
servers:
- url: https://bai-api.blv-oa.com/pb
@@ -19,12 +20,9 @@ tags:
description: 面向微信用户的认证接口;认证成功后仍统一使用全平台 `openid` 与 PocketBase 原生 token。
- name: 平台认证
description: 面向平台用户的认证接口;平台用户会生成 GUID 并写入统一 `openid` 字段。
- name: 字典管理
description: 面向 ManagePlatform 用户的系统字典维护接口。
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: PocketBaseAuthToken
schemas:
ApiResponse:
type: object
@@ -45,6 +43,10 @@ components:
status:
type: string
example: healthy
version:
type: string
description: 当前已部署 hooks 版本号,用于确认发布是否生效
example: 2026.03.26-health-probe.1
timestamp:
type: string
format: date-time
@@ -94,7 +96,7 @@ components:
users_id_number:
type: string
users_status:
type: number
type: string
users_rank_level:
type: number
users_auth_type:
@@ -132,34 +134,55 @@ components:
PocketBaseAuthResponse:
type: object
description: |
PocketBase 原生认证响应。
客户端可直接使用返回 `token` 与 PocketBase SDK 或当前 hooks 接口交互
项目统一认证响应。
所有对外接口统一返回 `code`、`msg`、`data`,认证成功时额外返回顶层 `token`
properties:
token:
code:
type: integer
example: 200
msg:
type: string
description: PocketBase 原生 auth token
record:
type: object
description: PocketBase auth record 原始对象
meta:
example: 登录成功
data:
type: object
properties:
code:
type: integer
example: 200
msg:
status:
type: string
example: 登录成功
data:
type: object
properties:
status:
type: string
enum: [register_success, login_success]
is_info_complete:
type: boolean
user:
$ref: '#/components/schemas/UserInfo'
enum: [register_success, login_success]
is_info_complete:
type: boolean
user:
$ref: '#/components/schemas/UserInfo'
token:
type: string
description: PocketBase 原生 auth token仅认证类接口在成功时额外返回
example:
code: 200
msg: 登录成功
data:
status: login_success
is_info_complete: true
user:
pb_id: vtukf6agem2xbcv
users_id: U202603260001
users_idtype: ManagePlatform
users_name: momo
users_phone: '13509214696'
users_phone_masked: '135****4696'
users_status: ''
users_rank_level: 0
users_auth_type: 0
users_type: 注册用户
users_picture: ''
openid: app_momo
company_id: ''
users_parent_id: ''
users_promo_code: ''
usergroups_id: ''
company: null
created: ''
updated: ''
token: eyJhbGciOi...
WechatLoginRequest:
type: object
required: [users_wx_code]
@@ -227,12 +250,13 @@ components:
type: string
PlatformLoginRequest:
type: object
required: [users_phone, password]
description: 平台用户登录请求体;前端使用手机号+密码提交,服务端内部转换为 PocketBase 原生 password auth。
required: [login_account, password]
description: 平台用户登录请求体;前端使用邮箱或手机号 + 密码提交,服务端内部转换为 PocketBase 原生 password auth。
properties:
users_phone:
login_account:
type: string
example: 13800138000
description: 支持邮箱或手机号
example: admin@example.com
password:
type: string
example: 12345678
@@ -251,10 +275,89 @@ components:
example: 0a1b2c3d4e5f6g
RefreshTokenData:
type: object
properties: {}
DictionaryItem:
type: object
required: [enum, description, sortOrder]
properties:
token:
enum:
type: string
description: 新签发的 PocketBase 原生 auth token
example: enabled
description:
type: string
example: 启用
sortOrder:
type: integer
example: 1
DictionaryRecord:
type: object
properties:
pb_id:
type: string
system_dict_id:
type: string
dict_name:
type: string
dict_word_is_enabled:
type: boolean
dict_word_parent_id:
type: string
dict_word_remark:
type: string
items:
type: array
items:
$ref: '#/components/schemas/DictionaryItem'
created:
type: string
updated:
type: string
DictionaryListRequest:
type: object
properties:
keyword:
type: string
description: 对 `dict_name` 的模糊搜索关键字
example: 状态
DictionaryDetailRequest:
type: object
required: [dict_name]
properties:
dict_name:
type: string
example: 用户状态
DictionaryMutationRequest:
type: object
required: [dict_name, items]
properties:
original_dict_name:
type: string
description: 更新时用于定位原始记录;新增时可不传
example: 用户状态
dict_name:
type: string
example: 用户状态
dict_word_is_enabled:
type: boolean
example: true
dict_word_parent_id:
type: string
example: ''
dict_word_remark:
type: string
example: 系统状态字典
items:
type: array
minItems: 1
items:
$ref: '#/components/schemas/DictionaryItem'
DictionaryDeleteRequest:
type: object
required: [dict_name]
properties:
dict_name:
type: string
example: 用户状态
paths:
/api/system/test-helloworld:
post:
@@ -314,13 +417,6 @@ paths:
1) 若 `Authorization` 对应 token 仍有效:直接按当前 auth record 续签(不调用微信接口)。
2) 若 token 已过期:仅在 body 提供 `users_wx_code` 时才走微信 code 重新签发。
返回体仅包含新的 `token`,不返回完整登录用户信息。
parameters:
- in: header
name: Authorization
required: false
schema:
type: string
description: 可选。建议传入旧 token`Bearer <token>`)以优先走有效 token 续签路径。
requestBody:
required: false
content:
@@ -337,8 +433,14 @@ paths:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/RefreshTokenData'
token:
type: string
description: 新签发的 PocketBase 原生 auth token
example:
code: 200
msg: 刷新成功
data: {}
token: eyJhbGciOi...
'400':
description: 参数错误或微信侧身份换取失败
'401':
@@ -416,9 +518,9 @@ paths:
tags: [平台认证]
summary: 平台用户登录
description: |
前端使用平台注册时保存的 `users_phone + password` 登录。
前端使用平台注册时保存的 `邮箱或手机号 + password` 登录。
仅允许 `users_idtype = ManagePlatform` 的用户通过该接口登录。
服务端会先按手机号定位平台用户,再使用该用户的 PocketBase 原生 identity当前为 `email`)执行原生 password auth。
服务端会根据 `login_account` 自动判断邮箱或手机号,并定位平台用户,再使用该用户的 PocketBase 原生 identity当前为 `email`)执行原生 password auth。
登录成功后直接返回 PocketBase 原生 auth token。
requestBody:
required: true
@@ -426,6 +528,9 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PlatformLoginRequest'
example:
login_account: 13509214696
password: Momo123456
responses:
'200':
description: 登录成功
@@ -433,6 +538,33 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/PocketBaseAuthResponse'
example:
code: 200
msg: 登录成功
data:
status: login_success
is_info_complete: false
user:
pb_id: vtukf6agem2xbcv
users_id: ''
users_idtype: ManagePlatform
users_name: momo
users_phone: '13509214696'
users_phone_masked: '135****4696'
users_status: ''
users_rank_level: 0
users_auth_type: 0
users_type: ''
users_picture: ''
openid: app_momo
company_id: ''
users_parent_id: ''
users_promo_code: ''
usergroups_id: ''
company: null
created: ''
updated: ''
token: eyJhbGciOi...
'400':
description: 参数错误、密码错误或用户类型不匹配
'404':
@@ -450,8 +582,6 @@ paths:
description: |
基于当前 `Authorization` 对应 auth record 中的统一 `openid` 定位当前微信用户。
当前接口仍用于微信资料完善场景。
security:
- bearerAuth: []
requestBody:
required: true
content:
@@ -474,3 +604,187 @@ paths:
description: token 无效或当前 auth record 缺少统一身份字段 openid
'400':
description: 参数错误、手机号已被注册或资料更新失败
/api/dictionary/list:
post:
tags: [字典管理]
summary: 查询字典列表
description: |
仅允许 `ManagePlatform` 用户访问。
支持按 `dict_name` 模糊搜索,返回字典全量信息,并将三个聚合字段组装为 `items`。
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/DictionaryListRequest'
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/DictionaryRecord'
'401':
description: token 无效或已过期
'403':
description: 非 ManagePlatform 用户无权访问
'415':
description: 请求体必须为 application/json
/api/dictionary/detail:
post:
tags: [字典管理]
summary: 查询指定字典
description: |
仅允许 `ManagePlatform` 用户访问。
按唯一键 `dict_name` 查询单条字典,并返回组装后的 `items`。
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DictionaryDetailRequest'
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/DictionaryRecord'
'400':
description: 参数错误
'401':
description: token 无效或已过期
'403':
description: 非 ManagePlatform 用户无权访问
'404':
description: 未找到对应字典
'415':
description: 请求体必须为 application/json
/api/dictionary/create:
post:
tags: [字典管理]
summary: 新增字典
description: |
仅允许 `ManagePlatform` 用户访问。
`system_dict_id` 由服务端自动生成;`dict_name` 必须唯一;
`items` 会分别序列化写入 `dict_word_enum`、`dict_word_description`、`dict_word_sort_order` 三个字段。
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DictionaryMutationRequest'
responses:
'200':
description: 新增成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/DictionaryRecord'
'400':
description: 参数错误或 dict_name 已存在
'401':
description: token 无效或已过期
'403':
description: 非 ManagePlatform 用户无权访问
'415':
description: 请求体必须为 application/json
'429':
description: 重复请求过于频繁
/api/dictionary/update:
post:
tags: [字典管理]
summary: 修改字典
description: |
仅允许 `ManagePlatform` 用户访问。
根据 `original_dict_name`(未传时回退为 `dict_name`)定位原记录并更新。
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DictionaryMutationRequest'
responses:
'200':
description: 修改成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/DictionaryRecord'
'400':
description: 参数错误或 dict_name 冲突
'401':
description: token 无效或已过期
'403':
description: 非 ManagePlatform 用户无权访问
'404':
description: 未找到待修改字典
'415':
description: 请求体必须为 application/json
'429':
description: 重复请求过于频繁
/api/dictionary/delete:
post:
tags: [字典管理]
summary: 删除字典
description: |
仅允许 `ManagePlatform` 用户访问。
按 `dict_name` 真删除对应记录。
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DictionaryDeleteRequest'
responses:
'200':
description: 删除成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
type: object
properties:
dict_name:
type: string
'400':
description: 参数错误或删除失败
'401':
description: token 无效或已过期
'403':
description: 非 ManagePlatform 用户无权访问
'404':
description: 未找到待删除字典
'415':
description: 请求体必须为 application/json
'429':
description: 重复请求过于频繁