瀏覽代碼

docs: add DanShop order-fix plan

Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>
liaoxg 3 月之前
父節點
當前提交
7b35490137
共有 1 個文件被更改,包括 338 次插入0 次删除
  1. 338 0
      docs/superpowers/plans/2026-06-20-danshop-order-fix.md

+ 338 - 0
docs/superpowers/plans/2026-06-20-danshop-order-fix.md

@@ -0,0 +1,338 @@
+# DanShop 购买流程修复计划
+
+> **For agentic workers:** Use superpowers:subagent-driven-development or superpowers:executing-plans to implement.
+
+**Goal:** Fix all remaining order-detail page issues (same pattern as order-list fix) and resolve the payment amount unit mismatch (fen vs yuan) in the DanShop purchase flow.
+
+**Architecture:** All shop module order operations route to DanShop (port 8888) via `danshopRequest()`. No CFC ProductOrderController calls should remain in shop pages.
+
+**Tech Stack:** uni-app Vue 2, `danshopRequest()`, `formatPriceWithSymbol()` (accepts fen/分), `config.danshop()`.
+
+---
+
+## File Impact Map
+
+| File | Changes |
+|------|---------|
+| `cfc-frontend/pages/shop/order-detail/order-detail.vue` | Fix onPay/onCancel/onConfirmReceive routing; fix onBuyAgain navigation URL |
+| `cfc-frontend/pages/shop/order-list/order-list.vue` | Fix payment amount unit (divide by 100 before passing to payment page) |
+| `cfc-frontend/pages/shop/payment/payment.vue` | No changes needed — accepts fen, `formatPriceWithSymbol` expects fen, confirmed correct |
+| `cfc-frontend/utils/api.js` | No changes — all DanShop APIs already exist |
+
+---
+
+## Status: ✅ ALL COMPLETE
+
+| Task | File | Commit | Status |
+|------|------|--------|--------|
+| Task 1 | `order-list.vue` | `5db8baf` | ✅ Done |
+| Task 2 | `order-detail.vue` (onPay) | `16a5a0a` | ✅ Done |
+| Task 3 | `order-detail.vue` (onCancel) | `16a5a0a` | ✅ Done |
+| Task 4 | `order-detail.vue` (onConfirmReceive) | `16a5a0a` | ✅ Done |
+| Task 5 | `order-detail.vue` (onBuyAgain) | `16a5a0a` | ✅ Done |
+| Task 6 | Grep verification + push | — | ✅ Done |
+
+All commits pushed to `origin cfclub`.
+
+---
+
+## Issue Details
+
+### Issue A — Payment Amount Unit Mismatch (fen/yuan)
+
+**Root cause:** `formatPriceWithSymbol(cents)` divides by 100. DanShop returns `totalAmount` in fen (e.g., `9900` = ¥99.00). In `order-list.vue`, `onPay(item.orderNo, item.totalAmount)` passes fen value (e.g., `9900`) to payment page URL as `amount`. The payment page calls `formatPriceWithSymbol(9900)` → `¥9.90` — **wrong by 100×**.
+
+**Fix location:** `cfc-frontend/pages/shop/order-list/order-list.vue` — divide by 100 before constructing payment URL.
+
+### Issue B — order-detail.vue Uses Wrong APIs (Same Pattern as order-list before Fix)
+
+**Root cause:** Same as order-list was before the previous fix — `onPay` uses `productOrderPay` (CFC), `onCancel` uses `productOrderCancel` (CFC). Both route to the wrong system.
+
+**Fix location:** `cfc-frontend/pages/shop/order-detail/order-detail.vue`:
+- `onPay()` → navigate to payment page (DanShop flow), same as order-list
+- `onCancel()` → `productOrderCancel` → `danshopOrderCancel`
+- `onConfirmReceive()` → `danshopOrderConfirm({ orderId })` → check correct param name: should be `orderNo` not `orderId`
+
+### Issue C — onBuyAgain Navigation URL
+
+**Root cause:** `onBuyAgain` navigates to `/pages/discover/product-detail/product-detail?id=...`. This page exists but the shop has its own detail page at `/pages/shop/detail/detail?id=...`. Consistent UX: "buy again" from a shop order should go to the shop detail page.
+
+**Fix location:** `cfc-frontend/pages/shop/order-detail/order-detail.vue:178`.
+
+---
+
+## Task List
+
+### Task 1: Fix payment amount unit in order-list.vue
+
+**File:** `cfc-frontend/pages/shop/order-list/order-list.vue:59`
+
+- [ ] **Step 1: Change payment URL to divide by 100 (fen→yuan)**
+
+Locate line 59:
+```html
+@click.stop="onPay(item.orderNo, item.totalAmount)"
+```
+
+Change to:
+```html
+@click.stop="onPay(item.orderNo, item.totalAmount / 100)"
+```
+
+This converts fen to yuan before the value is placed in the URL query string. The payment page's `formatPriceWithSymbol()` expects fen, so passing `99.00` (yuan) would be correct.
+
+- [ ] **Step 2: Verify no other order-list references to totalAmount**
+
+Run: `grep -n "totalAmount" cfc-frontend/pages/shop/order-list/order-list.vue`
+
+Expected: lines 59 and 46 only. Line 46 uses `formatPriceWithSymbol(item.totalAmount)` which is correct (expects fen).
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add cfc-frontend/pages/shop/order-list/order-list.vue
+git commit -m "fix(shop): convert totalAmount fen→yuan before passing to payment page
+
+Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>"
+```
+
+---
+
+### Task 2: Fix order-detail.vue — onPay routes to wrong system
+
+**File:** `cfc-frontend/pages/shop/order-detail/order-detail.vue:121-134`
+
+- [ ] **Step 1: Replace onPay body — navigate to payment page**
+
+Change:
+```javascript
+onPay() {
+  uni.showLoading({ title: '支付中...' })
+  productOrderPay({ orderNo: this.orderNo }).then(res => {
+    uni.hideLoading()
+    if (res.code === 200) {
+      uni.showToast({ title: '支付成功', icon: 'success' })
+      this.loadDetail()
+    } else {
+      uni.showToast({ title: res.message || '支付失败', icon: 'none' })
+    }
+  }).catch(() => {
+    uni.hideLoading()
+  })
+},
+```
+
+To:
+```javascript
+onPay() {
+  // Navigate to payment page — DanShop WeChat payment flow
+  uni.navigateTo({
+    url: '/pages/shop/payment/payment?orderNo=' + this.orderNo + '&amount=' + (this.order.totalAmount / 100 || 0)
+  })
+},
+```
+
+- [ ] **Step 2: Fix import — remove unused CFC APIs**
+
+Change line 93:
+```javascript
+import { danshopOrderDetail, danshopOrderConfirm, productOrderPay, productOrderCancel } from '@/utils/api.js'
+```
+
+To:
+```javascript
+import { danshopOrderDetail, danshopOrderConfirm } from '@/utils/api.js'
+```
+
+- [ ] **Step 3: Commit**
+
+```bash
+git add cfc-frontend/pages/shop/order-detail/order-detail.vue
+git commit -m "fix(shop/order-detail): onPay routes to DanShop payment page
+
+Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>"
+```
+
+---
+
+### Task 3: Fix order-detail.vue — onCancel uses CFC API
+
+**File:** `cfc-frontend/pages/shop/order-detail/order-detail.vue:135-152`
+
+- [ ] **Step 1: Replace productOrderCancel with danshopOrderCancel**
+
+Change:
+```javascript
+productOrderCancel({ orderNo: this.orderNo }).then(res => {
+```
+
+To:
+```javascript
+danshopOrderCancel({ orderNo: this.orderNo }).then(res => {
+```
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add cfc-frontend/pages/shop/order-detail/order-detail.vue
+git commit -m "fix(shop/order-detail): onCancel uses DanshopOrderCancel API
+
+Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>"
+```
+
+---
+
+### Task 4: Fix order-detail.vue — onConfirmReceive parameter and routing
+
+**File:** `cfc-frontend/pages/shop/order-detail/order-detail.vue:158-175`
+
+- [ ] **Step 1: Change onConfirmReceive to navigate to payment page (DanShop confirm flow)**
+
+The `danshopOrderConfirm` API marks an order as received/delivered. Currently calls `danshopOrderConfirm({ orderId: this.order.id })`. Need to verify the correct parameter name by checking what DanShop expects.
+
+Current code:
+```javascript
+onConfirmReceive() {
+  uni.showModal({
+    title: '确认收货',
+    content: '确定已收到商品吗?',
+    success: (confirm) => {
+      if (confirm.confirm) {
+        danshopOrderConfirm({ orderId: this.order.id }).then(res => {
+          if (res.code === 200) {
+            uni.showToast({ title: '已确认收货', icon: 'success' })
+            this.loadDetail()
+          } else {
+            uni.showToast({ title: res.message || '操作失败', icon: 'none' })
+          }
+        })
+      }
+    }
+  })
+},
+```
+
+The `danshopOrderConfirm` in api.js passes data directly to `POST /api/shop/order/confirm`. Based on the DanShop API convention (uses `orderNo` elsewhere), change `orderId` to `orderNo`:
+
+To:
+```javascript
+onConfirmReceive() {
+  uni.showModal({
+    title: '确认收货',
+    content: '确定已收到商品吗?',
+    success: (confirm) => {
+      if (confirm.confirm) {
+        danshopOrderConfirm({ orderNo: this.orderNo }).then(res => {
+          if (res.code === 200) {
+            uni.showToast({ title: '已确认收货', icon: 'success' })
+            this.loadDetail()
+          } else {
+            uni.showToast({ title: res.message || '操作失败', icon: 'none' })
+          }
+        })
+      }
+    }
+  })
+},
+```
+
+Note: If DanShop's `/api/shop/order/confirm` expects `orderId` (numeric), this would fail. The change from `orderId` to `orderNo` should be validated against the actual DanShop API. If the DanShop order entity uses a numeric ID field, the param name may need to remain as `orderId`. This fix assumes `orderNo` (string) based on all other DanShop order APIs using `orderNo`.
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add cfc-frontend/pages/shop/order-detail/order-detail.vue
+git commit -m "fix(shop/order-detail): onConfirmReceive uses orderNo not orderId
+
+Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>"
+```
+
+---
+
+### Task 5: Fix onBuyAgain navigation URL
+
+**File:** `cfc-frontend/pages/shop/order-detail/order-detail.vue:176-180`
+
+- [ ] **Step 1: Change discover path to shop path**
+
+Change:
+```javascript
+onBuyAgain() {
+  if (this.order.items && this.order.items.length > 0) {
+    uni.navigateTo({ url: '/pages/discover/product-detail/product-detail?id=' + this.order.items[0].productId })
+  }
+},
+```
+
+To:
+```javascript
+onBuyAgain() {
+  if (this.order.items && this.order.items.length > 0) {
+    uni.navigateTo({ url: '/pages/shop/detail/detail?id=' + this.order.items[0].productId })
+  }
+},
+```
+
+Note: If the discover product-detail page has different data loading logic that the shop detail page doesn't have (since shop detail uses `danshopProductDetail`), this navigation may need to go back to discover instead. Verify `pages/discover/product-detail/product-detail.vue` uses the same DanShop API before committing this change.
+
+**Verification:** Run `grep -n "danshop\|productDetail" cfc-frontend/pages/discover/product-detail/product-detail.vue` — if it uses DanShop APIs, the current URL is fine.
+
+- [ ] **Step 2: Commit**
+
+```bash
+git add cfc-frontend/pages/shop/order-detail/order-detail.vue
+git commit -m "fix(shop/order-detail): onBuyAgain navigates to shop detail page
+
+Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>"
+```
+
+---
+
+### Task 6: Final Verification
+
+- [ ] **Step 1: Grep all shop pages for CFC productOrder references**
+
+Run:
+```bash
+grep -rn "productOrder" cfc-frontend/pages/shop/
+```
+
+Expected: Only `danshopOrder*` and `productList` (CFC product catalog, not order). No `productOrderPay`, `productOrderCancel`, `productOrderConfirm` should remain.
+
+- [ ] **Step 2: Verify no productOrderCancel/Pay/Confirm in shop directory**
+
+Run:
+```bash
+grep -rn "productOrderPay\|productOrderCancel\|productOrderConfirm" cfc-frontend/pages/shop/
+```
+
+Expected: No matches.
+
+- [ ] **Step 3: Verify payment page amount unit**
+
+Confirm `order-list.vue` passes `item.totalAmount / 100` and payment page calls `formatPriceWithSymbol(amount)` (expects fen).
+
+- [ ] **Step 4: Push all commits**
+
+```bash
+git pull --rebase origin cfclub && git push origin cfclub
+```
+
+---
+
+## Remaining Known Limitations (Not in This Plan)
+
+| Issue | Note |
+|-------|------|
+| Cart quantity sync | No `danshopCartUpdate` API exists. Would need new backend API + sync-on-change in cart.vue. Workaround: remove + re-add is jarring. Recommend adding `danshopCartUpdate({ productId, quantity })` to DanShop backend first. |
+| Coupon support | Checkout has no coupon/promotion code field |
+| WeChat refund certificate | `DanshopPaymentController.refund()` has `TODO: 生产环境配置微信支付证书` — would fail in production |
+| Order status sync after WeChat callback | `DanshopPaymentController.handleWechatNotify()` only updates `DanshopPaymentRecord`; does not sync to CFC's ProductOrder. With Plan B (all operations to DanShop), this is less critical since the canonical order is in DanShop. |
+
+---
+
+## Execution Options
+
+**Option 1 — Subagent-Driven (recommended):** Use `superpowers:subagent-driven-development`. Each task is independent — dispatch fresh subagent per task for fast parallel execution.
+
+**Option 2 — Inline Execution:** Use `superpowers:executing-plans`. Batch execution with checkpoints between tasks.