Skip to content

v0.0.12 ​

✨ New Features ​

Go backend ​

  • New online recharge module (OrderTypeTopup=2):
    • Added model/topup.go defining TopupPreset, CreateTopupOrderInput, TopupOrderPlanInfo, plus business functions CreateTopupOrder, ActivateTopupByOrder, ResolveTopupAmount, GetTopupSettings, SaveTopupSettings.
    • Reuses GenerateOrderNo("TP") for recharge order numbers (prefix TP); writes them to the same orders table as type=2, avoiding an extra table.
    • ActivateTopupByOrder is idempotent (paid orders return immediately); on activation it calls IncreaseUserQuota and writes RecordTopupLog. A TODO marks quota non-reversal on refund (awaiting unified admin order management).
    • model.AutoMigrate creates the table at startup — no manual DDL.
  • New recharge system-setting keys (model/system_setting.go):
    • topup.enabled (master switch), topup.allow_custom (allow custom amount), topup.presets (JSON array of [{amount, bonus_quota}, ...]), topup.exchange_rate (default 1, i.e. 1 CNY = 1 quota; applies only to custom amounts).
    • Category constant SystemSettingCategoryTopup = "topup".
  • New HTTP endpoints:
    • POST /api/topup/order (CreateTopupOrder, user-auth): body { amount, preset_amount, pay_method }; pre-validates the switch / channel / amount, then persists the order and reuses controller/buildPayInfo for pay_url / qr_code.
    • GET /api/setting/topup & PUT /api/setting/topup (GetTopupSettings / PutTopupSettings, root only): read/write the four keys above.
  • Payment callback dispatched by order type (controller/payment.go::processNotify):
    • order.Type == OrderTypeTopup → ActivateTopupByOrder to credit quota.
    • Otherwise → ActivatePackageByOrder to activate the plan.
    • MockPay (admin test endpoint) dispatches the same way and surfaces "balance credited" / "plan activated" copy.

