# OpenSpec 变更记录:PocketBase Hooks 认证链路加固 ## 日期 - 2026-03-23 ## 范围 本次变更覆盖 `pocket-base/` 下 PocketBase hooks 项目的微信登录、资料更新、token 刷新、认证落库、错误可观测性、索引策略与运行规范。 --- ## 一、认证模型调整 ### 1. 认证体系 - 保持 PocketBase 原生 auth token 作为唯一正式认证令牌。 - 登录与刷新响应统一通过 `$apis.recordAuthResponse(...)` 返回 PocketBase 原生认证结果。 - `authMethod` 统一使用空字符串 `''`,避免触发不必要的 MFA / login alerts 校验。 ### 2. Header 规则 - 正式认证 Header 为:`Authorization: Bearer ` - 非标准 Header `Open-Authorization` 不属于本项目接口定义。 - `users_wx_openid` Header 已从 active hooks 鉴权链路移除。 --- ## 二、身份字段与数据模型约束 ### 1. openid 作为唯一业务身份锚点 - `tbl_auth_users` 仅保留 `openid` 作为微信身份锚点。 - 业务逻辑中不再使用 `users_wx_openid`。 - 用户查询、token 刷新、资料更新均基于 auth record 的 `openid`。 ### 2. auth 集合兼容字段 由于 `tbl_auth_users` 当前为 PocketBase `auth` 集合,登录注册时为兼容 PocketBase 原生 auth 校验,新增以下兼容策略: - `email` 使用占位格式:`@wechat.local` - 自动生成随机密码 - 自动补齐 `passwordConfirm` 说明: - 占位 `email` 仅用于满足 auth 集合保存条件,不代表用户真实邮箱。 - 业务主身份仍然是 `openid`。 ### 3. 自定义字段可空策略 - `tbl_auth_users` 的自定义字段目标约束为:除 `openid` 外,其余业务字段均允许为空。 - 已将 schema 脚本中的 `user_id` 改为非必填。 - 其余业务字段保持非必填。 --- ## 三、查询与排序修复 ### 1. 移除无意义的 `created` 排序 在 hooks 查询中,以下查询原先使用 `'-created'` 排序: - 按 `openid` 查询用户 - 按 `company_id` 查询公司 - 按 `users_phone` 查询重复手机号 该写法在 PocketBase 当前运行场景下触发: - `invalid sort field "created"` 现已统一移除排序参数,改为空排序字符串,因为这些查询本质上均为精确匹配或去重检查,不依赖排序。 --- ## 四、错误可观测性增强 ### 1. 登录路由显式错误响应 `POST /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 /api/system/test-helloworld` - `POST /api/system/health` - `POST /api/wechat/login` - `POST /api/wechat/profile` - `POST /api/wechat/refresh-token` 其中: ### `POST /api/wechat/login` - body 必填:`users_wx_code` - 自动以微信 code 换取 `openid` - 若不存在 auth 用户则尝试创建 `tbl_auth_users` 记录 - 成功时返回 PocketBase 原生 token + auth record + meta ### `POST /api/wechat/profile` - 需 `Authorization` - 基于当前 auth record 的 `openid` 定位用户 - 服务端用 `users_phone_code` 换取手机号后保存 ### `POST /api/wechat/refresh-token` - 需 `Authorization` - 直接基于当前 auth record 返回新的 PocketBase 原生 token --- ## 七、当前已知边界 1. `tbl_auth_users` 为 PocketBase `auth` 集合,因此仍受 PocketBase auth 内置规则影响。 2. 自定义字段“除 openid 外均可空”已在脚本层按目标放宽,但 auth 集合结构更新仍可能触发 PocketBase 服务端限制。 3. 若线上仍返回 PocketBase 默认 400,需要确保最新 hooks 已部署并重启生效。 --- ## 八、归档建议 部署时至少同步以下文件: - `pocket-base/bai-api-main.pb.js` - `pocket-base/bai_api_pb_hooks/` - `script/pocketbase.newpb.js` 并在 PocketBase 环境中执行 schema 同步后重启服务,再进行接口验证。