Skip to content

v0.0.13 — 套餐 features 字段重构 & 用户时间戳自动填充 ​

套餐 features 字段从字符串重构为 array<string>(新增 StringSlice 类型,三种历史形态零迁移兼容),后台套餐弹窗改为动态行编辑器避免用户手敲 JSON;同时修复 User 模型 created_at 始终为 0、updated_at 不刷新的历史 bug,并在用户列表新增「注册时间」列。

中文 ​

✨ 新增功能 ​

后端(Go) ​

  • 套餐 features 字段重构为 array<string>(Issue #11):
    • model/plan.go 新增 StringSlice 类型,同时实现 MarshalJSON / UnmarshalJSON / driver.Valuer / sql.Scanner 四个接口,作为 []string 在 JSON wire 与 DB text 列之间的双向桥接。
    • JSON wire 格式统一为 ["A","B","C"];DB 仍以 JSON 字符串写入 text 列,AutoMigrate 无 schema 变更。
    • UnmarshalJSON / Scan 兼容历史三种形态:JSON 数组字符串 / 换行分隔纯文本 / JSON 对象,统一归一为 []string。
    • 新增 Plan.GetFeatures() 返回拷贝后的 []string,供 controller 序列化使用,避免外部修改共享底层 slice。
    • 新增 model/plan_test.go:16 个用例 覆盖 Marshal/Unmarshal/Value/Scan 往返、nil、空、空白、CRLF、非法 JSON 等边界。
  • GetCurrentPlan 返回 features 数组形态(controller/plan.go):data["features"] 从裸字符串改为 Plan.GetFeatures(),与 /api/plan/detail 一致。前端 Plans.vue::parseFeatures 已 Array.isArray 兼容,无需联动修改。
  • User 模型时间戳自动填充(model/user.go):
    • 新增 User.BeforeCreate 钩子:CreatedAt / UpdatedAt 为 0 时自动写入 helper.GetTimestamp()。
    • 新增 User.BeforeUpdate 钩子:任何 Updates(...) 路径都会刷新 UpdatedAt。
    • 一并覆盖 Register / CreateUser / UpdateUser / ManageUser / EmailBind / GenerateAccessToken 等所有写入路径。
    • 修复 /api/user/?p=0 接口中 created_at 始终为 0、updated_at 不刷新的历史 bug。

前端(web/default-pro/) ​

  • 后台套餐弹窗「特性说明」改为动态行编辑器(views/setting/PlanSetting.vue):
    • 删除原 <a-textarea>,替换为 .features-editor 容器:每行 <a-input allow-clear> + 删除按钮,末尾「添加特性」虚线按钮。
    • 新增 formFeatures ref 与 form.features 解耦,专管动态行;提供 addFeature / addFeatureAfter / removeFeature / updateFeature 四个操作函数。
    • 某行 <a-input> 按 Enter 自动新增一行;末行空且唯一时禁用删除(防误清空)。
    • openModal 用 utils/plan.featuresFromRecord 回填(向后兼容旧数据形态);handleSave 用 sanitizeFeaturesList 过滤空行后直接以数组形态 POST/PUT /api/plan/,零格式歧义。
    • 新增 .features-editor / .features-editor-row / .features-editor-add / .features-editor-remove 样式。
  • 后台用户列表新增「注册时间」列(views/user/User.vue):
    • 表头新增「注册时间」列,渲染 u.created_at;单元格显示 YYYY-MM-DD 短日期,hover tooltip 显示完整本地时间。
    • 新增 formatDate / formatDateTime 工具函数,无值时回退 -。
    • 调整 .list-head / .list-row 的 grid 模板,新增 120px 列宽。
  • 新增 src/utils/plan.js 纯函数工具 + 14 个 node:test 单测:
    • normalizeFeaturesRaw(raw):把后端可能返回的多种形态(数组 / JSON 字符串 / 换行文本 / CRLF / JSON 对象 / null / 非法 JSON / 数字)统一归一为 []string,丢弃空项。
    • sanitizeFeaturesList(list):清洗表单输入,去空白、返回新数组不修改入参。
    • featuresFromRecord(raw):把后端字段转成弹窗表单数组(空记录回退到 1 行空输入)。
    • buildEmptyFeaturesForm():返回 1 个空字符串的初始表单。

🐛 问题修复 ​

  • 修复 User 表 created_at 始终为 0、updated_at 不刷新的历史 bug:通过新增 User.BeforeCreate / BeforeUpdate 钩子(见上)覆盖所有写入路径。
  • 修复后台套餐弹窗「特性说明」易填错导致前端为空:原本要求 admin 手敲 ["A","B","C"] JSON 字符串,用户格式填错前端就显示空;改为每行 <a-input> 动态行编辑器,提交时由 sanitizeFeaturesList 清洗,零格式歧义。

📚 文档 ​

  • docs/API.md 同步更新套餐 features 字段类型(Issue #11):
    • 字段说明表 features 行:string → array<string>,措辞改为「功能特性列表,每项一行展示在用户端套餐卡」。
    • GET /api/plan/ 响应体示例:"features": "功能特性描述" → ["API 调用 1000 次/月", "支持 GPT-4o"]。
    • POST /api/plan/ 请求体示例同步为数组形式。

⚠️ 升级注意事项 ​

  • 零数据库迁移:Plan.Features 字段类型未变(仍是 text 列),StringSlice 仅在 Go 层做 JSON ↔ DB 双向桥接;现有数据的 features 文本("A\nB\nC" 或 "{\"API\":true}" 等历史形态)会在首次读取时自动归一化为数组。
  • API 兼容性:GET /api/plan/ 与 GET /api/plan/current 的 features 字段由字符串改为数组;前端 Plans.vue::parseFeatures 早已 Array.isArray 兼容,现有用户端无感知。
  • 管理端操作:admin 进入「设置 → 套餐管理」编辑任意套餐时,特性说明将以「每行一个 input + 添加/删除」形式展示,老数据自动按行展开。
  • 后端编译:新增 model/plan_test.go 与 model/user_test.go,纯单元测试使用 glebarez/sqlite 内存库,不依赖外部 DB。
  • 前端构建:无新增依赖;建议 pnpm install && npm run build 后发布。