Skip to content

v0.0.12

Choose a tag to compare

@github-actions github-actions released this 06 Sep 09:03
· 1156 commits to master since this release

v0.0.11 — 在线充值(topup)全链路打通

用户可在控制台「我的余额」卡片直接发起在线充值,复用同一套微信 / 支付宝支付通道;后台新增独立的「充值」设置 Tab,管理员可配置快捷金额、自定义金额与换算比例;同步沉淀《AGENTS.md》开发规范并更新全语种 README。

中文

✨ 新增功能

后端(Go)

  • 新增「在线充值」业务模块(OrderTypeTopup=2):
    • 新增 model/topup.go,定义 TopupPreset、CreateTopupOrderInput、TopupOrderPlanInfo 等结构,提供 CreateTopupOrder、ActivateTopupByOrder、ResolveTopupAmount、GetTopupSettings、SaveTopupSettings 等业务函数。
    • 复用 GenerateOrderNo("TP") 生成充值订单号(TP 前缀),写入与套餐订单同一张 orders 表的 type=2 记录,节省一张表。
    • ActivateTopupByOrder 幂等:订单已支付直接返回;激活时调 IncreaseUserQuota 加额度并 RecordTopupLog,TODO 标注退款时不回退 quota(待统一订单管理上线)。
    • 启动时通过 model.AutoMigrate 自动建表,无需手写 DDL。
  • 新增充值系统设置键(model/system_setting.go):
    • topup.enabled(总开关)、topup.allow_custom(是否允许自定义金额)、topup.presets(JSON 数组:[{amount, bonus_quota}, ...])、topup.exchange_rate(默认 1,1 元 = = 1 quota,仅作用于自定义金额)。
    • 分类常量 SystemSettingCategoryTopup = "topup"。
  • 新增 HTTP 接口:
    • POST /api/topup/order(CreateTopupOrder,用户认证):参数 { amount, preset_amount, pay_method },前置校验开关 / 通道 / 金额,命中预设或自定义规则后写订单,复用 controller/buildPayInfo 拿 pay_url / qr_code。
    • GET /api/setting/topup 与 PUT /api/setting/topup(GetTopupSettings / PutTopupSettings,Root 权限):读写上述 4 个 key。
  • 支付回调按订单类型分发(controller/payment.go::processNotify):
    • order.Type == OrderTypeTopup → 调 ActivateTopupByOrder 给用户加 quota;
    • 其余 → 调 ActivatePackageByOrder 激活套餐。
    • MockPay(admin 测试接口)同步按 type 分发,提示文案分别显示「余额已到账」/「套餐已激活」。

