Просмотр исходного кода

docs(spec): 我的优惠券已领取券按钮修复(转赠+使用)+ 使用按券类型跳转 任务书

Agnes Agent 6 дней назад
Родитель
Сommit
94f0eeee53
1 измененных файлов с 98 добавлено и 0 удалено
  1. 98 0
      docs/superpowers/specs/2026-11-06-coupon-claimed-actions.md

+ 98 - 0
docs/superpowers/specs/2026-11-06-coupon-claimed-actions.md

@@ -0,0 +1,98 @@
+# 我的优惠券:已领取券按钮修复(转赠+使用)+ 使用按券类型跳转
+
+> 任务编号:待派发(cfc-frontend)
+> 日期:2026-11-06
+> 触发:用户 "我的优惠券中,显示的是已经领取的,当前还显示为'立即领取',点击会报优惠券不存在。已经领取的优惠券应该是转赠和使用两个按钮,如果是指定商品的优惠券,则直接跳到商品下单页面,并自动选上优惠券,如果是全品类的,跳到商城;如果是会员券,跳到开通会员。"
+
+## 一、需求
+
+1. **修复 bug**:「我的优惠券」页已领取的券仍显示「立即领取」,点击报「优惠券不存在」——应为「转赠」+「使用」两个按钮
+2. **使用按钮按券类型跳转**:
+   - **指定商品券**(applicableTo=PRODUCT):直接跳到该商品下单页面(确认订单),并**自动选上优惠券**
+   - **全品类券**(applicableTo=ALL):跳到商城
+   - **会员券**(applicableTo=MEMBERSHIP):跳到开通会员
+3. **转赠按钮**:跳转赠送会员券页
+
+## 二、根因(已定位)
+
+`coupons.vue`(我的优惠券)展示的是**用户已领取的券**(`getMyCoupons`),但 `canClaim(coupon)` 对 AVAILABLE 券返回 true → 显示「立即领取」→ `handleClaim` 调 `claimCoupon(coupon.id)`,而 `coupon.id` 是**券实例 id(family_coupon.id)**非券模板 id → 后端查无此模板 → 报「优惠券不存在」。
+
+## 三、现状(cfclub @ origin 2026-11-06)
+
+### 前端 coupons.vue(`cfc-frontend/pages/profile-extra/coupons.vue`,565 行)
+- L82-85:`<button v-if="canClaim(coupon)" class="claim-btn" @click="handleClaim(coupon)">立即领取</button>` —— **bug 根源**
+- L86-87:会员引导 `member-guide-btn`(applicableTo===MEMBERSHIP && !isMember && !isGift && AVAILABLE → 去开通会员)
+- L236-237:`canClaim` = `tabIndex===0 && (!status || status==='AVAILABLE')`
+- L247-251:`handleClaim` → `claimCoupon(coupon.id)` → 报「优惠券不存在」
+- L239:`goGiftCoupon()` 已有(跳 `/pages/membership/gift-coupon`)
+- L97 import:`getMyCoupons, claimCoupon, getExchangeableCoupons, exchangeCouponByCf, getMyMembership`(需补 `productDetail`)
+- data:`coupons`(getMyCoupons 结果)、`isMember`、tabs(可使用/已使用/已过期/CF值兑换)
+
+### 后端(零改动)
+- `CouponService.toCouponVO`(L100-128):返回 `{id(券实例id=family_coupon.id), userCouponId, couponId(模板id), name, type, value, discountRate, minSpend, applicableTo, status, productId, grantType, ...}` —— **前端已含 productId + applicableTo + id**
+- `CouponController` L130 `POST /api/coupon/my`(getMyCoupons);`/api/coupon/list`(getCouponList)返回的 `id` 同为券实例 id(checkout.vue L761-762 注释已确认)
+
+### 目标路由(已存在,复用)
+- 确认订单:`/pages/shop/checkout/checkout`(checkout.vue,onLoad 读 `checkoutItems` storage 优先;`options.productId` 直购;L644+)
+- 商城:`/pages/shop/index/index`
+- 开通会员:`/pages/membership/upgrade`
+- 赠送会员券:`/pages/membership/gift-coupon`(无参,页内自列可赠券)
+- 商品详情:`/pages/discover-detail/product-detail/product-detail?id=`(product-detail.vue L773 写 `checkoutItems` storage 后跳 checkout)
+- `productDetail({id})`:api.js L1684 → `POST /api/product/detail`(入参 `{id}`,返回 ProductDTO 含 name/coverImage/price)
+
+### checkout.vue 优惠券逻辑
+- L515 `selectedCoupon`;L615 `ownedCoupons()` computed(过滤 AVAILABLE、minSpend 适配、PRODUCT 券按 `c.productId===currentProductId` 匹配、非 ALL/PRODUCT 过滤)
+- L715 `loadCoupons()` → `getCouponList()` → `this.coupons = res.data`
+- L806 `selectCoupon(coupon)`(选择券 → 金额重算)
+- L644 `onLoad(options)`(当前未处理 `options.couponId`)
+
+## 四、方案(仅改前端 2 文件,后端零改动)
+
+### 4.1 `cfc-frontend/pages/profile-extra/coupons.vue`
+1. **模板 L82-89 替换**:删除「立即领取」`canClaim` 分支;已领取券(status AVAILABLE/USED/EXPIRED 均属已领取)显示「转赠」+「使用」两按钮(会员券且非会员且 !isGift 时,使用按钮文案可保留「去开通会员」或统一「使用」跳开通会员——按用户口径:使用按钮统一存在,点击后按类型跳转,**会员券使用跳开通会员**)
+   - 按钮区:`<view class="coupon-actions"><button class="use-btn" @click="useCoupon(coupon)">使用</button><button class="gift-btn" @click="giftCoupon(coupon)">转赠</button></view>`
+   - 已使用/已过期券:不显示操作按钮(或仅显示状态徽标,保持现状)
+2. **新增 `useCoupon(coupon)`**:
+   - `applicableTo === 'PRODUCT'`:`const d = await productDetail({id: coupon.productId})` → 写 `uni.setStorageSync('checkoutItems', [{productId: d.data.id, productName: d.data.name, coverImage: d.data.coverImage, unitPrice: d.data.price, quantity: 1}])` → `uni.navigateTo('/pages/shop/checkout/checkout?from=coupon&id=' + d.data.id + '&couponId=' + coupon.id)`
+   - `applicableTo === 'ALL'`:`uni.navigateTo({url: '/pages/shop/index/index'})`
+   - `applicableTo === 'MEMBERSHIP'`:`uni.navigateTo({url: '/pages/membership/upgrade'})`
+   - 兜底(ASSESSMENT/其他/null):跳商城 `/pages/shop/index/index`
+   - 未登录先 `openLoginSheet()` 或沿用页面既有登录检查(参考 product-detail L745-747 先查 userId)
+3. **新增 `giftCoupon(coupon)`**:`uni.navigateTo({url: '/pages/membership/gift-coupon'})`(复用已有 goGiftCoupon 亦可)
+4. **import 补 `productDetail`**(api.js)
+5. **删除/停用** `canClaim`/`handleClaim`(若 claimCoupon 不再被页面其他处使用则移除 import `claimCoupon`;保留 `getExchangeableCoupons/exchangeCouponByCf` 供 CF 值兑换 tab)
+
+### 4.2 `cfc-frontend/pages/shop/checkout/checkout.vue`
+1. **onLoad**:`this._autoCouponId = options && options.couponId ? parseInt(options.couponId) : null`(在 loadCoupons 之前)
+2. **loadCoupons 回调后**(`this.coupons = res.data` 之后):若 `this._autoCouponId` 存在,在 `this.coupons` 中找 `c.id === this._autoCouponId`(券实例 id)→ 命中则 `this.selectCoupon(matched)`(自动选上,触发金额重算);未命中静默忽略
+   - 注意:`ownedCoupons` 依赖 `actualAmount`/`items[0].productId`,自动选券须在 `calcAmount()` 与商品 items 就绪后执行——**在 loadCoupons 回调里先确保 items/amount 已计算**(onLoad 里 calcAmount 先于 loadCoupons 调用,顺序已满足;若命中券因 minSpend 不满足被 ownedCoupons 过滤,则直接对 `this.coupons` 匹配后仍设 `selectedCoupon` 交由服务端校验折扣,或仅匹配 ownedCoupons 内——**建议:匹配 ownedCoupons 内的券,避免选到不可用券**)
+
+## 五、验收标准
+
+1. 「我的优惠券」已领取券显示「转赠」「使用」两按钮,不再显示「立即领取」,点击不再报「优惠券不存在」
+2. 指定商品券点击「使用」→ 跳确认订单页,商品为该券对应商品,**优惠券已自动选中**
+3. 全品类券点击「使用」→ 跳商城
+4. 会员券点击「使用」→ 跳开通会员
+5. 点击「转赠」→ 跳赠送会员券页
+6. 已使用/已过期券不显示操作按钮
+7. 构建 `npm run build:mp-weixin` exit 0
+
+## 六、硬约束
+
+- 只改 `cfc-frontend/pages/profile-extra/coupons.vue` + `cfc-frontend/pages/shop/checkout/checkout.vue`;不改后端、不改 api.js(仅 coupons.vue 顶部加一行 import `productDetail`)、不改 pages.json
+- 不破坏 CF 值兑换 tab(getExchangeableCoupons/exchangeCouponByCf 保留)与领券中心(coupon-center.vue 独立,不受影响)
+- 自动选券只影响 `from=coupon` 场景;其他入口(购物车/商品详情/扫码特价)行为不变
+- 基于 `origin/cfclub` 建 `feature/coupon-claimed-actions` 分支,commit + push,**不自行合入 cfclub**
+- 完成后 `git status --porcelain` 干净
+
+## 七、风险
+
+- `productDetail` 可能返回不同字段(price vs 现价)→ 开发会话以实际返回为准,unitPrice 用返回的金额字段(price/salePrice 之一,参考 product-detail 的 displayPrice 取值)
+- 券对象 `id` 与 `userCouponId` 同值(券实例 id),`couponId` 是模板 id——**跳转必须用 `coupon.id`(实例 id)作为 checkout 的 couponId 参数**,勿用 `coupon.couponId`(模板 id,会导致后端查无)
+- `ownedCoupons` 对 MEMBERSHIP 券会过滤(checkout 不用于会员券),会员券走「使用→开通会员」不经过 checkout,无冲突
+- 指定商品券若 `productId` 为空(配置遗漏)→ 兜底跳商城,不报错
+
+## 八、验证数据(供开发/核验参考)
+
+- MySQL `192.168.16.251:3306/zxyj`:`family_coupon`(券实例 id)→ `coupon_id`(模板 id);`coupon` 表含 `applicable_to/product_id/type`
+- 建议测试:后台建 3 张券(PRODUCT 指定商品 / ALL 全品类 / MEMBERSHIP 会员)各发 1 张到测试家庭,验证 4 类跳转 + 自动选券 + 无「立即领取」残留