Repository navigation
v0.0.13
·
1146 commits
to master
since this release
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>+ 删除按钮,末尾「添加特性」虚线按钮。 - 新增
formFeaturesref 与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后发布。
English
✨ New Features
Go backend
- Refactored the plan
featuresfield toarray<string>(Issue #11):- Added a new
StringSlicetype inmodel/plan.gothat implementsMarshalJSON,UnmarshalJSON,driver.Valuer, andsql.Scanner— it bridges[]stringbetween the JSON wire format and the DBtextcolumn. - JSON wire format unified to
["A","B","C"]; the DB still stores a JSON string in thetextcolumn, soAutoMigrateis a no-op (zero schema migration). UnmarshalJSON/Scanaccept all three historical shapes (JSON array string, newline-delimited plain text, JSON object) and normalize them to[]string.- Added
Plan.GetFeatures()returning a copy-safe[]stringfor controller serialization so external code never mutates the underlying slice. - Added
model/plan_test.go: 16 cases covering Marshal/Unmarshal/Value/Scan round-trips plus nil, empty, whitespace, CRLF, and invalid JSON edge cases.
- Added a new
GetCurrentPlanreturns features as an array (controller/plan.go): replaceddata["features"] = up.Plan.Features(raw string) withdata["features"] = up.Plan.GetFeatures()so it matches/api/plan/detail. The user-sidePlans.vue::parseFeaturesalready handledArray.isArray, no coordinated front-end change required.Usermodel auto-fills timestamps (model/user.go):- Added
User.BeforeCreate: whenCreatedAt/UpdatedAtare 0 they are auto-filled viahelper.GetTimestamp(). - Added
User.BeforeUpdate: everyUpdates(...)path now refreshesUpdatedAt. - Covers every write path:
Register,CreateUser,UpdateUser,ManageUser,EmailBind,GenerateAccessToken, etc. - Fixes the historical bug where
/api/user/?p=0returnedcreated_at=0and never-refreshedupdated_at.
- Added
Frontend (web/default-pro/)
- Admin plan modal's "Feature description" is now a dynamic row editor (
views/setting/PlanSetting.vue):- Replaced the
<a-textarea>with a.features-editorcontainer: one<a-input allow-clear>per row + a delete button, with a trailing dashed "Add feature" button. - Introduced a
formFeaturesref decoupled fromform.features; addedaddFeature/addFeatureAfter/removeFeature/updateFeaturefor row manipulation. - Pressing Enter on any row auto-appends an empty row; when the last row is empty and the only one, delete is disabled to prevent accidental emptying.
openModalbackfills viautils/plan.featuresFromRecord(backward-compatible with legacy shapes);handleSavePOSTs/PUTs/api/plan/with asanitizeFeaturesList-cleaned array — zero format ambiguity.- Added
.features-editor/.features-editor-row/.features-editor-add/.features-editor-removestyles.
- Replaced the
- Added a "Registration time" column to the admin user list (
views/user/User.vue):- New header column rendering
u.created_at; cell showsYYYY-MM-DD, hover tooltip reveals the full local datetime. - Added
formatDate/formatDateTimehelpers; falls back to-on missing/zero timestamps. - Adjusted
.list-head/.list-rowgrid template to add a 120px column.
- New header column rendering
- New
src/utils/plan.jspure-function helpers + 14node:testunit tests:normalizeFeaturesRaw(raw): normalize any of the historical shapes (array / JSON string / newline text / CRLF / JSON object / null / invalid JSON / numbers) to[]string, dropping blanks.sanitizeFeaturesList(list): trim, filter blanks, return a fresh array without mutating the input.featuresFromRecord(raw): convert the backend field into modal form rows (empty record falls back to a single empty row).buildEmptyFeaturesForm(): returns a single empty-string initial form.
🐛 Bug Fixes
- Fixed the historical
Userbug wherecreated_atwas always 0 andupdated_atnever refreshed: addressed by the newUser.BeforeCreate/BeforeUpdatehooks above, covering every write path. - Fixed the admin plan modal "Feature description" being easy to mistype and rendering empty on the user side: the textarea required admin to hand-type a JSON
["A","B","C"]string, and any typo produced an empty list. The new dynamic-row editor withsanitizeFeaturesListremoves the format ambiguity.
📚 Documentation
docs/API.mdupdated to reflect the newfeaturestype (Issue #11):- Field-table row
features:string→array<string>; wording updated to "list of features, one per row in the user-facing plan card". GET /api/plan/response sample:"features": "功能特性描述"→["API 调用 1000 次/月", "支持 GPT-4o"].POST /api/plan/request sample synced to the array form.
- Field-table row
⚠️ Upgrade Notes
- Zero database migration:
Plan.Featurescolumn type is unchanged (stilltext);StringSliceis purely a Go-side bridge between JSON and the DB column. Existing data — whether plain text like"A\nB\nC"or legacy JSON-object shapes — is normalized to[]stringautomatically on first read. - API compatibility:
GET /api/plan/andGET /api/plan/currentnow returnfeaturesas an array. The user-sidePlans.vue::parseFeaturesalready handledArray.isArray, so existing user-facing code is unaffected. - Admin UX: when an admin opens any plan in Settings → Plan Management, the feature list now renders as one
<a-input>per row with add/delete buttons; legacy data is split into rows automatically. - Backend build: added
model/plan_test.goandmodel/user_test.go, both pure unit tests againstglebarez/sqlitein-memory +gorm.AutoMigrate, no external DB required. - Frontend build: no new dependencies. Recommend
pnpm install && npm run buildbefore publishing.