简介
本文件面向电商应用开发者,提供“商品列表查询”接口的完整参考。该接口用于按分类、品牌、时间归档、排序等条件分页获取已上架商品,并返回商品基本信息、价格信息、库存与销售状态等字段,便于前端构建商品浏览页或小程序商品列表。
项目结构
商品列表能力由以下层次协作实现:
- API 路由层:声明 /api/?route=product 下的资源路由,暴露 index(列表)、show(详情)等端点。
- API 控制器:接收请求参数,解析分类、分页、排序、品牌等条件,调用服务层组装数据。
- 前台服务层:封装商品列表的查询、筛选、排序、分页以及结果格式化逻辑。
- 模型层:定义商品表结构与查询作用域(如仅上架、按分类/品牌筛选、归档时间窗等)。
graph TB
Client["客户端"] --> Route["API 路由<br/>/api/?route=product"]
Route --> Ctrl["API 控制器<br/>ProductController::index"]
Ctrl --> Svc["前台服务<br/>ProductService::buildProductListData"]
Svc --> Model["商品模型<br/>Product (AR + Scopes)"]
Model --> DB["数据库"]
核心组件
- API 路由:将 /api/?route=product 映射到 ProductController 的 index/show 等方法。
- API 控制器:负责参数解析(分类 id/slug、品牌、分页、排序、归档),调用服务层并统一响应。
- 前台服务:实现商品列表的核心业务逻辑,包括:
- 默认分类回退策略(API 场景下未传分类时回退首个分类;显式 id=0 表示全部分类)。
- 支持品牌筛选、归档时间窗筛选、多字段排序。
- 分页与页面 URL 生成。
- 结果格式化:商品图片、描述截取、价格格式化、销售占比计算等。
- 商品模型:提供 published 过滤、分类/品牌/归档筛选、默认排序等查询作用域。
架构总览
下图展示一次商品列表请求从路由到数据返回的完整调用链。
sequenceDiagram
participant C as "客户端"
participant R as "API 路由"
participant Ctrl as "ProductController"
participant Svc as "ProductService"
participant M as "Product 模型"
participant DB as "数据库"
C->>R : GET /api/?route=product&...
R->>Ctrl : 分发到 index()
Ctrl->>Ctrl : 解析分类/品牌/分页/排序/归档
Ctrl->>Svc : buildProductListData(...)
Svc->>M : with('category')->published()->filterByCategory/Brand/Archive
M->>DB : 执行分页查询
DB-->>M : 商品集合
M-->>Svc : 商品集合
Svc->>Svc : 计算价格/销量占比/格式化字段
Svc-->>Ctrl : 列表数据+分页+排序选项
Ctrl-->>C : ApiResponse : : success(数据)
详细组件分析
API 路由与控制器
- 路由:通过 resource 方式注册 product 资源的 index 与 show,并额外挂载 attribute_list。
- 控制器 index:
- 分类解析:支持 id 与 category_slug 两种定位方式;当传入 id=0 时表示“全部分类”。
- 归档解析:year/month 组合形成时间窗,若存在则覆盖分类筛选。
- 分页:默认每页 6 条,page 参数为当前页码。
- 品牌:brand_id 可选。
- 排序:by 与 sort 参数控制排序字段与方向。
- 用户上下文:auth('api')->id() 用于收藏态等会员相关处理。
- 返回:统一使用 ApiResponse::success 包装数据。
服务层:商品列表构建
- 输入参数:
- catId:分类 id(0 表示全部;API 场景下未传且非全部分类时会回退到第一个分类)。
- page:当前页。
- pageSize:每页数量(控制器决定,API 默认 6)。
- brandId:品牌 id(0 表示不按品牌筛)。
- sortBy/sortDir:排序字段与方向。
- archive:归档时间窗(start/end/year/month)。
- isApi:是否 API 场景(影响 URL 生成与返回字段)。
- userId:当前登录用户 id(用于收藏态、会员价等)。
- allCategories:是否允许全部分类(API 中 id=0 时启用)。
- 查询流程:
- 预加载分类关联 with('category')。
- 仅查已上架商品 published()。
- 根据 archive 或 catId 选择 filterByArchive 或 filterByCategory。
- 应用品牌筛选 filterByBrand。
- 使用 ListSortOptionBuilder 生成排序 SQL 并 order。
- 分页 paginate。
- 结果加工:
- 提取商品 ID 列表,批量获取收藏标记与相册首图。
- 构造列表项:id、title、price、sale_price、thumb/image/image_other、created_at、description、favorites、url、cate_info。
- API 场景追加 stock、sales、sales_percentage。
- 返回 pager、sort_list、brand 等信息。
模型层:商品查询作用域
- 基础能力:
- 表名:product。
- casts:对 category_id、stock、sales、image、defined、created_at 等进行类型转换。
- appends:列表附加字段 thumb、image_other、add_time_short、time、name、description、url、favorites、cate_info。
- translatable:name/title/content/description 随语言切换。
- prefetchers:预热 url、language、attachment、gallery_first 以提升列表性能。
- 关键作用域:
- scopePublished:仅 status='1' 的上架商品。
- scopeFilterByCategory:按分类筛选(在 service 中通过 hasCategoryFilter 使用)。
- scopeFilterByBrand:按 brand_id 筛选。
- scopeFilterByArchive:按 created_at 时间窗筛选。
- scopeApplyDefaultOrder:默认排序 sort ASC, id DESC。
- 辅助方法:
- findPublishedById:按 id 取已上架商品。
- findFirstCategoryId:取第一个分类 id(用于 API 默认回退)。
- related:同分类随机推荐(含 forUser 会员视图)。
依赖关系分析
- 控制器依赖服务层进行业务拼装,服务层依赖模型的作用域完成复杂查询。
- 服务层还依赖定价服务、附件系统、收藏模块等扩展能力,以补充 sale_price、图片、收藏态等字段。
- 路由与控制器解耦,便于后续扩展更多子资源或中间件。
classDiagram
class ProductController {
+index(request)
+show(request)
}
class ProductService {
+buildProductListData(catId, page, pageSize, brandId, by, sort, archive, isApi, userId, allCategories)
+findCategoryById(catId)
}
class Product {
+scopePublished(query)
+scopeFilterByCategory(query, catId)
+scopeFilterByBrand(query, brandId)
+scopeFilterByArchive(query, archive)
+scopeApplyDefaultOrder(query)
}
ProductController --> ProductService : "调用"
ProductService --> Product : "查询"
性能考虑
- 列表查询采用 AR 模式并预加载分类关联 with('category'),减少 N+1 查询。
- 使用 prefetchers 预热 url、语言、附件缩略图与相册首图,降低重复 IO。
- 分页限制返回条数,避免一次性拉取过多数据。
- 排序通过 ListSortOptionBuilder 生成稳定排序 SQL,保证一致性。
- 建议:
- 合理设置 pageSize,避免过大导致响应缓慢。
- 对高频访问的分类可结合缓存策略(如 Redis)缓存分页结果或热门商品。
- 对图片等资源使用 CDN 加速。
故障排查指南
- 分类无效:
- 现象:返回错误提示“页面错误”。
- 原因:分类 id 或 slug 无法匹配到有效分类。
- 处理:检查传入的 id/category_slug 是否正确;必要时使用全部分类(id=0)。
- 无数据返回:
- 可能原因:分类下无已上架商品、品牌筛选过严、归档时间窗为空。
- 处理:放宽筛选条件或调整归档范围。
- 排序异常:
- 可能原因:by/sort 参数不在允许范围内。
- 处理:使用支持的排序字段(如 sales、price、created_at、sort),方向 asc/desc。
- 分页越界:
- 现象:返回空列表。
- 处理:确认 page 不超过总页数。
结论
商品列表接口通过清晰的分层设计与丰富的查询能力,满足电商场景中商品浏览的核心需求。开发者可按需组合分类、品牌、归档、排序与分页参数,快速构建高效的商品列表页面。
附录:接口规范与示例
接口概览
- HTTP 方法:GET
- URL 路径:/api/?route=product
- 认证:如需会员相关能力(收藏态、会员价等),请携带有效的 API 会话令牌。
请求参数
- 分类与归档
- id:分类 id。值为 0 时表示“全部分类”;不传时在 API 场景会回退到第一个分类。
- category_slug:分类别名,可与 id 配合定位分类。
- year/month:归档时间窗,组合后按创建时间筛选。
- 品牌
- brand_id:品牌 id,0 或不传表示不限品牌。
- 分页
- page:当前页码,默认 1。
- 排序
- by:排序字段,支持 sales、price、created_at、sort。
- sort:排序方向,asc 或 desc。
- 其他
- 以上参数均为可选,未传时使用默认值。
响应格式
- 成功响应
- code:200
- data:对象,包含:
- title:页面标题(分类名称或默认文案)
- category_id:当前分类 id
- product_category:分类树(用于导航)
- product_list:商品数组,每项包含:
- id:商品 id
- category_id:分类 id
- title:商品标题
- defined:自定义属性键值对
- price:价格显示文本(未设置时显示“面议”)
- sale_price:促销价信息(可能为空)
- thumb:缩略图地址
- image:主图地址
- image_other:相册首图地址
- created_at:创建时间
- description:描述摘要
- favorites:收藏状态(含 class 与 text)
- url:商品详情页链接(小程序/网页)
- cate_info:所属分类信息(category_id、name、url)
- stock:库存数量(API 场景)
- sales:销量(API 场景)
- sales_percentage:销量占比百分比(API 场景)
- pager:分页信息(total、per_page、current_page、last_page 等)
- sort_list:可用排序字段列表
- brand:品牌信息(当指定 brand_id 且开启品牌功能时)
- 错误响应
- 当分类无效或商品不存在时,抛出领域异常并返回错误提示(如“页面错误”)。
请求示例
- 获取某分类下的商品列表(第 1 页,按销量降序):
- GET /api/?route=product&id=1&page=1&by=sales&sort=desc
- 获取全部分类下的商品(小程序首页全量分页):
- GET /api/?route=product&id=0&page=1
- 按品牌筛选并按价格升序:
- GET /api/?route=product&brand_id=10&by=price&sort=asc
- 按归档时间窗筛选:
- GET /api/?route=product&year=2024&month=6&page=1
响应示例(节选)
- 成功
- 200 OK
- data.product_list[0]:
- id: 123
- title: "示例商品"
- price: "¥199.00"
- sale_price: { value: 169.00, format: "¥169.00" }
- thumb: "https://cdn/.../thumb.jpg"
- image: "https://cdn/.../main.jpg"
- image_other: "https://cdn/.../first.jpg"
- stock: 50
- sales: 120
- sales_percentage: 71
- url: "https://app/product/123"
- 失败
- 4xx/5xx 或统一错误体,message 包含“页面错误”等提示。
高级查询说明
- 商品状态过滤:接口默认仅返回已上架商品(status='1'),无需额外参数。
- 分类筛选:通过 id 或 category_slug 指定;id=0 表示全部分类。
- 品牌筛选:通过 brand_id 指定,未开启品牌功能时忽略。
- 归档筛选:通过 year 与 month 组合,按商品创建时间范围筛选。
- 排序:by 支持 sales、price、created_at、sort;sort 支持 asc、desc。