- 新增 openapi-miniapp-company.yaml 文件,定义 tbl_company 的基础 CRUD 接口文档,包括查询、创建、更新和删除公司记录的详细描述和示例。 - 新增 pocketbase.file-fields-to-attachments.js 脚本,用于迁移 PocketBase 中的文件字段到文本字段,并处理 tbl_attachments 集合的公开规则。
16 KiB
OpenSpec 变更记录:PocketBase Hooks 认证链路加固
日期
- 2026-03-23
范围
本次变更覆盖 pocket-base/ 下 PocketBase hooks 项目的微信登录、平台注册/登录、资料更新、token 刷新、认证落库、错误可观测性、索引策略、字典管理、附件管理、文档管理、文档操作历史、页面辅助操作、健康检查版本探针、OpenAPI 鉴权与统一响应规范。
一、认证模型调整
1. 认证体系
- 保持 PocketBase 原生 auth token 作为唯一正式认证令牌。
- 登录与刷新响应统一为项目标准结构:
code、msg、data,认证成功时额外返回顶层token。 authMethod统一使用空字符串'',避免触发不必要的 MFA / login alerts 校验。
2. Header 规则
- 正式认证 Header 为:
Authorization: Bearer <token> - 非标准 Header
Open-Authorization不属于本项目接口定义。 users_wx_openidHeader 已从 active hooks 鉴权链路移除。
二、身份字段与数据模型约束
1. openid 作为唯一业务身份锚点
tbl_auth_users统一保留openid作为全平台身份锚点。- 微信用户:
openid = 微信 openid - 平台用户:
openid = 服务端生成的 GUID - 业务逻辑中不再使用
users_wx_openid。 - 用户查询、token 刷新、资料更新均基于 auth record 的
openid。
2. auth 集合兼容字段
由于 tbl_auth_users 当前为 PocketBase auth 集合,登录注册时为兼容 PocketBase 原生 auth 校验,新增以下兼容策略:
email使用占位格式:- 微信用户:
<openid>@wechat.local - 平台用户:
<openid>@manage.local
- 微信用户:
- 自动生成随机密码
- 自动补齐
passwordConfirm
说明:
- 占位
email仅用于满足 auth 集合保存条件,不代表用户真实邮箱。 - 业务主身份仍然是
openid。
3. 自定义字段可空策略
tbl_auth_users的自定义字段目标约束为:除openid外,其余业务字段均允许为空。- 已将 schema 脚本中的
user_id改为非必填。 - 其余业务字段保持非必填。
4. 用户图片字段统一改为附件 ID 语义
users_pictureusers_id_pic_ausers_id_pic_busers_title_picture
以上字段已统一改为保存 tbl_attachments.attachments_id,不再直接保存 PocketBase file 字段或外部图片 URL。
查询用户信息时,hooks 会自动联查 tbl_attachments 并补充:
users_picture_urlusers_id_pic_a_urlusers_id_pic_b_urlusers_title_picture_url
说明:
tbl_attachments仍由附件表保存实际文件本体;- 业务表仅负责保存附件 ID;
- hooks 中原有
ManagePlatform访问限制保持不变。
三、查询与排序修复
1. 移除无意义的 created 排序
在 hooks 查询中,以下查询原先使用 '-created' 排序:
- 按
openid查询用户 - 按
company_id查询公司 - 按
users_phone查询重复手机号
该写法在 PocketBase 当前运行场景下触发:
invalid sort field "created"
现已统一移除排序参数,改为空排序字符串,因为这些查询本质上均为精确匹配或去重检查,不依赖排序。
四、错误可观测性增强
1. 登录路由显式错误响应
POST /pb/api/wechat/login 新增局部 try/catch:
- 保留业务状态码
- 返回
{ code, msg, data } - 写入
logger.error('微信登录失败', ...)
2. 全局错误包装顺序修正
routerUse(...)全局错误包装提前到路由注册前。- 统一兼容
err.statusCode/err.status。
3. auth 保存失败透传
新增 saveAuthUserRecord(record) 包装 $app.save(record):
- 失败时统一抛出
保存认证用户失败 - 附带
originalMessage与originalData
目的:
- 避免 PocketBase 默认
Something went wrong while processing your request.吞掉具体原因。
五、数据库索引策略修复
1. users_phone 索引调整
原设计:
users_phone唯一索引
问题:
- 新用户注册阶段手机号为空,多个空值会触发唯一约束冲突,导致注册失败。
现调整为:
users_phone普通索引
说明:
- 手机号唯一性改由业务逻辑在资料完善阶段校验。
- 允许多个未完善资料用户以空手机号存在。
六、接口契约同步结果
当前 active PocketBase hooks 契约如下:
POST /pb/api/system/test-helloworldPOST /pb/api/system/healthPOST /pb/api/system/refresh-tokenPOST /pb/api/platform/registerPOST /pb/api/platform/loginPOST /pb/api/wechat/loginPOST /pb/api/wechat/profilePOST /pb/api/dictionary/listPOST /pb/api/dictionary/detailPOST /pb/api/dictionary/createPOST /pb/api/dictionary/updatePOST /pb/api/dictionary/deletePOST /pb/api/attachment/listPOST /pb/api/attachment/detailPOST /pb/api/attachment/uploadPOST /pb/api/attachment/deletePOST /pb/api/document/listPOST /pb/api/document/detailPOST /pb/api/document/createPOST /pb/api/document/updatePOST /pb/api/document/deletePOST /pb/api/document-history/list
其中平台用户链路补充为:
POST /pb/api/platform/register
- body 必填:
users_name、users_phone、password、passwordConfirm、users_picture - 自动生成 GUID 并写入统一身份字段
openid - 写入
users_idtype = ManagePlatform - 成功时返回统一结构:
code、msg、data,并在顶层额外返回token
POST /pb/api/platform/login
- body 必填:
login_account、password - 仅允许
users_idtype = ManagePlatform - 前端使用邮箱或手机号 + 密码提交
- 服务端先通过 PocketBase
auth-with-password校验身份,再由当前 hooks 进程签发正式 token - 成功时返回统一结构:
code、msg、data,并在顶层额外返回token
其中:
POST /pb/api/wechat/login
- body 必填:
users_wx_code - 自动以微信 code 换取微信侧
openid并写入统一身份字段 - 若不存在 auth 用户则尝试创建
tbl_auth_users记录 - 写入
users_idtype = WeChat - 成功时返回统一结构:
code、msg、data,并在顶层额外返回token
POST /pb/api/wechat/profile
- 需
Authorization - 基于当前 auth record 的
openid定位用户 - 服务端用
users_phone_code换取手机号后保存
POST /pb/api/system/refresh-token
- body 可选:
users_wx_code(允许为空) Authorization可选:- 若 token 仍有效:基于当前 auth record 续签
- 若 token 已过期:回退到微信 code 重签流程
- 若 token 已过期且未提供
users_wx_code,返回:token已过期,请上传users_wx_code - 返回统一结构:
code、msg、data,并在顶层额外返回新token - 属于系统级通用认证能力,不限定为微信专属接口
字典管理接口
新增 dictionary 分类接口,统一要求平台管理用户访问:
POST /pb/api/dictionary/list- 支持按
dict_name模糊搜索 - 返回字典全量信息,并将
dict_word_enum、dict_word_description、dict_word_image、dict_word_sort_order组装为items
- 支持按
POST /pb/api/dictionary/detail- 按
dict_name查询单条字典
- 按
POST /pb/api/dictionary/create- 新增字典,
system_dict_id自动生成,dict_name唯一
- 新增字典,
POST /pb/api/dictionary/update- 按
original_dict_name/dict_name更新字典
- 按
POST /pb/api/dictionary/delete- 按
dict_name真删除字典
- 按
说明:
dict_word_enum、dict_word_description、dict_word_image、dict_word_sort_order已统一改为 JSON 字符串持久化,其中dict_word_sort_order已改为text类型。- 字典项图片需先调用
/pb/api/attachment/upload上传,再把返回的attachments_id写入dict_word_image对应位置。 - 查询时统一聚合为:
items: [{ enum, description, image, imageUrl, imageAttachment, sortOrder }]
附件管理接口
新增 attachment 分类接口,统一要求平台管理用户访问:
POST /pb/api/attachment/list- 支持按
attachments_id、attachments_filename模糊搜索 - 支持按
attachments_status过滤 - 返回附件元数据以及 PocketBase 文件流链接
attachments_url
- 支持按
POST /pb/api/attachment/detail- 按
attachments_id查询单个附件 - 返回文件流链接与下载链接
- 按
POST /pb/api/attachment/upload- 使用
multipart/form-data - 文件字段固定为
attachments_link - 上传成功后自动生成
attachments_id - 自动写入
attachments_owner = 当前用户 openid
- 使用
POST /pb/api/attachment/delete- 按
attachments_id真删除附件 - 若该附件已被
tbl_document.document_image、document_video或document_file中的任一附件列表引用,则拒绝删除
- 按
说明:
tbl_attachments.attachments_link为 PocketBasefile字段,保存实际文件本体。- 对外查询时会额外补充:
attachments_urlattachments_download_url
文档管理接口
新增 document 分类接口,统一要求平台管理用户访问:
POST /pb/api/document/list- 支持按
document_id、document_title、document_subtitle、document_summary、document_keywords模糊搜索 - 支持按
document_status、document_type过滤 - 返回时会自动联查
tbl_attachments - 额外补充:
document_image_urlsdocument_video_urlsdocument_file_urlsdocument_image_attachmentsdocument_video_attachmentsdocument_file_attachments
- 支持按
POST /pb/api/document/detail- 按
document_id查询单条文档 - 返回与附件表联动解析后的多文件流链接
- 按
POST /pb/api/document/create- 新增文档
document_id可不传,由服务端自动生成document_title、document_type为必填;其余字段均允许为空document_image、document_video、document_file支持传入多个已存在的attachments_iddocument_type前端从单个字典来源中多选枚举值,最终按system_dict_id@dict_word_enum|...保存document_keywords、document_product_categories、document_application_scenarios、document_hotel_type统一从固定字典多选并按|保存- 其中
document_product_categories改为从文档-产品关联文档读取,document_application_scenarios改为从文档-筛选依据读取,document_hotel_type改为从文档-适用场景读取 document_status仅保留有效/过期两种状态,并由生效日期与到期日期自动计算- 成功后会写入一条文档操作历史,类型为
create
POST /pb/api/document/update- 按
document_id更新文档 document_title、document_type为必填;其余字段均允许为空- 若传入附件字段,则会校验多个
attachments_id是否都存在 - 多选字段的持久化格式与新增接口一致
- 成功后会写入一条文档操作历史,类型为
update
- 按
POST /pb/api/document/delete- 按
document_id真删除文档 - 删除前会写入一条文档操作历史,类型为
delete
- 按
说明:
document_image、document_video、document_file当前保存的是多个attachments_id,底层以|分隔文本持久化,不是 PocketBase 文件字段。- 文档查询时通过
attachments_id -> tbl_attachments反查实际文件,并返回可直接访问的数据流链接数组。 document_owner语义为“上传者 openid”。
文档操作历史接口
新增 document-history 分类接口,统一要求平台管理用户访问:
POST /pb/api/document-history/list- 不传
document_id时返回全部文档历史 - 传入
document_id时仅返回该文档历史 - 结果按创建时间倒序排列
- 不传
说明:
- 操作历史表为
tbl_document_operation_history - 当前由文档新增、修改、删除接口自动写入
- 主要字段为:
doh_document_iddoh_operation_typedoh_user_iddoh_current_countdoh_remark
七、页面与运维辅助能力新增
1. PocketBase 页面
当前页面入口:
/pb/manage/pb/manage/login/pb/manage/dictionary-manage/pb/manage/document-manage
页面能力:
- 首页支持跳转到子页面
- 字典管理页支持:
- Bearer Token 粘贴与本地保存
dict_name模糊搜索- 指定字典查询
- 行内编辑基础字段
- 弹窗编辑枚举项
- 为每个枚举项单独上传图片,并保存对应
attachments_id - 回显字典项图片缩略图与文件流链接
- 新增 / 删除字典
- 返回主页
- 文档管理页支持:
- 先上传附件到
tbl_attachments - 再把返回的多个
attachments_id写入tbl_document.document_image/document_video/document_file - 图片、视频、文件都支持多选上传
- 新增文档
- 编辑已有文档并回显多图片、多视频
- 从文档中移除附件并在保存后删除对应附件记录
- 查询文档列表
- 直接展示 PocketBase 文件流链接
- 删除文档
- 先上传附件到
说明:
- 原页面
page-b.js已替换为document-manage.js - 页面实际走的接口链路为:
/pb/api/attachment/upload/pb/api/document/create/pb/api/document/update/pb/api/attachment/delete/pb/api/document/list/pb/api/document/delete
2. 健康检查版本探针
POST /pb/api/system/health 新增:
data.version
用途:
- 通过修改
APP_VERSION判断 hooks 是否已成功部署并生效
配置来源:
- 进程环境变量
APP_VERSION - 或
runtime.js
八、OpenAPI 与 Apifox 调试策略调整
1. 统一返回结构
所有对外接口统一返回:
codemsgdata
认证成功类接口额外返回:
token
不再返回以下顶层字段:
recordmeta
2. 鉴权文档策略
OpenAPI 文档中已取消 bearerAuth 鉴权组件与接口级重复 Authorization 参数。
统一约定:
- 在 Apifox 环境中配置全局 Header:
Authorization: Bearer {{token}} - 不再依赖文档中的 Bearer 组件自动注入
此举目的是:
- 避免接口页重复出现局部
Authorization - 统一依赖环境变量完成鉴权注入
九、当前已知边界
tbl_auth_users为 PocketBaseauth集合,因此仍受 PocketBase auth 内置规则影响。- 自定义字段“除 openid 外均可空”已在脚本层按目标放宽,但 auth 集合结构更新仍可能触发 PocketBase 服务端限制。
- 若线上仍返回 PocketBase 默认 400,需要确保最新 hooks 已部署并重启生效。
- 平台登录通过回源 PocketBase REST 完成密码校验,因此
POCKETBASE_API_URL必须配置为 PocketBase 进程/容器内部可达地址,不应使用外部 HTTPS 域名。 - Apifox 环境中需自行维护全局 Header:
Authorization: Bearer {{token}},否则鉴权接口不会自动携带 token。
十、归档建议
部署时至少同步以下文件:
pocket-base/bai-api-main.pb.jspocket-base/bai-web-main.pb.jspocket-base/bai_api_pb_hooks/pocket-base/bai_web_pb_hooks/pocket-base/spec/openapi.yamlscript/pocketbase.js
并在 PocketBase 环境中执行 schema 同步后重启服务,再进行接口验证。
建议归档后的发布核验顺序:
POST /pb/api/system/health:确认data.versionPOST /pb/api/platform/login:确认返回统一结构与顶层tokenPOST /pb/api/dictionary/list:确认鉴权与字典接口可用
十一、归档状态
- 已将本轮字典管理、PocketBase 页面、统一响应、平台登录兼容修复、健康检查版本探针、OpenAPI/Apifox 鉴权策略调整并入本变更记录。
- 本记录可作为当前 PocketBase hooks 阶段性归档基线继续维护。