feat: 完善微信认证功能,新增用户资料更新与token刷新接口
- 新增 userService.js,包含用户认证、资料更新、token 刷新等功能 - 新增 wechatService.js,处理微信API交互,获取openid和手机号 - 新增 appError.js,封装应用错误处理 - 新增 logger.js,提供日志记录功能 - 新增 response.js,统一成功响应格式 - 新增 sanitize.js,提供输入数据清洗功能 - 更新 OpenAPI 文档,描述新增接口及请求响应格式 - 更新 PocketBase 数据库结构,调整用户表字段及索引策略 - 增强错误处理机制,确保错误信息可观测性 - 更新变更记录文档,详细记录本次变更内容
This commit is contained in:
114
docs/ARCHIVE.md
114
docs/ARCHIVE.md
@@ -2,6 +2,120 @@
|
||||
|
||||
## 归档日期
|
||||
|
||||
- 2026-03-23
|
||||
|
||||
## 归档范围
|
||||
|
||||
本次归档覆盖 PocketBase hooks 项目在微信登录注册、PocketBase 原生 token、openid 身份收敛、错误观测、索引修复与 auth 兼容字段方面的修复与规范同步,涉及:
|
||||
|
||||
- `pocket-base/` 作为正式 hooks 项目继续收敛规范
|
||||
- 微信登录链路错误显式返回
|
||||
- `recordAuthResponse` 使用空 `authMethod`
|
||||
- 登录/资料更新阶段 auth 保存失败信息透传
|
||||
- 移除 hooks 查询中的 `-created` 排序,修复 `invalid sort field "created"`
|
||||
- `users_phone` 唯一索引改普通索引,允许空手机号用户注册
|
||||
- `tbl_auth_users` 继续以 `openid` 作为业务身份锚点
|
||||
- 为 PocketBase `auth` 集合兼容写入占位 `email`、随机密码与 `passwordConfirm`
|
||||
- OpenSpec 变更文档补录到 `pocket-base/spec/`
|
||||
|
||||
---
|
||||
|
||||
## 一、接口与认证结果
|
||||
|
||||
### 当前 active hooks 接口
|
||||
|
||||
- `POST /api/system/test-helloworld`
|
||||
- `POST /api/system/health`
|
||||
- `POST /api/wechat/login`
|
||||
- `POST /api/wechat/profile`
|
||||
- `POST /api/wechat/refresh-token`
|
||||
|
||||
### 当前认证规则
|
||||
|
||||
- 正式鉴权仅使用 `Authorization: Bearer <token>`
|
||||
- `Open-Authorization` 不属于接口契约
|
||||
- `users_wx_openid` Header 已移除
|
||||
- 业务身份由当前 auth record 的 `openid` 唯一确定
|
||||
|
||||
---
|
||||
|
||||
## 二、数据模型与落库策略
|
||||
|
||||
### 1. `tbl_auth_users`
|
||||
|
||||
- 维持 PocketBase `auth` 集合
|
||||
- 业务身份锚点为 `openid`
|
||||
- 目标规则:除 `openid` 外,自定义业务字段均允许为空
|
||||
|
||||
### 2. auth 集合兼容字段
|
||||
|
||||
由于 `tbl_auth_users` 是 auth 集合,为满足 PocketBase 原生 auth 保存要求,登录创建阶段补充:
|
||||
|
||||
- `email = <openid>@wechat.local`
|
||||
- 随机密码
|
||||
- `passwordConfirm`
|
||||
|
||||
说明:
|
||||
|
||||
- 上述 `email` 为占位认证标识,不代表真实邮箱。
|
||||
|
||||
---
|
||||
|
||||
## 三、问题修复归档
|
||||
|
||||
### 1. 通用 400 `Something went wrong while processing your request.`
|
||||
|
||||
处理方式:
|
||||
|
||||
- 登录路由本地 try/catch 显式返回 `{ code, msg, data }`
|
||||
- 全局错误包装提前注册
|
||||
- 保存 auth 用户时透传原始错误信息
|
||||
|
||||
### 2. `invalid sort field "created"`
|
||||
|
||||
原因:
|
||||
|
||||
- hooks 内多个精确查询误用了 `-created` 排序
|
||||
|
||||
处理方式:
|
||||
|
||||
- 统一移除 active hooks 中所有 `-created` 排序
|
||||
|
||||
### 3. 注册成功前数据库无新记录
|
||||
|
||||
已识别的高风险点:
|
||||
|
||||
- `users_phone` 唯一索引会让空手机号重复冲突
|
||||
|
||||
处理方式:
|
||||
|
||||
- 改为普通索引
|
||||
- 手机号唯一性改由资料完善阶段业务校验负责
|
||||
|
||||
---
|
||||
|
||||
## 四、规范同步位置
|
||||
|
||||
本次 OpenSpec 记录新增:
|
||||
|
||||
- `pocket-base/spec/changes.2026-03-23-pocketbase-hooks-auth-hardening.md`
|
||||
|
||||
当前 active 契约文件:
|
||||
|
||||
- `pocket-base/spec/openapi.yaml`
|
||||
|
||||
---
|
||||
|
||||
## 五、当前边界
|
||||
|
||||
1. `tbl_auth_users` 作为 PocketBase `auth` 集合,仍受 PocketBase 内置 auth 规则影响。
|
||||
2. schema 脚本放宽自定义字段必填约束时,PocketBase 服务端更新 auth 集合可能返回通用 500,需要结合服务端日志进一步确认。
|
||||
3. 若线上仍报旧错误,通常表示最新 hooks 或 schema 尚未部署生效。
|
||||
|
||||
---
|
||||
|
||||
## 归档日期
|
||||
|
||||
- 2026-03-20
|
||||
|
||||
## 归档范围
|
||||
|
||||
17
docs/api.md
17
docs/api.md
@@ -104,11 +104,11 @@
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `users_wx_code` | string | 是 | 微信小程序登录临时凭证 code,用于换取 `users_wx_openid` |
|
||||
| `users_wx_code` | string | 是 | 微信小程序登录临时凭证 code,用于换取 `openid` |
|
||||
|
||||
### 处理逻辑
|
||||
|
||||
- 使用 `users_wx_code` 向微信服务端换取 `users_wx_openid`
|
||||
- 使用 `users_wx_code` 向微信服务端换取 `openid`
|
||||
- 如果数据库中不存在该用户,则自动创建新账号:
|
||||
- 初始化 `users_type = 游客`
|
||||
- 如果数据库中已存在该用户,则直接登录
|
||||
@@ -135,7 +135,7 @@
|
||||
"users_phone": "13800138000",
|
||||
"users_phone_masked": "138****8000",
|
||||
"users_picture": "https://example.com/avatar.png",
|
||||
"users_wx_openid": "oAbCdEfGh123456789",
|
||||
"openid": "oAbCdEfGh123456789",
|
||||
"company_id": "C10001",
|
||||
"company": null,
|
||||
"pb_id": "abc123xyz",
|
||||
@@ -154,7 +154,6 @@
|
||||
- **请求方式**:`POST`
|
||||
- **请求头**:
|
||||
- `Content-Type: application/json`
|
||||
- `users_wx_openid: 微信用户唯一标识`
|
||||
- `Authorization: Bearer <token>`
|
||||
|
||||
### 请求参数
|
||||
@@ -177,7 +176,7 @@
|
||||
|
||||
### 处理逻辑
|
||||
|
||||
- 从请求头 `users_wx_openid` 读取当前用户身份
|
||||
- 从 `Authorization` 对应的 PocketBase auth record 读取当前用户 `openid`
|
||||
- 校验 `Authorization`
|
||||
- 不再从 body 读取 `users_wx_code`
|
||||
- 使用 `users_phone_code` 调微信官方接口换取真实手机号
|
||||
@@ -200,7 +199,7 @@
|
||||
"users_phone": "13800138000",
|
||||
"users_phone_masked": "138****8000",
|
||||
"users_picture": "https://example.com/avatar.png",
|
||||
"users_wx_openid": "oAbCdEfGh123456789",
|
||||
"openid": "oAbCdEfGh123456789",
|
||||
"company_id": "",
|
||||
"company": null,
|
||||
"pb_id": "abc123xyz",
|
||||
@@ -218,7 +217,7 @@
|
||||
- **接口地址**:`/wechat/refresh-token`
|
||||
- **请求方式**:`POST`
|
||||
- **请求头**:
|
||||
- `users_wx_openid: 微信用户唯一标识`
|
||||
- `Authorization: Bearer <token>`
|
||||
|
||||
> 说明:本接口**不要求旧 `Authorization`**。
|
||||
|
||||
@@ -228,7 +227,7 @@
|
||||
|
||||
### 处理逻辑
|
||||
|
||||
- 仅通过请求头中的 `users_wx_openid` 定位用户
|
||||
- 仅通过当前 `Authorization` 对应认证用户定位身份
|
||||
- 若用户存在,则签发新的 JWT token
|
||||
- 若用户不存在,则返回 `404`
|
||||
|
||||
@@ -279,7 +278,7 @@ Authorization: Bearer <token>
|
||||
### 3. `refresh-token` 接口当前只需要:
|
||||
|
||||
```http
|
||||
users_wx_openid: <openid>
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
不需要旧 `Authorization`。
|
||||
|
||||
95
docs/newpb.md
Normal file
95
docs/newpb.md
Normal file
@@ -0,0 +1,95 @@
|
||||
针对你的复杂权限需求,建议采用 **“RBAC(基于角色的访问控制)+ ABAC(基于属性的访问控制)+ 个性化覆盖(Overrides)”** 的混合模式。这种结构能支持从“大颗粒度角色”到“极细颗粒度字段/行”的动态权限矩阵。
|
||||
|
||||
以下是为你设计的实施方案及关键表结构规划:
|
||||
|
||||
### 一、 核心表方案设计
|
||||
|
||||
为了实现“字段级”和“行级”的动态控制,我们需要将 **资源(Resource)**、**权限定义(Permission)**、**角色(Role)** 与 **用户(User)** 彻底解耦。
|
||||
|
||||
#### 1. 用户基础表:`tbl_auth_users`
|
||||
作为全局身份锚点,记录用户的静态信息和动态属性(部门、等级)。
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **user_id** | BigInt (PK) | 内部全局唯一 ID |
|
||||
| **openid** | String (Unique) | **全局身份锚点**,微信唯一标识 |
|
||||
| **user_name** | String | 姓名/昵称 |
|
||||
| **org_id** | Int | 所属组织/部门 ID(影响行级权限的关键属性) |
|
||||
| **rank_level** | Int | 职级/等级(影响动态权限矩阵的关键属性) |
|
||||
| **status** | Int | 账户状态 (1: 正常, 0: 禁用) |
|
||||
| **user_type** | Int | 账户类型 (0: 微信小程序,1: 管理平台,2: 其他) |
|
||||
|
||||
---
|
||||
|
||||
#### 2. 资源定义表:`tbl_auth_resources`
|
||||
定义系统中哪些表、哪些字段属于受控资源。
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **res_id** | Int (PK) | 资源 ID |
|
||||
| **table_name** | String | 数据库表名 |
|
||||
| **column_name** | String | 字段名(如果是表级权限,此项可为空或设为 '*') |
|
||||
| **res_type** | Enum | 资源类型:`TABLE`(行/全表), `COLUMN`(字段级) |
|
||||
|
||||
---
|
||||
|
||||
#### 3. 角色与基础权限:`tbl_auth_roles` & `tbl_auth_role_perms`
|
||||
实现通用的权限模板,方便批量管理。
|
||||
|
||||
* **`tbl_auth_roles`**: 角色表(如:财务经理、普通销售)。
|
||||
* **`tbl_auth_role_perms`**: 角色权限关联表,定义该角色对某个资源的操作(读/写/无)。
|
||||
|
||||
---
|
||||
|
||||
#### 4. **核心:个性化权限覆盖表 `tbl_auth_user_overrides`**
|
||||
这是满足你“某个用户对某个字段单独设置”需求的关键。
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **id** | BigInt (PK) | 自增 ID |
|
||||
| **user_id** | BigInt | 用户 ID(关联 `tbl_auth_users`) |
|
||||
| **res_id** | Int | 资源 ID(关联 `tbl_auth_resources`) |
|
||||
| **access_level** | Int | 权限值 (0: 无权, 1: 只读, 2: 读写) |
|
||||
| **priority** | Int | 优先级(当角色权限与个人设置冲突时,以此为准) |
|
||||
|
||||
---
|
||||
|
||||
#### 5. **核心:行级过滤策略表 `tbl_auth_row_scopes`**
|
||||
实现“某个用户只能看自己部门/某几个项目”的动态逻辑。
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **id** | Int (PK) | 策略 ID |
|
||||
| **target_type** | Enum | 目标:`USER` 或 `ROLE` |
|
||||
| **target_id** | BigInt | 对应的 UserID 或 RoleID |
|
||||
| **table_name** | String | 作用的表名 |
|
||||
| **filter_sql** | String | 过滤逻辑。例如:`dept_id = {user.org_id}` 或 `creator_id = {user.user_id}` |
|
||||
|
||||
---
|
||||
|
||||
### 二、 权限实施方案逻辑
|
||||
|
||||
你的权限矩阵页面将由以下逻辑驱动:
|
||||
|
||||
#### 1. 权限计算路径 (Effective Permissions)
|
||||
当用户访问某个数据时,系统按照以下顺序合并权限:
|
||||
1. **取基础属性**:获取用户的 `org_id` 和 `rank_level`。
|
||||
2. **取角色权限**:获取该用户所属角色对应的资源权限列表。
|
||||
3. **应用个性化覆盖**:查询 `tbl_auth_user_overrides`。如果该表中有记录,则**覆盖**(或叠加)角色权限。
|
||||
4. **注入行级过滤**:如果是查询操作,解析 `tbl_auth_row_scopes` 中的 `filter_sql`,将 `{user.xxx}` 变量替换为当前用户的真实值。
|
||||
|
||||
#### 2. 动态更新机制
|
||||
* **组织/等级变更**:当 `tbl_auth_users` 中的 `org_id` 或 `rank_level` 变化时,由于行级过滤表(`tbl_auth_row_scopes`)引用的是动态变量,**权限会自动生效**,无需重新授权。
|
||||
* **缓存策略**:建议将计算后的“最终权限清单”缓存到 Redis。当用户在后台矩阵页面修改权限,或者发生组织架构调整时,通过 `wx_openid` **主动失效(Purge)** 该用户的 Redis 缓存。
|
||||
|
||||
#### 3. 字段级权限实现 (Field-Level)
|
||||
在接口层(中间件),根据计算出的字段级权限清单(即用户对该 Table 下哪些 Column 有读权),动态过滤返回的 JSON 结构。
|
||||
|
||||
### 三、 总结:你需要几张表?
|
||||
|
||||
为了实现你描述的系统,最精简需要 **5 张表**:
|
||||
1. **`tbl_auth_users`**:用户主体(含 OpenID、部门、等级)。
|
||||
2. **`tbl_auth_resources`**:资源清单(表名、字段名)。
|
||||
3. **`tbl_auth_roles`**:角色定义。
|
||||
4. **`tbl_auth_role_perms`** / **`tbl_auth_user_overrides`**:权限映射(解决字段级和个人特权)。
|
||||
5. **`tbl_auth_row_scopes`**:行级过滤表达式(解决多维数据隔离)。
|
||||
Reference in New Issue
Block a user