Files
guilixu-admin/.workbuddy/memory/2026-07-15.md
赵忠林 09af76e4ea fix(subscription): 修复价格单位错误导致金额多100倍问题
- 确认 product.price 实际单位为分,修正所有价格计算需除以100转为元
- 后端 generatePayQrcode 方法价格计算改为 price/100,年付按10个月计费,季度付按3个月计费
- 前端 dashboard/index.vue 月付价格除以100,年付价格按月付价乘以10计算,保持与后端一致
- 保持 subscribe 价格计算逻辑,避免价格单位不一致的错误
- 修复后价格显示和支付逻辑统一,解决用户反馈金额偏大问题
2026-07-15 14:12:54 +08:00

9.0 KiB
Raw Blame History

2026-07-15

修复 shopOrder 页面默认待发货tab数据不正确

  • 根因:activeKey 默认为 'undelivered',但 datasource 函数只设了 where.type = 0,没有设 statusFilterstatusFilter 只在用户点击 tab 触发 onTabs 时才设置。所以页面初次加载时查的是全部订单而非待发货订单。
  • 修复:提取 getStatusFilterByTab(key) 公共映射函数,在 datasource 中当 where.statusFilter 未设置时从 activeKey 自动推导。onTabs 也简化为复用同一函数。
  • 文件:src/views/shop/shopOrder/index.vue

Dashboard 订单总数点击跳转支持指定 tab

  • Dashboard 中"订单总数"/"总营业额"跳转链接改为 /shop/shopOrder?tab=all"待发货订单"改为 ?tab=undelivered"退款申请"改为 ?tab=refunded
  • shopOrder 页面读取 route.query.tab 初始化 activeKey,无效值回退到 'undelivered'
  • 文件:src/views/shop/dashboard/index.vuesrc/views/shop/shopOrder/index.vue

Dashboard 待处理事项新增"待付款订单"

  • 在待发货订单上方新增"待付款订单"统计项,使用 statusFilter=0 查询,点击跳转 /shop/shopOrder?tab=unpaid
  • shopOrder 页面取消注释"待付款"tabvalidTabs 加入 unpaid
  • 新增 dot-gold 样式。
  • 文件:src/views/shop/dashboard/index.vuesrc/views/shop/shopOrder/index.vue

Dashboard 引入 useTenantStore创建时间读租户信息

  • 引入 useTenantStoreloadData 中调用 tenantStore.fetchTenantInfo() 替代直接调用 getTenantInfo() API。
  • 基本信息"创建时间"改为读取 tenantStore.company?.createTime
  • 运行天数也改为从 tenantStore.company?.createTime 计算,移除独立的 tenantCreateTime ref。
  • 文件:src/views/shop/dashboard/index.vue
  • 通过 getShopSettingCategoryValues('basic') 获取商城设置,读取 shopLogo 字段。
  • 使用 getCompressedImageUrl(shopLogo, { width: 200, quality: 90 }) 压缩图片。
  • 在 welcome-banner 左侧用 a-avatar (64px, square) 展示,无 Logo 时不显示。
  • 文件:src/views/shop/dashboard/index.vue

新增 useAppSubscriptionStorewebsopy 应用订阅特殊接口)

  • 新增 src/api/app/appSubscription/model.tsAppSubscription 实体及参数/返回类型,对照后端 com.gxwebsoft.app.entity.AppSubscription
  • 新增 src/api/app/appSubscription/index.ts:封装 AppSubscriptionController 全部 14 个接口。
    • 特殊接口域名 https://websopy-api.websoft.top/api(与项目主 API 域名不同),复用 @/utils/request(共享登录态 token、401 处理),传绝对 URL。
    • 后端无 context-pathbaseURL=https://websopy-api.websoft.top/api,接口路径用 /app/subscription/xxx
    • 后端 BaseController 成功 code=0success(IPage) 转成 PageResult{list,count},与项目 @/api 的 PageResult 结构一致。
  • 新增 src/store/modules/appSubscription.tsuseAppSubscriptionStorePinia Options API 风格,对齐 site.ts/statistics.ts聚合列表/详情/支付状态/订阅管理方法。列表/详情缓存5分钟checkStatus/checkPurchased 实时请求;订阅/支付/管理类操作成功后调 invalidateCache() 失效缓存。

