You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

5.8 KiB

全局配置与用户配置模块设计文档

日期: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_configsuser_configs 语义分离,便于维护和查询。
  • 用户配置覆盖全局配置时,仅影响对应用户,不改写全局值。

4. 配置注册表(Registry)

注册表用于定义“正式支持的配置项”,避免任意 key 无约束扩散。

每个配置项包含:

  • key
  • scopeglobal | user | both
  • valueTypestring | 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 保持轻职责,不承担复杂上下文装配。