简介
本文件为 DouPHP 产品控制器的开发文档,聚焦前台、API 与后台三端的产品控制器职责与实现。内容涵盖:
- 产品展示、搜索、分类浏览、详情查看等业务逻辑
- 列表分页、筛选条件(品牌、归档时间)、排序规则等复杂查询处理
- 详情页数据组装、相关商品推荐、评论展示、属性价格联动等
- 扩展点:新增展示字段、集成第三方服务(如优惠券、收藏、属性)
- 性能优化建议与常见问题解决方案
项目结构
产品功能由“路由 → 控制器 → 服务 → 模型”分层组织,前后端共享部分业务服务,后台提供管理操作。
graph TB
subgraph "前端"
FR["front/route/product.php"]
FC["front/controller/product/ProductController.php"]
FS["front/service/product/ProductService.php"]
FM["front/model/product/Product.php"]
FMC["front/model/product/ProductCategory.php"]
end
subgraph "API"
AR["api/route/product.php"]
AC["api/controller/product/ProductController.php"]
end
subgraph "后台"
BR["admin/route/product.php"]
BC["admin/controller/product/ProductController.php"]
BS["admin/service/product/ProductService.php"]
end
FR --> FC
FC --> FS
FS --> FM
FS --> FMC
AR --> AC
AC --> FS
BR --> BC
BC --> BS
核心组件
- 前台产品控制器:负责分类列表页与详情页渲染,组装 SEO、面包屑、导航、分页、相关商品等视图数据。
- API 产品控制器:面向小程序/移动端接口,提供列表、详情、属性价格计算。
- 后台产品控制器:提供商品 CRUD、批量操作、缩略图重建、型号关联等管理能力。
- 前台服务 ProductService:封装列表构建、详情构建、排序选项、价格计算、属性联动等核心业务。
- 后台服务 ProductService:封装后台列表、表单默认值、增删改、缩略图批量处理、型号关联 HTML 片段等。
- 模型 Product / ProductCategory:声明式 ORM、作用域过滤、多语言、附件与相册、默认排序、相关查询等。
架构总览
下图展示了请求从路由到控制器、服务、模型的调用链,以及关键数据流向。
sequenceDiagram
participant U as "用户/客户端"
participant R as "路由"
participant C as "产品控制器"
participant S as "产品服务"
participant M as "产品模型"
participant E as "外部模块(收藏/属性/优惠券)"
U->>R : 访问产品列表/详情
R->>C : 分发到 index/show
C->>S : 构建列表/详情数据
S->>M : 查询并应用过滤/排序/分页
S->>E : 读取收藏状态/属性价格/优惠券
M-->>S : 返回数据集合
S-->>C : 返回结构化数据
C-->>U : 渲染页面或返回 JSON
详细组件分析
前台产品控制器(列表与详情)
- 列表页(index)
- 解析分类 ID、归档时间窗;获取分页大小;调用服务构建列表数据(含品牌筛选、排序)。
- 组装分类信息、面包屑、SEO、导航、分页、相关商品、栏目名称等视图变量。
- 详情页(show)
- 解析产品 ID;调用服务构建详情数据;加载属性列表、评论、数据定义、相关商品、提升商品等。
- 生成 Schema 与 SEO 信息,渲染模板。
flowchart TD
Start(["进入列表页"]) --> ParseCat["解析分类ID/归档"]
ParseCat --> BuildList["调用服务构建列表数据"]
BuildList --> AssembleView["组装视图数据<br/>SEO/导航/分页/相关商品"]
AssembleView --> Render["渲染分类列表模板"]
Render --> End(["结束"])
API 产品控制器(小程序/移动端)
- 列表接口:支持全部分类(id=0)与按品牌、排序、归档筛选;返回标题、分类树、分页数据。
- 详情接口:返回商品主体、自定义字段、开关配置、优惠券列表。
- 属性价格接口:根据选中属性动态计算价格、积分变化。
sequenceDiagram
participant App as "小程序/客户端"
participant AR as "API路由"
participant AC as "API产品控制器"
participant AS as "前台产品服务"
participant AM as "产品模型"
App->>AR : GET /api/product (列表/详情/属性)
AR->>AC : 路由分发
AC->>AS : buildProductListData/buildProductShowData/buildApiAttributeData
AS->>AM : 查询+过滤+排序+分页
AM-->>AS : 数据
AS-->>AC : 结构化结果
AC-->>App : ApiResponse : : success(...)
后台产品控制器(管理端)
- 列表:按分类、关键词分页,展示价格、库存、积分、排序、状态等。
- 新增/编辑:默认数据构建、富文本本地化图片、主图上传、会员价序列化、日志记录。
- 删除:二次确认、审计日志。
- 批量操作:批量删除、批量迁移分类。
- 工具:缩略图重建、型号关联 Ajax。
flowchart TD
AStart(["后台入口"]) --> LIndex["列表查询(分类/关键词/分页)"]
LIndex --> Create["新增表单(默认数据/图库HTML)"]
Create --> Store["提交新增(校验/入库/日志)"]
Store --> Edit["编辑表单(格式化/预览)"]
Edit --> Update["提交更新(校验/入库/日志)"]
Update --> Delete["删除(二次确认/日志)"]
Delete --> Batch["批量操作(删除/迁移)"]
Batch --> Tools["工具(缩略图/型号关联)"]
Tools --> AEnd(["完成"])
列表分页、筛选与排序
- 分页:通过服务层统一分页,页大小来自配置;API 场景默认固定大小。
- 筛选:
- 分类:支持按分类过滤或归档时间窗过滤(二者互斥)。
- 品牌:可选 brand_id 过滤。
- 归档:year/month 时间窗过滤。
- 排序:
- 支持销量、价格、创建时间、排序字段;默认方向与可选项由服务层构建。
- 列表页 URL 会携带当前筛选参数以维持分页链接一致性。
flowchart TD
QStart["接收查询参数"] --> PSize["确定分页大小"]
PSize --> FilterCat{"是否归档?"}
FilterCat -- 是 --> Archive["应用归档时间窗过滤"]
FilterCat -- 否 --> CatFilter["应用分类过滤"]
Archive --> BrandFilter["应用品牌过滤(可选)"]
CatFilter --> BrandFilter
BrandFilter --> SortBuild["构建排序选项/SQL"]
SortBuild --> Paginate["执行分页查询"]
Paginate --> PostProcess["后处理(收藏态/相册首图/价格)"]
PostProcess --> QEnd["返回结果"]
详情页数据、相关商品与评论
- 详情数据:
- 基础信息、价格格式化、会员售价、相册列表、收藏状态、品牌信息、自定义字段、内容 Markdown 渲染。
- 相关商品:
- 同分类随机推荐,带会员视图字段(销售价、收藏态)。
- 评论展示:
- 若启用评论模块,按分页加载评论数据。
- 数据定义:
- 自定义键值对字段在详情中统一解析为数组,便于模板渲染。
sequenceDiagram
participant V as "视图"
participant PC as "前台控制器"
participant PS as "前台服务"
participant PM as "产品模型"
participant CM as "评论模块"
V->>PC : 请求产品详情
PC->>PS : buildProductShowData(id, userId)
PS->>PM : 查询已上架商品
PM-->>PS : 商品实体
PS->>PS : 价格/相册/收藏/品牌/自定义字段
PS-->>PC : 详情数据
PC->>CM : 加载评论(可选)
CM-->>PC : 评论分页数据
PC-->>V : 渲染详情模板
属性价格联动(API)
- 输入:商品 ID、已选属性数据。
- 处理:
- 读取基础价格与积分;
- 根据属性选择累加价格变化,同步调整售价与积分;
- 返回属性列表与价格盒(原价、售价、积分)。
- 适用:小程序/移动端选择规格后实时显示价格。
flowchart TD
AIn["属性选择数据"] --> Base["读取基础价格/积分"]
Base --> Calc["累加属性价格变化"]
Calc --> Out["返回属性列表与价格盒"]
数据模型与关系
- 产品模型:
- 表名、字段类型转换、多语言字段、预取器(URL、多语言、附件、相册首图)。
- 作用域:已上架、分类过滤、品牌过滤、归档过滤、默认排序、相关查询。
- 分类模型:
- 分类树、多语言名称、列表/关联时预热多语言。
classDiagram
class Product {
+int category_id
+string title
+string content
+string description
+string image
+float price
+int stock
+int sales
+string model
+published()
+filterByCategory(catId)
+filterByBrand(brandId)
+filterByArchive(archive)
+related(catId, number, userId)
}
class ProductCategory {
+int id
+int parent_id
+string name
}
Product --> ProductCategory : "belongs_to(category_id)"
依赖关系分析
- 控制器依赖:
- 前台控制器依赖:导航构建、面包屑、SEO、Schema、产品服务。
- API 控制器依赖:前台服务、响应封装。
- 后台控制器依赖:后台服务、Markdown 渲染、审计日志。
- 服务依赖:
- 前台服务依赖:定价服务、Markdown 渲染、排序选项构建、附件、收藏、品牌、语言。
- 后台服务依赖:存储磁盘、定价服务、Markdown、附件、审计日志。
- 模型依赖:
- 产品模型依赖:ORM、作用域、附件、相册、多语言、品牌关联。
- 分类模型依赖:分类树、多语言。
graph LR
FC["前台控制器"] --> PS["前台服务"]
AC["API控制器"] --> PS
BC["后台控制器"] --> BS["后台服务"]
PS --> PM["产品模型"]
PS --> PCM["分类模型"]
PS --> PR["定价服务"]
PS --> MD["Markdown渲染"]
PS --> AT["附件/相册"]
PS --> FAV["收藏模块"]
BC --> AUD["审计日志"]
BC --> IMG["图片处理"]
性能考虑
- 列表查询优化
- 使用 with('category') 批量加载分类,避免 N+1 查询。
- 使用 prefetched 的附件与相册首图映射,减少重复 IO。
- 合理设置分页大小,避免单页过大。
- 价格与属性计算
- 仅在需要时计算会员售价与属性价格变化,避免无谓开销。
- 缓存与预热
- 利用模型 prefetchers 预热 URL、多语言、附件、相册首图。
- 分类树与品牌信息按需加载。
- 数据库索引
- 建议在 category_id、brand_id、created_at、status 等常用过滤字段建立索引。
- 输出优化
- 列表描述截取长度限制,减少传输体积。
- API 仅返回必要字段,降低带宽消耗。
故障排查指南
- 页面错误(page_wrong)
- 现象:分类或产品不存在时抛出领域异常。
- 排查:检查路由参数解析是否正确;确认分类/产品是否存在且已上架。
- 参考位置:
- front/controller/product/ProductController.php:90-99
- front/controller/product/ProductController.php:170-185
- api/controller/product/ProductController.php:50-54
- api/controller/product/ProductController.php:88-97
- 列表为空
- 可能原因:分类过滤过严、归档时间窗不匹配、品牌筛选无结果、未上架。
- 排查:逐步放宽筛选条件;检查 status、created_at、brand_id。
- 参考位置:
- front/service/product/ProductService.php:111-121
- front/model/product/Product.php:217-281
- 价格显示异常
- 可能原因:price 为 0 显示“面议”;会员售价未正确计算;属性价格变化未累加。
- 排查:检查定价服务与属性选择数据;确认 features 开关。
- 参考位置:
- front/service/product/ProductService.php:141-182
- front/service/product/ProductService.php:228-235
- front/service/product/ProductService.php:311-344
- 缩略图重建失败
- 可能原因:路径拼接错误、权限不足、图片质量配置不当。
- 排查:检查磁盘配置、thumb_directory、image_quality;确认绝对路径与子目录。
- 参考位置:
- admin/service/product/ProductService.php:296-363
- 批量操作无效
- 可能原因:未勾选、action 不支持、参数非法。
- 排查:检查 checkbox、action、new_cat_id;确认后端校验。
- 参考位置:
- admin/service/product/ProductService.php:452-487
结论
DouPHP 产品控制器采用清晰的分层架构,前台与 API 共享服务层,后台独立管理流程。通过模型作用域与服务层组合,实现了灵活的列表筛选、排序与分页;详情页整合了价格、属性、收藏、评论等能力。遵循本文的性能建议与故障排查步骤,可有效提升稳定性与用户体验。
附录
扩展指南
- 新增展示字段
- 在模型 casts/appends 中声明新字段;在服务层列表/详情构建中追加字段;在模板中渲染。
- 参考位置:
- front/model/product/Product.php:61-85
- front/service/product/ProductService.php:138-182
- front/service/product/ProductService.php:222-258
- 集成第三方服务
- 收藏:通过 Module::make('favorites') 获取收藏状态与映射。
- 属性:通过 AttributeService 获取属性列表与价格变化。
- 优惠券:通过 Module::make('coupon') 获取可用券列表。
- 参考位置:
- front/service/product/ProductService.php:128-136
- front/service/product/ProductService.php:311-344
- front/controller/product/ProductController.php:204-213
- api/controller/product/ProductController.php:108-113
- 添加新的排序项
- 在 ListSortOptionBuilder 中注册新排序字段与 SQL;在控制器传入 by/sort 参数。
- 参考位置:
- front/service/product/ProductService.php:100-107