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

88 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 2026-07-15
## 修复 shopOrder 页面默认待发货tab数据不正确
- 根因:`activeKey` 默认为 `'undelivered'`,但 `datasource` 函数只设了 `where.type = 0`,没有设 `statusFilter``statusFilter` 只在用户点击 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.vue``src/views/shop/shopOrder/index.vue`
## Dashboard 待处理事项新增"待付款订单"
- 在待发货订单上方新增"待付款订单"统计项,使用 `statusFilter=0` 查询,点击跳转 `/shop/shopOrder?tab=unpaid`
- shopOrder 页面取消注释"待付款"tab`validTabs` 加入 `unpaid`
- 新增 `dot-gold` 样式。
- 文件:`src/views/shop/dashboard/index.vue``src/views/shop/shopOrder/index.vue`
## Dashboard 引入 useTenantStore创建时间读租户信息
- 引入 `useTenantStore``loadData` 中调用 `tenantStore.fetchTenantInfo()` 替代直接调用 `getTenantInfo()` API。
- 基本信息"创建时间"改为读取 `tenantStore.company?.createTime`
- 运行天数也改为从 `tenantStore.company?.createTime` 计算,移除独立的 `tenantCreateTime` ref。
- 文件:`src/views/shop/dashboard/index.vue`
## Dashboard 欢迎横幅新增商城Logo
- 通过 `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.ts`AppSubscription 实体及参数/返回类型,对照后端 `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=0`success(IPage)` 转成 `PageResult{list,count}`,与项目 `@/api` 的 PageResult 结构一致。
- 新增 `src/store/modules/appSubscription.ts``useAppSubscriptionStore`Pinia Options API 风格,对齐 site.ts/statistics.ts聚合列表/详情/支付状态/订阅管理方法。列表/详情缓存5分钟`checkStatus`/`checkPurchased` 实时请求;订阅/支付/管理类操作成功后调 `invalidateCache()` 失效缓存。
## 确认线下收款新增上传凭证功能
- 背景:原"确认线下收款"用 `Modal.confirm` + `createVNode` 动态拼一个 input只有备注没有上传凭证。用户需要加凭证上传。
- 字段决策:新增 `paymentVoucher?: string` 字段(不复用 comments/merchantRemarks因为凭证是图片URL非文本。顺带修复 `buyerRemarks`/`merchantRemarks` 类型 bug`undefined``string?`)。
- 重构为独立组件 `src/views/shop/shopOrder/components/OfflinePaymentModal.vue`
-`<a-modal>` + `<a-form>` + `<a-upload list-type="picture-card">` 单张图片上传。
- 上传走 `uploadOss``src/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.ts``src/api/shop/shopOrder/index.ts``src/views/shop/shopOrder/components/OfflinePaymentModal.vue`(新增)、`src/views/shop/shopOrder/index.vue`
- 后端待办(已完成):后端项目 `/Users/gxwebsoft/JAVA/guilixu-java`MyBatis-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.sql``ALTER 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}=1`Web 端 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.generatePayQrcode`websopy-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/100``yearPrice = monthPrice*10`(原为 price 直接 + *12
- 结论product.price 实际单位是**分**,所有价格展示/计算都需 /100 转元。