确认线下收款新增上传凭证功能

  • 背景:原"确认线下收款"用 Modal.confirm + createVNode 动态拼一个 input只有备注没有上传凭证。用户需要加凭证上传。
  • 字段决策:新增 paymentVoucher?: string 字段(不复用 comments/merchantRemarks因为凭证是图片URL非文本。顺带修复 buyerRemarks/merchantRemarks 类型 bugundefinedstring?)。
  • 重构为独立组件 src/views/shop/shopOrder/components/OfflinePaymentModal.vue
    • <a-modal> + <a-form> + <a-upload list-type="picture-card"> 单张图片上传。
    • 上传走 uploadOsssrc/api/system/file),存储返回的 path(与项目 UploadCert 组件约定一致),预览用 getUrl(path) 补全。
    • 凭证必填,备注可选;校验 jpg/png、≤10MB支持点击预览大图。
  • API confirmOfflinePayment(id, remarks?, paymentVoucher?) 新增第三参数,通过 query params 传给后端。
  • index.vue:移除原 Modal.confirm/createVNode 逻辑,改为打开新组件(v-model:visible + :data="current" + @done="reload"),与 DeliveryModal 模式一致。
  • 类型检查:新增 0 错误,顺带消掉原 okType: 'success' 的类型错误antdv4 ButtonType 不含 success新组件去掉了 ok-type
  • 改动文件:src/api/shop/shopOrder/model/index.tssrc/api/shop/shopOrder/index.tssrc/views/shop/shopOrder/components/OfflinePaymentModal.vue(新增)、src/views/shop/shopOrder/index.vue
  • 后端待办(已完成):后端项目 /Users/gxwebsoft/JAVA/guilixu-javaMyBatis-Plus 3.4.3.3,无 Flyway/LiquibaseDDL 手动管理SQL 脚本放 sql/ 目录):
    • ShopOrder.java 实体新增 paymentVoucher 字段payTime 之后)。
    • ShopOrderController.confirmOfflinePayment 新增 @RequestParam paymentVoucher
    • ShopOrderService 接口 + ShopOrderServiceImpl 实现方法签名加 paymentVoucher 参数lambdaUpdate 加 .set(paymentVoucher 非空, ShopOrder::getPaymentVoucher, paymentVoucher)
    • DDL 脚本 sql/shop_order_payment_voucher.sqlALTER TABLE shop_order ADD COLUMN payment_voucher VARCHAR(500) NULL ... AFTER pay_time
    • 注意:方法签名从 2 参变 3 参,全项目仅 Controller 一处调用,无断裂。

Dashboard 到期时间显示订阅信息 + 立即订阅/扫码支付

  • 改造 src/views/shop/dashboard/index.vue 基本信息「到期时间」行三态渲染active 显示 expireTime + 已激活 tag + ≤7天到期提醒pending 显示待支付 + 去支付;无订阅显示立即订阅)。
  • productId 关联条件app_product.tenantId = userStore.info.tenantId(非 siteInfo.appId。通过 pageProducts({tenantId,current:1,size:1}) 取该租户应用。
  • 新增 src/api/app/appProduct/{model.ts,index.ts}AppProduct 类型 + pageProducts 接口GET /api/app/product/page分页参数 current/size注意与项目 page/limit 不同)。
  • 「立即订阅」流程:选 month/year → 免费应用调 subscribe() 直接激活;付费应用调 generatePayQrcode() 生成小程序码 → Modal 展示二维码 → 每 2.5s 轮询 checkStatus(subscriptionNo)paid 后关闭+刷新(最多 5 分钟。envVersion 按 import.meta.env.DEV 自动判断 trial/release。
  • pending 订阅「去支付」复用 subscriptionStore.pay(id,'wechat')(用 'miniappQrcode' in result 类型守卫窄化)生成小程序码,不重复创建订阅。
  • 小程序端支付页 websopy-taro/src/passport/pay/index.tsx:扫码 scene=subscriptionNo → detail-by-no → mp-prepay → requestPayment → mp-confirm后端写 Redis wxpay:paid:{no}=1Web 端 checkStatus 据此感知。
  • 移除 dashboard 不再使用的 siteInfo 解构(原到期时间读 siteInfo.expirationTime,已改为订阅 expireTime修复 noUnusedLocals 报错。

修复后端 generatePayQrcode 报错 "Field 'price_type' doesn't have a default value"

  • 现象dashboard 点「立即订阅」付费应用调 generatePayQrcode 时,后端 insert app_subscription 报 price_type 无默认值。
  • 根因:后端 AppSubscriptionController.generatePayQrcodewebsopy-java创建订阅时漏了 setPriceType(),而 DB price_type 为 NOT NULL 无默认值。对比 subscribe 方法有 setPriceType
  • 参考 websopy-pc/app/pages/console/pay/[subscriptionNo].vue:它用 subscribe 创建订阅 + pay(id,'wechat') 生成二维码(两步),避开了 generatePayQrcode,所以没遇到此 bug。
  • 修复(后端 AppSubscriptionController.java generatePayQrcode 方法):补全 setPriceType(product.getPriceType()) + setOriginalPrice/setPayStatus(0)/setTenantId,对齐 subscribe。前端无需改动,重启后端即可。
  • 注意:subscribe 价格计算用 price/100(当分转元),generatePayQrcode 直接用 price当元两者不一致schema 标注 price 为「元」,故 generatePayQrcode 价格逻辑更合理,保留不动。

修复价格多了100倍product.price 实际单位是分)

  • 用户反馈:generatePayQrcode 算出的金额多了100倍。确认 product.price 实际存的是(非 schema 标注的元),故 subscribe/100 是对的,generatePayQrcode 直接用 price 是错的。
  • 后端 AppSubscriptionController.generatePayQrcode 价格计算改为 price/100 转元(对齐 subscribe年付按10个月10对齐 subscribe 年付优惠),季付3月付*1。
  • 前端 dashboard/index.vue 参考价同步修正:monthPrice = price/100yearPrice = monthPrice*10(原为 price 直接 + *12
  • 结论product.price 实际单位是,所有价格展示/计算都需 /100 转元。