简介
本文件面向 DouPHP 框架的产品服务,系统性梳理后台与前台商品管理的核心能力与实现方式。内容覆盖商品信息管理、分类管理、属性联动、库存与价格计算、促销处理、搜索过滤等关键业务逻辑;并说明产品与服务层中与其他组件(订单、购物车、库存、优惠券)的协作模式。文末提供典型产品管理场景的使用示例,帮助开发者正确使用产品服务 API 进行商品开发与管理。
项目结构
围绕“产品”领域,代码按前后端职责分层组织:
- 后台管理侧:负责商品与分类的增删改查、批量操作、缩略图重建、型号关联等。
- 前台展示侧:负责商品列表、详情渲染、排序筛选、品牌过滤、会员价与属性联动价格计算等。
- 模型层:定义查询作用域、关联关系、数据格式化与预取策略。
- 外部协作:通过定价服务、附件服务、Markdown 渲染、导航同步、收藏服务等完成跨模块协同。
graph TB
subgraph "后台"
A["ProductService(后台)"]
B["CategoryService(后台)"]
end
subgraph "前台"
C["ProductService(前台)"]
D["Product(前台模型)"]
end
subgraph "通用服务"
E["PricingService(定价)"]
F["Attachment(附件)"]
G["MarkdownRenderer(内容渲染)"]
H["NavCategorySync(导航同步)"]
end
A --> E
A --> F
A --> G
B --> H
C --> E
C --> D
C --> F
C --> G
核心组件
- 后台商品服务(ProductService):负责商品列表构建、新增/更新/删除、批量操作、缩略图重建、型号关联等。
- 后台分类服务(CategoryService):负责分类树、表单默认数据、新增/更新/删除及与导航同步。
- 前台商品服务(ProductService):负责商品列表页与详情页数据组装、排序与品牌筛选、会员价与属性联动价格计算。
- 前台商品模型(Product):定义查询作用域(分类、品牌、归档、默认排序)、关联关系、附件与多语言预取、列表附加字段等。
架构总览
下图展示了从请求到数据返回的关键路径,涵盖后台商品管理与前台商品展示的核心交互。
sequenceDiagram
participant Admin as "后台控制器"
participant PS as "后台 ProductService"
participant Model as "后台 Product 模型"
participant Pricing as "PricingService"
participant Attach as "Attachment 服务"
participant Log as "审计日志"
Admin->>PS : insert/update/delete/action
PS->>Model : create/find/update/destroy
PS->>Pricing : levelPrice/salePrice(如需)
PS->>Attach : store/gallery/url(主图/相册)
PS->>Log : writeAdminLog(创建/更新/删除)
Model-->>PS : 结果
PS-->>Admin : 视图数据或响应
详细组件分析
后台商品服务(ProductService)
- 列表构建:分页查询商品,补齐分类名、图片、会员价档位等模板字段,支持关键词与分类筛选。
- 新增流程:校验草稿令牌与管理员ID,序列化会员价,清洗内容并本地化远程图片,写入商品记录,上传主图并生成缩略图,认领草稿资产,写审计日志。
- 编辑流程:读取商品并转换为模板格式,处理主图/正文/型号列表/自定义字段换行。
- 删除流程:二次确认机制,记录审计日志后删除。
- 批量操作:支持批量删除与批量迁移分类。
- 缩略图重建:根据存储配置与尺寸参数逐条重建缩略图,前端进度遮罩更新。
- 型号关联:按 model 字符串将多个商品归组,支持添加/删除子项并返回最新 HTML 片段。
flowchart TD
Start(["进入 insert"]) --> Validate["校验草稿令牌与管理员ID"]
Validate --> LevelPrice{"是否启用用户等级价?"}
LevelPrice --> |是| CalcLevel["调用定价服务序列化等级价"]
LevelPrice --> |否| ContentXSS["内容XSS清洗"]
CalcLevel --> ContentXSS
ContentXSS --> LocalImg{"是否本地化远程图片?"}
LocalImg --> |是| StoreDraft["存储草稿图片并替换URL"]
LocalImg --> |否| CreateRecord["创建商品记录"]
StoreDraft --> CreateRecord
CreateRecord --> UploadMain["上传主图并生成缩略图"]
UploadMain --> Claim["认领草稿资产"]
Claim --> Audit["写审计日志"]
Audit --> End(["返回新ID"])
后台分类服务(CategoryService)
- 分类树:扁平化分类列表,可选附带属性列表。
- 表单默认数据:初始化分类字段与默认值。
- 新增/更新:根据图标模式(文本/图片)处理图标字段,可选同步至导航,写审计日志。
- 删除:检查占用与父级关系,二次确认后清理语言与导航中间节点,执行删除并写日志。
sequenceDiagram
participant Ctrl as "后台控制器"
participant CatS as "CategoryService"
participant Nav as "NavCategorySync"
participant Log as "审计日志"
Ctrl->>CatS : insert/update/delete
CatS->>Nav : syncForCategory(可选)
CatS->>Log : writeAdminLog(创建/更新/删除)
CatS-->>Ctrl : 结果
前台商品服务(ProductService)
- 列表页数据:支持分类、品牌、归档区间、排序选项;批量预取分类、附件与首图;为API场景补充销量与库存百分比。
- 详情页数据:仅加工商品主体,格式化价格、计算销售价、加载相册、品牌信息、型号列表、多语言内容与自定义字段解析。
- 属性联动价格:在API场景下,根据选中属性值的价格变动动态调整基础价与销售价,并同步积分换算。
sequenceDiagram
participant FrontC as "前台控制器"
participant FPS as "前台 ProductService"
participant Model as "Product(前台模型)"
participant Pricing as "PricingService"
participant Attach as "Attachment"
participant Fav as "收藏服务(可选)"
FrontC->>FPS : buildProductListData(...)
FPS->>Model : with('category')->published()->filterByBrand()->order()->paginate()
FPS->>Fav : mapFavoritedIds(可选)
FPS->>Attach : galleryFirstMap(批量首图)
FPS-->>FrontC : product_list, pager, sort_list, brand
FrontC->>FPS : buildProductShowData(productId, userId)
FPS->>Model : findPublishedById()
FPS->>Pricing : salePrice('product', productId, userId)
FPS->>Attach : gallery('product', productId, 'gallery')
FPS-->>FrontC : 商品详情数据
前台商品模型(Product)
- 查询作用域:分类筛选、品牌筛选、归档时间窗、默认排序、仅含主图、仅上架商品。
- 关联关系:分类、品牌。
- 数据格式化:casts 对 image/defined/created_at 等进行类型转换;appends 提供 thumb/image_other 等附加字段;prefetchers 预热 URL、多语言、附件与首图。
- 工具方法:findPublishedById、findFirstCategoryId、related 推荐等。
classDiagram
class Product {
+table "product"
+casts "image, defined, created_at"
+appends "thumb, image_other, ..."
+prefetchers "url, language, attachment, ..."
+category() BelongsTo
+brand() BelongsTo
+scopePublished(query) Builder
+scopeFilterByBrand(query, brandId) Builder
+scopeFilterByArchive(query, archive) Builder
+scopeApplyDefaultOrder(query) Builder
+findPublishedById(id) Model?
+findFirstCategoryId() mixed
+related(catId, number, userId) Collection
}
后台商品模型(Product)
- 关联分类:用于后台列表显示分类名称。
- 关键字筛选:标题模糊匹配。
- 默认排序:根据配置决定是否启用手动排序。
- 仅上架:status=1。
依赖关系分析
- 定价服务(PricingService):被后台与前台商品服务共同使用,用于等级价序列化与销售价计算。
- 附件服务(Attachment):用于主图上传、缩略图生成、相册首图获取与URL转换。
- Markdown 渲染器:用于内容HTML渲染。
- 导航同步服务:分类新增/更新时可选同步至导航。
- 收藏服务:前台列表与详情中判断收藏状态。
- 订单/购物车/库存:购物车加入商品时进行库存校验;下单时可能锁定库存;优惠券折扣在服务层计算。
graph LR
PS_Back["后台 ProductService"] --> Pricing["PricingService"]
PS_Back --> Attach["Attachment"]
PS_Back --> MD["MarkdownRenderer"]
PS_Front["前台 ProductService"] --> Pricing
PS_Front --> Attach
PS_Front --> MD
CategorySvc["CategoryService"] --> NavSync["NavCategorySync"]
CartSvc["CartService"] --> Stock["库存校验"]
CheckoutSvc["CheckoutService"] --> Order["订单持久化"]
CouponSvc["CouponService"] --> Discount["折扣计算"]
性能考虑
- 列表查询优化:使用 AR 的 with('category') 批量加载分类,避免 N+1 查询;使用 prefetchers 预热 URL、多语言、附件与首图,减少重复IO。
- 分页与排序:合理设置 pageSize,结合默认排序与作用域过滤,降低数据库压力。
- 缩略图重建:采用流式输出与 flush/ob_flush 提升长任务体验,避免超时。
- 附件处理:统一通过 Attachment 服务访问与生成缩略图,复用缓存与配置。
- 价格计算:通过定价服务集中处理等级价与销售价,避免分散逻辑导致重复计算。
故障排查指南
- 非法参数:当缺少必要参数(如草稿令牌、管理员ID、商品ID)时,服务抛出领域异常并返回错误页面,需检查入参合法性。
- 商品不存在:更新或删除前会校验记录是否存在,若不存在则提示相应错误。
- 分类删除限制:若分类被占用或存在子分类,删除将被阻止并给出提示信息。
- 库存校验失败:加入购物车时若库存不足或超出限制,将返回错误码与消息,需检查库存配置与数量。
- 优惠券折扣:优惠券计算受有效期、门槛、封顶金额等规则影响,需核对券配置与适用条件。
结论
DouPHP 的产品服务以清晰的前后台分层与可复用的服务/模型设计,实现了商品与分类的全生命周期管理。通过定价服务、附件服务、Markdown 渲染与导航同步等通用能力,保证了功能扩展性与一致性。与订单、购物车、库存、优惠券的协作遵循明确的边界与契约,便于维护与演进。建议在实际开发中严格遵循服务接口约定,合理使用查询作用域与预取策略,确保性能与稳定性。
附录:产品管理场景示例
以下示例基于现有服务方法,展示如何正确调用以完成常见产品管理任务。为避免泄露实现细节,仅提供调用思路与关键步骤。
-
新增商品
- 准备表单数据(标题、分类、价格、库存、自定义字段等)。
- 调用后台 ProductService 的 insert 方法,传入数据、草稿令牌与管理员ID。
- 服务内部会处理等级价序列化、内容清洗、远程图片本地化、主图上传与缩略图生成,并写入审计日志。
- 参考路径:insert:174-213
-
编辑商品
- 调用 buildProductEditData 获取编辑页数据(包含图片URL、型号列表、内容HTML等)。
- 提交更新时调用 update 方法,服务会处理主图/正文更新与等级价序列化,并记录审计日志。
- 参考路径:buildProductEditData:223-247、update:260-294
-
删除商品
- 首次调用 delete 获取二次确认信息;再次携带 confirm 标志执行删除。
- 参考路径:delete:427-450
-
批量操作
- 选择多个商品后调用 action,指定 del_all 或 category_move,并传入 new_cat_id(迁移分类时使用)。
- 参考路径:action:459-485
-
重建缩略图
- 调用 buildThumbData 获取待处理文件与遮罩配置;随后调用 thumbFlush 逐条重建缩略图。
- 参考路径:buildThumbData:302-316、thumbFlush:325-343
-
型号关联
- 调用 model 方法,mode=add 添加子项,mode=del 删除指定或整组;返回最新型号列表 HTML。
- 参考路径:model:373-388、buildProductModelHtml:400-417
-
前台商品列表
- 调用 buildProductListData,传入分类、品牌、排序、归档区间等参数;服务会返回商品列表、分页、排序选项与品牌信息。
- 参考路径:buildProductListData:76-201
-
前台商品详情
- 调用 buildProductShowData,传入商品ID与用户ID;服务返回格式化后的商品详情(价格、销售价、相册、品牌、型号列表等)。
- 参考路径:buildProductShowData:210-258
-
属性联动价格(API)
- 调用 buildApiAttributeData,传入商品ID、用户ID与属性数据;服务返回属性列表与价格盒(基础价、销售价、积分)。
- 参考路径:buildApiAttributeData:311-344
-
与购物车/库存协作
- 加入购物车时,CartService 会进行库存校验;若通过则写入购物车表。
- 参考路径:CartService 加入商品:108-134
-
与优惠券协作
- 结算时 CouponService 根据券类型、门槛与封顶金额计算折扣。
- 参考路径:CouponService discount:111-168