用户使用流程图 — 购物车与优惠券
端口说明: 📱 小程序 | 🖥 管理后台 | 📋 规划师端 | ⚙️ 系统自动
分层说明: 🏠 页面 → 🔗 API → ⚙️ Service → 📦 数据实体
flowchart TD
%% ============ 颜色定义 ============
classDef page fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef api fill:#fff3e0,stroke:#f57c00,stroke-width:1px
classDef service fill:#e8f5e9,stroke:#388e3c,stroke-width:1px
classDef data fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px
classDef external fill:#fce4ec,stroke:#d32f2f,stroke-width:1px,stroke-dasharray:3 2
classDef actor fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,stroke-dasharray:5 3
%% ============ 角色 ============
ROLE_PARENT(("👤 家长")):::actor
ROLE_ADMIN(("🔧 管理员")):::actor
%% ================================================================
%% 阶段一:购物车管理
%% ================================================================
subgraph 阶段一[阶段一:购物车管理]
direction TB
P1["🏠 小程序:商品详情页"]:::page
P1 -->|"选择SKU、数量"| P1a["🏠 加入购物车弹窗"]:::page
P1a -->|"productId, quantity, skuId"| A1["🔗 POST /api/cart/add"]:::api
A1 -->|"userId, productId, quantity, skuId"| S1["⚙️ CartService.addToCart()"]:::service
S1 -->|"查询商品是否存在"| S1a["⚙️ ProductMapper.selectById()"]:::service
S1a -->|"商品不存在返回null"| P1a
S1 -->|"购物车中已有 → 累加数量"| S1b["⚙️ CartMapper.updateById()"]:::service
S1 -->|"购物车中没有 → 新建记录"| S1c["⚙️ CartMapper.insert()"]:::service
S1b -->|"Cart记录"| D1["📦 cart 表"]:::data
S1c --> D1
D1 -->|"userId"| P2["🏠 小程序:购物车列表页"]:::page
P2 -->|"userId"| A2["🔗 POST /api/cart/list"]:::api
A2 -->|"userId"| S2["⚙️ CartService.getCartList()"]:::service
S2 -->|"按创建时间倒序查询"| S2a["⚙️ CartMapper.selectList()"]:::service
S2a -->|"Cart列表"| D1
S2 -->|"装配商品信息+SKU规格"| S2b["⚙️ buildCartItemDTO()"]:::service
S2b -->|"CartItemDTO(含名称/图片/价格/规格)"| D2["📦 CartItemDTO(返回前端)"]:::data
P2 -->|"调整数量"| P3["🏠 数量调整按钮"]:::page
P3 -->|"productId, quantity"| A3["🔗 POST /api/cart/update"]:::api
A3 -->|"userId, productId, quantity"| S3["⚙️ CartService.updateQuantity()"]:::service
S3 -->|"数量≤0则删除,否则更新数量"| S3a["⚙️ CartMapper.updateById() / delete()"]:::service
S3a -->|"更新记录"| D1
P2 -->|"删除商品"| P4["🏠 删除按钮"]:::page
P4 -->|"productId"| A4["🔗 POST /api/cart/remove"]:::api
A4 -->|"userId, productId"| S4["⚙️ CartService.removeItem()"]:::service
S4 -->|"按userId+productId删除"| S4a["⚙️ CartMapper.delete()"]:::service
S4a --> D1
P2 -->|"查看购物车角标"| P5["🏠 TabBar购物车角标"]:::page
P5 -->|"userId"| A5["🔗 POST /api/cart/count"]:::api
A5 -->|"userId"| S5["⚙️ CartService.getCartCount()"]:::service
S5 -->|"统计该用户购物车条目数"| S5a["⚙️ CartMapper.selectCount()"]:::service
S5a -->|"条目数"| D1
end
%% ================================================================
%% 阶段二:优惠券领取与使用
%% ================================================================
subgraph 阶段二[阶段二:优惠券领取与使用]
D1 -->|"userId"| P6["🏠 小程序:优惠券列表页"]:::page
P6 -->|"userId"| A6["🔗 POST /api/coupon/list"]:::api
A6 -->|"userId"| S6["⚙️ CouponService.listAvailable()"]:::service
S6 -->|"查询用户可用券(状态=AVAILABLE、有效期內)"| S6a["⚙️ UserCouponMapper + CouponMapper 联合查询"]:::service
S6a -->|"UserCoupon → Coupon"| D3["📦 user_coupon + coupon 表"]:::data
P6 -->|"领取优惠券"| P7["🏠 领取按钮"]:::page
P7 -->|"couponId"| A7["🔗 POST /api/coupon/claim"]:::api
A7 -->|"userId, couponId"| S7["⚙️ CouponService.claim()"]:::service
S7 -->|"检查: 券存在、未领完、有效期內、未重复领取"| S7a["⚙️ 校验条件"]:::service
S7a -->|"校验不通过返回错误消息"| P7
S7 -->|"创建UserCoupon记录"| S7b["⚙️ UserCouponMapper.insert()"]:::service
S7b -->|"user_coupon记录(状态=AVAILABLE)"| D4["📦 user_coupon 表"]:::data
S7 -->|"累加usedCount"| S7c["⚙️ CouponMapper.updateById()"]:::service
S7c -->|"更新coupon.usedCount"| D5["📦 coupon 表"]:::data
P6 -->|"下单时选择优惠券"| P8["🏠 订单确认页"]:::page
P8 -->|"userCouponId, orderType, orderAmount"| A8["🔗 POST /api/coupon/apply"]:::api
A8 -->|"userId, userCouponId, orderType, orderAmount"| S8["⚙️ CouponService.apply()"]:::service
S8 -->|"校验: 券归属、有效期、适用范围、最低消费"| S8a["⚙️ 校验条件"]:::service
S8a -->|"校验不通过返回null"| P8
S8 -->|"计算折扣=min(券面值, 订单金额)"| S8b["⚙️ 折扣计算"]:::service
S8b -->|"折扣金额"| D6["📦 discount(返回前端)"]:::data
P8 -->|"提交订单 → 标记券已用"| A8b["🔗 POST /api/coupon/markUsed(内部调用)"]:::api
A8b -->|"userCouponId, orderId"| S8c["⚙️ CouponService.markUsed()"]:::service
S8c -->|"状态→USED, 记录使用时间/订单ID"| D4
end
%% ================================================================
%% 阶段三:管理后台优惠券管理
%% ================================================================
subgraph 阶段三[阶段三:管理后台优惠券管理]
P9["🏠 管理后台:优惠券管理页"]:::page
P9 -->|"查看列表"| A9["🔗 POST /api/admin/coupon/list"]:::api
A9 -->|"role=admin"| S9["⚙️ CouponService.listAll()"]:::service
S9 -->|"查询所有优惠券"| D5
P9 -->|"创建优惠券"| P9a["🏠 创建表单"]:::page
P9a -->|"name, type, value, minSpend, validFrom, ..."| A10["🔗 POST /api/admin/coupon/create"]:::api
A10 -->|"role=admin, Coupon对象"| S10["⚙️ CouponMapper.insert()"]:::service
S10 -->|"写入新优惠券"| D5
P9 -->|"编辑优惠券"| P9b["🏠 编辑表单"]:::page
P9b -->|"id, 更新字段"| A11["🔗 POST /api/admin/coupon/update"]:::api
A11 -->|"role=admin, Coupon对象"| S11["⚙️ CouponMapper.updateById()"]:::service
S11 -->|"更新优惠券"| D5
P9 -->|"发放给用户"| P9c["🏠 发放弹窗:选择用户"]:::page
P9c -->|"couponId, userIds[]"| A12["🔗 POST /api/admin/coupon/issue"]:::api
A12 -->|"role=admin, couponId, userIds"| S12["⚙️ CouponService.issueToUser()"]:::service
S12 -->|"批量创建UserCoupon(状态=AVAILABLE)"| S12a["⚙️ UserCouponMapper.insert()"]:::service
S12a -->|"user_coupon记录"| D4
P9 -->|"删除优惠券"| P9d["🏠 删除确认弹窗"]:::page
P9d -->|"id"| A13["🔗 POST /api/admin/coupon/delete"]:::api
A13 -->|"role=admin, id"| S13["⚙️ CouponMapper.deleteById()"]:::service
S13 -->|"删除优惠券"| D5
end
%% ================================================================
%% 角色关联
%% ================================================================
ROLE_PARENT -.- P1
ROLE_PARENT -.- P2
ROLE_PARENT -.- P6
ROLE_PARENT -.- P8
ROLE_ADMIN -.- P9
端点明细
购物车
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/cart/add |
添加商品到购物车 |
📱小程序 |
{productId, quantity(默认1), skuId(可选)} |
CartItemDTO |
POST /api/cart/list |
获取购物车列表 |
📱小程序 |
— |
List<CartItemDTO>(含商品名/图/价格/规格) |
POST /api/cart/update |
更新商品数量 |
📱小程序 |
{productId, quantity} |
— |
POST /api/cart/remove |
从购物车移除商品 |
📱小程序 |
{productId} |
— |
POST /api/cart/count |
获取购物车条目数 |
📱小程序 |
— |
int |
优惠券(用户端)
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/coupon/list |
获取可用优惠券列表 |
📱小程序 |
— |
List<Coupon> |
POST /api/coupon/claim |
领取优惠券 |
📱小程序 |
{couponId} |
String("领取成功" / 错误消息) |
POST /api/coupon/apply |
应用优惠券计算折扣 |
📱小程序 |
{userCouponId, orderType, orderAmount} |
{discount}(折扣金额/分) |
优惠券(管理后台)
| 端点 |
说明 |
端口 |
请求数据 |
响应数据 |
POST /api/admin/coupon/list |
获取全部优惠券 |
🖥管理后台 |
— |
List<Coupon> |
POST /api/admin/coupon/create |
创建优惠券 |
🖥管理后台 |
{name, type, value, minSpend, applicableTo, validFrom, validUntil, totalCount} |
String |
POST /api/admin/coupon/update |
更新优惠券 |
🖥管理后台 |
{id, ...更新字段} |
String |
POST /api/admin/coupon/issue |
向用户发放优惠券 |
🖥管理后台 |
{couponId, userIds[]} |
String |
POST /api/admin/coupon/delete |
删除优惠券 |
🖥管理后台 |
{id} |
String |
数据实体关系
erDiagram
Coupon ||--o{ UserCoupon : "一个券可被多次发放"
User ||--o{ UserCoupon : "一个用户可有多张券"
User ||--o{ Cart : "一个用户可有多个购物车条目"
Product ||--o{ Cart : "一个商品可在多个购物车中"
ProductSku ||--o{ Cart : "一个SKU可在多个购物车中"
Cart {
Long id PK
Long userId FK
Long productId FK
Long skuId FK "nullable"
string specOptionIds "JSON"
int quantity
datetime createdAt
datetime updatedAt
}
Coupon {
Long id PK
string name
string type "FIXED等"
int value "金额(分)"
int minSpend "最低消费(分)"
string applicableTo "ALL / MEMBERSHIP"
date validFrom
date validUntil
int totalCount "总发放量"
int usedCount "已使用量"
datetime createdAt
}
UserCoupon {
Long id PK
Long userId FK
Long couponId FK
string status "AVAILABLE / USED"
datetime receivedAt
datetime usedAt "nullable"
Long orderId FK "nullable"
}
完整性分析
| # |
维度 |
评估 |
说明 |
| 1 |
流程完整性 |
✅ 完整 |
覆盖购物车全生命周期(增删改查计数)、用户优惠券(查看/领取/使用)、管理后台(CRUD/发放)3个阶段 |
| 2 |
异常路径 |
⚠️ 部分覆盖 |
添加时商品不存在(返回null)、领取时券已领完/已过期/已重复领取(返回错误消息)、应用券时不满足条件(返回null)在图中体现;订单提交后券标记失败的补偿逻辑未画入 |
| 3 |
端点覆盖 |
✅ 完整 |
13个端点全部映射到流程图中,与代码实际暴露的CartController(5个)、CouponController(3个)、AdminCouponController(5个)一致 |
| 4 |
角色覆盖 |
✅ 完整 |
用户端(家长)进行购物车管理、优惠券领取与使用;管理员在后管端进行优惠券CRUD和发放 |
| 5 |
数据实体 |
✅ 完整 |
cart表、coupon表、user_coupon表、CartItemDTO均在图中映射 |
| 6 |
一致性 |
✅ 与代码一致 |
所有端点路径与实际Controller中的@PostMapping匹配,Service方法名与实际代码一致 |