前端(web/default-pro/)

  • 新增可复用组件 components/TopupModal.vue:
    • props:modelValue、settings、title;emits:update:modelValue、success、error、pay。
    • 顶部「选择金额」chip 列表:依次渲染所有 settings.presets,最后一个 chip 固定为「自定义」(当 settings.allow_custom=true 时显示),点击后才显示金额输入框。
    • 中部支付方式选择器(pay-picker-item 微信 #07C160 / 支付宝 #1677FF 官方品牌色),复用 Plans.vue 既有样式。
    • 底部「支付金额」+「确认充值」按钮;选中自定义但未填金额时禁用提交。
  • 新增设置页 views/setting/TopupSetting.vue(root 可见):表单竖排三段:①总开关(enabled)②允许自定义金额(allow_custom)③自定义金额 1 元 = X quota(exchange_rate,默认 1);快捷金额用 <a-table> 行行内编辑(amount / bonus_quota),支持新增 / 删除;提交前前端预校验金额重复(validateTopupPresets)。
  • 新增路由 /setting/topup:router/index.js 注册子路由,Setting.vue 菜单新增「充值」项(root 可见,icon icon-subscribe-add)。
  • 新增 api/topup.js:topupApi.createOrder({ amount, preset_amount, pay_method })。
  • 扩展 api/setting.js:getTopup() / putTopup(data)。
  • 新增 utils/topup.js 纯函数工具:formatNumber / formatAmount / validateTopupPresets / calcCustomBonus,可在 Node 环境下用 node:test 单测。
  • 仪表盘接入充值弹窗:
    • 控制台 Dashboard.vue 余额卡的「充值」按钮改为弹出 TopupModal,点击前预拉 /api/setting/topup + /api/payment/status,任一失败给出明确 toast;<a-modal> 使用 :visible + @update:visible(踩坑修复:曾误用 :model-value,导致弹窗永远不显示)。
    • 同时弹出二维码 / 转账信息 <a-modal,刷新余额(/api/user/self)。
  • 订单管理表格新增「订单类型」列(Orders.vue):套餐 / 充值 两种两色 chip(type-plan 蓝 / type-topup 橙),方便用户区分。
  • 移除运营 Tab 内的「允许余额充值」占位开关(OperationSetting.vue):该字段已迁移到新充值 Tab,UI 与后端 GetPlanSettings / PutPlanSettings 同步删除读写;DB 中旧 plan.allow_topup 行保留无害。
  • 修复套餐界面硬编码套餐名白名单(Plans.vue):删除 VALID_PLAN_NAMES = ['lite','air','pro','max'] 过滤逻辑,改为显示后端返回的所有套餐(用户反映该过滤导致测试套餐不可见)。

🐛 问题修复

  • 修复仪表盘充值按钮点击无反应:原 <TopupModal> 在 TopupModal.vue 内部把 arco <a-modal> 的可见性绑定到了 :model-value,而 arco 的 <a-modal> 可见性属性是 visible,导致 v-model="topupModalVisible=true 时弹窗永远不显示。已改为 :visible="modelValue" + @update:visible="(v) => emit('update:modelValue', v)"。
  • 修复充值按钮缓存导致的「管理员开启充值但仪表盘无反应」:原代码仅在 topupSettings.value 为 null 时拉取一次,admin 刚开启充值但仪表盘缓存旧值时按钮不会再次请求。已改为每次点击都强制重新拉取 /api/setting/topup,并把错误提示文案改成「请联系管理员在「设置-充值」中开启」。
  • 修复 <a-spin> 在 TopupSetting 设置页父容器宽度异常:补齐 style="width: 100%"。
  • 修复 TopupSetting 表单横排三列不对齐:改为 layout="vertical" 三段竖排。

🚀 数据完整性 / 校验

  • 快捷金额金额不能重复:后端 model.SaveTopupSettings 用 seenAmounts map 检测重复金额,重复时返回「快捷金额重复:XX 元已存在」并整体拒绝保存;前端 utils/topup.js::validateTopupPresets 提前预校验,避免一次无效请求。

📚 文档

  • 新增项目根目录 AGENTS.md(480 行):面向 Agent / 贡献者的开发规范速查,覆盖 TL;DR / 项目结构 / 开发环境 / 提交规范(Conventional Commits + 中英双语 body + 一文件一 commit 粒度)/ 版本号 / 命名(topup 全局统一)/ 注释约定(文件级 4 行注释 + 导出符号注释 + TODO/XXX + 修改时如何更新)/ 后端规范(响应格式、model 不依赖 Gin、订单号前缀 TB/UP/TP)/ 前端规范(arco <a-modal> 用 :visible 而非 :model-value、<a-spin> 必须 style="width:100%"、工具函数用 node:test)/ 数据库 / 踩坑清单 / 工作流程(先 Plan 模式输出方案再编码)。
  • 同步更新 README.md 与 7 个语言版本(readme/README.{en,zh-TW,ja,ko,ar,de,ru}.md):新增「💰 在线充值(余额)」功能亮点章节;订单与支付章节补充订单类型列 + processNotify 分发说明;开发计划「在线充值」「充值设置中心」从规划移入已完成,新增「充值退款闭环」为进行中。

🔧 重构 / 工程

  • 移除运营 Tab 的 plan.allow_topup 字段读写(controller/setting_payment.go):精简 GetPlanSettings / PutPlanSettings 返回结构,DB 行保留(不会主动删除)。
  • 清理调试日志:移除为排查充值按钮不响应问题加在 Dashboard.vue::onRechargeClick 的 console.log。

⚠️ 升级注意事项

  • 数据库迁移:新增 4 个 system_settings 键(topup.enabled / topup.allow_custom / topup.presets / topup.exchange_rate),无需迁移脚本,AutoMigrate + 首次调用会按需写入。
  • orders 表复用:本次新增 type=2 的充值订单与既有 type=1 套餐订单存同一张表,无 DDL 变更。
  • 运营 Tab 「套餐运营」小节:UI 上不再有「允许余额充值(仅占位)」开关;如需启用充值,请进入设置 → 充值。
  • 充值功能默认关闭(topup.enabled 未设置时为 false),管理员需到「设置 → 充值」手动开启并配置至少 1 个 preset 或允许自定义金额,前端充值按钮才会触发弹窗。
  • 退款:本期不支持;充值订单 status=3 仅标记,quota 不回退,待统一订单管理上线后补全。
  • 前端构建:新增依赖 qrcode(已在 pnpm-lock.yaml),建议 pnpm install && npm run build 后发布。

English

✨ 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.