# 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` ## 新增 useAppSubscriptionStore(websopy 应用订阅特殊接口) - 新增 `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-path;baseURL=`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`: - 用 `` + `` + `` 单张图片上传。 - 上传走 `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/Liquibase,DDL 手动管理,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 转元。 ## 改用 subscribe+pay 避开 generatePayQrcode 的 price_type 报错 - 现象:用户反馈 `generatePayQrcode` 仍报 `price_type` 无默认值(报错 SQL 字段集是修复前的,说明后端未重启/未重编译,`setPriceType` 修复未生效)。 - 关键认知:`generatePayQrcode` 后端**不接收 priceType 参数**(从 `product.getPriceType()` 取),所以前端传 priceType 给它也没用,无法绕过。 - 修复(前端方案,不依赖后端重启):dashboard `confirmSubscribe` 改用 `subscribe` + `pay` 两步(参考 websopy-pc 的 `pay/[subscriptionNo].vue` 模式): - `subscribe` 创建订阅(后端有 `setPriceType`,不报错;价格 `/100` 正确)→ 免费 `status=active` 直接成功 - 付费 `status=pending` → `pay(subscriptionId,'wechat')` 生成小程序码 → Modal 展示 + 轮询 `checkStatus` - 移除 `confirmSubscribe` 里的 `isFreeProduct` 分支(改用 `subResult.status` 判断);`isFreeProduct` 仍用于订阅 Modal 的免费提示。 - vue-tsc 验证 dashboard 零 error TS(总错误 1778 不变)。 - 后端 `generatePayQrcode` 的 `setPriceType` 修复仍保留(重启后端后该接口也能用),但前端已不依赖它。 ## 订阅 productId 改为固定 65(小程序商城) - 需求:dashboard 立即订阅请求的 productId 从动态(按 `tenantId` 查 `app_product`)改为写死 `65`。 - 改动 `src/views/shop/dashboard/index.vue`: - imports:`pageProducts` → `getProductDetail`。 - `loadSubscriptionInfo`:移除 `userStore.info.tenantId` 判断和 `pageProducts({tenantId,current:1,size:1})` 调用,改为 `getProductDetail(65)` 查固定产品详情;定义 `FIXED_PRODUCT_ID = 65` 常量,订阅列表过滤和 `currentProduct.productId` 统一使用该常量。 - `confirmSubscribe` 无需改动(用 `currentProduct.productId`,已自动是 65)。 - `getProductDetail(65)` 返回的 product 含 name/priceType/price,前端参考价 `monthPrice = price/100`、`yearPrice = monthPrice*10` 仍适用。 - vue-tsc 验证 dashboard 零 error TS(总错误 1778 不变)。 ## shopGoodsBrowse 列表商品图片改用 OSS 压缩函数 - 文件:`src/views/shop/shopGoodsBrowse/index.vue` - 引入 `getCompressedImageUrl`(来自 `src/utils/image.ts`),将商品图片 `:src="record.goodsImage"` 改为 `:src="getCompressedImageUrl(record.goodsImage)"`。 - 与 `shopGoods`、`shopOrder`、`dashboard` 等页面保持一致:默认宽度 240px、质量 90、自动补全 OSS 域名、跳过已含 `x-oss-process` 的 URL。 - 预览行为未改(仍走 a-image 默认 src 预览)。