Frontend (web/default-pro/) ​

  • New reusable component components/TopupModal.vue:
    • Props: modelValue, settings, title; emits: update:modelValue, success, error, pay.
    • Top "Choose amount" chip row: renders all settings.presets in order; the last chip is always "Custom" when settings.allow_custom=true. The amount input is shown only after clicking Custom.
    • Payment-method picker (pay-picker-item) with WeChat (#07C160) and Alipay (#1677FF) brand colors, reusing the styles from Plans.vue.
    • Footer shows "Pay amount" + "Confirm recharge"; submission is disabled when Custom is selected but no amount is entered.
  • New settings view views/setting/TopupSetting.vue (root-only): vertical layout with three sections — ① master switch (enabled), ② allow custom amount (allow_custom), ③ custom-amount 1 CNY = X quota (exchange_rate, default 1); preset rows use an <a-table> with inline-editable amount / bonus_quota columns, plus add / delete buttons. Front-end pre-validates duplicate amounts via validateTopupPresets before submission.
  • New route /setting/topup: registered in router/index.js; sidebar menu entry "充值" added in Setting.vue (root-only, icon icon-subscribe-add).
  • New api/topup.js: topupApi.createOrder({ amount, preset_amount, pay_method }).
  • Extended api/setting.js: getTopup() / putTopup(data).
  • New utils/topup.js pure-function helpers: formatNumber, formatAmount, validateTopupPresets, calcCustomBonus — usable in Node.js via node:test.
  • Dashboard recharge button wired up:
    • Dashboard.vue now opens TopupModal from the "Recharge" button on the balance card, prefetching /api/setting/topup + /api/payment/status to give precise error copy when either is missing. The underlying <a-modal> uses :visible + @update:visible (bug fix: the previous :model-value binding never showed the dialog because Arco's <a-modal> prop is visible).
    • Also opens a QR-code / bank-transfer modal and refreshes the balance via /api/user/self.
  • Order-management table gets an "Order type" column (Orders.vue): two-color chips for "Plan" / "Recharge" (type-plan blue, type-topup orange).
  • Removed the "Allow balance recharge" placeholder switch from the Operations tab (OperationSetting.vue): that field has migrated to the new Top-up tab; the UI and backend GetPlanSettings / PutPlanSettings no longer read or write it. The DB row is kept for backwards compatibility.
  • Removed the hard-coded plan-name whitelist from Plans.vue: the VALID_PLAN_NAMES = ['lite','air','pro','max'] filter was hiding test plans reported by the user; we now render whatever the backend returns.

🐛 Bug Fixes ​

  • Dashboard "Recharge" button did nothing on click: TopupModal.vue previously bound the inner <a-modal> to :model-value="modelValue", but Arco's <a-modal> visibility prop is visible, so the dialog never showed even when topupModalVisible flipped to true. Switched to :visible="modelValue" + @update:visible="(v) => emit('update:modelValue', v)".
  • Stale-cached settings made the recharge button appear unresponsive after admin enabled recharge: the dashboard only fetched settings when topupSettings.value was null, so toggling topup.enabled in admin did not propagate until the dashboard was hard-reloaded. Now we force-re-fetch on every click and surface clearer error copy ("contact the admin to enable it under Settings → Top-up").
  • <a-spin> width anomaly on TopupSetting page: added the required style="width: 100%".
  • Misaligned three-column form on TopupSetting: switched to layout="vertical" so the three sections stack vertically.

🚀 Data integrity / validation ​

  • Preset amounts cannot repeat: model.SaveTopupSettings uses a seenAmounts map; duplicates return "快捷金额重复:XX 元已存在" and the whole save is rejected. The new utils/topup.js::validateTopupPresets mirrors this check on the frontend to avoid round-tripping invalid payloads.

📚 Documentation ​

  • New AGENTS.md at project root (480 lines): a single-source-of-truth guide for agents and contributors covering TL;DR / project layout / dev environment / commit conventions (Conventional Commits + bilingual body + one-file-per-commit granularity) / versioning / naming (topup is the global name) / comment conventions (4-line file header, exported-symbol comments, TODO/XXX, edit-time updates) / backend conventions (response format, model layer is HTTP-free, order-number prefixes TB/UP/TP) / frontend conventions (Arco <a-modal> uses :visible not :model-value, <a-spin> requires style="width:100%", util tests via node:test) / database / pitfall checklist / recommended workflow (Plan mode → per-file commit).
  • Synchronized README.md and all 7 language copies (readme/README.{en,zh-TW,ja,ko,ar,de,ru}.md): added a new "💰 Online Top-up (Quota Balance)" feature-highlight section; the existing "Orders & Real Payments" section now mentions the order-type column and processNotify dispatch; the "Online quota top-up" and "Top-up settings center" roadmap items moved from Planned → Done; added a new "Top-up refund loop" in-progress item awaiting unified admin order management.

🔧 Refactor / tooling ​

  • Removed plan.allow_topup reads/writes from controller/setting_payment.go: trimmed GetPlanSettings / PutPlanSettings response shape; DB row preserved.
  • Cleaned up debug logs from Dashboard.vue::onRechargeClick (left over from the "recharge button does nothing" investigation).

⚠️ Upgrade Notes ​

  • Database migration: 4 new system_settings keys (topup.enabled / topup.allow_custom / topup.presets / topup.exchange_rate). No migration script required — AutoMigrate plus first-write will populate them as needed.
  • orders table is reused: new type=2 recharge rows share the existing table with type=1 plan orders; no DDL change.
  • Operations tab "Plan Operations" sub-section: the placeholder switch "Allow balance recharge" has been removed from the UI. Use Settings → Top-up instead.
  • Recharge is off by default (topup.enabled is false until an admin toggles it). The dashboard button will not open the dialog until an admin enables recharge and configures at least one preset (or allows custom amounts).
  • Refunds are not supported in this release: recharge orders with status=3 are merely flagged; quota is not reversed. The full refund loop will land with the unified admin order-management feature.
  • Frontend build: qrcode is now a runtime dependency (already in pnpm-lock.yaml). Recommend pnpm install && npm run build before publishing.