92 lines
5.7 KiB
Markdown
92 lines
5.7 KiB
Markdown
---
|
||
name: 允许免费额度配置为 0
|
||
overview: 修复新用户免费额度配置无法设为 0 的问题:配置字段改为 *int64 以区分「未配置」与「显式 0」,未配置回落内置默认,显式 0 生效为 0。
|
||
todos:
|
||
- id: change-setting-fields
|
||
content: 将 UserDefaultQuotaSettingS 四字段改为 *int64 并更新注释
|
||
status: completed
|
||
- id: fix-default-quota-value
|
||
content: 改 defaultQuotaValue 为指针判空判据,允许 0 生效
|
||
status: completed
|
||
dependencies:
|
||
- change-setting-fields
|
||
- id: sync-constants-comment
|
||
content: 同步 constants.go 内置默认常量注释语义
|
||
status: completed
|
||
dependencies:
|
||
- fix-default-quota-value
|
||
- id: sync-docs
|
||
content: 同步 README、config.example.yaml 与接口文档说明
|
||
status: completed
|
||
dependencies:
|
||
- fix-default-quota-value
|
||
- id: build-verify
|
||
content: 执行 go build ./... 与 go vet ./... 验证编译
|
||
status: completed
|
||
dependencies:
|
||
- sync-constants-comment
|
||
- sync-docs
|
||
- id: review-config-usage
|
||
content: 用 [subagent:code-explorer] 复核配置消费点与文档无旧语义残留
|
||
status: completed
|
||
dependencies:
|
||
- build-verify
|
||
---
|
||
|
||
## 产品概述
|
||
|
||
修复新用户免费额度初始化的配置语义缺陷:原有实现把「配置为 0」误判为「未配置」而回落内置默认,导致无法将某类免费额度配成 0(例如要求新用户 AI 推荐额度为 0、必须购买后才可用)。
|
||
|
||
## 核心功能
|
||
|
||
- **区分「未配置」与「显式 0」**:`UserDefaultQuota` 四个配置字段改为指针类型,未配置(nil)回落内置默认 20/3/10/5,显式配置 0 则生效为 0
|
||
- **保持兜底能力**:整段配置缺失、单个键缺失、配置为负数三种情况仍回落内置默认,不破坏既有部署
|
||
- **文档同步**:README 配置项说明、config.example.yaml 注释、接口文档额度说明统一为「允许配 0,未配置回落默认」
|
||
- **行为不变**:四类额度的初始化流程、流水记录、唯一索引幂等逻辑完全不动
|
||
|
||
## 技术栈
|
||
|
||
- Go 1.27,模块 `git.lzcwy.cn/zhengchiliu/zt-closet-backend`
|
||
- 配置:`pkg/setting`(viper 只读 `configs/config.yaml`,`ReadSection` 走 `UnmarshalKey`/mapstructure)
|
||
- 消费点唯一:`internal/service/quota.go` 的 `defaultQuotaValue`(grep 已确认无其它引用)
|
||
|
||
## 实现方案
|
||
|
||
采用**指针字段区分三态**:把 `UserDefaultQuotaSettingS` 的 `Cos/AI/OutfitShare/ClosetShare` 由 `int64` 改为 `*int64`。viper 解码时键存在则分配指针(含 0),键不存在则保持 nil,从而在类型层面区分「未配置」与「配置为 0」。
|
||
|
||
取值判据(`defaultQuotaValue`):
|
||
|
||
- `cfg == nil`(整段缺失)→ 内置默认
|
||
- `cfg.X == nil`(该键缺失)→ 内置默认
|
||
- `*cfg.X >= 0` → 用配置值(**0 生效,本次修复目标**)
|
||
- `*cfg.X < 0` → 内置默认(防御:负数会让 `total` 为负,破坏额度校验)
|
||
|
||
关键决策与取舍:
|
||
|
||
1. **用 `*int64` 而非把判据改成 `>= 0`**:后者能让 0 生效,但「键缺失」解码后同样是 0,会静默把兜底默认值变成 0——对现有未显式配置的环境是破坏性变更。指针方案零歧义,代价仅是多一层判空。
|
||
2. **用指针而非 `viper.IsSet` 逐键查询**:配置结构仍由结构体承载,`ReadSection` 用法与其它配置段一致,不引入第二套读取方式,符合现有架构。
|
||
3. **负数回落而非报错**:保持 best-effort 风格(与额度初始化失败仅告警不阻断一致),不引入启动期配置校验的新失败路径。
|
||
4. **保留内置常量值 20/3/10/5 不变**:只改判据与注释,不动 `ensureDefaultQuota` 的创建+流水事务逻辑。
|
||
|
||
性能与可靠性:该路径仅在首次登录注册时执行 4 次,指针判空为 O(1) 无额外开销;风险点仅为指针解引用,通过判空分支覆盖,并有 `go build`/`go vet` 兜底。
|
||
|
||
## 实现要点(防回归)
|
||
|
||
- 改字段类型后必须全量编译,`global.UserDefaultQuotaSetting` 的其它消费点若有解引用会直接编译报错(当前仅 quota.go 一处)。
|
||
- 判空顺序必须为 `cfg != nil && cfg.X != nil && *cfg.X >= 0`,避免 nil 解引用 panic。
|
||
- 注释需同步:结构体注释「值 <=0 视为未设置」、`constants.go:42`「配置缺省或 <=0 时回落」、`defaultQuotaValue` 函数注释,三处都要改成新语义,否则后续维护者会按旧语义改回去。
|
||
- 不要扩大范围:`Upload.MaxSizeMB`、`MattingModel.MaxRetry` 等其它 `<=0` 兜底语义不同(0 无业务意义),本次不动,仅在结论中提示。
|
||
- 不改动 `configs/config.yaml`(用户本地文件,已被 .gitignore 忽略)。
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
pkg/setting/section.go # [MODIFY] UserDefaultQuotaSettingS 四字段 int64 → *int64;
|
||
# 注释改为「nil=未配置回落默认;非 nil 生效,允许 0」
|
||
internal/service/quota.go # [MODIFY] defaultQuotaValue 判据改为指针判空 + >=0 生效;
|
||
# 函数注释同步(未配置/负数回落,0 生效)
|
||
internal/service/constants.go # [MODIFY] 第 42 行注释「配置缺省或 <=0 时回落」→「未配置(nil)或负数时回落」
|
||
README.md # [MODIFY] 第 195 行配置表说明改为「未配置回落内置默认 20/3/10/5;可显式配 0」
|
||
configs/config.example.yaml # [MODIFY] UserDefaultQuota 段注释补充「可为 0 表示不赠送;留空则回落内置默认」
|
||
接口文档.md # [MODIFY] 第 488/1432/1446 行及 5.2 节额度表格补「可由 UserDefaultQuota 配置,允许为 0」
|
||
``` |