自动修改
This commit is contained in:
@@ -35,7 +35,7 @@ User (Owner A) generates token → Shop scans/confirms (you)
|
||||
|
||||
## Hard rules
|
||||
|
||||
- Redis token: `redeem:token:{token}` TTL 300s
|
||||
- Redis token: `redeem:token:{token}` TTL **180s**(V3.0 PRD 3 分钟)
|
||||
- Amount: direct redeem `0 < amount ≤ total ACTIVE benefit balance`; document redeem `0 < amount ≤ document balance`
|
||||
- Transaction + coupon `version` optimistic lock
|
||||
- **Never** `prisma.order.update` in redeem module
|
||||
|
||||
@@ -48,4 +48,4 @@ export class TradeController {
|
||||
|
||||
- 埋点 → `log_user_analytics`;业务审计 → `common_event`
|
||||
- 支付 → `log_third_party` + `user_order.pay_*`(无 payments 表)
|
||||
- 核销码 → Redis 5min(无 DB 表)
|
||||
- 核销码 → Redis 3min(`REDEEM_TOKEN_TTL_SECONDS`)
|
||||
|
||||
@@ -20,7 +20,7 @@ V2 手册 §五(v3.1,28 表)。schema 与手册保持一致。
|
||||
## 关键约定
|
||||
|
||||
- 无 `payments` / `user_order_item` / `redeem_tokens` 表
|
||||
- 核销码仅 Redis 5 分钟
|
||||
- 核销码仅 Redis **3 分钟**(`REDEEM_TOKEN_TTL_SECONDS=180`)
|
||||
- 支付流水 → `log_third_party` + `user_order.pay_*`
|
||||
- 业务事件 → `common_event`;埋点 → `log_user_analytics`
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ H5 三端引用 `@dukang/shared-ui`(`tokens.css`)。admin-web 使用 Ant Des
|
||||
### 订单 5 Tab
|
||||
|
||||
```
|
||||
all | pending_pay | pending_ship | pending_receive | completed
|
||||
待付款 | 已付款 (`paid`) | 已完成
|
||||
```
|
||||
|
||||
## h5-shop 路由(刘景尧)
|
||||
@@ -112,7 +112,7 @@ all | pending_pay | pending_ship | pending_receive | completed
|
||||
|
||||
## UI 硬约束
|
||||
|
||||
- C 端订单 **5 Tab**(含 pending_ship)
|
||||
- C 端订单 **3 Tab**(`pending_pay` / `paid` / `completed`)
|
||||
- 个人中心无会员等级(V1 不做会员体系)
|
||||
- 清香型 4 SKU 可购;酱香/浓香灰态
|
||||
- 门店列表仅 `OPEN` 状态
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ V2 目标:`mini-user` / `mini-partner` / `mini-hq` 替换对应 H5(除门店
|
||||
- 类型从 `@dukang/shared-types` import,禁止复制枚举字符串
|
||||
- 禁止 import `server/` 或另一个 `apps/*` 的源码
|
||||
- UI 共享组件优先 `@dukang/shared-ui`
|
||||
- C 端订单列表 **5 Tab**(含 `pending_ship`)
|
||||
- C 端订单列表 **3 Tab**:`待付款 | 已付款 | 已完成`(Tab key: `pending_pay` / `paid` / `completed`)
|
||||
|
||||
## 新页面 workflow
|
||||
|
||||
@@ -32,7 +32,7 @@ V2 目标:`mini-user` / `mini-partner` / `mini-hq` 替换对应 H5(除门店
|
||||
|
||||
### h5-user(jacy-dukang)
|
||||
|
||||
主链路:登录 → 首页/详情 → 下单 Mock 支付 → 5 Tab 订单 → 权益 → 核销码 → 门店列表
|
||||
主链路:登录 → 首页/详情 → 下单 Mock 支付 → **3 Tab** 订单 → 权益 → 核销码 → 门店列表
|
||||
|
||||
**勿改**:门店核销确认 UI(属 h5-shop)、合伙人录店(属 h5-partner)
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ const DEFAULT_AVATAR =
|
||||
|
||||
const ORDER_SHORTCUTS = [
|
||||
{ tab: 'pending_pay', icon: 'payments', label: '待付款' },
|
||||
{ tab: 'pending_ship', icon: 'package_2', label: '待发货' },
|
||||
{ tab: 'pending_receive', icon: 'local_shipping', label: '配送中' },
|
||||
{ tab: 'paid', icon: 'package_2', label: '已付款' },
|
||||
{ tab: 'completed', icon: 'task_alt', label: '已完成' },
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -8,17 +8,16 @@ import { request } from '../lib/api';
|
||||
const TABS = [
|
||||
{ key: 'all', label: '全部' },
|
||||
{ key: 'pending_pay', label: '待付款' },
|
||||
{ key: 'pending_ship', label: '待发货' },
|
||||
{ key: 'pending_receive', label: '待收货' },
|
||||
{ key: 'paid', label: '已付款' },
|
||||
{ key: 'completed', label: '已完成' },
|
||||
];
|
||||
|
||||
const STATUS_LABEL: Record<string, string> = {
|
||||
PENDING_PAY: '待付款',
|
||||
PENDING_SHIP: '待发货',
|
||||
OUT_WAREHOUSE: '出库中',
|
||||
SHIPPING: '配送中',
|
||||
PENDING_RECEIVE: '待收货',
|
||||
PENDING_SHIP: '已付款',
|
||||
OUT_WAREHOUSE: '已付款',
|
||||
SHIPPING: '已付款',
|
||||
PENDING_RECEIVE: '已付款',
|
||||
COMPLETED: '已完成',
|
||||
};
|
||||
|
||||
|
||||
@@ -140,11 +140,11 @@ export default function PayPage() {
|
||||
if (!paid) {
|
||||
alert('支付结果确认中,请稍后在订单列表查看');
|
||||
}
|
||||
navigate('/orders?tab=pending_ship');
|
||||
navigate('/orders?tab=paid');
|
||||
return;
|
||||
}
|
||||
|
||||
navigate('/orders?tab=pending_ship');
|
||||
navigate('/orders?tab=paid');
|
||||
} catch (e) {
|
||||
if (isWechatAuthRequiredError(e)) {
|
||||
setNeedsWechatAuth(true);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { REDEEM_TOKEN_TTL_SECONDS } from '@dukang/shared-types';
|
||||
import PageHeader from '@dukang/shared-ui/PageHeader';
|
||||
|
||||
export default function RedeemCodePage() {
|
||||
const navigate = useNavigate();
|
||||
const token = sessionStorage.getItem('redeemToken') || '';
|
||||
const amount = sessionStorage.getItem('redeemAmount') || '0';
|
||||
const [secondsLeft, setSecondsLeft] = useState(300);
|
||||
const [secondsLeft, setSecondsLeft] = useState(REDEEM_TOKEN_TTL_SECONDS);
|
||||
|
||||
useEffect(() => {
|
||||
const expireAt = sessionStorage.getItem('redeemExpireAt');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { REDEEM_TOKEN_TTL_SECONDS } from '@dukang/shared-types';
|
||||
import { request } from '../lib/api';
|
||||
|
||||
const REDEEM_TOKEN_TTL_SECONDS = 300;
|
||||
const POLL_INTERVAL_MS = 2500;
|
||||
|
||||
type BenefitSummary = {
|
||||
@@ -236,7 +236,7 @@ export default function RedeemPage() {
|
||||
type="button"
|
||||
className="redeem-header-btn"
|
||||
aria-label="帮助"
|
||||
onClick={() => window.alert('核销金额不超过可用余额,核销码 5 分钟内有效。')}
|
||||
onClick={() => window.alert('核销金额不超过可用余额,核销码 3 分钟内有效。')}
|
||||
>
|
||||
<span className="material-symbols-outlined">help_outline</span>
|
||||
</button>
|
||||
@@ -301,7 +301,7 @@ export default function RedeemPage() {
|
||||
|
||||
<div className="redeem-security">
|
||||
<span className="material-symbols-outlined">shield</span>
|
||||
<p>安全提示:请在核销前核实金额,核销码将在 5 分钟后自动失效。</p>
|
||||
<p>安全提示:请在核销前核实金额,核销码将在 3 分钟后自动失效。</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
validateRedeemAmount,
|
||||
allocateBenefitCoupons,
|
||||
calcBenefitSummary,
|
||||
orderTabToStatuses,
|
||||
} from './index';
|
||||
|
||||
describe('calcBenefitAmount', () => {
|
||||
@@ -87,6 +88,24 @@ describe('allocateBenefitCoupons', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('orderTabToStatuses', () => {
|
||||
it('maps V3.0 three user tabs', () => {
|
||||
expect(orderTabToStatuses('pending_pay')).toEqual(['PENDING_PAY']);
|
||||
expect(orderTabToStatuses('paid')).toEqual([
|
||||
'PENDING_SHIP',
|
||||
'OUT_WAREHOUSE',
|
||||
'SHIPPING',
|
||||
'PENDING_RECEIVE',
|
||||
]);
|
||||
expect(orderTabToStatuses('completed')).toEqual(['COMPLETED']);
|
||||
});
|
||||
|
||||
it('keeps legacy tab keys mapped to paid', () => {
|
||||
expect(orderTabToStatuses('pending_ship')).toEqual(orderTabToStatuses('paid'));
|
||||
expect(orderTabToStatuses('pending_receive')).toEqual(orderTabToStatuses('paid'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcBenefitSummary', () => {
|
||||
it('max redeem equals total balance', () => {
|
||||
expect(calcBenefitSummary([300, 400])).toEqual({
|
||||
|
||||
@@ -115,10 +115,10 @@ export function orderTabToStatuses(tab: string): string[] | undefined {
|
||||
switch (tab) {
|
||||
case 'pending_pay':
|
||||
return ['PENDING_PAY'];
|
||||
case 'paid':
|
||||
case 'pending_ship':
|
||||
return ['PENDING_SHIP', 'OUT_WAREHOUSE'];
|
||||
case 'pending_receive':
|
||||
return ['SHIPPING', 'PENDING_RECEIVE'];
|
||||
return ['PENDING_SHIP', 'OUT_WAREHOUSE', 'SHIPPING', 'PENDING_RECEIVE'];
|
||||
case 'completed':
|
||||
return ['COMPLETED'];
|
||||
default:
|
||||
|
||||
@@ -40,9 +40,13 @@ export enum OrderStatus {
|
||||
export enum OrderTab {
|
||||
ALL = 'all',
|
||||
PENDING_PAY = 'pending_pay',
|
||||
PENDING_SHIP = 'pending_ship',
|
||||
PENDING_RECEIVE = 'pending_receive',
|
||||
/** V3.0 PRD:已付款(含待发货/配送中,内部多状态聚合) */
|
||||
PAID = 'paid',
|
||||
COMPLETED = 'completed',
|
||||
/** @deprecated 兼容旧链接,映射同 PAID */
|
||||
PENDING_SHIP = 'pending_ship',
|
||||
/** @deprecated 兼容旧链接,映射同 PAID */
|
||||
PENDING_RECEIVE = 'pending_receive',
|
||||
}
|
||||
|
||||
export enum DeliveryType {
|
||||
@@ -91,6 +95,9 @@ export const CLIENT_APP_ACTOR_MAP: Record<ClientApp, ActorType> = {
|
||||
|
||||
export const REDEEM_DIRECT_LIMIT_POLICY = 'TOTAL_ACTIVE_BALANCE';
|
||||
export const REDEEM_DOCUMENT_LIMIT_POLICY = 'DOCUMENT_BALANCE';
|
||||
export const REDEEM_TOKEN_TTL_SECONDS = 300;
|
||||
/** V3.0 PRD:核销码 3 分钟有效 */
|
||||
export const REDEEM_TOKEN_TTL_SECONDS = 180;
|
||||
/** 短信/核销验证码 3 分钟有效(NFR-003) */
|
||||
export const SMS_CODE_TTL_SECONDS = 180;
|
||||
/** 核销成功后供用户端轮询结果,略长于 token TTL */
|
||||
export const REDEEM_RESULT_TTL_SECONDS = 600;
|
||||
export const REDEEM_RESULT_TTL_SECONDS = 360;
|
||||
|
||||
@@ -80,7 +80,7 @@ async function main() {
|
||||
await prisma.commonCityCommissionRule.create({
|
||||
data: {
|
||||
cityId: city.id,
|
||||
orderCommissionRate: 0.05,
|
||||
orderCommissionRate: 0,
|
||||
redeemCommissionRate: 0.03,
|
||||
partnerProfitRate: 0.35,
|
||||
storeSettlementRate: 0.6,
|
||||
@@ -111,10 +111,10 @@ async function main() {
|
||||
}
|
||||
|
||||
const productDefs = [
|
||||
{ skuCode: 'QX-001', name: '杜康·白水古酿 500ml', subtitle: '清香型 52度 礼盒装', price: 599, sortOrder: 1, img: 'https://picsum.photos/seed/dukang1/400/400' },
|
||||
{ skuCode: 'QX-002', name: '杜康·年份陈酿(十年)', subtitle: '清香型 42度 纯粮酿造', price: 880, sortOrder: 2, img: 'https://picsum.photos/seed/dukang2/400/400' },
|
||||
{ skuCode: 'QX-003', name: '杜康·御享1号 珍藏版', subtitle: '高端定制 限量发售', price: 1299, sortOrder: 3, img: 'https://picsum.photos/seed/dukang3/400/400' },
|
||||
{ skuCode: 'QX-004', name: '杜康·经典传承', subtitle: '清香型 纯粮固态', price: 399, sortOrder: 4, img: 'https://picsum.photos/seed/dukang4/400/400' },
|
||||
{ skuCode: 'JZ-10', name: '酒祖杜康(国标特级10)', subtitle: '清香型 53度', price: 128, sortOrder: 1, img: 'https://picsum.photos/seed/jiuzu10/400/400' },
|
||||
{ skuCode: 'JZ-15', name: '酒祖杜康(国标特级15)', subtitle: '清香型 53度', price: 168, sortOrder: 2, img: 'https://picsum.photos/seed/jiuzu15/400/400' },
|
||||
{ skuCode: 'JZ-20', name: '酒祖杜康(国标特级20)', subtitle: '清香型 53度', price: 298, sortOrder: 3, img: 'https://picsum.photos/seed/jiuzu20/400/400' },
|
||||
{ skuCode: 'JZ-30', name: '酒祖杜康(国标特级30)', subtitle: '清香型 53度', price: 498, sortOrder: 4, img: 'https://picsum.photos/seed/jiuzu30/400/400' },
|
||||
];
|
||||
|
||||
const products = [];
|
||||
@@ -126,7 +126,7 @@ async function main() {
|
||||
name: def.name,
|
||||
subtitle: def.subtitle,
|
||||
aromaType: 'QINGXIANG',
|
||||
spec: def.skuCode === 'QX-002' ? '500ml | 42度' : def.skuCode === 'QX-004' ? '500ml | 46度' : '500ml | 52度',
|
||||
spec: '500ml | 53度',
|
||||
price: def.price,
|
||||
benefitAmount: def.price,
|
||||
status: 'ON_SALE',
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { loadAppConfig } from '@dukang/shared-types';
|
||||
import { SMS_CODE_TTL_SECONDS, loadAppConfig } from '@dukang/shared-types';
|
||||
import { RedisService } from '../../common/redis/redis.service';
|
||||
|
||||
const CODE_TTL_SECONDS = 300;
|
||||
const RATE_TTL_SECONDS = 60;
|
||||
|
||||
function codeKey(phone: string, scene: string) {
|
||||
@@ -36,7 +35,7 @@ export class SmsCodeStore {
|
||||
|
||||
async generateAndStore(phone: string, scene: string): Promise<string> {
|
||||
const code = this.config.mockSms ? this.config.mockSmsCode : randomSixDigitCode();
|
||||
await this.redis.client.set(codeKey(phone, scene), code, 'EX', CODE_TTL_SECONDS);
|
||||
await this.redis.client.set(codeKey(phone, scene), code, 'EX', SMS_CODE_TTL_SECONDS);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ export class AdminCitiesService {
|
||||
status: (dto.status ?? 'PENDING') as 'PENDING' | 'ACTIVE' | 'PAUSED',
|
||||
commissionRule: {
|
||||
create: {
|
||||
orderCommissionRate: 0.05,
|
||||
orderCommissionRate: 0,
|
||||
redeemCommissionRate: 0.03,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -225,7 +225,7 @@ export class SettlementService {
|
||||
const cityIds = cities.map((c) => c.id);
|
||||
const defaultOrderRate = cities[0]?.commissionRule?.orderCommissionRate
|
||||
? Number(cities[0].commissionRule.orderCommissionRate)
|
||||
: 0.05;
|
||||
: 0;
|
||||
const defaultRedeemRate = cities[0]?.commissionRule?.redeemCommissionRate
|
||||
? Number(cities[0].commissionRule.redeemCommissionRate)
|
||||
: 0.03;
|
||||
|
||||
+55
-28
@@ -14,14 +14,15 @@
|
||||
|------|------|
|
||||
| **整体完成度(粗估)** | Wave 1 约 **45%**;Wave 2 约 **10%**;Wave 3 约 **5%** |
|
||||
| **可跑通的主链路** | 登录 → 浏览商品 → 同城下单 Mock/微信支付 → 权益 1:1 → **扫码核销** → T+1 门店 payout 记录 → 总部确认打款 |
|
||||
| **最大阻塞** | 端形态(无小程序 C 端)、规则常量冲突(TTL/Tab/SKU/佣金)、Wave 1 缺口(手机号核销/规则弹窗/试核销/自动推小飞侠) |
|
||||
| **最大阻塞** | C 端小程序迁移(暂缓)、Wave 1 缺口(手机号核销/规则弹窗/试核销/自动推小飞侠) |
|
||||
| **P0 冲突** | C2~C7、C14 **已修复**(2026-07-12) |
|
||||
| **冒烟覆盖** | `scripts/smoke-v3.mjs` 仅覆盖窄 W1 切片,**不等于** V3.0 ACC 全量 |
|
||||
|
||||
### Wave 门禁状态
|
||||
|
||||
| 门禁 | 状态 | 一句话 |
|
||||
|------|------|--------|
|
||||
| **W1-S1** 下单+支付+权益 | 🔶 | 链路可跑,SKU/订单 Tab/TTL 不合规 |
|
||||
| **W1-S1** 下单+支付+权益 | 🔶 | TTL/Tab/SKU/佣金已对齐;仍缺小程序形态 |
|
||||
| **W1-S2** 门店核销双通道 | 🔶 | 扫码 ✅;手机号核销 ❌ |
|
||||
| **W1-S3** 合伙人子账号 | ✅ | CRUD + 推广员菜单裁剪已有 |
|
||||
| **W1-S4** 拓店→审核→C 端可见 | 🔶 | 三步录入有;试核销100/负责人复核/SOP 缺失 |
|
||||
@@ -40,26 +41,54 @@
|
||||
|
||||
---
|
||||
|
||||
## 1. ⚠️ 冲突项(必须先改)
|
||||
## 1. ⚠️ 冲突项与改造计划
|
||||
|
||||
> 代码已存在,但与 V3.0 PRD **直接矛盾**,继续开发会累积返工。
|
||||
> 代码已存在但与 V3.0 PRD 直接矛盾。**P0 已于 2026-07-12 执行**;P1/P2 排后续迭代。
|
||||
|
||||
| # | V3.0 要求 | 当前实现 | 证据位置 | 改造建议 |
|
||||
|---|-----------|----------|----------|----------|
|
||||
| C1 | C 端 = **微信小程序** | `apps/h5-user` H5;无 `mini-user` | `apps/h5-user/` | 新建小程序或明确 H5 为过渡并排迁移 |
|
||||
| C2 | 核销码 TTL **3 分钟** | **300 秒(5 分钟)** | `packages/shared-types/src/enums.ts` `REDEEM_TOKEN_TTL_SECONDS=300`;`apps/h5-user/src/pages/RedeemPage.tsx` 文案 | 改常量为 180 + 全端文案 |
|
||||
| C3 | 短信验证码 **3 分钟** | Redis TTL **300 秒** | `server/dukang-api/src/integrations/sms/sms-code.store.ts` | 改 `CODE_TTL_SECONDS=180` |
|
||||
| C4 | 订单 Tab:**待付款 / 已付款 / 已完成** | **5 Tab**(含待发货/待收货) | `apps/h5-user/src/pages/OrderListPage.tsx`;`OrderTab` enum | 改 Tab 映射与列表 API |
|
||||
| C5 | 订单状态机三态展示 | **9 态** `OrderStatus` | `packages/shared-types/src/enums.ts` | PRD 三 Tab 映射到细粒度状态,或收敛状态机 |
|
||||
| C6 | 4 款 **酒祖杜康** + 锚点价 | preV1 杜康系列 ¥399–1299 | `server/dukang-api/prisma/seed-v31.ts` | 替换 seed + 商品管理数据 |
|
||||
| C7 | 默认佣金 **0% 订单 + 3% 核销** | seed **5% 订单佣金** | `seed-v31.ts` `orderCommissionRate: 0.05` | 改 seed 与开城默认 |
|
||||
| C8 | **一号多店**(同号可多店主账号) | `StoreAccount.phone @unique`;创建时**拒绝**重复手机 | `schema.prisma` `StoreAccount`;`store.service.ts` `partnerCheckStorePhone` | 改模型:主账号与门店多对多 |
|
||||
| C9 | 支付后**自动推小飞侠**(同城) | 支付后 delivery `MANUAL`;总部手动推单 | `trade.service.ts`;`admin-xiaofeixia.service.ts` | 支付回调后接 xiaofeixia |
|
||||
| C10 | 门店结算 = **门店主动提现**(W2) | 核销后**自动生成** `StorePayout` | `settlement.service.ts` `createStorePayout` | 增加余额账本 + 提现申请流 |
|
||||
| C11 | 工单 **四类型** | enum 仅 **3 类** `REFUND/RESHIPMENT/ALERT` | `schema.prisma` `TicketType` | 扩展 enum + 用户建单 UI |
|
||||
| C12 | 配送类型含 **现场提货** | `DeliveryType` 仅 `LOCAL/CROSS_CITY` | `packages/shared-types/src/enums.ts` | 新增 `ON_SITE_PICKUP` |
|
||||
| C13 | 多合伙人(全城/区域平级) | 城市 `partnerId` **单合伙人** | `schema.prisma` `CommonCity.partnerId` | 新增合伙人管辖/区县/佣金快照模型 |
|
||||
| C14 | 文档/规则残留 5min、5 Tab | `apps/AGENTS.md`、backend rules 仍写旧口径 | `.cursor/rules/`、`apps/AGENTS.md` | 批量对齐 V3.0 |
|
||||
### 1.1 改造计划总览
|
||||
|
||||
| 阶段 | 冲突项 | 内容 | 状态 |
|
||||
|------|--------|------|------|
|
||||
| **P0** | C2~C7、C14 | TTL、订单三 Tab、SKU seed、佣金默认、文档规则 | ✅ **已执行** |
|
||||
| **P1** | C1 | C 端 H5 过渡;目标微信小程序另立项 | 📋 已决策暂缓 |
|
||||
| **P1** | C9 | 支付后自动推小飞侠 | 📋 待做(Wave 1) |
|
||||
| **P2** | C8 | 一号多店(schema + 拓店确认) | 📋 待做(Wave 2) |
|
||||
| **P2** | C10 | 门店主动提现 + FIN 护栏 | 📋 待做(Wave 2) |
|
||||
| **P2** | C12 | 现场提货 `ON_SITE_PICKUP` | 📋 待做(Wave 2) |
|
||||
| **P3** | C11 | 工单四类型 | 📋 待做(Wave 3) |
|
||||
| **P3** | C13 | 多合伙人管辖/佣金快照 | 📋 待做(Wave 2~3) |
|
||||
|
||||
### 1.2 冲突明细
|
||||
|
||||
| # | V3.0 要求 | 改造前 | 状态 | 改造记录(2026-07-12) |
|
||||
|---|-----------|--------|------|------------------------|
|
||||
| C1 | C 端微信小程序 | `h5-user` H5 | 📋 暂缓 | 工程决策:H5 过渡,小程序另立项 |
|
||||
| C2 | 核销码 TTL **3 分钟** | 300s | ✅ 已修复 | `REDEEM_TOKEN_TTL_SECONDS=180`;C 端文案 |
|
||||
| C3 | 短信验证码 **3 分钟** | 300s | ✅ 已修复 | `SMS_CODE_TTL_SECONDS=180` |
|
||||
| C4 | 订单 Tab 三态 | 5 Tab | ✅ 已修复 | `pending_pay` / `paid` / `completed` |
|
||||
| C5 | 列表展示三态 | 9 态直出 | ✅ 已修复 | 内部状态保留;Tab/列表映射三态 |
|
||||
| C6 | 4 款酒祖杜康 + 锚点价 | preV1 SKU | ✅ 已修复 | `seed-v31.ts` 四 SKU ¥128~498 |
|
||||
| C7 | 佣金 0%+3% | seed 5% 订单 | ✅ 已修复 | seed + `admin-cities` + settlement 默认 |
|
||||
| C8 | 一号多店 | phone @unique | 📋 P2 | 待 schema + `partnerCheckStorePhone` |
|
||||
| C9 | 支付后推小飞侠 | MANUAL delivery | 📋 P1 | 待 `trade.service` 支付回调 |
|
||||
| C10 | 门店主动提现 | 自动 StorePayout | 📋 P2 | 待提现申请流 |
|
||||
| C11 | 工单四类型 | 3 enum | 📋 P3 | 待扩展 `TicketType` |
|
||||
| C12 | 现场提货 | 无 DeliveryType | 📋 P2 | 待 `ON_SITE_PICKUP` |
|
||||
| C13 | 多合伙人 | 单 partnerId | 📋 P2~3 | 待管辖/快照模型 |
|
||||
| C14 | 文档残留旧口径 | 5min/5 Tab | ✅ 已修复 | `apps/AGENTS.md`、rules、agents、skills |
|
||||
|
||||
### 1.3 P0 已改文件清单
|
||||
|
||||
| 模块 | 文件 |
|
||||
|------|------|
|
||||
| 契约 | `packages/shared-types/src/enums.ts` |
|
||||
| 规则 | `packages/domain/src/index.ts`、`index.test.ts` |
|
||||
| 短信 | `server/.../sms/sms-code.store.ts` |
|
||||
| 佣金 | `seed-v31.ts`、`admin-cities.service.ts`、`settlement.service.ts` |
|
||||
| C 端 | `OrderListPage.tsx`、`MinePage.tsx`、`PayPage.tsx`、`RedeemPage.tsx`、`RedeemCodePage.tsx` |
|
||||
| 文档 | `apps/AGENTS.md`、`.cursor/rules/*`、`.cursor/agents/owner-d-shop-redeem.md` |
|
||||
|
||||
> **重跑 seed**:本地需 `cd server/dukang-api && pnpm prisma:seed` 后商品/佣金数据才与 C6/C7 一致。
|
||||
|
||||
---
|
||||
|
||||
@@ -73,13 +102,13 @@
|
||||
| REQ-U-002 | 登录 + 下单强制手机号 | `OrderConfirmPage.tsx`、`PhoneVerifiedGuard` | ✅ |
|
||||
| REQ-U-003 | 微信支付 | `pay-wechat.ts` + 后端 callback | Mock/真实均有 |
|
||||
| REQ-U-004 | 定位/开城 | `HomePage.tsx`、`wechat-location.ts` | 🔶 H5 定位 API |
|
||||
| REQ-U-005~006 | 商品列表/详情 | `HomePage.tsx`、`ProductDetailPage.tsx` | ⚠️ SKU 数据见 C7 |
|
||||
| REQ-U-005~006 | 商品列表/详情 | `HomePage.tsx`、`ProductDetailPage.tsx` | ✅ seed 已对齐酒祖杜康 |
|
||||
| REQ-U-007 | 同城 ≥2 瓶 | `packages/domain` + `trade.service.ts` | ✅ |
|
||||
| REQ-U-010 | 锁单写入 30min | `trade.service.ts` `payExpireAt` | 🔶 无自动取消 job |
|
||||
| REQ-U-011 | 权益 1:1 | `benefit.service.ts` | ✅ |
|
||||
| REQ-U-012 | 客服入口 | `CustomerServicePage.tsx` | 🔶 Mock 对话 |
|
||||
| REQ-U-013~014 | 门店列表/详情 | `StoreListPage.tsx` 等 | ✅ 仅 OPEN |
|
||||
| REQ-U-015 | 出码核销 | `RedeemPage.tsx` | ⚠️ TTL 见 C3 |
|
||||
| REQ-U-015 | 出码核销 | `RedeemPage.tsx` | ✅ TTL 3 分钟 |
|
||||
| REQ-U-017 | 核销后评价 | `RedeemSuccessPage.tsx` | ✅ |
|
||||
| REQ-U-018 | 权益页 Tab | `BenefitPage.tsx` | ✅ |
|
||||
| REQ-U-019 | 个人中心 | `MinePage.tsx` | ✅ |
|
||||
@@ -220,12 +249,9 @@
|
||||
|
||||
## 6. 建议改造顺序(仅 V3.0 视角)
|
||||
|
||||
### P0 — 消除冲突(1~2 天)
|
||||
### P0 — 消除冲突 ✅ 已完成(2026-07-12)
|
||||
|
||||
1. C2/C3:核销码 + 短信 TTL → 3 分钟
|
||||
2. C6/C7:seed SKU + 佣金默认值
|
||||
3. C4/C5:订单三 Tab 与 API 映射
|
||||
4. C14:更新 `apps/AGENTS.md`、`.cursor/rules` 残留旧口径
|
||||
C2~C7、C14 见 §1.3。
|
||||
|
||||
### P1 — 补齐 Wave 1 门禁(3~5 天)
|
||||
|
||||
@@ -264,5 +290,6 @@
|
||||
|
||||
| 日期 | 说明 |
|
||||
|------|------|
|
||||
| 2026-07-12 | 明确总部端保持 WebAdmin,不改为 H5;移除原 C2 端形态冲突 |
|
||||
| 2026-07-12 | **P0 已执行**:C2~C7、C14 代码与文档对齐;§1 改为计划+状态表 |
|
||||
| 2026-07-12 | 明确总部端保持 WebAdmin,不改为 H5 |
|
||||
| 2026-07-11 | 首版:V3.0 PRD 全量对照当前 monorepo |
|
||||
|
||||
Reference in New Issue
Block a user