巴青农资商城

商品分类技术方案.md 13KB

商品分类 — 技术方案(C 端)

依据: 《商品分类功能需求.md》v1.0.1
关联: 平台《商品分类技术方案.md》v1.2、《商品管理技术方案.md》v1.3、《店铺管理技术方案.md》v1.2.6、《商城首页技术方案.md》v1.1、《关联需求分析.md》v1.6
范围: C 端 /api/category/** 只读(全部分类树、一级下二级 Tab、二级下商品分页列表); 平台/店铺分类 CRUD、 搜索执行、 新建业务主表。
原则: 无新建表;复用 biz_goods_categorybiz_goodsbiz_shop;全部 @Anonymous;搜索栏 无接口(GC-S1)。


1. 技术架构

选型
基础框架 RuoYi v3.9.2springboot2 分支)
数据库 MySQL 5.7.39
ORM / 响应 MyBatis;分类树 AjaxResult;商品列表 TableDataInfo(分页)
鉴权 C 端 @Anonymous(访客/会员均可读)
分页 RuoYi PageHelper + startPage()/goods
缓存 本期不做 Redis;读库即时生效

1.1 模块落位

baqing-shop/src/main/java/com/ruoyi/web/modules/home/
├── controller/CategoryAppController.java     # /api/category/**
├── service/ICategoryAppService.java
├── service/impl/CategoryAppServiceImpl.java
├── mapper/CategoryAppMapper.java             # 二级 Tab、分类商品 SQL
├── dto/CategoryGoodsQuery.java
├── constant/CategoryAppConstants.java        # sortBy 枚举值
└── vo/HomeHotGoodsVO.java                    # 商品卡片(与首页热销共用)

已有(category 模块 · Facade):
└── com.ruoyi.web.modules.category.facade.ICategoryFacade
        listVisibleByShopId(null)               # 平台可见分类树(A 页 / tree)
        isCategoryVisible(categoryId)         # 二级校验(/goods)

已有(home 模块 · 首页一级导航,勿混用):
└── HomeAppController GET /api/home/categories
        仅一级扁平列表,供首页横向导航;**不含** children

resources/mapper/home/
└── CategoryAppMapper.xml

说明: C 端分类与 C 端首页 同属 home不同基路径/api/category vs /api/home)。平台分类 CRUD 仍在 /agri/categoryPlatformCategoryController)。

1.2 协作链

biz_goods_category(shop_id IS NULL)
    ├── GET /api/category/tree              → A 页 · 一级+二级树
    ├── GET /api/category/{level1Id}/level2-tabs → B 页 · 二级 Tab
    └── (校验)isCategoryVisible

biz_goods + biz_shop
    └── GET /api/category/goods?categoryId=&sortBy=&pageNum=&pageSize=
            → B 内嵌列表 / C 独立列表(共用)

【页面与接口】
    首页 HM7 ──一级──► B:level2-tabs + goods(默认首 Tab)
    首页「更多」──► A:tree ──点二级──► C:goods(同 goods 接口)
    搜索栏:纯前端 GC-S1(无接口)
关联模块 协作
平台 · 商品分类 维护 biz_goods_category;C 端只读 show_flag / 排序
商品分类 Facade listVisibleByShopId(null) 过滤空壳一级、父隐子隐(GC6)
商城首页 /api/home/categories 一级快捷导航;点击进入本模块 B 页
商品 / 审核 列表 goods_status=2(出售中)
店铺管理 JOIN shop_name 实时;不过滤 停业/库存(GC13)
会员管理 不校验 Token
商品详情 列表点击跳转;可购四条件在详情/Facade 校验(GC12)

1.3 跨模块 Facade

接口 本模块用法
ICategoryFacade.listVisibleByShopId(null) /tree 平台全量可见树(含 children)
ICategoryFacade.isCategoryVisible /goods 校验二级 categoryId
IPlatformCategoryService 不直接 暴露 C 端 HTTP
IGoodsPurchaseFacade / 详情 本模块 不调用;下单层校验

2. 数据库设计

2.1 原则

无新建表。 只读下列已有表;DDL 见 sql/

本模块用途 权威 DDL
biz_goods_category 平台一/二级导航 sql/biz_goods_category.sql
biz_goods 二级下商品列表 sql/biz_goods.sql
biz_shop 商品卡片店铺名 sql/biz_shop.sql

2.2 本模块读字段与条件

biz_goods_category — 平台分类(shop_id IS NULL

场景 条件 输出字段
tree(Facade 内存组装) 一级 show=1 且下有可见二级;二级 isCategoryVisible categoryId, categoryName, categoryPic, sortNo, hotFlag + children[]
level2-tabs level=2, parent_id=#{level1Id}, show=1, del=0;且一级可见 同上(无 children)

biz_goods + biz_shop — 分类商品列表

字段 条件 / 说明
g.category_id = 所选二级 ID
g.goods_status 2 出售中(GC8)
g.del_flag 0
s.del_flag 0(JOIN)
g.sales_count, g.sale_price, g.goods_sn 排序键(GC9)
g.goods_id, g.goods_sn, g.goods_name, g.main_pic, g.sale_price 输出
s.shop_id, s.shop_name JOIN;实时(GC10)

浏览层: SQL 不过滤 shop_status不过滤 stock(GC13)。

2.3 与首页 /api/home/categories 差异

/api/home/categories /api/category/tree
层级 一级 扁平 一级 + 二级
空壳一级 SQL 仅 show=1(首页导航宜与 GC6 对齐:无可见二级的不展示) Facade 排除 无可见二级的一级
用途 首页横向导航 +「更多」 A 全部分类页

2.4 建议索引(可选)

-- 可选:与首页热销共用
-- idx_category_id on biz_goods(category_id)
-- idx_shop_status 等现有索引一般够用

3. C 端接口设计

基路径: /api/category
鉴权: 全部 @Anonymous

3.1 接口一览

方法 路径 页面 说明
GET /tree A 全部分类 平台可见分类树
GET /{level1Id}/level2-tabs B Tab 区 指定一级下二级列表
GET /goods B/C 商品列表 二级下出售中商品 分页

本期不提供: 搜索 API、聚合 /api/category/index、店铺分类读接口。

3.2 可见分类树 GET /api/category/tree

说明
Query
实现 ICategoryFacade.listVisibleByShopId(null)

data CategoryVisibleVO[]category 模块已有 VO)

字段 说明
categoryId 一级 ID
categoryName 名称
categoryPic 图片
sortNo 排序
hotFlag 是否热门
children CategoryVisibleChildVO[](二级)

children 元素: categoryId, categoryName, categoryPic, sortNo, hotFlag

前端(A 页): 左列 = data;右列 = 当前选中一级的 children;点 child → 路由 C 页并带 categoryId

3.3 一级下二级 Tab GET /api/category/{level1Id}/level2-tabs

说明
Path level1Id — 平台一级 ID
校验 一级存在、shop_id IS NULLlevel=1show=1;否则 ServiceException

data CategoryVisibleChildVO[](按 sort_no, create_time 升序)

SQL 片段:

SELECT category_id, category_name, category_pic, hot_flag, sort_no
FROM biz_goods_category
WHERE shop_id IS NULL
  AND category_level = '2'
  AND parent_id = #{level1Id}
  AND show_flag = '1'
  AND del_flag = '0'
ORDER BY sort_no ASC, create_time ASC

前端(B 页): 首页带入 level1Id → 本接口渲染 Tab → 默认首 Tab 调 /goods;切换 Tab(C1)仅改 categoryId 重新请求 /goods

3.4 分类商品列表 GET /api/category/goods

说明
Query 见下表
分页 pageNumpageSize(RuoYi 默认)
响应 TableDataInfo{ code, msg, rows, total }

Query:

参数 必填 说明
categoryId 平台二级 分类 ID
sortBy 默认 sales_descCategoryAppConstants.DEFAULT_SORT
pageNum 页码
pageSize 每页条数

sortBy 合法值:

排序
sales_desc 销量降序 → goods_sn 升序
sales_asc 销量升序 → goods_sn 升序
price_desc 售价降序 → goods_sn 升序
price_asc 售价升序 → goods_sn 升序

校验:

  1. categoryId 非空
  2. categoryFacade.isCategoryVisible(categoryId) 为 true,否则「分类不存在或不可见」
  3. sortBy 非法 →「排序参数无效」

rows 元素: HomeHotGoodsVO(与 /api/home/hot-goods 同结构)

字段 类型 说明
goodsId long
goodsSn string
goodsName string
mainPic string
salePrice decimal
shopId long
shopName string 实时

SQL 片段:

SELECT g.goods_id, g.goods_sn, g.goods_name, g.main_pic, g.sale_price,
       s.shop_id, s.shop_name
FROM biz_goods g
INNER JOIN biz_shop s ON g.shop_id = s.shop_id AND s.del_flag = '0'
WHERE g.goods_status = '2'
  AND g.del_flag = '0'
  AND g.category_id = #{categoryId}
ORDER BY
  -- 动态 sortBy,同键 goods_sn ASC

3.5 搜索栏

说明
占位 前端写死「搜索兽药、饲料、店铺」(GC3)
接口

4. Service 分层

CategoryAppController
    → ICategoryAppService.listCategoryTree()
    → ICategoryAppService.listLevel2Tabs(level1Id)
    → ICategoryAppService.listCategoryGoods(query)   # 分页由 Controller startPage

CategoryAppServiceImpl
    → ICategoryFacade.listVisibleByShopId(null)
    → ICategoryFacade.isCategoryVisible(categoryId)
    → BizGoodsCategoryMapper.selectById(level1Id, null)   # 一级校验
    → CategoryAppMapper.selectPlatformLevel2Tabs(level1Id)
    → CategoryAppMapper.selectGoodsByCategory(categoryId, sortBy)
方法 说明
listCategoryTree() 透传 Facade;shopId=null → 平台树
listLevel2Tabs() 校验一级可见 → Mapper 查二级
listCategoryGoods() 校验二级可见 + sortBy → Mapper; 校验可购四条件

5. 与平台后台对照

平台操作 影响的 C 端接口
平台分类改 show/排序/名称/图 /tree/level2-tabs
平台分类改 show=0 树/Tab 即时隐藏;/goods 校验失败
商品上下架 /goods 增删行
店铺改名 /goodsshopName
店铺停业 仍出现在 /goods;下单在详情拦截
会员禁用 不影响 三接口

6. 业务规则映射

规则 实现
GC1 shop_id IS NULL / Facade 平台树
GC2 @Anonymous
GC3~GC-S2 无搜索 API
GC4 /tree + 前端 A→C 路由
GC5 /level2-tabs + /goods;Tab 切换前端 C1
GC5a /goods + 前端面包屑(路径非接口字段)
GC6 Facade listVisibleByShopId + isCategoryVisible
GC7 SQL / Facade 排序
GC8~GC10 /goods SQL + HomeHotGoodsVO
GC9 sortBy 四档 + 默认 sales_desc
GC12 Service 调可购 Facade
GC13 SQL 不过滤店态/库存
GC14 三接口 独立;前端模块级空态
GC15 无写接口

7. 前端接口调用建议

页面 调用顺序
A 全部分类 GET /tree → 点二级 → GET /goods?categoryId=
B 一级分类 GET /{level1Id}/level2-tabsGET /goods?categoryId=首Tab → 切换 Tab 仅重调 /goods
C 独立列表 GET /goods?categoryId=level1Id 由路由/状态展示面包屑)

C1: Tab 切换时前端 列表回顶 后再请求 /goods(产品层,非后端字段)。


8. 实现状态

状态
GET /api/category/tree 待实现
GET /api/category/{level1Id}/level2-tabs 待实现
GET /api/category/goods 待实现
CategoryAppController / CategoryAppServiceImpl 待创建
CategoryAppMapper.xml 待创建
C 端分类前端 已实现(见《商品分类前端技术方案.md》v1.0)

9. 非本期

说明
GET /api/category/index 聚合 前端分调三接口即可
Redis 缓存
/api/search/** 搜索模块
店铺商品分类 C 端读 GC1 排除
列表返回销量/库存字段 GC10 卡片不展示
商品详情 API 商品详情模块

10. 修订记录

版本 说明
v1.0 首版;无新建表;/api/category 三读接口;关联平台分类 Facade、商城首页 v1.1

文档版本:v1.0 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《商品分类功能需求.md》v1.0.1、《商品分类技术方案.md》v1.2(平台)、《商城首页技术方案.md》v1.1