# 平台文档管理功能实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 实现平台文档管理功能,支持运营端文档 CRUD + 分类管理,门户端文档展示 **Architecture:** 在 `txw-yygl-service-biz` 中新增文档和分类的 DO/Mapper/Service/Controller,前端使用 Vue 2 + mavon-editor,门户端使用 marked 渲染 Markdown **Tech Stack:** Spring Boot + MyBatis-Plus + Vue 2 + TDesign + mavon-editor + marked --- ## 文件结构总览 ### 后端(txw-yygl-service-biz) ``` src/main/java/com/css/txw/yygl/ ├── controller/ │ └── DocumentController.java # 文档 + 分类 API ├── service/ │ ├── DocumentService.java # 接口 │ └── impl/ │ └── DocumentServiceImpl.java # 实现 ├── mapper/ │ └── DocumentMapper.java # MyBatis Mapper ├── pojo/ │ ├── domain/ │ │ ├── DocumentCategoryDO.java # 分类实体 │ │ └── DocumentDO.java # 文档实体 │ └── vo/ │ ├── DocumentListVO.java # 列表响应 │ └── DocumentDetailVO.java # 详情响应 └── resources/mapper/ └── DocumentMapper.xml # Mapper XML ``` ### 前端(txw-yygl-web) ``` src/pages/document/ ├── list/index.vue # 文档列表页 ├── edit/index.vue # 新增/编辑页 └── category/index.vue # 分类管理页 ``` ### 前端(txw-mhzc-web) ``` src/pages/help/ ├── index.vue # 文档中心首页 └── detail/index.vue # 文档详情页 ``` --- ## 第一阶段:数据库表创建 ### Task 1: 创建数据库表 **SQL文件:** 直接在 `txw-mhzc` 数据库执行 ```sql -- 文档分类表 CREATE TABLE document_category ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', name VARCHAR(100) NOT NULL COMMENT '分类名称', sort INT DEFAULT 0 COMMENT '排序号', status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' ) COMMENT '文档分类表'; -- 文档表 CREATE TABLE document ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', category_id BIGINT NOT NULL COMMENT '分类ID', title VARCHAR(200) NOT NULL COMMENT '文档标题', content TEXT NOT NULL COMMENT 'Markdown正文内容', status TINYINT DEFAULT 0 COMMENT '状态:0-草稿,1-已发布', sort INT DEFAULT 0 COMMENT '排序号', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', FOREIGN KEY (category_id) REFERENCES document_category(id) ) COMMENT '文档表'; ``` - [ ] **Step 1: 在 txw-mhzc 数据库执行上述 SQL** --- ## 第二阶段:后端实现 ### Task 2: 创建 DO 实体类 **文件:** `txw-yygl-service-biz/src/main/java/com/css/txw/yygl/pojo/domain/` - [ ] **Step 1: 创建 DocumentCategoryDO.java** ```java package com.css.txw.yygl.pojo.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.Date; import lombok.Data; @TableName(value = "document_category") @Data public class DocumentCategoryDO implements Serializable { @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField(value = "name") private String name; @TableField(value = "sort") private Integer sort; @TableField(value = "status") private Integer status; @TableField(value = "create_time") private Date createTime; @TableField(value = "update_time") private Date updateTime; @TableField(exist = false) private static final long serialVersionUID = 1L; } ``` - [ ] **Step 2: 创建 DocumentDO.java** ```java package com.css.txw.yygl.pojo.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.io.Serializable; import java.util.Date; import lombok.Data; @TableName(value = "document") @Data public class DocumentDO implements Serializable { @TableId(value = "id", type = IdType.AUTO) private Long id; @TableField(value = "category_id") private Long categoryId; @TableField(value = "title") private String title; @TableField(value = "content") private String content; @TableField(value = "status") private Integer status; @TableField(value = "sort") private Integer sort; @TableField(value = "create_time") private Date createTime; @TableField(value = "update_time") private Date updateTime; @TableField(exist = false) private static final long serialVersionUID = 1L; } ``` --- ### Task 3: 创建 VO 类 **文件:** `txw-yygl-service-biz/src/main/java/com/css/txw/yygl/pojo/vo/` - [ ] **Step 1: 创建 DocumentListVO.java** ```java package com.css.txw.yygl.pojo.vo; import lombok.Data; import java.util.Date; @Data public class DocumentListVO { private Long id; private Long categoryId; private String categoryName; private String title; private Integer status; private Integer sort; private Date createTime; private Date updateTime; } ``` - [ ] **Step 2: 创建 DocumentDetailVO.java** ```java package com.css.txw.yygl.pojo.vo; import lombok.Data; import java.util.Date; @Data public class DocumentDetailVO { private Long id; private Long categoryId; private String categoryName; private String title; private String content; private Integer status; private Integer sort; private Date createTime; private Date updateTime; } ``` - [ ] **Step 3: 创建 CategoryVO.java** ```java package com.css.txw.yygl.pojo.vo; import lombok.Data; import java.util.Date; @Data public class CategoryVO { private Long id; private String name; private Integer sort; private Integer status; private Date createTime; } ``` --- ### Task 4: 创建 Mapper 接口和 XML **文件:** `txw-yygl-service-biz/src/main/java/com/css/txw/yygl/mapper/` - [ ] **Step 1: 创建 DocumentCategoryMapper.java** ```java package com.css.txw.yygl.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.css.txw.yygl.pojo.domain.DocumentCategoryDO; import org.apache.ibatis.annotations.Mapper; @Mapper public interface DocumentCategoryMapper extends BaseMapper { } ``` - [ ] **Step 2: 创建 DocumentMapper.java** ```java package com.css.txw.yygl.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.css.txw.yygl.pojo.domain.DocumentDO; import org.apache.ibatis.annotations.Mapper; @Mapper public interface DocumentMapper extends BaseMapper { } ``` **文件:** `txw-yygl-service-biz/src/main/resources/mapper/` - [ ] **Step 3: 创建 DocumentCategoryMapper.xml** ```xml id,name,sort,status,create_time,update_time ``` - [ ] **Step 4: 创建 DocumentMapper.xml** ```xml id,category_id,title,content,status,sort,create_time,update_time ``` --- ### Task 5: 创建 Service 层 **文件:** `txw-yygl-service-biz/src/main/java/com/css/txw/yygl/service/` - [ ] **Step 1: 创建 DocumentService.java 接口** ```java package com.css.txw.yygl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.css.txw.yygl.pojo.domain.DocumentDO; import com.css.txw.yygl.pojo.vo.DocumentDetailVO; import com.css.txw.yygl.pojo.vo.DocumentListVO; import java.util.List; public interface DocumentService extends IService { List getDocumentList(Long categoryId, Integer status); DocumentDetailVO getDocumentDetail(Long id); void saveDocument(DocumentDO document); void updateDocument(DocumentDO document); void deleteDocument(Long id); } ``` - [ ] **Step 2: 创建 DocumentServiceImpl.java 实现** ```java package com.css.txw.yygl.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.css.txw.yygl.mapper.DocumentCategoryMapper; import com.css.txw.yygl.mapper.DocumentMapper; import com.css.txw.yygl.pojo.domain.DocumentDO; import com.css.txw.yygl.pojo.vo.DocumentDetailVO; import com.css.txw.yygl.pojo.vo.DocumentListVO; import com.css.txw.yygl.service.DocumentService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @Service public class DocumentServiceImpl extends ServiceImpl implements DocumentService { @Autowired private DocumentCategoryMapper categoryMapper; @Override public List getDocumentList(Long categoryId, Integer status) { var wrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper(); if (categoryId != null) wrapper.eq(DocumentDO::getCategoryId, categoryId); if (status != null) wrapper.eq(DocumentDO::getStatus, status); wrapper.orderByAsc(DocumentDO::getSort).orderByDesc(DocumentDO::getCreateTime); List list = this.list(wrapper); return list.stream().map(doc -> { DocumentListVO vo = new DocumentListVO(); vo.setId(doc.getId()); vo.setCategoryId(doc.getCategoryId()); vo.setTitle(doc.getTitle()); vo.setStatus(doc.getStatus()); vo.setSort(doc.getSort()); vo.setCreateTime(doc.getCreateTime()); vo.setUpdateTime(doc.getUpdateTime()); return vo; }).collect(Collectors.toList()); } @Override public DocumentDetailVO getDocumentDetail(Long id) { DocumentDO doc = this.getById(id); if (doc == null) return null; DocumentDetailVO vo = new DocumentDetailVO(); vo.setId(doc.getId()); vo.setCategoryId(doc.getCategoryId()); vo.setTitle(doc.getTitle()); vo.setContent(doc.getContent()); vo.setStatus(doc.getStatus()); vo.setSort(doc.getSort()); vo.setCreateTime(doc.getCreateTime()); vo.setUpdateTime(doc.getUpdateTime()); return vo; } @Transactional @Override public void saveDocument(DocumentDO document) { this.save(document); } @Transactional @Override public void updateDocument(DocumentDO document) { this.updateById(document); } @Transactional @Override public void deleteDocument(Long id) { this.removeById(id); } } ``` - [ ] **Step 3: 创建 DocumentCategoryService.java 接口** ```java package com.css.txw.yygl.service; import com.baomidou.mybatisplus.extension.service.IService; import com.css.txw.yygl.pojo.domain.DocumentCategoryDO; import com.css.txw.yygl.pojo.vo.CategoryVO; import java.util.List; public interface DocumentCategoryService extends IService { List getCategoryList(); void saveCategory(DocumentCategoryDO category); void updateCategory(DocumentCategoryDO category); void deleteCategory(Long id); } ``` - [ ] **Step 4: 创建 DocumentCategoryServiceImpl.java 实现** ```java package com.css.txw.yygl.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.css.txw.yygl.mapper.DocumentCategoryMapper; import com.css.txw.yygl.pojo.domain.DocumentCategoryDO; import com.css.txw.yygl.pojo.vo.CategoryVO; import com.css.txw.yygl.service.DocumentCategoryService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @Service public class DocumentCategoryServiceImpl extends ServiceImpl implements DocumentCategoryService { @Override public List getCategoryList() { var wrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper(); wrapper.eq(DocumentCategoryDO::getStatus, 1); wrapper.orderByAsc(DocumentCategoryDO::getSort); List list = this.list(wrapper); return list.stream().map(c -> { CategoryVO vo = new CategoryVO(); vo.setId(c.getId()); vo.setName(c.getName()); vo.setSort(c.getSort()); vo.setStatus(c.getStatus()); vo.setCreateTime(c.getCreateTime()); return vo; }).collect(Collectors.toList()); } @Transactional @Override public void saveCategory(DocumentCategoryDO category) { this.save(category); } @Transactional @Override public void updateCategory(DocumentCategoryDO category) { this.updateById(category); } @Transactional @Override public void deleteCategory(Long id) { this.removeById(id); } } ``` --- ### Task 6: 创建 Controller **文件:** `txw-yygl-service-biz/src/main/java/com/css/txw/yygl/controller/` - [ ] **Step 1: 创建 DocumentController.java** ```java package com.css.txw.yygl.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.css.ggzc.framework.common.pojo.CommonResult; import com.css.txw.yygl.pojo.domain.DocumentDO; import com.css.txw.yygl.pojo.vo.DocumentDetailVO; import com.css.txw.yygl.pojo.vo.DocumentListVO; import com.css.txw.yygl.service.DocumentService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; @RestController @RequestMapping("/document") @Tag(name = "文档管理") @Validated public class DocumentController { @Resource private DocumentService documentService; @GetMapping("/list") @Operation(summary = "文档列表") public CommonResult> getDocumentList( @RequestParam(required = false) Long categoryId, @RequestParam(required = false) Integer status) { return CommonResult.success(documentService.getDocumentList(categoryId, status)); } @GetMapping("/{id}") @Operation(summary = "文档详情") public CommonResult getDocumentDetail(@PathVariable Long id) { return CommonResult.success(documentService.getDocumentDetail(id)); } @PostMapping @Operation(summary = "新增文档") public CommonResult saveDocument(@RequestBody DocumentDO document) { documentService.saveDocument(document); return CommonResult.success("success"); } @PutMapping("/{id}") @Operation(summary = "编辑文档") public CommonResult updateDocument(@PathVariable Long id, @RequestBody DocumentDO document) { document.setId(id); documentService.updateDocument(document); return CommonResult.success("success"); } @DeleteMapping("/{id}") @Operation(summary = "删除文档") public CommonResult deleteDocument(@PathVariable Long id) { documentService.deleteDocument(id); return CommonResult.success("success"); } } ``` - [ ] **Step 2: 创建 DocumentCategoryController.java** ```java package com.css.txw.yygl.controller; import com.css.ggzc.framework.common.pojo.CommonResult; import com.css.txw.yygl.pojo.domain.DocumentCategoryDO; import com.css.txw.yygl.pojo.vo.CategoryVO; import com.css.txw.yygl.service.DocumentCategoryService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; @RestController @RequestMapping("/document/category") @Tag(name = "文档分类管理") @Validated public class DocumentCategoryController { @Resource private DocumentCategoryService categoryService; @GetMapping("/list") @Operation(summary = "分类列表") public CommonResult> getCategoryList() { return CommonResult.success(categoryService.getCategoryList()); } @PostMapping @Operation(summary = "新增分类") public CommonResult saveCategory(@RequestBody DocumentCategoryDO category) { categoryService.saveCategory(category); return CommonResult.success("success"); } @PutMapping("/{id}") @Operation(summary = "编辑分类") public CommonResult updateCategory(@PathVariable Long id, @RequestBody DocumentCategoryDO category) { category.setId(id); categoryService.updateCategory(category); return CommonResult.success("success"); } @DeleteMapping("/{id}") @Operation(summary = "删除分类") public CommonResult deleteCategory(@PathVariable Long id) { categoryService.deleteCategory(id); return CommonResult.success("success"); } } ``` --- ## 第三阶段:前端实现(txw-yygl-web) ### Task 7: 安装 Markdown 编辑器依赖 - [ ] **Step 1: 在 txw-yygl-web 安装 mavon-editor** ```bash cd txw-yygl-web && npm install mavon-editor --save ``` --- ### Task 8: 创建文档管理页面 **文件:** `txw-yygl-web/src/pages/document/list/index.vue` - [ ] **Step 1: 创建文档列表页** ```vue ``` **文件:** `txw-yygl-web/src/pages/document/edit/index.vue` - [ ] **Step 2: 创建文档编辑页(含 mavon-editor)** ```vue ``` **文件:** `txw-yygl-web/src/pages/document/category/index.vue` - [ ] **Step 3: 创建分类管理页** ```vue ``` --- ## 第四阶段:前端实现(txw-mhzc-web) ### Task 9: 安装 Markdown 渲染依赖 - [ ] **Step 1: 在 txw-mhzc-web 安装 marked 和 highlight.js** ```bash cd txw-mhzc-web && npm install marked highlight.js --save ``` --- ### Task 10: 创建门户文档中心页面 **文件:** `txw-mhzc-web/src/pages/help/index.vue` - [ ] **Step 1: 创建文档中心首页** ```vue ``` **文件:** `txw-mhzc-web/src/pages/help/detail/index.vue` - [ ] **Step 2: 创建文档详情页(Markdown 渲染)** ```vue ``` --- ## 第五阶段:路由配置 ### Task 11: 配置 yygl-web 路由 **文件:** `txw-yygl-web/src/core/router/` - [ ] **Step 1: 在路由配置中添加文档管理路由** 在现有路由配置中新增: ```javascript { path: '/document', component: () => import('@/pages/document'), children: [ { path: 'list', component: () => import('@/pages/document/list') }, { path: 'edit/:id?', component: () => import('@/pages/document/edit') }, { path: 'category', component: () => import('@/pages/document/category') } ] } ``` --- ### Task 12: 配置 mhzc-web 路由 **文件:** `txw-mhzc-web/src/core/router/` - [ ] **Step 1: 在路由配置中添加文档中心路由** ```javascript { path: '/help', component: () => import('@/pages/help'), children: [ { path: '', component: () => import('@/pages/help/index') }, { path: ':id', component: () => import('@/pages/help/detail') } ] } ``` --- ## 执行方式 **Plan 完成。两种执行方式:** 1. **Subagent-Driven(推荐)** - 每 task 由独立子 agent 执行,任务间有审核 2. **Inline Execution** - 在当前会话中批量执行,带检查点审核 **选择哪种方式?**