简介
本技术文档面向 DouPHP 的内容管理服务,聚焦文章、案例、课程、下载四类内容的统一管理能力。内容服务层负责后台的列表构建、新增/更新/删除、批量操作、附件处理、Markdown 渲染、审计日志等;前台服务提供列表与详情展示、点击统计等能力。模型层通过可复用 trait 实现分类筛选、默认排序、内容字段转换、删除联动清理等通用逻辑,从而以最小成本扩展新的内容类型。
项目结构
内容管理采用“模块 + 领域”的组织方式,每个内容类型拥有独立的 admin 与 front 子域,包含控制器、服务、模型、请求校验、路由与视图。服务层是业务编排的核心,协调模型、附件、Markdown 渲染与审计日志;模型层封装数据访问与查询构造器扩展。
graph TB
subgraph "后台"
A["文章服务<br/>ArticleService"]
B["案例服务<br/>CasesService"]
C["课程服务<br/>CourseService"]
D["下载服务<br/>DownloadService"]
end
subgraph "前台"
F["前台文章服务<br/>Front ArticleService"]
end
subgraph "模型"
M1["文章模型<br/>Article"]
end
A --> M1
B --> M1
C --> M1
D --> M1
F --> M1
核心组件
- 后台内容服务(文章/案例/课程/下载)
- 职责:列表数据构建、新增/更新/删除、批量操作、附件存储、Markdown 渲染、审计日志记录。
- 共性:统一的参数校验入口在 Request 层,服务专注业务流程编排与跨组件协作。
- 前台文章服务
- 职责:文章列表与详情数据组装、Markdown 渲染、点击量统计。
- 模型与查询扩展
- 职责:字段白名单、关联关系、默认排序、关键字/分类筛选、内容字段转换、删除时清理关联资源。
架构总览
内容管理遵循分层架构:控制器接收请求并委托服务;服务编排模型、附件、Markdown、审计;模型封装数据访问与查询构造器扩展。各内容类型的服务类高度同构,便于统一维护与扩展。
sequenceDiagram
participant Admin as "后台控制器"
participant Svc as "内容服务"
participant Model as "内容模型"
participant Att as "附件服务"
participant MD as "Markdown渲染"
participant Aud as "审计日志"
Admin->>Svc : 调用 insert/update/delete/action
Svc->>Att : 存储/转换图片与正文图片
Svc->>Model : create/find/save/destroy
Svc->>MD : toHtml(可选)
Svc->>Aud : writeAdminLog(CREATE/UPDATE/DELETE)
Svc-->>Admin : 返回结果或错误
详细组件分析
后台文章服务(ArticleService)
- 列表构建:按分类与关键词过滤,分页并补齐模板字段。
- 新增流程:清洗正文、暂存远程图片到草稿、持久化主记录、上传主图、认领草稿、写审计日志。
- 编辑流程:校验存在性、处理正文与主图、保存并写审计日志。
- 删除流程:二次确认分支、实际删除并写审计日志。
- 批量操作:支持批量删除与批量转移分类。
flowchart TD
Start(["进入 insert"]) --> Clean["清洗正文/XSS"]
Clean --> Draft{"是否包含远程图片?"}
Draft --> |是| StoreDraft["存储草稿图片"]
Draft --> |否| Persist["创建记录"]
StoreDraft --> Persist
Persist --> UploadMain["上传主图"]
UploadMain --> Claim["认领草稿归属"]
Claim --> Audit["记录审计日志"]
Audit --> End(["返回新ID"])
后台案例服务(CasesService)
- 与文章服务同构:列表构建、新增/更新/删除、批量操作,差异在于实体名与路由命名空间。
- 正文与图片处理流程一致,均通过附件服务与 Markdown 渲染。
后台课程服务(CourseService)
- 与文章/案例服务同构:列表构建、新增/更新/删除、批量操作。
- 使用相同的服务模式,便于统一扩展与维护。
后台下载服务(DownloadService)
- 与文章服务同构,额外字段 download_link、size 参与列表与表单。
- 正文与图片处理流程一致。
前台文章服务(Front ArticleService)
- 列表构建:支持分类、归档(年/月)过滤,分页并格式化摘要与链接。
- 详情获取:查找已发布文章,Markdown 渲染正文。
- 点击统计:调用模型方法增加点击量。
sequenceDiagram
participant Front as "前台控制器"
participant FService as "前台文章服务"
participant Model as "前台文章模型"
participant MD as "Markdown渲染"
Front->>FService : buildArticleListData(catId, page, pageSize, archive)
FService->>Model : with('category')->published()->filterByCategory()->filterByArchive()->applyDefaultOrder()->paginate()
Model-->>FService : 分页数据
FService-->>Front : 列表数据
Front->>FService : buildArticleShowData(id)
FService->>Model : findPublishedById(id)
Model-->>FService : 文章对象
FService->>MD : toHtml(content)
MD-->>FService : HTML
FService-->>Front : 详情数据
模型抽象与继承关系(以文章为例)
- 基础模型:继承框架 ORM 基类,提供通用 CRUD。
- Trait 复用:
- 内容字段转换:统一处理 content 等字段的存取格式。
- 分类筛选:提供 filterByCategory 等查询作用域。
- 删除联动:删除时清理关联资源。
- 自定义扩展:
- 字段白名单:限制 fillable 字段,保障安全写入。
- 列表 casts:统一 image、created_at、status 等字段格式化。
- 默认排序:根据配置启用 sort 或仅按 id 排序。
- 关键字筛选:标题模糊匹配。
classDiagram
class Model {
+create(data)
+find(id)
+whereKey(id)
+whereIn(field, ids)
+destroy(ids)
+field(columns)
+with(relations)
+paginate(perPage, page, options)
}
class Article {
+table : "article"
+fillable : [...]
+casts : {...}
+prefetchers : {...}
+category() BelongsTo
+scopeFilterByKeyword(query, keyword)
+scopeApplyDefaultOrder(query)
}
Model <|-- Article
依赖关系分析
- 服务对模型的依赖:所有后台服务通过各自模型进行数据访问,并使用公共查询作用域(如 filterByCategory、applyDefaultOrder)。
- 服务对基础设施的依赖:
- 附件服务:用于主图与正文内图片的存储、URL 生成、草稿认领。
- Markdown 渲染:将 content 转换为 HTML 供模板展示。
- 审计日志:记录管理员的创建、更新、删除行为。
- 前台服务依赖 Markdown 渲染与前台模型,提供对外读取能力。
graph LR
Svc["后台服务"] --> Model["内容模型"]
Svc --> Att["附件服务"]
Svc --> MD["Markdown渲染"]
Svc --> Aud["审计日志"]
FrontSvc["前台文章服务"] --> FModel["前台文章模型"]
FrontSvc --> MD
性能与缓存
- 列表查询优化
- 使用 field 指定列减少数据传输。
- 使用 with('category') 预加载关联,避免 N+1 查询。
- 使用 applyDefaultOrder 与 filterByCategory/filterByKeyword 组合条件,充分利用数据库索引。
- 附件 URL 预热
- 模型定义 prefetchers 对 image 字段进行批量 URL 预热,减少重复计算。
- Markdown 渲染
- 仅在需要展示的编辑页或详情页进行 toHtml 转换,避免不必要的渲染开销。
- 分页
- 统一分页大小(后台 15,前台可配置),降低单次响应体积。
- 建议
- 为 title、category_id、created_at 建立合适索引以提升筛选与排序性能。
- 对高频访问的前台列表可考虑引入应用级缓存(如 Redis),结合失效策略保证一致性。
故障排查指南
- 新增失败
- 检查 draftToken 与 adminId 是否为空或不合法,非法时会抛出领域异常并跳转回退页面。
- 检查附件存储路径与权限,确保能写入主图与正文图片。
- 更新失败
- 校验目标记录是否存在,不存在会抛出未找到异常。
- 检查 XSS 清洗与 Markdown 渲染是否引发异常。
- 删除失败
- 二次确认分支需携带 confirm 参数,否则返回确认信息。
- 删除前会读取 title/image 用于提示文案。
- 批量操作失败
- checkbox 必须为非空数组且经整型过滤,否则抛出选择为空异常。
- action 仅支持 del_all 与 category_move,其他值会抛出不支持动作异常。
结论
DouPHP 内容管理服务通过统一的服务层与可复用的模型 trait,实现了文章、案例、课程、下载等多内容类型的标准化管理。服务层专注于业务流程编排,模型层提供一致的查询与数据转换能力,前台服务提供稳定的读取接口。该设计具备良好的可扩展性与可维护性,便于快速新增内容类型与功能增强。
附录:开发示例
新增一个内容类型(以“白皮书”为例)
- 创建模型
- 新建模型类,继承框架 ORM 基类,声明表名、fillable、casts、prefetchers。
- 添加 category 关联与 scopeFilterByKeyword、scopeApplyDefaultOrder 等查询作用域。
- 创建服务
- 新建后台服务类,实现列表构建、新增、更新、删除、批量操作等方法,复用附件与 Markdown 渲染。
- 注册路由与控制器
- 在 admin 与 front 下分别注册路由与控制器,绑定服务方法。
- 前端视图
- 准备列表与表单视图,绑定服务返回的数据结构。
参考路径
- Article.php:28-123
- ArticleService.php:55-312
管理内容分类
- 列表与筛选
- 使用 filterByCategory 按分类 ID 过滤,配合 with('category') 预加载分类信息。
- 批量转移
- 通过 action 的 category_move 将多条内容转移到新分类。
参考路径
- ArticleService.php:63-99
- ArticleService.php:286-312
内容发布流程
- 后台编辑
- 新增/更新时清洗正文、处理远程图片、上传主图、记录审计日志。
- 前台展示
- 列表使用 published 过滤已发布内容,详情使用 Markdown 渲染正文。
- 点击统计
- 详情页调用记录点击量的方法,累计阅读数。
参考路径
- ArticleService.php:130-243
- ArticleService.php(前台):53-151
SEO 优化要点
- 字段支持
- slug、keywords、description 字段可用于生成友好 URL 与搜索引擎元信息。
- 列表与详情
- 列表输出 description 摘要与分类链接,详情输出完整内容。
- 建议
- 为 slug 建立唯一索引,提升路由解析效率。
- 在前端模板中基于 keywords/description 生成 meta 标签。
参考路径
- Article.php:68-81
- ArticleService.php(前台):71-99
搜索与推荐(现状与建议)
- 现状
- 当前列表支持按标题关键字模糊筛选与按分类过滤。
- 建议
- 引入全文检索引擎(如 Elasticsearch)以支持更复杂的搜索需求。
- 基于点击量与时间衰减实现简单推荐算法,提升内容曝光度。
参考路径
- Article.php:94-108
- ArticleService.php(前台):53-99