Browse Source
Document the design for a mixed global/user configuration system with request-context read helpers and service/API-only writes, including boundaries for defineWrappedResponseHandler responsibilities. Made-with: Cursorfeat/auth-access-control
1 changed files with 190 additions and 0 deletions
@ -0,0 +1,190 @@ |
|||
# 全局配置与用户配置模块设计文档 |
|||
|
|||
日期:2026-04-16 |
|||
范围:实现全局配置与当前登录用户配置的读写能力(写走 service + API,读走快捷函数) |
|||
目标:在不破坏现有请求处理结构的前提下,提供统一、可扩展、可维护的配置能力 |
|||
|
|||
## 1. 需求边界 |
|||
|
|||
### 1.1 必做功能 |
|||
|
|||
- 支持全局配置读写 |
|||
- 支持当前登录用户私有配置读写 |
|||
- 读取提供快捷函数,支持优先级合并(用户 > 全局 > 默认) |
|||
- 写入仅允许通过 service + API |
|||
- 在请求上下文中提供只读配置门面,便于 API 快捷读取 |
|||
|
|||
### 1.2 明确不做 |
|||
|
|||
- 跨用户配置代写(如普通接口直接写任意 userId) |
|||
- 复杂配置权限系统(本期仅预留管理员写全局入口) |
|||
- 进程级缓存、分布式缓存和缓存失效广播 |
|||
- 配置版本历史与审计回溯 |
|||
|
|||
## 2. 总体架构 |
|||
|
|||
模块分为四层: |
|||
|
|||
1. **Schema / Migration** |
|||
- 存储全局配置和用户配置数据。 |
|||
2. **Config Registry** |
|||
- 定义配置键、默认值、作用域、值类型、是否允许用户覆盖。 |
|||
3. **Config Service** |
|||
- 提供校验、读取、合并、写入能力。 |
|||
4. **Request Context Facade** |
|||
- 在 `event.context` 上暴露只读配置快捷函数。 |
|||
|
|||
### 2.1 关于 `defineWrappedResponseHandler` 的定位 |
|||
|
|||
- `defineWrappedResponseHandler` 应保持“响应包装 + 统一错误处理”为主职责。 |
|||
- 配置门面的初始化逻辑优先放到 Nitro plugin(请求生命周期)中。 |
|||
- `defineWrappedResponseHandler` 仅做轻介入(例如类型收敛或兜底检查),不承载复杂装配逻辑。 |
|||
|
|||
该方案可保留快捷读取体验,同时避免 handler 职责膨胀。 |
|||
|
|||
## 3. 数据模型设计 |
|||
|
|||
采用“注册表强约束 + key-value 存储扩展”的混合模式。 |
|||
|
|||
### 3.1 全局配置表 `app_configs` |
|||
|
|||
- `key`:主键 |
|||
- `value`:序列化后的值(字符串) |
|||
- `valueType`:值类型标记(`string | number | boolean | json`) |
|||
- `updatedAt`:更新时间 |
|||
|
|||
### 3.2 用户配置表 `user_configs` |
|||
|
|||
- `userId`:用户 ID |
|||
- `key`:配置键 |
|||
- `value`:序列化后的值(字符串) |
|||
- `valueType`:值类型标记 |
|||
- `updatedAt`:更新时间 |
|||
- 唯一键:`(userId, key)` |
|||
|
|||
说明: |
|||
|
|||
- `app_configs` 与 `user_configs` 语义分离,便于维护和查询。 |
|||
- 用户配置覆盖全局配置时,仅影响对应用户,不改写全局值。 |
|||
|
|||
## 4. 配置注册表(Registry) |
|||
|
|||
注册表用于定义“正式支持的配置项”,避免任意 key 无约束扩散。 |
|||
|
|||
每个配置项包含: |
|||
|
|||
- `key` |
|||
- `scope`:`global | user | both` |
|||
- `valueType`:`string | number | boolean | json` |
|||
- `defaultValue` |
|||
- `userOverridable` |
|||
- 可选 `validate(value)` 业务校验 |
|||
|
|||
设计原则: |
|||
|
|||
- 核心配置通过注册表获得稳定约束(类型、默认值、可覆盖规则)。 |
|||
- 底层表结构保持 key-value 形态,支持后续扩展而不频繁改表结构。 |
|||
|
|||
## 5. 读取设计 |
|||
|
|||
### 5.1 请求内读取(主路径) |
|||
|
|||
通过 `event.context.config` 提供只读能力: |
|||
|
|||
- `getGlobal(key)`:读取全局配置,不存在则返回默认值 |
|||
- `getUser(key)`:读取当前登录用户私有配置;未登录返回 `undefined` |
|||
- `get(key)`:按 `user -> global -> default` 顺序返回 |
|||
|
|||
### 5.2 请求级缓存 |
|||
|
|||
在单次请求内使用轻量内存缓存(如 `Map`): |
|||
|
|||
- 同一 key 多次读取不重复查库 |
|||
- `get` / `getUser` / `getGlobal` 共用缓存结果 |
|||
- 缓存生命周期限定在当前请求 |
|||
|
|||
### 5.3 非请求场景读取 |
|||
|
|||
提供 service 级快捷函数(不依赖 `event.context`),用于定时任务或后台逻辑: |
|||
|
|||
- `readGlobalConfig(key)` |
|||
- `readMergedUserConfig(userId, key)` |
|||
|
|||
## 6. 写入设计(仅 service + API) |
|||
|
|||
### 6.1 全局配置写入 |
|||
|
|||
接口建议:`PUT /api/config/global` |
|||
|
|||
- 入参:`{ key, value }` |
|||
- 逻辑:校验 key、scope、value 后执行 upsert |
|||
- 权限:管理员能力(本期可先预留校验位置) |
|||
|
|||
### 6.2 当前用户配置写入 |
|||
|
|||
接口建议:`PUT /api/config/me` |
|||
|
|||
- 入参:`{ key, value }` |
|||
- 前置:要求已登录 |
|||
- 逻辑:校验 key 可否用户覆盖、校验值合法性后 upsert |
|||
|
|||
### 6.3 用户配置重置 |
|||
|
|||
接口建议:`DELETE /api/config/me/:key` |
|||
|
|||
- 删除用户侧覆盖项 |
|||
- 读取时自动回退全局值或默认值 |
|||
|
|||
## 7. 错误模型 |
|||
|
|||
建议统一业务错误码(命名可按项目规范微调): |
|||
|
|||
- `CONFIG_KEY_NOT_FOUND` |
|||
- `CONFIG_SCOPE_INVALID` |
|||
- `CONFIG_VALUE_INVALID` |
|||
- `CONFIG_FORBIDDEN` |
|||
- `UNAUTHORIZED` |
|||
|
|||
目标: |
|||
|
|||
- 调用方可稳定识别错误类型 |
|||
- 日志与监控便于聚合分析 |
|||
|
|||
## 8. 代码落点建议 |
|||
|
|||
- `server/service/config/registry.ts` |
|||
- 配置项定义与校验规则 |
|||
- `server/service/config/index.ts` |
|||
- 读取、合并、写入 service |
|||
- `server/plugins/04.config-context.ts`(或补充 `00.global.ts`) |
|||
- 注入 `event.context.config` 只读门面 |
|||
- `server/api/config/global.put.ts` |
|||
- 全局配置写入 |
|||
- `server/api/config/me.put.ts` |
|||
- 当前用户配置写入 |
|||
- `server/api/config/me/[key].delete.ts` |
|||
- 删除用户覆盖项 |
|||
- `packages/drizzle-pkg/...` |
|||
- `app_configs` / `user_configs` schema 与迁移 |
|||
|
|||
## 9. 验收标准 |
|||
|
|||
1. `get(key)` 可按 `user -> global -> default` 正确合并 |
|||
2. 未登录时 `getUser(key)` 返回 `undefined` |
|||
3. 全局写入成功后读取立即生效 |
|||
4. 用户写入后优先覆盖全局值 |
|||
5. 非法 key 或非法值写入会被拒绝 |
|||
6. 不允许用户覆盖的 key 在用户接口写入被拒绝 |
|||
|
|||
## 10. 实施顺序建议 |
|||
|
|||
1. 增加配置表 schema 与数据库迁移 |
|||
2. 实现 registry 与 config service |
|||
3. 注入请求上下文只读门面(plugin) |
|||
4. 实现全局/用户配置写入 API |
|||
5. 补充读取、写入、错误分支测试 |
|||
6. 按验收标准做手工验证 |
|||
|
|||
--- |
|||
|
|||
本设计文档结论:在 `event.context` 扩展配置读取能力是合理的,但应限定为“请求级只读门面”;写入始终通过 service + API,`defineWrappedResponseHandler` 保持轻职责,不承担复杂上下文装配。 |
|||
Loading…
Reference in new issue