Skip to content

v0.0.13 ​

✨ New Features ​

Go backend ​

  • Refactored the plan features field to array<string> (Issue #11):
    • Added a new StringSlice type in model/plan.go that implements MarshalJSON, UnmarshalJSON, driver.Valuer, and sql.Scanner — it bridges []string between the JSON wire format and the DB text column.
    • JSON wire format unified to ["A","B","C"]; the DB still stores a JSON string in the text column, so AutoMigrate is a no-op (zero schema migration).
    • UnmarshalJSON / Scan accept 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 []string for 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.
  • GetCurrentPlan returns features as an array (controller/plan.go): replaced data["features"] = up.Plan.Features (raw string) with data["features"] = up.Plan.GetFeatures() so it matches /api/plan/detail. The user-side Plans.vue::parseFeatures already handled Array.isArray, no coordinated front-end change required.
  • User model auto-fills timestamps (model/user.go):
    • Added User.BeforeCreate: when CreatedAt / UpdatedAt are 0 they are auto-filled via helper.GetTimestamp().
    • Added User.BeforeUpdate: every Updates(...) path now refreshes UpdatedAt.
    • Covers every write path: Register, CreateUser, UpdateUser, ManageUser, EmailBind, GenerateAccessToken, etc.
    • Fixes the historical bug where /api/user/?p=0 returned created_at=0 and never-refreshed updated_at.

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-editor container: one <a-input allow-clear> per row + a delete button, with a trailing dashed "Add feature" button.
    • Introduced a formFeatures ref decoupled from form.features; added addFeature / addFeatureAfter / removeFeature / updateFeature for 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.
    • openModal backfills via utils/plan.featuresFromRecord (backward-compatible with legacy shapes); handleSave POSTs/PUTs /api/plan/ with a sanitizeFeaturesList-cleaned array — zero format ambiguity.
    • Added .features-editor / .features-editor-row / .features-editor-add / .features-editor-remove styles.
  • Added a "Registration time" column to the admin user list (views/user/User.vue):
    • New header column rendering u.created_at; cell shows YYYY-MM-DD, hover tooltip reveals the full local datetime.
    • Added formatDate / formatDateTime helpers; falls back to - on missing/zero timestamps.
    • Adjusted .list-head / .list-row grid template to add a 120px column.
  • New src/utils/plan.js pure-function helpers + 14 node:test unit 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 User bug where created_at was always 0 and updated_at never refreshed: addressed by the new User.BeforeCreate / BeforeUpdate hooks 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 with sanitizeFeaturesList removes the format ambiguity.

📚 Documentation ​

  • docs/API.md updated to reflect the new features type (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.

⚠️ Upgrade Notes ​

  • Zero database migration: Plan.Features column type is unchanged (still text); StringSlice is 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 []string automatically on first read.
  • API compatibility: GET /api/plan/ and GET /api/plan/current now return features as an array. The user-side Plans.vue::parseFeatures already handled Array.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.go and model/user_test.go, both pure unit tests against glebarez/sqlite in-memory + gorm.AutoMigrate, no external DB required.
  • Frontend build: no new dependencies. Recommend pnpm install && npm run build before publishing.