巴青农资商城

购物车技术方案.md 19KB

购物车 — 技术方案(C 端)

依据: 《购物车功能需求.md》v1.0
关联: 平台《商品管理功能需求》v1.3.3、《商品审核功能需求》v1.0、《商品分类技术方案》v1.2、《店铺管理技术方案》v1.3.6、《会员管理技术方案》v1.3、《订单管理技术方案》v1.0.1、《关联需求分析.md》v1.6 §11;C 端《商品详情内页技术方案》v1.0、《我的服务技术方案》v1.1
范围: C 端 /api/cart/** 购物车行 CRUD、加购写入、勾选态、失效判定、同店结算预校验;不含 确认订单、支付、履约。
原则: 新建 biz_member_cart_item;商品/店铺/分类 只读 实时档案;不预扣库存;可购 四条件 复用 IGoodsPurchaseFacade;v1.0 统一规格spec_key 固定默认键)。


1. 技术架构

选型
基础框架 RuoYi v3.9.2springboot2 分支)
数据库 MySQL 5.7.39
ORM / 响应 MyBatis;AjaxResultcodemsgdata
鉴权 全部接口须 会员 TokenMemberWebConfig/api/cart/**
分页 购物车 不分页(单会员行数可控);列表一次返回
缓存 本期不做 Redis

1.1 模块落位

baqing-shop/src/main/java/com/ruoyi/web/modules/cart/
├── controller/CartAppController.java           # /api/cart/**
├── service/ICartAppService.java
├── service/impl/CartAppServiceImpl.java
├── mapper/CartAppMapper.java
├── domain/BizMemberCartItem.java
├── dto/
│   ├── CartItemAddDTO.java                     # 加购
│   ├── CartItemQuantityDTO.java
│   ├── CartItemCheckedBatchDTO.java
│   ├── CartItemBatchDeleteDTO.java
│   └── CartCheckoutPrepareDTO.java             # 去结算
├── vo/
│   ├── CartListVO.java                         # 按店分组列表
│   ├── CartShopGroupVO.java
│   ├── CartItemVO.java
│   └── CartCheckoutPrepareVO.java
└── constant/CartConstants.java

resources/mapper/cart/CartAppMapper.xml
sql/biz_member_cart_item.sql                    # 新建

account/(已有 · 协作)
├── config/MemberWebConfig.java                 # 追加 /api/cart/**
├── support/MemberContext.java
└── facade/IMemberFacade.isMemberEnabled

goods/(已有 · 协作)
├── facade/IGoodsPurchaseFacade.canPurchase
├── mapper/BizGoodsMapper
└── constant/GoodsConstants

store/(已有 · 协作)
└── facade/IAgriShopFacade.isShopOpenForOrder

category/(已有 · 协作)
└── facade/ICategoryFacade.isCategoryVisible

1.2 协作链

【上游写入】
    商品详情 POST /api/cart/items
        → 四条件校验 + 同规格合并累加

【本模块】
    GET  /api/cart                         → 条目列表(cartItemId 倒序,含店铺)+ 勾选合计
    PUT  /api/cart/items/{id}/quantity     → 改数量(≤库存)
    PUT  /api/cart/items/checked           → 批量更新勾选
    DELETE /api/cart/items/{id}            → 移出
    DELETE /api/cart/items                 → 批量移出
    DELETE /api/cart/invalid               → 清理失效行
    POST /api/cart/checkout/prepare        → 同店结算预校验 → 确认订单(待建)

【下游】
    确认订单(待建)← checkout/prepare 返回 shopId + 行快照
    下单成功(待建)→ ICartFacade.removeByOrderItems(占位)

biz_member_cart_item
    ├── JOIN biz_goods     → 名称/主图/价/库/状态
    ├── JOIN biz_shop      → 店名/店态
    └── Facade             → 四条件、分类可见
关联模块 协作
平台 · 商品管理/审核 维护 biz_goods;下架 不删 购物车行;列表 实时 读态
平台 · 店铺管理 维护 biz_shop;停业 可展示禁结算
平台 · 商品分类 分类隐藏 → 行 可展示禁结算
平台 · 会员管理 禁用会员 不可 调本模块
平台 · 订单管理 支付成功扣库存;下单后 移除/扣减 购物车行(确认订单专册)
C 端 · 商品详情 唯一 加购入口 POST /api/cart/items
C 端 · 确认订单 待建设;本模块 仅 prepare
C 端 · 我的服务 地址 不在 购物车;结算页引用地址簿

1.3 跨模块 Facade

接口 本模块用法
IGoodsPurchaseFacade.canPurchase(goodsId) 加购、去结算 四条件(出售中/店开业/分类可见/库存>0)
BizGoodsMapper.selectById 读实时 sale_pricestock;改量/累加上限
IMemberFacade.isMemberEnabled 写操作前校验会员 启用
IAgriShopFacade / ICategoryFacade 已含于 canPurchase;列表失效 细分文案补充 读档
ICartFacade(规划) 订单模块 下单成功后 removeItems(memberId, cartItemIds)

库存与数量: canPurchase 仅校验 stock>0;本 Service 额外 校验 quantity <= stock(加购累加、改量、prepare)。


2. 数据库设计

2.1 新建 · 会员购物车行 biz_member_cart_item

字段 类型 说明
cart_item_id bigint PK 自增
member_id bigint 会员 ID(biz_member.member_id
shop_id bigint 店铺 ID(加购时自 biz_goods.shop_id 冗余,便于分组)
goods_id bigint 商品 ID
spec_key varchar(64) 规格键;v1.0 统一规格固定 ''(空串)
spec_text varchar(256) 加购时 规格展示快照(如「默认」或规格项拼接)
quantity int 数量,≥1
checked char(1) 0 未勾选 / 1 已勾选;新行默认 1
create_time datetime 首次加购
update_time datetime 最近改量/勾选

约束与索引:

说明
唯一 uk_member_goods_spec (member_id, goods_id, spec_key) — 同规格合并
索引 idx_member_update (member_id, update_time DESC) — 列表排序
索引 idx_member_shop (member_id, shop_id) — 按店分组

DDL: sql/biz_member_cart_item.sql

CREATE TABLE IF NOT EXISTS `biz_member_cart_item` (
  `cart_item_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '购物车行ID',
  `member_id` bigint(20) NOT NULL COMMENT '会员ID',
  `shop_id` bigint(20) NOT NULL COMMENT '店铺ID',
  `goods_id` bigint(20) NOT NULL COMMENT '商品ID',
  `spec_key` varchar(64) NOT NULL DEFAULT '' COMMENT '规格键;v1统一规格为空串',
  `spec_text` varchar(256) NOT NULL DEFAULT '默认' COMMENT '规格展示快照',
  `quantity` int(11) NOT NULL DEFAULT '1' COMMENT '数量',
  `checked` char(1) NOT NULL DEFAULT '1' COMMENT '0未勾选1已勾选',
  `create_time` datetime DEFAULT NULL COMMENT '加购时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`cart_item_id`),
  UNIQUE KEY `uk_member_goods_spec` (`member_id`, `goods_id`, `spec_key`),
  KEY `idx_member_update` (`member_id`, `update_time`),
  KEY `idx_member_shop` (`member_id`, `shop_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员购物车';

2.2 复用表(无 DDL 变更)

本模块用途 权威 DDL
biz_goods 主图、名称、售价、库存、状态 sql/biz_goods.sql
biz_shop 店名、店态、删标 sql/biz_shop.sql
biz_goods_category ICategoryFacade 判可见 sql/biz_goods_category.sql
biz_member 会员启用态 sql/biz_member.sql

2.3 规格字段(v1.0 定稿)

定稿
SKU 矩阵 ;与详情 v1 统一规格 一致
spec_key 固定 '';预留多规格扩展
spec_text 加购请求传入;空则 Service 读 biz_goods_attrattr_type=2)拼接,仍无则 「默认」
合并维度 member_id + goods_id + spec_key

2.4 失效判定(读时计算,不落库

invalidType 条件 展示文案方向 可勾选
NONE 四条件满足且 quantity <= stock
OUT_OF_STOCK 出售中但 stock=0quantity>stock 缺货 / 库存不足
OFF_SHELF goods_status != '2' 已下架 / 不可购买
SHOP_CLOSED shop_status='1' 店铺休息中
CATEGORY_HIDDEN 分类不可见 商品不可购买
GOODS_DELETED 商品 del_flag='2' 商品失效
SHOP_DELETED 店铺 del_flag='2' 店铺失效

浏览与下单分离: 历史行 保留invalidType != NONEpurchasable=false不可 参与 prepare。


3. C 端接口设计

基路径: /api/cart
鉴权: 全部须 Authorization: Bearer {token};未登录 → 401「请先登录」
MemberWebConfig 追加: .addPathPatterns(..., "/api/cart/**")

3.1 接口一览

方法 路径 说明
GET /api/cart 购物车条目列表
POST /api/cart/items 加购(详情页调用)
PUT /api/cart/items/{cartItemId}/quantity 修改数量
PUT /api/cart/items/checked 批量更新勾选
DELETE /api/cart/items/{cartItemId} 移出单行
DELETE /api/cart/items 批量移出(body 传 id 列表)
DELETE /api/cart/invalid 清理当前会员全部失效行
POST /api/cart/checkout/prepare 去结算 预校验

本期不提供:

说明
跨店合单 prepare CT-S1 不支持
平台/商家查会员购物车 无 B 端 API
购物车搜索/推荐 需求 CT 非本期

3.2 购物车列表 GET /api/cart

说明
数据范围 仅当前会员
响应 AjaxResultdata: CartListVO

CartListVO

字段 类型 说明
items array 购物车条目列表(主结构),见下
checkedSummary object 当前 已勾选且可购 行合计(前端底栏参考)

items[]CartItemVO

字段 类型 说明
cartItemId long
goodsId long 进详情
goodsName string 实时
mainPic string 实时
specText string 加购快照
salePrice decimal 当前售价
quantity int
subtotal decimal salePrice × quantity
checked boolean
purchasable boolean 是否可勾选结算
invalidType string §2.4 枚举;可购时为 NONE
invalidMsg string 前端 Toast/标签;可购时 null
shopId long 所属店铺
shopName string 店铺名称(实时)
shopAvatar string 店铺头像
shopStatus string 0 开业 / 1 停业

排序: cart_item_id DESC(最新加购/合并行在前)。

空态: items=[] → 前端「购物车是空的」。

checkedSummary

字段 说明
checkedCount 已勾选 且 purchasable 的行数
checkedQuantity 上述行数量之和
checkedAmount 上述行 subtotal 之和

同店结算: 前端底栏以 当前操作店 内勾选行为准;若勾选跨店,不得 合单 prepare(CT-S4)。

3.3 加购 POST /api/cart/items

说明
调用方 商品详情「加入购物车」
校验 会员启用;quantity >= 1四条件 + quantity <= stock

Body · CartItemAddDTO

字段 必填 说明
goodsId
quantity 默认 1
specKey v1 不传或传 ''
specText 不传则 Service 生成

逻辑:

canPurchase(goodsId) → 不通过 → 400 + reason
quantity > stock     → 400「库存不足」
已存在同 member+goods+spec_key 行
    → newQty = min(quantity + 旧quantity, stock)
    → UPDATE quantity, update_time;checked 保持
不存在
    → INSERT;checked='1';shop_id 取自 goods

响应: AjaxResult.success(data){ cartItemId, quantity }

3.4 修改数量 PUT /api/cart/items/{cartItemId}/quantity

Body: { "quantity": 3 }

规则 说明
归属 行须属于 当前会员
下限 quantity >= 1
上限 quantity <= biz_goods.stock(实时)
失效 改量后 不重算 checked;列表读时刷新 purchasable

失败 → 400 + 明确 msg;成功 → { cartItemId, quantity }

3.5 批量勾选 PUT /api/cart/items/checked

Body · CartItemCheckedBatchDTO

{
  "items": [
    { "cartItemId": 1, "checked": true },
    { "cartItemId": 2, "checked": false }
  ]
}
规则 说明
归属 全部 id 须属当前会员
失效行 允许 写入 checked;前端 置灰不可勾选;prepare 时 过滤

3.6 移出

接口 Body 说明
DELETE .../items/{cartItemId} 单行;非本人 → 404
DELETE .../items { "cartItemIds": [1,2] } 批量;忽略 不存在 id

物理删除 购物车行;幂等。

3.7 清理失效 DELETE /api/cart/invalid

说明
范围 当前会员全部 purchasable=false
响应 { "removedCount": n }

3.8 去结算预校验 POST /api/cart/checkout/prepare

说明
用途 跳转 确认订单服务端 再校验(CT-S2、§10)

Body · CartCheckoutPrepareDTO

字段 必填 说明
cartItemIds 用户勾选行 ID,至少 1 条

校验顺序:

1. 全部 cartItemId 属于当前会员
2. 解析 shop_id → 必须 **同一店铺**(否则 400「请选择同一店铺的商品结算」)
3. 逐行:canPurchase + quantity <= stock
4. 通过 → 返回 CheckoutPrepareVO

data · CartCheckoutPrepareVO

字段 类型 说明
shopId long
shopName string
items array { cartItemId, goodsId, goodsName, mainPic, specText, salePrice, quantity, subtotal }
goodsAmount decimal 商品合计(不含运费)

确认订单(待建): 接收 shopId + items[] 做地址/运费/提交订单;下单成功后调用 ICartFacade.removeItems 删除对应 cart_item_id

3.9 错误与边界

情形 行为
Token 失效 401
会员禁用 403「账号已禁用」
商品/行不存在 404
库存/四条件不满足 400 + reason / invalidMsg
prepare 跨店 400「请选择同一店铺的商品结算」

4. Service 分层

CartAppController
    → ICartAppService
        ├── list(memberId)              → 分组 + 失效计算 + checkedSummary
        ├── addItem(memberId, dto)      → canPurchase + merge/insert
        ├── updateQuantity(...)         → stock cap
        ├── updateChecked(...)          → batch
        ├── remove / removeBatch
        ├── cleanInvalid(memberId)
        └── prepareCheckout(memberId, ids) → 同店 + 四条件

CartAppMapper
    ├── selectByMemberId(memberId)        → JOIN goods + shop
    ├── selectByIdAndMember(...)
    ├── insert / updateQuantity / updateChecked
    └── deleteByIds / deleteInvalidByMember
要点 说明
memberId MemberContext 取,禁止 客户端传
价格 列表/prepare 读实时 sale_price 在 cart 表存价
事务 加购 merge、批量删 单事务
日志 写操作 @Log(可选)

5. 与平台后台联动

平台/商家操作 购物车行为
商品 下架/审核失败/待审 保留;列表 OFF_SHELF不可 prepare
商品 改价 列表读 新价;prepare 用 新价
商品 减库存 列表可能 OUT_OF_STOCK;改量/prepare 拦截
店铺 停业 保留SHOP_CLOSED;prepare 拦截
店铺 删店 SHOP_DELETED;可 清理失效
分类 隐藏 CATEGORY_HIDDEN
会员 禁用 403,全接口不可用
订单 支付成功 确认订单模块 删行(不占本模块)

对齐《关联需求分析》§8:不自动删 购物车行;仅 标失效 + 用户 批量清理


6. 与兄弟模块分工

能力 商品详情 /api/goods 本模块 /api/cart 确认订单(待建)
详情展示
GET .../can-purchase 内部复用 Facade
POST 加购 前端调 cart
列表/改量/勾选/清理
prepare 接收参数
地址/运费/提交订单

MemberWebConfig 变更:

.addPathPatterns("/api/member/**", "/api/merchant/entry/**",
        "/api/shop/*/follow", "/api/cart/**", "/api/checkout/**", "/api/order/**")
.excludePathPatterns("/api/member/register", "/api/member/login", ...,
        "/api/merchant/entry/agreement", "/api/merchant/entry/status");

完整 exclude 列表见《会员注册登录技术方案》§3.1。


7. 业务规则映射

需求规则 实现
CT0 须登录 /api/cart/** 拦截器
CT1 仅当前会员 SQL member_id = #{memberId}
CT2 按店分组 shop_id 冗余 + 列表 groups[]
CT3 行字段 CartItemVO
CT4 勾选 checked 字段 + PUT .../checked
CT5 改量 ≥1 ≤库存 updateQuantity
CT6 移出/清理失效 DELETE 系列
CT7 不支持跨店 prepareCheckout 校验同 shop_id
CT8 库存/可购校验 列表读时 + prepare
CT9 失效进详情 前端 goodsId 路由
CT10 详情加购、同规格合并 POST /items + uk_member_goods_spec
新行默认勾选 INSERT checked='1'
不预扣库存 无库存锁定表

8. 规划 · ICartFacade(供订单模块)

public interface ICartFacade {
    /** 下单成功后移除已结算购物车行 */
    void removeItems(Long memberId, List<Long> cartItemIds);
}

实现落位 cart.facade.CartFacadeImpl;订单模块 待实现 前可不建。


9. 修订记录

版本 说明
v1.0 首版:biz_member_cart_item、8 个 C 端 API、按店分组、四条件、同店 prepare;关联平台商品/店铺/分类/会员/订单与详情

文档版本:v1.0 · 关联《购物车功能需求.md》v1.0、《商品详情内页技术方案》v1.0、《订单管理技术方案》v1.0.1、《店铺管理技术方案》v1.3.6、《关联需求分析.md》v1.6 · 技术栈 RuoYi v3.9.2-springboot2 + MySQL 5.7.39