fix(education): align Flyway delivery review
This commit is contained in:
@@ -1,709 +0,0 @@
|
||||
# API 设计规范知识库
|
||||
|
||||
convention:
|
||||
id: "api-designer"
|
||||
name: "API 设计规范"
|
||||
category: "设计规范"
|
||||
description: "定义项目 REST API 的设计规范,包括接口命名、请求响应格式、权限控制、文档注解等,确保 API 设计的统一性和规范性"
|
||||
|
||||
# ============================================
|
||||
# 第一部分:设计原则
|
||||
# ============================================
|
||||
philosophy:
|
||||
design_principles:
|
||||
- principle: "操作式 URL 设计"
|
||||
description: "使用动词表达操作意图,而非 RESTful 资源式命名"
|
||||
example: "/system/user/create 而非 POST /users"
|
||||
- principle: "统一响应格式"
|
||||
description: "所有接口使用 CommonResult 包装,确保响应格式一致"
|
||||
- principle: "权限分级控制"
|
||||
description: "通过 @PermitAll 和 @PreAuthorize 实现公开接口与权限接口的分离"
|
||||
- principle: "文档即代码"
|
||||
description: "使用 OpenAPI 3.0 注解,接口文档与代码同步维护"
|
||||
|
||||
# ============================================
|
||||
# 第二部分:接口规范
|
||||
# ============================================
|
||||
interface_conventions:
|
||||
# URL 命名规范
|
||||
url_naming:
|
||||
format: "/{模块}/{功能}/{操作}"
|
||||
rules:
|
||||
- "模块名与功能名使用小写字母"
|
||||
- "操作名使用动词或动词短语"
|
||||
- "多词使用连字符分隔(如 update-status)"
|
||||
examples:
|
||||
- path: "/system/user/create"
|
||||
description: "创建用户"
|
||||
- path: "/system/user/page"
|
||||
description: "用户分页列表"
|
||||
- path: "/pay/order/get"
|
||||
description: "获取订单详情"
|
||||
- path: "/system/user/update-status"
|
||||
description: "更新用户状态"
|
||||
|
||||
# HTTP 方法映射
|
||||
http_methods:
|
||||
- method: "POST"
|
||||
operations: ["create", "import", "send", "login", "logout"]
|
||||
description: "创建类操作"
|
||||
idempotent: false
|
||||
- method: "PUT"
|
||||
operations: ["update", "update-status", "update-password"]
|
||||
description: "更新类操作"
|
||||
idempotent: true
|
||||
- method: "DELETE"
|
||||
operations: ["delete", "delete-list"]
|
||||
description: "删除类操作"
|
||||
idempotent: true
|
||||
- method: "GET"
|
||||
operations: ["get", "page", "list", "export-excel", "get-import-template"]
|
||||
description: "查询类操作"
|
||||
idempotent: true
|
||||
safe: true
|
||||
|
||||
# 标准端点定义
|
||||
standard_endpoints:
|
||||
- endpoint: "/create"
|
||||
method: "POST"
|
||||
purpose: "创建单个实体"
|
||||
request: "@RequestBody @Valid XxxSaveReqVO"
|
||||
response: "CommonResult<Long>"
|
||||
permission: "{模块}:{功能}:create"
|
||||
|
||||
- endpoint: "/update"
|
||||
method: "PUT"
|
||||
purpose: "更新单个实体"
|
||||
request: "@RequestBody @Valid XxxSaveReqVO"
|
||||
response: "CommonResult<Boolean>"
|
||||
permission: "{模块}:{功能}:update"
|
||||
|
||||
- endpoint: "/delete"
|
||||
method: "DELETE"
|
||||
purpose: "删除单个实体"
|
||||
request: "@RequestParam(\"id\") Long id"
|
||||
response: "CommonResult<Boolean>"
|
||||
permission: "{模块}:{功能}:delete"
|
||||
parameter_annotation: "@Parameter(name = \"id\", description = \"编号\", required = true)"
|
||||
|
||||
- endpoint: "/delete-list"
|
||||
method: "DELETE"
|
||||
purpose: "批量删除实体"
|
||||
request: "@RequestParam(\"ids\") List<Long> ids"
|
||||
response: "CommonResult<Boolean>"
|
||||
permission: "{模块}:{功能}:delete"
|
||||
parameter_annotation: "@Parameter(name = \"ids\", description = \"编号列表\", required = true)"
|
||||
|
||||
- endpoint: "/get"
|
||||
method: "GET"
|
||||
purpose: "获取单个实体详情"
|
||||
request: "@RequestParam(\"id\") Long id"
|
||||
response: "CommonResult<XxxRespVO>"
|
||||
permission: "{模块}:{功能}:query"
|
||||
parameter_annotation: "@Parameter(name = \"id\", description = \"编号\", required = true)"
|
||||
|
||||
- endpoint: "/page"
|
||||
method: "GET"
|
||||
purpose: "分页查询列表"
|
||||
request: "@Valid XxxPageReqVO"
|
||||
response: "CommonResult<PageResult<XxxRespVO>>"
|
||||
permission: "{模块}:{功能}:query"
|
||||
|
||||
- endpoint: "/list"
|
||||
method: "GET"
|
||||
purpose: "列表查询(不分页)"
|
||||
request: "@RequestParam(\"ids\") List<Long> ids 或无参数"
|
||||
response: "CommonResult<List<XxxRespVO>>"
|
||||
permission: "{模块}:{功能}:query"
|
||||
|
||||
- endpoint: "/export-excel"
|
||||
method: "GET"
|
||||
purpose: "导出 Excel"
|
||||
request: "@Valid XxxPageReqVO"
|
||||
response: "void(直接写入 HttpServletResponse)"
|
||||
permission: "{模块}:{功能}:export"
|
||||
special_annotation: "@ApiAccessLog(operateType = EXPORT)"
|
||||
|
||||
- endpoint: "/get-import-template"
|
||||
method: "GET"
|
||||
purpose: "获取导入模板"
|
||||
request: "无参数"
|
||||
response: "void(直接写入 HttpServletResponse)"
|
||||
permission: "通常无需权限或 {模块}:{功能}:import"
|
||||
|
||||
- endpoint: "/import"
|
||||
method: "POST"
|
||||
purpose: "导入 Excel"
|
||||
request: "@RequestParam(\"file\") MultipartFile file, @RequestParam(\"updateSupport\") Boolean updateSupport"
|
||||
response: "CommonResult<XxxImportRespVO>"
|
||||
permission: "{模块}:{功能}:import"
|
||||
parameter_annotation: "@Parameters({...})"
|
||||
|
||||
# ============================================
|
||||
# 第三部分:注解规范
|
||||
# ============================================
|
||||
annotation_standards:
|
||||
# Controller 类注解
|
||||
class_annotations:
|
||||
- annotation: "@Tag"
|
||||
format: "@Tag(name = \"管理后台 - {模块名}\")"
|
||||
purpose: "OpenAPI 3.0 文档分组"
|
||||
import: "io.swagger.v3.oas.annotations.tags.Tag"
|
||||
- annotation: "@RestController"
|
||||
purpose: "REST 控制器"
|
||||
import: "org.springframework.web.bind.annotation.RestController"
|
||||
- annotation: "@RequestMapping"
|
||||
format: "@RequestMapping(\"/{模块}/{功能}\")"
|
||||
purpose: "路由前缀"
|
||||
import: "org.springframework.web.bind.annotation.RequestMapping"
|
||||
- annotation: "@Validated"
|
||||
purpose: "参数校验支持"
|
||||
import: "org.springframework.validation.annotation.Validated"
|
||||
|
||||
# 方法注解
|
||||
method_annotations:
|
||||
- annotation: "@Operation"
|
||||
format: "@Operation(summary = \"{操作描述}\")"
|
||||
purpose: "接口文档说明"
|
||||
import: "io.swagger.v3.oas.annotations.Operation"
|
||||
examples:
|
||||
- "@Operation(summary = \"创建用户\")"
|
||||
- "@Operation(summary = \"获得用户分页列表\")"
|
||||
- "@Operation(summary = \"导出用户 Excel\")"
|
||||
|
||||
- annotation: "@PermitAll"
|
||||
purpose: "公开接口(无需登录)"
|
||||
import: "cn.iocoder.yudao.framework.security.core.annotations.PermitAll"
|
||||
when_to_use: "登录、登出、注册、验证码等无需认证的接口"
|
||||
|
||||
- annotation: "@PreAuthorize"
|
||||
format: "@PreAuthorize(\"@ss.hasPermission('{模块}:{功能}:{操作}')\")"
|
||||
purpose: "权限控制"
|
||||
import: "org.springframework.security.access.prepost.PreAuthorize"
|
||||
examples:
|
||||
- "@PreAuthorize(\"@ss.hasPermission('system:user:create')\")"
|
||||
- "@PreAuthorize(\"@ss.hasPermission('pay:order:query')\")"
|
||||
|
||||
- annotation: "@Parameter"
|
||||
format: "@Parameter(name = \"{参数名}\", description = \"{描述}\", required = {是否必填}, example = \"{示例}\")"
|
||||
purpose: "单参数文档"
|
||||
import: "io.swagger.v3.oas.annotations.Parameter"
|
||||
when_to_use: "单个 @RequestParam 参数时使用"
|
||||
|
||||
- annotation: "@Parameters"
|
||||
format: "@Parameters({ @Parameter(name = \"xxx\", ...), @Parameter(name = \"yyy\", ...) })"
|
||||
purpose: "多参数文档"
|
||||
import: "io.swagger.v3.oas.annotations.Parameters"
|
||||
when_to_use: "多个 @RequestParam 参数时使用"
|
||||
|
||||
- annotation: "@ApiAccessLog"
|
||||
format: "@ApiAccessLog(operateType = EXPORT)"
|
||||
purpose: "操作日志记录"
|
||||
import: "cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog"
|
||||
when_to_use: "导出、导入等需要记录日志的操作"
|
||||
constants_import: "static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT"
|
||||
|
||||
# 参数注解
|
||||
parameter_annotations:
|
||||
- annotation: "@RequestBody"
|
||||
purpose: "接收 JSON 请求体"
|
||||
import: "org.springframework.web.bind.annotation.RequestBody"
|
||||
usage: "@RequestBody @Valid XxxReqVO reqVO"
|
||||
- annotation: "@RequestParam"
|
||||
purpose: "接收 URL 查询参数"
|
||||
import: "org.springframework.web.bind.annotation.RequestParam"
|
||||
usage: "@RequestParam(\"id\") Long id"
|
||||
- annotation: "@Valid"
|
||||
purpose: "参数校验"
|
||||
import: "javax.validation.Valid"
|
||||
usage: "@Valid @RequestBody XxxReqVO reqVO"
|
||||
|
||||
# ============================================
|
||||
# 第四部分:请求响应规范
|
||||
# ============================================
|
||||
request_response:
|
||||
# 统一响应结构
|
||||
common_result:
|
||||
class: "cn.iocoder.yudao.framework.common.pojo.CommonResult<T>"
|
||||
structure:
|
||||
code: "Integer(0 表示成功)"
|
||||
msg: "String(错误提示信息)"
|
||||
data: "T(返回数据)"
|
||||
usage:
|
||||
success: "return success(data)"
|
||||
success_boolean: "return success(true)"
|
||||
success_id: "return success(id)"
|
||||
import: "static cn.iocoder.yudao.framework.common.pojo.CommonResult.success"
|
||||
|
||||
# 分页响应结构
|
||||
page_result:
|
||||
class: "cn.iocoder.yudao.framework.common.pojo.PageResult<T>"
|
||||
structure:
|
||||
total: "Long(总记录数)"
|
||||
list: "List<T>(当前页数据列表)"
|
||||
usage: "CommonResult<PageResult<XxxRespVO>> getXxxPage(...)"
|
||||
empty: "new PageResult<>(pageResult.getTotal())"
|
||||
|
||||
# 分页请求参数
|
||||
page_param:
|
||||
class: "cn.iocoder.yudao.framework.common.pojo.PageParam"
|
||||
fields:
|
||||
pageNo: "Integer(页码,从 1 开始,默认 1,最小值 1)"
|
||||
pageSize: "Integer(每页条数,默认 10,范围 1-200)"
|
||||
constants:
|
||||
PAGE_NO: "1"
|
||||
PAGE_SIZE: "10"
|
||||
PAGE_SIZE_NONE: "-1(不分页,用于导出等场景)"
|
||||
usage: "分页查询 VO 继承 PageParam"
|
||||
|
||||
# 请求 VO 类型
|
||||
request_vo_types:
|
||||
- type: "SaveReqVO"
|
||||
purpose: "新增/修改请求"
|
||||
naming: "XxxSaveReqVO"
|
||||
features:
|
||||
- "修改时包含 id 字段"
|
||||
- "包含 @NotBlank/@NotNull 校验注解"
|
||||
- "使用 @Schema 注解描述字段"
|
||||
- type: "PageReqVO"
|
||||
purpose: "分页查询请求"
|
||||
naming: "XxxPageReqVO"
|
||||
features:
|
||||
- "继承 PageParam"
|
||||
- "包含查询条件字段"
|
||||
- "时间字段使用 @DateTimeFormat"
|
||||
- type: "ImportReqVO"
|
||||
purpose: "导入请求"
|
||||
naming: "XxxImportReqVO 或使用 @RequestParam 接收 file"
|
||||
features:
|
||||
- "包含 file 和 updateSupport 参数"
|
||||
- type: "RespVO"
|
||||
purpose: "响应对象"
|
||||
naming: "XxxRespVO"
|
||||
features:
|
||||
- "包含 @Schema 注解描述字段"
|
||||
- "包含 createTime 等只读字段"
|
||||
|
||||
# VO 注解规范
|
||||
vo_annotations:
|
||||
- annotation: "@Schema"
|
||||
purpose: "OpenAPI 3.0 字段描述"
|
||||
format: "@Schema(description = \"{字段描述}\", requiredMode = Schema.RequiredMode.REQUIRED, example = \"{示例}\")"
|
||||
import: "io.swagger.v3.oas.annotations.media.Schema"
|
||||
- annotation: "@ExcelProperty"
|
||||
purpose: "Excel 导出字段"
|
||||
format: "@ExcelProperty(\"{列名}\")"
|
||||
import: "com.alibaba.excel.annotation.ExcelProperty"
|
||||
- annotation: "@DictFormat"
|
||||
purpose: "字典转换"
|
||||
format: "@DictFormat(\"{字典类型}\")"
|
||||
|
||||
# ============================================
|
||||
# 第五部分:权限标识规范
|
||||
# ============================================
|
||||
permission_naming:
|
||||
format: "{模块}:{功能}:{操作}"
|
||||
rules:
|
||||
- "模块名与功能名使用小写字母"
|
||||
- "操作名使用标准动词"
|
||||
standard_operations:
|
||||
- operation: "query"
|
||||
description: "查询权限(包括 get、page、list)"
|
||||
- operation: "create"
|
||||
description: "新增权限"
|
||||
- operation: "update"
|
||||
description: "修改权限"
|
||||
- operation: "delete"
|
||||
description: "删除权限"
|
||||
- operation: "export"
|
||||
description: "导出权限"
|
||||
- operation: "import"
|
||||
description: "导入权限"
|
||||
examples:
|
||||
- "system:user:query"
|
||||
- "system:user:create"
|
||||
- "system:user:update"
|
||||
- "system:user:delete"
|
||||
- "system:user:export"
|
||||
- "system:user:import"
|
||||
- "pay:order:query"
|
||||
- "pay:order:create"
|
||||
|
||||
# 权限 SQL 配置
|
||||
permission_sql:
|
||||
menu_format: |
|
||||
INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, status)
|
||||
VALUES ('{功能名称}管理', '', 2, 0, {父菜单ID}, '{功能}', 'ep:document', '{模块}/{功能}/index', 0);
|
||||
|
||||
button_format: |
|
||||
INSERT INTO system_menu (name, permission, type, sort, parent_id, status) VALUES
|
||||
('{功能名称}查询', '{模块}:{功能}:query', 3, 1, @menuId, 0),
|
||||
('{功能名称}新增', '{模块}:{功能}:create', 3, 2, @menuId, 0),
|
||||
('{功能名称}修改', '{模块}:{功能}:update', 3, 3, @menuId, 0),
|
||||
('{功能名称}删除', '{模块}:{功能}:delete', 3, 4, @menuId, 0),
|
||||
('{功能名称}导出', '{模块}:{功能}:export', 3, 5, @menuId, 0);
|
||||
|
||||
# ============================================
|
||||
# 第六部分:错误码体系
|
||||
# ============================================
|
||||
error_codes:
|
||||
format: "1_模块编号_功能编号_错误序号"
|
||||
success_code: 0
|
||||
|
||||
# 全局错误码
|
||||
global_errors:
|
||||
- code: 0
|
||||
message: "成功"
|
||||
- code: 400
|
||||
message: "请求参数不正确"
|
||||
- code: 401
|
||||
message: "账号未登录"
|
||||
- code: 403
|
||||
message: "没有该操作权限"
|
||||
- code: 404
|
||||
message: "请求未找到"
|
||||
- code: 405
|
||||
message: "请求方法不正确"
|
||||
- code: 423
|
||||
message: "请求失败,请稍后重试"
|
||||
- code: 429
|
||||
message: "请求过于频繁"
|
||||
- code: 500
|
||||
message: "系统异常"
|
||||
- code: 501
|
||||
message: "功能未实现/未开启"
|
||||
- code: 502
|
||||
message: "错误的配置项"
|
||||
- code: 900
|
||||
message: "重复请求"
|
||||
- code: 901
|
||||
message: "演示模式,禁止写操作"
|
||||
- code: 999
|
||||
message: "未知错误"
|
||||
|
||||
# 模块编号速查
|
||||
module_codes:
|
||||
- module: "infra"
|
||||
code_prefix: "1_001"
|
||||
description: "基础设施模块"
|
||||
- module: "system"
|
||||
code_prefix: "1_002"
|
||||
description: "系统管理模块"
|
||||
- module: "member"
|
||||
code_prefix: "1_003"
|
||||
description: "会员模块"
|
||||
- module: "pay"
|
||||
code_prefix: "1_007"
|
||||
description: "支付模块"
|
||||
- module: "product"
|
||||
code_prefix: "1_008"
|
||||
description: "商品模块"
|
||||
- module: "trade"
|
||||
code_prefix: "1_011"
|
||||
description: "交易模块"
|
||||
- module: "promotion"
|
||||
code_prefix: "1_013"
|
||||
description: "促销模块"
|
||||
|
||||
# 错误码定义模板
|
||||
error_code_template: |
|
||||
// ========== {实体名称} 相关错误码 1_XXX_XXX_XXX ==========
|
||||
ErrorCode XXX_NOT_EXISTS = new ErrorCode(1_002_001_000, "{实体名称}不存在");
|
||||
ErrorCode XXX_CODE_DUPLICATE = new ErrorCode(1_002_001_001, "已存在该编码的{实体名称}");
|
||||
ErrorCode XXX_NAME_DUPLICATE = new ErrorCode(1_002_001_002, "已存在该名称的{实体名称}");
|
||||
ErrorCode XXX_CAN_NOT_DELETE = new ErrorCode(1_002_001_003, "{实体名称}不能删除,原因:{}");
|
||||
ErrorCode XXX_STATUS_ERROR = new ErrorCode(1_002_001_004, "{实体名称}状态不正确");
|
||||
|
||||
# ============================================
|
||||
# 第七部分:Controller 模板
|
||||
# ============================================
|
||||
controller_template:
|
||||
# 基础 CRUD 模板
|
||||
basic_crud: |
|
||||
package cn.iocoder.yudao.module.{模块}.controller.admin.{功能};
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
||||
import cn.iocoder.yudao.module.{模块}.controller.admin.{功能}.vo.*;
|
||||
import cn.iocoder.yudao.module.{模块}.dal.dataobject.{功能}.XxxDO;
|
||||
import cn.iocoder.yudao.module.{模块}.service.{功能}.XxxService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - {实体名称}")
|
||||
@RestController
|
||||
@RequestMapping("/{模块}/{功能}")
|
||||
@Validated
|
||||
public class XxxController {
|
||||
|
||||
@Resource
|
||||
private XxxService xxxService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建{实体名称}")
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:create')")
|
||||
public CommonResult<Long> createXxx(@Valid @RequestBody XxxSaveReqVO createReqVO) {
|
||||
return success(xxxService.createXxx(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新{实体名称}")
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:update')")
|
||||
public CommonResult<Boolean> updateXxx(@Valid @RequestBody XxxSaveReqVO updateReqVO) {
|
||||
xxxService.updateXxx(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除{实体名称}")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:delete')")
|
||||
public CommonResult<Boolean> deleteXxx(@RequestParam("id") Long id) {
|
||||
xxxService.deleteXxx(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得{实体名称}")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:query')")
|
||||
public CommonResult<XxxRespVO> getXxx(@RequestParam("id") Long id) {
|
||||
XxxDO xxx = xxxService.getXxx(id);
|
||||
return success(BeanUtils.toBean(xxx, XxxRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得{实体名称}分页")
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:query')")
|
||||
public CommonResult<PageResult<XxxRespVO>> getXxxPage(@Valid XxxPageReqVO pageReqVO) {
|
||||
PageResult<XxxDO> pageResult = xxxService.getXxxPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, XxxRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出{实体名称} Excel")
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportXxxExcel(@Valid XxxPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<XxxDO> list = xxxService.getXxxPage(pageReqVO).getList();
|
||||
List<XxxExcelVO> excelList = BeanUtils.toBean(list, XxxExcelVO.class);
|
||||
ExcelUtils.write(response, "{实体名称}.xls", "数据", XxxExcelVO.class, excelList);
|
||||
}
|
||||
}
|
||||
|
||||
# 公开接口模板
|
||||
public_endpoint: |
|
||||
@PostMapping("/login")
|
||||
@PermitAll
|
||||
@Operation(summary = "使用账号密码登录")
|
||||
public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
|
||||
return success(authService.login(reqVO));
|
||||
}
|
||||
|
||||
# 批量操作模板
|
||||
batch_operations: |
|
||||
@DeleteMapping("/delete-list")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@Operation(summary = "批量删除{实体名称}")
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:delete')")
|
||||
public CommonResult<Boolean> deleteXxxList(@RequestParam("ids") List<Long> ids) {
|
||||
xxxService.deleteXxxList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
# 导入导出模板
|
||||
import_export: |
|
||||
@GetMapping("/get-import-template")
|
||||
@Operation(summary = "获得导入{实体名称}模板")
|
||||
public void importTemplate(HttpServletResponse response) throws IOException {
|
||||
// 手动创建导出 demo
|
||||
List<XxxImportExcelVO> list = Arrays.asList(
|
||||
XxxImportExcelVO.builder().name("示例1").code("TEST1").build(),
|
||||
XxxImportExcelVO.builder().name("示例2").code("TEST2").build()
|
||||
);
|
||||
ExcelUtils.write(response, "{实体名称}导入模板.xls", "数据", XxxImportExcelVO.class, list);
|
||||
}
|
||||
|
||||
@PostMapping("/import")
|
||||
@Operation(summary = "导入{实体名称}")
|
||||
@Parameters({
|
||||
@Parameter(name = "file", description = "Excel 文件", required = true),
|
||||
@Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true")
|
||||
})
|
||||
@PreAuthorize("@ss.hasPermission('{模块}:{功能}:import')")
|
||||
public CommonResult<XxxImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
|
||||
@RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
|
||||
List<XxxImportExcelVO> list = ExcelUtils.read(file, XxxImportExcelVO.class);
|
||||
return success(xxxService.importXxxList(list, updateSupport));
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 第八部分:设计流程
|
||||
# ============================================
|
||||
design_workflow:
|
||||
steps:
|
||||
- step: 1
|
||||
name: "实体分析"
|
||||
action: "分析数据库设计或 PRD,确定实体字段和业务含义"
|
||||
input: ["db_design_file", "prd_file", "模块 skill 文档"]
|
||||
output: "实体字段清单"
|
||||
checklist:
|
||||
- "确认实体所属模块"
|
||||
- "阅读对应模块的 skill 文档"
|
||||
- "确定需要哪些标准端点"
|
||||
|
||||
- step: 2
|
||||
name: "端点设计"
|
||||
action: "根据业务需求确定需要的 API 端点"
|
||||
considerations:
|
||||
- "是否需要完整 CRUD(create/update/delete/get/page)"
|
||||
- "是否需要批量操作(delete-list)"
|
||||
- "是否需要导入导出(export-excel/import)"
|
||||
- "是否需要状态切换(update-status)"
|
||||
- "是否有公开接口需求(使用 @PermitAll)"
|
||||
|
||||
- step: 3
|
||||
name: "请求响应设计"
|
||||
action: "设计各端点的请求参数和响应格式"
|
||||
templates:
|
||||
- "使用 SaveReqVO 处理新增/修改"
|
||||
- "使用 PageReqVO 处理分页查询(继承 PageParam)"
|
||||
- "使用 RespVO 处理响应"
|
||||
- "使用 CommonResult 包装响应"
|
||||
|
||||
- step: 4
|
||||
name: "权限设计"
|
||||
action: "确定各端点的权限标识"
|
||||
rules:
|
||||
- "公开接口使用 @PermitAll"
|
||||
- "权限接口使用 @PreAuthorize"
|
||||
- "权限标识格式:模块:功能:操作"
|
||||
- "查询类接口统一使用 query 权限"
|
||||
|
||||
- step: 5
|
||||
name: "错误码设计"
|
||||
action: "定义业务错误码"
|
||||
rules:
|
||||
- "格式:1_模块编号_功能编号_错误序号"
|
||||
- "常见错误:不存在、重复、状态错误、操作限制"
|
||||
|
||||
- step: 6
|
||||
name: "文档生成"
|
||||
action: "生成 Controller 代码和 API 文档"
|
||||
output: ["Controller.java", "VO classes", "错误码定义", "权限 SQL"]
|
||||
|
||||
# ============================================
|
||||
# 第九部分:检查清单
|
||||
# ============================================
|
||||
checklist:
|
||||
before_design:
|
||||
- "确认实体所属模块"
|
||||
- "阅读对应模块的 skill 文档"
|
||||
- "了解现有类似接口的设计模式"
|
||||
- "准备表结构设计"
|
||||
|
||||
during_design:
|
||||
- "URL 命名符合规范(/{模块}/{功能}/{操作})"
|
||||
- "HTTP 方法与操作类型匹配"
|
||||
- "请求 VO 类型选择正确"
|
||||
- "响应格式使用 CommonResult"
|
||||
- "权限标识命名正确"
|
||||
- "注解使用完整"
|
||||
|
||||
after_design:
|
||||
- "所有接口添加 @Operation 注解"
|
||||
- "参数添加 @Parameter 或 @Parameters 注解"
|
||||
- "权限接口添加 @PreAuthorize 注解"
|
||||
- "公开接口添加 @PermitAll 注解"
|
||||
- "导出接口添加 @ApiAccessLog 注解"
|
||||
- "错误码定义完整"
|
||||
|
||||
# ============================================
|
||||
# 第十部分:与 entity-implementation.md 协同
|
||||
# ============================================
|
||||
collaboration:
|
||||
relationship:
|
||||
- doc: "api-designer.yaml"
|
||||
role: "API 设计阶段规范指导"
|
||||
focus: "设计决策、端点规划、权限标识"
|
||||
- doc: "entity-implementation.md"
|
||||
role: "完整实现流程指导"
|
||||
focus: "实现细节、模板代码、各层规范"
|
||||
usage_flow:
|
||||
- "1. 使用 api-designer.yaml 进行 API 设计决策"
|
||||
- "2. 参考 entity-implementation.md 进行完整实现"
|
||||
- "3. 两者 Controller 模板保持一致"
|
||||
|
||||
# ============================================
|
||||
# 快速参考
|
||||
# ============================================
|
||||
quick_reference:
|
||||
http_methods:
|
||||
POST: "创建类操作(create, import, send)"
|
||||
PUT: "更新类操作(update, update-status)"
|
||||
DELETE: "删除类操作(delete, delete-list)"
|
||||
GET: "查询类操作(get, page, list, export-excel)"
|
||||
|
||||
standard_endpoints:
|
||||
/create: "创建 → CommonResult<Long>"
|
||||
/update: "更新 → CommonResult<Boolean>"
|
||||
/delete: "删除 → CommonResult<Boolean>"
|
||||
/delete-list: "批量删除 → CommonResult<Boolean>"
|
||||
/get: "详情 → CommonResult<XxxRespVO>"
|
||||
/page: "分页 → CommonResult<PageResult<XxxRespVO>>"
|
||||
/list: "列表 → CommonResult<List<XxxRespVO>>"
|
||||
/export-excel: "导出 → void"
|
||||
/import: "导入 → CommonResult<XxxImportRespVO>"
|
||||
|
||||
permission_operations:
|
||||
query: "查询(get/page/list)"
|
||||
create: "新增"
|
||||
update: "修改"
|
||||
delete: "删除"
|
||||
export: "导出"
|
||||
import: "导入"
|
||||
|
||||
response_types:
|
||||
CommonResult<Long>: "创建类响应"
|
||||
CommonResult<Boolean>: "操作类响应"
|
||||
CommonResult<XxxRespVO>: "详情类响应"
|
||||
CommonResult<PageResult<XxxRespVO>>: "分页类响应"
|
||||
CommonResult<List<XxxRespVO>>: "列表类响应"
|
||||
|
||||
vo_types:
|
||||
XxxSaveReqVO: "新增/修改请求"
|
||||
XxxPageReqVO: "分页查询请求(继承 PageParam)"
|
||||
XxxRespVO: "响应对象"
|
||||
XxxExcelVO: "Excel 导出对象"
|
||||
XxxImportExcelVO: "Excel 导入对象"
|
||||
XxxImportRespVO: "导入结果响应"
|
||||
|
||||
imports:
|
||||
controller_common:
|
||||
- "io.swagger.v3.oas.annotations.tags.Tag"
|
||||
- "io.swagger.v3.oas.annotations.Operation"
|
||||
- "io.swagger.v3.oas.annotations.Parameter"
|
||||
- "io.swagger.v3.oas.annotations.Parameters"
|
||||
- "org.springframework.security.access.prepost.PreAuthorize"
|
||||
- "cn.iocoder.yudao.framework.common.pojo.CommonResult"
|
||||
- "cn.iocoder.yudao.framework.common.pojo.PageResult"
|
||||
- "static cn.iocoder.yudao.framework.common.pojo.CommonResult.success"
|
||||
@@ -1,518 +0,0 @@
|
||||
# CRUD 代码生成 Skill
|
||||
# 整合性入口指南 - 引用已有的模板文档
|
||||
|
||||
skill:
|
||||
id: "crud-generator"
|
||||
name: "CRUD 代码生成"
|
||||
version: "1.0.0"
|
||||
category: "design"
|
||||
description: "根据数据库表结构自动生成符合yudao项目规范的完整CRUD功能代码"
|
||||
created_at: "2026-03-31"
|
||||
updated_at: "2026-03-31"
|
||||
|
||||
# ============================================
|
||||
# 第一部分:触发条件
|
||||
# ============================================
|
||||
trigger:
|
||||
commands:
|
||||
- "/crud-gen"
|
||||
- "/generate-crud"
|
||||
keywords:
|
||||
- "生成CRUD"
|
||||
- "创建实体代码"
|
||||
- "生成增删改查"
|
||||
- "实体类实现"
|
||||
events:
|
||||
- name: "table_created"
|
||||
condition: "数据库表创建完成"
|
||||
|
||||
# ============================================
|
||||
# 第二部分:输入参数
|
||||
# ============================================
|
||||
input:
|
||||
parameters:
|
||||
- name: "module"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "模块编码(system/infra/pay/mes等)"
|
||||
examples: ["system", "infra", "pay", "mes"]
|
||||
|
||||
- name: "feature"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "功能编码(user/order/process等)"
|
||||
examples: ["user", "order", "process"]
|
||||
|
||||
- name: "entity_name"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "实体名称(中文)"
|
||||
examples: ["用户", "订单", "工序"]
|
||||
|
||||
- name: "entity_class"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "实体类名(英文,首字母大写)"
|
||||
examples: ["User", "Order", "Process"]
|
||||
|
||||
- name: "table_name"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "数据库表名"
|
||||
examples: ["system_user", "pay_order", "mes_process"]
|
||||
|
||||
- name: "is_tenant"
|
||||
type: "boolean"
|
||||
required: false
|
||||
default: true
|
||||
description: "是否多租户实体(true使用TenantBaseDO,false使用BaseDO)"
|
||||
|
||||
- name: "fields"
|
||||
type: "array"
|
||||
required: true
|
||||
description: "业务字段列表"
|
||||
item_schema:
|
||||
name: "string"
|
||||
type: "string"
|
||||
required: "boolean"
|
||||
description: "string"
|
||||
|
||||
- name: "enable_export"
|
||||
type: "boolean"
|
||||
required: false
|
||||
default: false
|
||||
description: "是否启用Excel导出功能"
|
||||
|
||||
- name: "enable_import"
|
||||
type: "boolean"
|
||||
required: false
|
||||
default: false
|
||||
description: "是否启用Excel导入功能"
|
||||
|
||||
- name: "enable_batch"
|
||||
type: "boolean"
|
||||
required: false
|
||||
default: true
|
||||
description: "是否启用批量删除功能"
|
||||
|
||||
# ============================================
|
||||
# 第三部分:执行流程(5阶段)
|
||||
# ============================================
|
||||
workflow:
|
||||
description: "CRUD代码生成按以下5个阶段顺序执行"
|
||||
|
||||
phases:
|
||||
# 阶段1:结构分析
|
||||
- name: "结构分析"
|
||||
description: "分析表结构,确定实体属性和生成范围"
|
||||
duration: "5min"
|
||||
steps:
|
||||
- step: "解析表名和字段定义"
|
||||
action: "读取表结构信息,提取字段列表"
|
||||
- step: "确定DO基类"
|
||||
action: "根据is_tenant参数选择BaseDO或TenantBaseDO"
|
||||
rules:
|
||||
- "is_tenant=true → TenantBaseDO(含tenantId)"
|
||||
- "is_tenant=false → BaseDO"
|
||||
- step: "生成文件清单"
|
||||
action: "根据参数确定需要创建的文件列表"
|
||||
decision_table:
|
||||
- condition: "默认"
|
||||
files: ["DO", "Mapper", "Service", "ServiceImpl", "Controller", "SaveReqVO", "PageReqVO", "RespVO"]
|
||||
- condition: "enable_export=true"
|
||||
add_files: ["ExcelVO", "export-excel接口"]
|
||||
- condition: "enable_import=true"
|
||||
add_files: ["ImportExcelVO", "ImportRespVO", "import接口"]
|
||||
- condition: "enable_batch=true"
|
||||
add_files: ["delete-list接口"]
|
||||
- step: "分配错误码编号"
|
||||
action: "根据模块编号分配错误码段"
|
||||
reference: "api-designer.yaml 第六部分 module_codes"
|
||||
|
||||
# 阶段2:VO生成
|
||||
- name: "VO生成"
|
||||
description: "生成请求和响应VO类"
|
||||
duration: "5min"
|
||||
reference: "entity-implementation.md 第3.6节"
|
||||
steps:
|
||||
- step: "生成 SaveReqVO"
|
||||
description: "新增/修改共用请求对象"
|
||||
template_path: "entity-implementation.md 第621-657行"
|
||||
rules:
|
||||
- "包含id字段(修改时必填)"
|
||||
- "包含所有可创建/更新字段"
|
||||
- "排除:creator, createTime, updater, updateTime, deleted, tenantId"
|
||||
- "添加校验注解(@NotBlank/@NotNull/@Size)"
|
||||
- step: "生成 PageReqVO"
|
||||
description: "分页查询请求对象"
|
||||
template_path: "entity-implementation.md 第661-695行"
|
||||
rules:
|
||||
- "继承 PageParam"
|
||||
- "包含常用查询条件字段"
|
||||
- "时间字段使用 @DateTimeFormat"
|
||||
- step: "生成 RespVO"
|
||||
description: "响应对象"
|
||||
template_path: "entity-implementation.md 第699-733行"
|
||||
rules:
|
||||
- "包含所有展示字段"
|
||||
- "包含 createTime 等只读字段"
|
||||
- step: "生成 ExcelVO(可选)"
|
||||
condition: "enable_export=true 或 enable_import=true"
|
||||
template_path: "entity-implementation.md 第737-773行"
|
||||
|
||||
# 阶段3:DAL生成
|
||||
- name: "DAL生成"
|
||||
description: "生成数据访问层代码"
|
||||
duration: "3min"
|
||||
reference: "entity-implementation.md 第3.2-3.3节"
|
||||
steps:
|
||||
- step: "生成 DO实体类"
|
||||
template_path: "entity-implementation.md 第128-180行"
|
||||
rules:
|
||||
- "@TableName 指定表名"
|
||||
- "@TableId 标注主键"
|
||||
- "继承正确基类"
|
||||
- "枚举字段添加引用注释"
|
||||
- step: "生成 Mapper接口"
|
||||
template_path: "entity-implementation.md 第234-281行"
|
||||
rules:
|
||||
- "继承 BaseMapperX<EntityDO>"
|
||||
- "定义 selectPage 分页查询方法"
|
||||
- "使用 LambdaQueryWrapperX 构建条件"
|
||||
- "使用 xxxIfPresent() 方法链"
|
||||
|
||||
# 阶段4:Service生成
|
||||
- name: "Service生成"
|
||||
description: "生成业务逻辑层代码"
|
||||
duration: "5min"
|
||||
reference: "entity-implementation.md 第3.4节"
|
||||
steps:
|
||||
- step: "生成 Service接口"
|
||||
template_path: "entity-implementation.md 第313-377行"
|
||||
methods:
|
||||
- "createXxx(SaveReqVO) → Long"
|
||||
- "updateXxx(SaveReqVO) → void"
|
||||
- "deleteXxx(Long id) → void"
|
||||
- "getXxx(Long id) → XxxDO"
|
||||
- "getXxxPage(PageReqVO) → PageResult<XxxDO>"
|
||||
- step: "生成 ServiceImpl实现"
|
||||
template_path: "entity-implementation.md 第382-485行"
|
||||
rules:
|
||||
- "@Service + @Validated 注解"
|
||||
- "新增方法校验唯一性"
|
||||
- "更新/删除方法校验存在性"
|
||||
- "使用 exception(ERROR_CODE) 抛异常"
|
||||
- "使用 BeanUtils.toBean() 转换对象"
|
||||
|
||||
# 阶段5:Controller生成
|
||||
- name: "Controller生成"
|
||||
description: "生成HTTP接口层代码"
|
||||
duration: "7min"
|
||||
reference: "api-designer.yaml 第七部分"
|
||||
steps:
|
||||
- step: "生成 Controller类"
|
||||
template_path: "api-designer.yaml 第420-511行"
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - {实体名称}')"
|
||||
- "@RestController"
|
||||
- "@RequestMapping('/{module}/{feature}')"
|
||||
- "@Validated"
|
||||
- step: "生成标准端点"
|
||||
endpoints:
|
||||
- endpoint: "/create"
|
||||
method: "POST"
|
||||
template_path: "api-designer.yaml 第67-72行"
|
||||
- endpoint: "/update"
|
||||
method: "PUT"
|
||||
template_path: "api-designer.yaml 第74-79行"
|
||||
- endpoint: "/delete"
|
||||
method: "DELETE"
|
||||
template_path: "api-designer.yaml 第81-87行"
|
||||
- endpoint: "/get"
|
||||
method: "GET"
|
||||
template_path: "api-designer.yaml 第97-103行"
|
||||
- endpoint: "/page"
|
||||
method: "GET"
|
||||
template_path: "api-designer.yaml 第105-110行"
|
||||
- step: "生成扩展端点(可选)"
|
||||
conditionals:
|
||||
- condition: "enable_batch=true"
|
||||
endpoint: "/delete-list"
|
||||
template_path: "api-designer.yaml 第523-531行"
|
||||
- condition: "enable_export=true"
|
||||
endpoint: "/export-excel"
|
||||
template_path: "api-designer.yaml 第119-126行"
|
||||
- condition: "enable_import=true"
|
||||
endpoints: ["/get-import-template", "/import"]
|
||||
template_path: "api-designer.yaml 第534-557行"
|
||||
- step: "生成权限SQL"
|
||||
template_path: "api-designer.yaml 第333-344行"
|
||||
|
||||
# ============================================
|
||||
# 第四部分:输出产物
|
||||
# ============================================
|
||||
output:
|
||||
base_path: "yudao-module-{module}-biz/src/main/java/cn/iocoder/yudao/module/{module}"
|
||||
|
||||
artifacts:
|
||||
# DAL层
|
||||
- name: "DO实体类"
|
||||
path: "dal/dataobject/{feature}/{Entity}DO.java"
|
||||
description: "数据库实体映射类"
|
||||
- name: "Mapper接口"
|
||||
path: "dal/mysql/{feature}/{Entity}Mapper.java"
|
||||
description: "数据访问接口"
|
||||
|
||||
# Service层
|
||||
- name: "Service接口"
|
||||
path: "service/{feature}/{Entity}Service.java"
|
||||
description: "业务服务接口"
|
||||
- name: "Service实现"
|
||||
path: "service/{feature}/{Entity}ServiceImpl.java"
|
||||
description: "业务服务实现类"
|
||||
|
||||
# Controller层
|
||||
- name: "Controller"
|
||||
path: "controller/admin/{feature}/{Entity}Controller.java"
|
||||
description: "HTTP API控制器"
|
||||
|
||||
# VO类
|
||||
- name: "SaveReqVO"
|
||||
path: "controller/admin/{feature}/vo/{Entity}SaveReqVO.java"
|
||||
description: "新增/修改请求对象"
|
||||
- name: "PageReqVO"
|
||||
path: "controller/admin/{feature}/vo/{Entity}PageReqVO.java"
|
||||
description: "分页查询请求对象"
|
||||
- name: "RespVO"
|
||||
path: "controller/admin/{feature}/vo/{Entity}RespVO.java"
|
||||
description: "响应对象"
|
||||
|
||||
# 可选产物
|
||||
- name: "ExcelVO"
|
||||
path: "controller/admin/{feature}/vo/{Entity}ExcelVO.java"
|
||||
condition: "enable_export=true"
|
||||
description: "Excel导出对象"
|
||||
- name: "ImportExcelVO"
|
||||
path: "controller/admin/{feature}/vo/{Entity}ImportExcelVO.java"
|
||||
condition: "enable_import=true"
|
||||
description: "Excel导入对象"
|
||||
- name: "ImportRespVO"
|
||||
path: "controller/admin/{feature}/vo/{Entity}ImportRespVO.java"
|
||||
condition: "enable_import=true"
|
||||
description: "导入结果响应"
|
||||
|
||||
# 配置文件
|
||||
- name: "错误码定义"
|
||||
path: "enums/ErrorCodeConstants.java"
|
||||
action: "追加"
|
||||
description: "业务错误码定义"
|
||||
- name: "权限配置SQL"
|
||||
path: "sql/menu/{module}_{feature}_menu.sql"
|
||||
description: "菜单和按钮权限SQL"
|
||||
|
||||
# ============================================
|
||||
# 第五部分:命名规范速查
|
||||
# ============================================
|
||||
naming_standards:
|
||||
# 文件命名
|
||||
files:
|
||||
do: "{Entity}DO"
|
||||
mapper: "{Entity}Mapper"
|
||||
service: "{Entity}Service"
|
||||
service_impl: "{Entity}ServiceImpl"
|
||||
controller: "{Entity}Controller"
|
||||
vo_save: "{Entity}SaveReqVO"
|
||||
vo_page: "{Entity}PageReqVO"
|
||||
vo_resp: "{Entity}RespVO"
|
||||
vo_excel: "{Entity}ExcelVO"
|
||||
vo_import: "{Entity}ImportExcelVO"
|
||||
|
||||
# 权限标识
|
||||
permission:
|
||||
format: "{module}:{feature}:{operation}"
|
||||
operations:
|
||||
- "query"
|
||||
- "create"
|
||||
- "update"
|
||||
- "delete"
|
||||
- "export"
|
||||
- "import"
|
||||
example: "system:user:create"
|
||||
|
||||
# 错误码
|
||||
error_code:
|
||||
format: "1_{module_code}_{feature_code}_{seq}"
|
||||
module_codes:
|
||||
infra: "001"
|
||||
system: "002"
|
||||
member: "003"
|
||||
pay: "007"
|
||||
product: "008"
|
||||
trade: "011"
|
||||
promotion: "013"
|
||||
example: "1_002_001_000"
|
||||
|
||||
# 数据库表名
|
||||
table:
|
||||
format: "{module}_{feature}"
|
||||
example: "system_user"
|
||||
|
||||
# ============================================
|
||||
# 第六部分:关联文档引用
|
||||
# ============================================
|
||||
references:
|
||||
primary:
|
||||
- path: "skills/design/api-designer.yaml"
|
||||
description: "API设计规范、Controller模板、权限标识、错误码体系"
|
||||
sections:
|
||||
- "第二部分:接口规范(标准端点定义)"
|
||||
- "第五部分:权限标识规范"
|
||||
- "第六部分:错误码体系"
|
||||
- "第七部分:Controller模板"
|
||||
|
||||
- path: "skills/usage/entity-implementation.md"
|
||||
description: "实体类实现完整流程、各层代码模板"
|
||||
sections:
|
||||
- "3.2节:DO实体类模板"
|
||||
- "3.3节:Mapper层模板"
|
||||
- "3.4节:Service层模板"
|
||||
- "3.5节:Controller层模板"
|
||||
- "3.6节:VO类模板"
|
||||
- "3.7节:错误码定义"
|
||||
- "3.8节:权限配置SQL"
|
||||
|
||||
- path: "skills/design/db-designer.yaml"
|
||||
relationship: "前置依赖"
|
||||
description: "数据库表结构设计规范"
|
||||
|
||||
secondary:
|
||||
- path: "skills/modules/{module}/skill-{module}.yaml"
|
||||
relationship: "模块参考"
|
||||
description: "具体模块的技术规范和扩展指南"
|
||||
example: "skills/modules/system/skill-system.yaml"
|
||||
|
||||
# ============================================
|
||||
# 第七部分:质量检查清单
|
||||
# ============================================
|
||||
checklist:
|
||||
before_generate:
|
||||
- item: "确认模块编码正确"
|
||||
check: "module参数在module_codes中有对应编号"
|
||||
- item: "确认表名符合命名规范"
|
||||
check: "表名格式为 {module}_{feature}"
|
||||
- item: "阅读对应模块的skill文档"
|
||||
reference: "skills/modules/{module}/skill-{module}.yaml"
|
||||
- item: "确认字段类型映射正确"
|
||||
check: "数据库类型与Java类型对应"
|
||||
|
||||
during_generate:
|
||||
- item: "DO类继承正确的基类"
|
||||
check: "TenantBaseDO(多租户)或 BaseDO(单租户)"
|
||||
- item: "Mapper使用BaseMapperX和LambdaQueryWrapperX"
|
||||
check: "继承BaseMapperX,条件构建使用xxxIfPresent()"
|
||||
- item: "Service包含业务校验方法"
|
||||
check: "新增校验唯一性,更新/删除校验存在性"
|
||||
- item: "Controller注解完整"
|
||||
check: "@Tag/@RestController/@RequestMapping/@Validated"
|
||||
- item: "接口方法注解完整"
|
||||
check: "@Operation/@PreAuthorize/@Parameter"
|
||||
|
||||
after_generate:
|
||||
- item: "错误码编号符合规范"
|
||||
format: "1_{module_code}_{feature_code}_{seq}"
|
||||
- item: "权限标识格式正确"
|
||||
format: "{module}:{feature}:{operation}"
|
||||
- item: "所有文件包路径正确"
|
||||
base: "cn.iocoder.yudao.module.{module}"
|
||||
- item: "VO类添加@Schema注解"
|
||||
check: "所有字段有description"
|
||||
- item: "导入语句无缺失"
|
||||
check: "所有注解有对应import"
|
||||
|
||||
# ============================================
|
||||
# 第八部分:与原skill的差异调整
|
||||
# ============================================
|
||||
migration_notes:
|
||||
# 命名规范调整
|
||||
naming_adjustments:
|
||||
- original: "CreateDTO"
|
||||
target: "XxxSaveReqVO"
|
||||
reason: "yudao系统新增/修改共用一个VO"
|
||||
- original: "UpdateDTO"
|
||||
target: "XxxSaveReqVO"
|
||||
reason: "与CreateDTO合并,通过id字段区分"
|
||||
- original: "QueryDTO"
|
||||
target: "XxxPageReqVO"
|
||||
reason: "yudao系统分页查询VO继承PageParam"
|
||||
- original: "VO"
|
||||
target: "XxxRespVO"
|
||||
reason: "响应对象命名规范"
|
||||
- original: "ListVO"
|
||||
target: "XxxRespVO"
|
||||
reason: "分页列表使用同一响应VO"
|
||||
|
||||
# 技术栈调整
|
||||
tech_adjustments:
|
||||
- original: "BaseMapper<Entity>"
|
||||
target: "BaseMapperX<XxxDO>"
|
||||
reason: "yudao扩展的Mapper基类"
|
||||
- original: "LambdaQueryWrapper"
|
||||
target: "LambdaQueryWrapperX"
|
||||
reason: "支持xxxIfPresent()简化条件构建"
|
||||
- original: "Result<T>"
|
||||
target: "CommonResult<T>"
|
||||
reason: "yudao统一响应包装类"
|
||||
- original: "Page<T>"
|
||||
target: "PageResult<T>"
|
||||
reason: "yudao分页结果类"
|
||||
- original: "@Api (Swagger 2)"
|
||||
target: "@Tag (OpenAPI 3)"
|
||||
reason: "Swagger版本升级"
|
||||
- original: "@ApiOperation"
|
||||
target: "@Operation"
|
||||
reason: "OpenAPI 3.0注解"
|
||||
|
||||
# 目录结构调整
|
||||
path_adjustments:
|
||||
- original: "controller/{Entity}Controller.java"
|
||||
target: "controller/admin/{feature}/{Entity}Controller.java"
|
||||
- original: "dto/{Entity}DTO.java"
|
||||
target: "controller/admin/{feature}/vo/{Entity}VO.java"
|
||||
- original: "mapper/{Entity}Mapper.java"
|
||||
target: "dal/mysql/{feature}/{Entity}Mapper.java"
|
||||
- original: "entity/{Entity}.java"
|
||||
target: "dal/dataobject/{feature}/{Entity}DO.java"
|
||||
|
||||
# ============================================
|
||||
# 快速参考
|
||||
# ============================================
|
||||
quick_reference:
|
||||
standard_endpoints:
|
||||
POST_create: "创建 → CommonResult<Long>"
|
||||
PUT_update: "更新 → CommonResult<Boolean>"
|
||||
DELETE_delete: "删除 → CommonResult<Boolean>"
|
||||
GET_get: "详情 → CommonResult<XxxRespVO>"
|
||||
GET_page: "分页 → CommonResult<PageResult<XxxRespVO>>"
|
||||
DELETE_delete-list: "批量删除 → CommonResult<Boolean>"
|
||||
GET_export-excel: "导出 → void"
|
||||
POST_import: "导入 → CommonResult<XxxImportRespVO>"
|
||||
|
||||
permission_operations:
|
||||
query: "查询(get/page/list)"
|
||||
create: "新增"
|
||||
update: "修改"
|
||||
delete: "删除"
|
||||
export: "导出"
|
||||
import: "导入"
|
||||
|
||||
base_classes:
|
||||
BaseDO: "单租户场景 - creator/createTime/updater/updateTime/deleted"
|
||||
TenantBaseDO: "多租户场景 - 额外含tenantId"
|
||||
|
||||
mapper_methods:
|
||||
selectPage: "分页查询"
|
||||
selectOne: "查询单条"
|
||||
selectList: "查询列表"
|
||||
insert: "插入"
|
||||
updateById: "更新"
|
||||
deleteById: "删除"
|
||||
@@ -1,654 +0,0 @@
|
||||
# 数据库设计 Skill
|
||||
# 根据业务需求生成符合项目规范的 PostgreSQL 表结构
|
||||
# 本项目默认且唯一首选数据库方言为 PostgreSQL;除非用户明确指定,否则禁止生成 MySQL 语法。
|
||||
|
||||
skill:
|
||||
id: "db-designer"
|
||||
name: "数据库设计 Skill"
|
||||
version: "1.0.0"
|
||||
category: "design"
|
||||
description: "根据业务需求自动生成符合项目规范的 PostgreSQL 数据库设计,包括表结构、索引、ER图等"
|
||||
created_at: "2026-03-31"
|
||||
updated_at: "2026-03-31"
|
||||
|
||||
# ============================================
|
||||
# 触发条件
|
||||
# ============================================
|
||||
trigger:
|
||||
commands:
|
||||
- "/db-design"
|
||||
- "/db-designer"
|
||||
|
||||
keywords:
|
||||
- "设计数据库"
|
||||
- "生成表结构"
|
||||
- "创建数据模型"
|
||||
- "建表"
|
||||
- "DDL"
|
||||
|
||||
events:
|
||||
- name: "prd_approved"
|
||||
condition: "PRD文档审核通过"
|
||||
- name: "architecture_defined"
|
||||
condition: "系统架构设计完成"
|
||||
|
||||
# ============================================
|
||||
# 输入参数
|
||||
# ============================================
|
||||
input:
|
||||
parameters:
|
||||
- name: "module_code"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "模块编码(如 mes、erp、crm、bpm 等)"
|
||||
examples: ["mes", "erp", "crm", "bpm", "pay", "fz"]
|
||||
|
||||
- name: "table_name"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "表名(不含前缀)"
|
||||
examples: ["work_order", "product", "customer", "letter"]
|
||||
|
||||
- name: "business_fields"
|
||||
type: "array"
|
||||
required: true
|
||||
description: "业务字段列表"
|
||||
|
||||
- name: "indexes"
|
||||
type: "array"
|
||||
required: false
|
||||
default: []
|
||||
description: "额外索引定义"
|
||||
|
||||
- name: "create_date"
|
||||
type: "string"
|
||||
required: false
|
||||
default: "today"
|
||||
description: "创建日期(用于文件命名)"
|
||||
|
||||
# ============================================
|
||||
# 设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
business_position: "数据库设计是业务建模的核心环节,将业务需求转化为可存储的数据结构"
|
||||
|
||||
design_principles:
|
||||
- "多租户隔离:所有业务表包含 tenant_id 字段,索引必须包含 tenant_id"
|
||||
- "审计追踪:标准化审计字段,支持数据变更追踪"
|
||||
- "逻辑删除:使用 deleted 字段实现软删除,避免数据物理删除"
|
||||
- "索引优化:根据查询场景设计索引,遵循最左匹配原则"
|
||||
- "命名规范:统一的表名、字段名、索引名命名规则"
|
||||
|
||||
# ============================================
|
||||
# 目录结构规范
|
||||
# ============================================
|
||||
directory_structure:
|
||||
base_path: "sql/postgresql"
|
||||
layout: |
|
||||
sql/postgresql/
|
||||
├── {module}/ # 按模块组织 PostgreSQL 迁移脚本
|
||||
├── table/ # 通用表结构定义
|
||||
│ ├── create/ # 建表脚本
|
||||
│ └── update/ # 表结构更新脚本
|
||||
├── index/ # 索引定义
|
||||
├── view/ # 视图定义
|
||||
├── data/ # 初始数据
|
||||
└── init/ # 模块初始化脚本
|
||||
|
||||
directories:
|
||||
- name: "table/create"
|
||||
purpose: "存放建表 SQL 脚本"
|
||||
note: "每个表一个文件,按日期命名"
|
||||
- name: "table/update"
|
||||
purpose: "存放表结构更新 SQL 脚本"
|
||||
note: "ALTER TABLE、ADD COLUMN 等增量变更"
|
||||
- name: "sequence"
|
||||
purpose: "需要显式序列时存放序列脚本"
|
||||
note: "默认优先使用 GENERATED BY DEFAULT AS IDENTITY;仅兼容既有 @KeySequence 约定时显式建序列"
|
||||
- name: "index"
|
||||
purpose: "存放索引创建脚本"
|
||||
note: "可按表名组织子目录"
|
||||
- name: "view"
|
||||
purpose: "存放视图创建脚本"
|
||||
note: "复杂查询可封装为视图"
|
||||
- name: "data"
|
||||
purpose: "存放初始数据 SQL 脚本"
|
||||
note: "INSERT 语句,初始化字典、菜单等"
|
||||
- name: "init"
|
||||
purpose: "存放模块初始化脚本"
|
||||
note: "按模块组织,如 bpm.sql, quartz.sql"
|
||||
|
||||
# ============================================
|
||||
# 文件命名规范
|
||||
# ============================================
|
||||
file_naming:
|
||||
# 建表文件命名
|
||||
create_table:
|
||||
format: "YYYY_MM_DD_{表名}_create.sql"
|
||||
examples:
|
||||
- "2025_08_09_fz_letter_create.sql"
|
||||
- "2026_03_25_mes_work_order_create.sql"
|
||||
rules:
|
||||
- "日期前缀使用实际创建日期"
|
||||
- "表名使用小写字母和下划线"
|
||||
- "统一使用 _create.sql 后缀"
|
||||
|
||||
# 更新文件命名
|
||||
update_table:
|
||||
format: "YYYY_MM_DD_{表名}_update.sql"
|
||||
examples:
|
||||
- "2025_08_15_fz_metaletter_update.sql"
|
||||
- "2026_01_07_fz_attachment_rel_update.sql"
|
||||
rules:
|
||||
- "日期前缀使用实际更新日期"
|
||||
- "同一表多次更新使用不同日期"
|
||||
- "统一使用 _update.sql 后缀"
|
||||
special_cases:
|
||||
- pattern: "YYYY_MM_DD_{表名}_index_update.sql"
|
||||
purpose: "专门用于索引更新"
|
||||
|
||||
# 序列文件命名
|
||||
sequence:
|
||||
format: "YYYY_MM_DD_{表名}_seq_create.sql"
|
||||
examples:
|
||||
- "2025_07_24_fz_metaletter_seq_create.sql"
|
||||
rules:
|
||||
- "与建表日期保持一致"
|
||||
- "序列名通常为 表名_seq"
|
||||
|
||||
# 初始数据文件命名
|
||||
initial_data:
|
||||
format: "模块名.sql"
|
||||
examples:
|
||||
- "fazhi.sql"
|
||||
- "bpm.sql"
|
||||
- "quartz.sql"
|
||||
- "ruoyi-vue-pro.sql"
|
||||
rules:
|
||||
- "按模块或功能命名"
|
||||
- "包含该模块的初始数据"
|
||||
|
||||
# ============================================
|
||||
# 表命名规范
|
||||
# ============================================
|
||||
table_naming:
|
||||
# 表名前缀规范
|
||||
prefixes:
|
||||
- prefix: "sys_"
|
||||
usage: "系统核心表"
|
||||
examples: ["sys_user", "sys_role", "sys_menu", "sys_dict_data"]
|
||||
- prefix: "mes_"
|
||||
usage: "制造执行系统表"
|
||||
examples: ["mes_work_order", "mes_product", "mes_workshop"]
|
||||
- prefix: "erp_"
|
||||
usage: "企业资源计划表"
|
||||
examples: ["erp_purchase", "erp_sale", "erp_inventory"]
|
||||
- prefix: "crm_"
|
||||
usage: "客户关系管理表"
|
||||
examples: ["crm_customer", "crm_contract", "crm_clue"]
|
||||
- prefix: "bpm_"
|
||||
usage: "工作流表"
|
||||
examples: ["bpm_process_definition", "bpm_process_instance"]
|
||||
- prefix: "pay_"
|
||||
usage: "支付模块表"
|
||||
examples: ["pay_order", "pay_refund", "pay_channel"]
|
||||
- prefix: "member_"
|
||||
usage: "会员模块表"
|
||||
examples: ["member_user", "member_level", "member_address"]
|
||||
- prefix: "infra_"
|
||||
usage: "基础设施表"
|
||||
examples: ["infra_file", "infra_config"]
|
||||
- prefix: "fz_"
|
||||
usage: "法制业务表"
|
||||
examples: ["fz_letter", "fz_replay", "fz_handle_unit"]
|
||||
|
||||
# 表名规则
|
||||
rules:
|
||||
- "使用小写字母,单词间用下划线分隔"
|
||||
- "使用名词,表示实体或关系"
|
||||
- "避免使用 PostgreSQL 保留字"
|
||||
- "长度不超过 64 个字符"
|
||||
- "关联表命名:主表_关联表_rel,如 fz_letter_label_rel"
|
||||
|
||||
# 字段命名规则
|
||||
column_rules:
|
||||
- "使用小写字母和下划线"
|
||||
- "布尔类型使用 is_ 前缀,如 is_deleted, is_enabled"
|
||||
- "时间类型使用 _time 或 _date 后缀"
|
||||
- "外键使用 关联表_id 格式,如 user_id, dept_id"
|
||||
- "主键统一命名为 id"
|
||||
|
||||
# ============================================
|
||||
# 必需字段规范
|
||||
# ============================================
|
||||
required_columns:
|
||||
# 字段定义
|
||||
columns:
|
||||
- name: "id"
|
||||
type: "BIGINT"
|
||||
constraint: "GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY"
|
||||
comment: "主键ID"
|
||||
position: 1
|
||||
|
||||
- name: "tenant_id"
|
||||
type: "BIGINT"
|
||||
default: "0"
|
||||
constraint: "NOT NULL"
|
||||
comment: "租户编号"
|
||||
position: 2
|
||||
note: "所有业务表必须包含,位于 id 之后"
|
||||
|
||||
- name: "creator"
|
||||
type: "VARCHAR(64)"
|
||||
default: "''"
|
||||
comment: "创建者"
|
||||
|
||||
- name: "create_time"
|
||||
type: "TIMESTAMP"
|
||||
default: "CURRENT_TIMESTAMP"
|
||||
constraint: "NOT NULL"
|
||||
comment: "创建时间"
|
||||
|
||||
- name: "updater"
|
||||
type: "VARCHAR(64)"
|
||||
default: "''"
|
||||
comment: "更新者"
|
||||
|
||||
- name: "update_time"
|
||||
type: "TIMESTAMP"
|
||||
default: "CURRENT_TIMESTAMP"
|
||||
constraint: "NOT NULL"
|
||||
comment: "更新时间"
|
||||
note: "PostgreSQL 不支持 ON UPDATE CURRENT_TIMESTAMP,由应用层或触发器维护"
|
||||
|
||||
- name: "deleted"
|
||||
type: "BOOLEAN"
|
||||
default: "false"
|
||||
constraint: "NOT NULL"
|
||||
comment: "是否删除"
|
||||
|
||||
# 可选备注字段
|
||||
remark:
|
||||
name: "remark"
|
||||
type: "VARCHAR(500)"
|
||||
default: "NULL"
|
||||
comment: "备注"
|
||||
position: "before_audit"
|
||||
|
||||
# ============================================
|
||||
# 索引设计规范
|
||||
# ============================================
|
||||
index_standards:
|
||||
# 索引命名规则
|
||||
naming:
|
||||
primary_key: "PRIMARY KEY (id)"
|
||||
unique_key: "uk_{字段名} 或 uk_{字段1_字段2}"
|
||||
normal_index: "idx_{字段名} 或 idx_{字段1_字段2}"
|
||||
|
||||
# 索引设计原则
|
||||
principles:
|
||||
- "所有索引必须包含 tenant_id 作为第一列"
|
||||
- "唯一索引必须包含 deleted 字段(软删除场景)"
|
||||
- "外键字段必须建立索引"
|
||||
- "经常用于查询条件的字段建立索引"
|
||||
- "唯一约束字段建立唯一索引"
|
||||
- "组合索引注意字段顺序(最左匹配)"
|
||||
- "避免过多索引,影响写入性能"
|
||||
|
||||
# 索引模板
|
||||
templates:
|
||||
unique_with_tenant:
|
||||
pattern: "CREATE UNIQUE INDEX uk_tenant_{col} ON {table} (tenant_id, {col}) WHERE deleted = false"
|
||||
example: "CREATE UNIQUE INDEX uk_tenant_order ON mes_work_order (tenant_id, order_no) WHERE deleted = false"
|
||||
|
||||
normal_with_tenant:
|
||||
pattern: "CREATE INDEX idx_tenant_{col} ON {table} (tenant_id, {col})"
|
||||
example: "CREATE INDEX idx_tenant_status ON mes_work_order (tenant_id, status)"
|
||||
|
||||
foreign_key:
|
||||
pattern: "CREATE INDEX idx_tenant_{fk} ON {table} (tenant_id, {fk})"
|
||||
example: "CREATE INDEX idx_tenant_line ON mes_work_order (tenant_id, line_id)"
|
||||
|
||||
# ============================================
|
||||
# 建表模板
|
||||
# ============================================
|
||||
create_table_template:
|
||||
structure: |
|
||||
-- ============================================
|
||||
-- 文件名:{filename}
|
||||
-- 描述:{table_comment}
|
||||
-- 作者:{author}
|
||||
-- 日期:{date}
|
||||
-- ============================================
|
||||
|
||||
CREATE TABLE {table_name} (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
-- 业务字段
|
||||
{business_columns}
|
||||
-- 通用字段
|
||||
remark VARCHAR(500) DEFAULT NULL,
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
COMMENT ON TABLE {table_name} IS '{table_comment}';
|
||||
{column_comments}
|
||||
{indexes}
|
||||
|
||||
# 字段类型推荐
|
||||
column_types:
|
||||
string_short: "VARCHAR(64)"
|
||||
string_medium: "VARCHAR(255)"
|
||||
string_long: "VARCHAR(500)"
|
||||
text: "TEXT"
|
||||
integer: "INT"
|
||||
bigint: "BIGINT"
|
||||
decimal_amount: "DECIMAL(18,2)"
|
||||
decimal_quantity: "DECIMAL(10,4)"
|
||||
boolean: "BOOLEAN"
|
||||
datetime: "TIMESTAMP"
|
||||
date: "DATE"
|
||||
status: "SMALLINT"
|
||||
|
||||
# ============================================
|
||||
# 更新脚本规范
|
||||
# ============================================
|
||||
update_standards:
|
||||
# 更新文件命名
|
||||
naming: "YYYY_MM_DD_{表名}_update.sql"
|
||||
|
||||
# 字段操作模板
|
||||
operations:
|
||||
add_column: |
|
||||
-- 新增字段并添加字段注释
|
||||
ALTER TABLE {table} ADD COLUMN {column} {type};
|
||||
COMMENT ON COLUMN {table}.{column} IS '{comment}';
|
||||
-- 示例
|
||||
ALTER TABLE fz_letter ADD COLUMN priority SMALLINT DEFAULT 0;
|
||||
COMMENT ON COLUMN fz_letter.priority IS '优先级';
|
||||
|
||||
modify_column: |
|
||||
-- 修改字段类型
|
||||
ALTER TABLE {table} ALTER COLUMN {column} TYPE {new_type} USING {column}::{new_type};
|
||||
COMMENT ON COLUMN {table}.{column} IS '{comment}';
|
||||
-- 示例
|
||||
ALTER TABLE fz_letter ALTER COLUMN content TYPE TEXT USING content::TEXT;
|
||||
COMMENT ON COLUMN fz_letter.content IS '信件内容';
|
||||
|
||||
drop_column: |
|
||||
-- 删除字段(确保无业务依赖)
|
||||
ALTER TABLE {table} DROP COLUMN {column};
|
||||
|
||||
add_index: |
|
||||
-- 添加普通索引
|
||||
CREATE INDEX idx_tenant_{col} ON {table} (tenant_id, {col});
|
||||
-- 添加软删除唯一索引
|
||||
CREATE UNIQUE INDEX uk_tenant_{col} ON {table} (tenant_id, {col}) WHERE deleted = false;
|
||||
|
||||
# 文件头部模板
|
||||
header: |
|
||||
-- ============================================
|
||||
-- 更新说明:{更新目的}
|
||||
-- 更新日期:{YYYY-MM-DD}
|
||||
-- 更新人:{姓名}
|
||||
-- ============================================
|
||||
|
||||
# 注释规范
|
||||
comment_standards:
|
||||
- "文件头部说明更新目的"
|
||||
- "每个变更语句添加注释"
|
||||
- "危险操作(删除字段、删除数据)添加警告注释"
|
||||
|
||||
# ============================================
|
||||
# 数据初始化规范
|
||||
# ============================================
|
||||
data_standards:
|
||||
# INSERT 语句格式
|
||||
insert_format: |
|
||||
-- 插入菜单数据
|
||||
INSERT INTO `sys_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`)
|
||||
VALUES
|
||||
(1001, '信件管理', '', 2, 1, 0, 'letter', 'ep:document', 'fazhi/letter/index', 0),
|
||||
(1002, '信件查询', 'fz:letter:query', 3, 1, 1001, '', '', '', 0);
|
||||
|
||||
# 字典数据
|
||||
dict_data: |
|
||||
-- 字典类型
|
||||
INSERT INTO `sys_dict_type` (`name`, `type`, `status`, `remark`)
|
||||
VALUES ('信件状态', 'fz_letter_status', 0, '信件流转状态');
|
||||
|
||||
-- 字典数据
|
||||
INSERT INTO `sys_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`)
|
||||
VALUES
|
||||
(1, '待处理', '0', 'fz_letter_status', 0),
|
||||
(2, '处理中', '1', 'fz_letter_status', 0),
|
||||
(3, '已完成', '2', 'fz_letter_status', 0);
|
||||
|
||||
# ============================================
|
||||
# 编写规范
|
||||
# ============================================
|
||||
coding_standards:
|
||||
# SQL 格式
|
||||
formatting:
|
||||
- "关键字使用大写:SELECT, FROM, WHERE, JOIN, ORDER BY"
|
||||
- "表名、字段名使用小写;仅在名称冲突或保留字场景使用双引号"
|
||||
- "每个字段占一行,逗号放在行尾"
|
||||
- "复杂查询使用缩进和换行提高可读性"
|
||||
|
||||
# 注释规范
|
||||
comments:
|
||||
file_header: |
|
||||
-- ============================================
|
||||
-- 文件名:{文件名}
|
||||
-- 描述:{功能描述}
|
||||
-- 作者:{作者}
|
||||
-- 日期:{YYYY-MM-DD}
|
||||
-- ============================================
|
||||
table_comment: "CREATE TABLE 后必须使用 COMMENT ON TABLE 添加表注释"
|
||||
column_comment: "每个字段必须使用 COMMENT ON COLUMN 添加字段注释"
|
||||
index_comment: "复杂索引添加注释说明用途"
|
||||
|
||||
# 事务处理
|
||||
transaction:
|
||||
- "多条关联语句使用事务包裹"
|
||||
- "DDL 语句(部分数据库)自动提交,注意顺序"
|
||||
- "大数据量操作分批执行"
|
||||
|
||||
# ============================================
|
||||
# 版本管理
|
||||
# ============================================
|
||||
version_control:
|
||||
principles:
|
||||
- "建表后表结构变更使用 update 脚本,不修改原文件"
|
||||
- "每个变更独立文件,便于追踪和回滚"
|
||||
- "日期前缀确保文件顺序和变更时间线"
|
||||
- "版本升级时按日期顺序执行所有脚本"
|
||||
|
||||
change_log:
|
||||
format: |
|
||||
-- 变更记录:
|
||||
-- 2025-08-09:创建表
|
||||
-- 2025-11-11:新增 priority 字段
|
||||
-- 2026-01-07:新增 category 字段,添加索引
|
||||
|
||||
# ============================================
|
||||
# 执行流程
|
||||
# ============================================
|
||||
workflow:
|
||||
phases:
|
||||
- name: "需求分析"
|
||||
description: "解析业务需求,提取实体和关系"
|
||||
steps:
|
||||
- "识别核心业务实体"
|
||||
- "分析实体间关系(一对多、多对多)"
|
||||
- "确定实体属性列表"
|
||||
- "确定表名前缀(模块编码)"
|
||||
|
||||
- name: "实体设计"
|
||||
description: "设计实体属性和字段"
|
||||
steps:
|
||||
- "设计业务属性字段"
|
||||
- "添加系统必需字段(审计字段)"
|
||||
- "定义主键策略(BIGINT GENERATED BY DEFAULT AS IDENTITY;兼容既有代码时配合 @KeySequence)"
|
||||
- "设置外键关系"
|
||||
|
||||
- name: "多租户设计"
|
||||
description: "添加租户隔离字段和索引"
|
||||
steps:
|
||||
- "添加 tenant_id 字段(第二位置)"
|
||||
- "确保所有索引包含 tenant_id"
|
||||
- "唯一索引包含 deleted 字段"
|
||||
|
||||
- name: "索引设计"
|
||||
description: "根据查询场景设计索引"
|
||||
steps:
|
||||
- "分析查询场景(列表、详情、关联、搜索)"
|
||||
- "设计主键索引"
|
||||
- "设计唯一索引(业务唯一键)"
|
||||
- "设计普通索引(查询优化)"
|
||||
- "设计外键索引"
|
||||
|
||||
- name: "文件生成"
|
||||
description: "生成符合规范的 SQL 文件"
|
||||
steps:
|
||||
- "确定文件命名(日期前缀)"
|
||||
- "生成文件头部注释"
|
||||
- "生成 CREATE TABLE 语句"
|
||||
- "生成索引定义"
|
||||
- "添加表注释"
|
||||
|
||||
# ============================================
|
||||
# 质量检查清单
|
||||
# ============================================
|
||||
quality_check:
|
||||
before_create:
|
||||
- "确认表名符合命名规范"
|
||||
- "确认包含所有必需字段"
|
||||
- "确认字段类型和长度合理"
|
||||
- "确认添加了必要的索引"
|
||||
- "确认添加了表注释和字段注释"
|
||||
- "确认文件命名符合规范"
|
||||
|
||||
after_create:
|
||||
- "测试建表脚本可正常执行"
|
||||
- "确认所有索引包含 tenant_id"
|
||||
- "确认唯一索引包含 deleted"
|
||||
- "确认外键字段有索引"
|
||||
- "检查索引是否生效"
|
||||
- "更新相关文档"
|
||||
|
||||
before_update:
|
||||
- "确认变更的必要性"
|
||||
- "评估对现有数据的影响"
|
||||
- "准备回滚方案"
|
||||
- "选择低峰期执行"
|
||||
|
||||
# ============================================
|
||||
# 快速参考
|
||||
# ============================================
|
||||
quick_reference:
|
||||
table_prefixes:
|
||||
sys_: "系统表"
|
||||
mes_: "制造执行系统表"
|
||||
erp_: "企业资源计划表"
|
||||
crm_: "客户关系管理表"
|
||||
bpm_: "工作流表"
|
||||
pay_: "支付表"
|
||||
member_: "会员表"
|
||||
infra_: "基础设施表"
|
||||
fz_: "法制业务表"
|
||||
|
||||
file_suffixes:
|
||||
_create.sql: "建表脚本"
|
||||
_update.sql: "更新脚本"
|
||||
_seq_create.sql: "序列脚本"
|
||||
|
||||
common_columns:
|
||||
id: "主键ID(BIGINT)"
|
||||
tenant_id: "租户编号(BIGINT)"
|
||||
creator: "创建者(VARCHAR(64))"
|
||||
create_time: "创建时间(TIMESTAMP)"
|
||||
updater: "更新者(VARCHAR(64))"
|
||||
update_time: "更新时间(TIMESTAMP)"
|
||||
deleted: "逻辑删除(BOOLEAN)"
|
||||
remark: "备注(VARCHAR(500))"
|
||||
|
||||
# ============================================
|
||||
# 示例
|
||||
# ============================================
|
||||
examples:
|
||||
work_order:
|
||||
input:
|
||||
module_code: "mes"
|
||||
table_name: "mes_work_order"
|
||||
table_comment: "生产工单表"
|
||||
business_fields:
|
||||
- name: "order_no"
|
||||
type: "VARCHAR(64)"
|
||||
required: true
|
||||
comment: "工单编号"
|
||||
unique: true
|
||||
- name: "product_id"
|
||||
type: "BIGINT"
|
||||
required: true
|
||||
comment: "产品ID"
|
||||
- name: "status"
|
||||
type: "TINYINT"
|
||||
required: true
|
||||
default: "0"
|
||||
comment: "状态:0-待下发,1-已下发,2-生产中,3-已完成,4-已关闭"
|
||||
|
||||
output_file: "2026_03_25_mes_work_order_create.sql"
|
||||
|
||||
output_sql: |
|
||||
-- PostgreSQL 示例(字段注释通过 COMMENT ON COLUMN 补充)
|
||||
CREATE TABLE mes_work_order (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
order_no VARCHAR(64) NOT NULL,
|
||||
product_id BIGINT NOT NULL,
|
||||
status SMALLINT NOT NULL DEFAULT 0,
|
||||
plan_start_time TIMESTAMP NOT NULL,
|
||||
plan_end_time TIMESTAMP NOT NULL,
|
||||
remark VARCHAR(500) DEFAULT NULL,
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted BOOLEAN NOT NULL DEFAULT false
|
||||
);
|
||||
|
||||
COMMENT ON TABLE mes_work_order IS '生产工单表';
|
||||
CREATE UNIQUE INDEX uk_tenant_order
|
||||
ON mes_work_order (tenant_id, order_no) WHERE deleted = false;
|
||||
CREATE INDEX idx_tenant_status ON mes_work_order (tenant_id, status);
|
||||
CREATE INDEX idx_tenant_time ON mes_work_order (tenant_id, plan_start_time);
|
||||
|
||||
# ============================================
|
||||
# 错误处理
|
||||
# ============================================
|
||||
error_handling:
|
||||
errors:
|
||||
- code: "DB001"
|
||||
message: "表名不符合命名规范"
|
||||
solution: "检查表名前缀和格式"
|
||||
|
||||
- code: "DB002"
|
||||
message: "缺少必需字段"
|
||||
solution: "确保包含 id, tenant_id, 审计字段"
|
||||
|
||||
- code: "DB003"
|
||||
message: "索引缺少 tenant_id"
|
||||
solution: "所有索引必须包含 tenant_id 作为第一列"
|
||||
|
||||
- code: "DB004"
|
||||
message: "文件命名不符合规范"
|
||||
solution: "使用 YYYY_MM_DD_{表名}_create.sql 格式"
|
||||
|
||||
- code: "DB005"
|
||||
message: "唯一索引缺少 deleted"
|
||||
solution: "唯一索引必须包含 deleted 字段"
|
||||
@@ -1,836 +0,0 @@
|
||||
# 实体类设计 Skill
|
||||
# 根据 PostgreSQL 表结构生成符合 yudao-vue-pro 规范的实体类(DO)
|
||||
# 本项目默认数据库方言为 PostgreSQL,类型映射不得回退为 MySQL 默认。
|
||||
|
||||
skill:
|
||||
id: "design-entity"
|
||||
name: "实体类设计 Skill"
|
||||
version: "1.0.0"
|
||||
category: "design"
|
||||
description: "根据 PostgreSQL 表结构自动生成符合 yudao 项目规范的实体类(DO),支持多租户、审计字段、JSONB 字段等特性"
|
||||
created_at: "2026-03-31"
|
||||
updated_at: "2026-03-31"
|
||||
|
||||
# ============================================
|
||||
# 触发条件
|
||||
# ============================================
|
||||
trigger:
|
||||
# 命令触发
|
||||
commands:
|
||||
- "/entity-design"
|
||||
- "/design-entity"
|
||||
- "/entity-generator"
|
||||
|
||||
# 自然语言触发
|
||||
keywords:
|
||||
- "生成实体类"
|
||||
- "创建DO"
|
||||
- "生成DO实体"
|
||||
- "实体类设计"
|
||||
- "数据对象"
|
||||
|
||||
# 事件触发
|
||||
events:
|
||||
- name: "db_design_completed"
|
||||
condition: "数据库表结构设计完成"
|
||||
- name: "sql_created"
|
||||
condition: "建表 SQL 文件创建完成"
|
||||
|
||||
# ============================================
|
||||
# 输入参数
|
||||
# ============================================
|
||||
input:
|
||||
parameters:
|
||||
- name: "module_code"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "模块编码(如 mes、erp、crm、system)"
|
||||
examples: ["mes", "erp", "crm", "system", "pay"]
|
||||
|
||||
- name: "table_name"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "表名(含前缀,如 mes_work_order)"
|
||||
examples: ["mes_work_order", "erp_product", "system_user"]
|
||||
|
||||
- name: "table_comment"
|
||||
type: "string"
|
||||
required: true
|
||||
description: "表注释/描述"
|
||||
|
||||
- name: "business_fields"
|
||||
type: "array"
|
||||
required: true
|
||||
description: "业务字段列表(不含通用字段)"
|
||||
schema:
|
||||
- name: "字段名"
|
||||
type: "数据类型"
|
||||
required: "是否必填"
|
||||
comment: "字段注释"
|
||||
enum_ref: "枚举引用(可选)"
|
||||
json_field: "是否JSON字段(可选)"
|
||||
java_type: "JSON字段的Java类型(可选,如 Set<Long>)"
|
||||
|
||||
- name: "tenant_type"
|
||||
type: "string"
|
||||
required: false
|
||||
default: "multi"
|
||||
description: "租户类型:single-单租户,multi-多租户,ignore-忽略租户"
|
||||
examples: ["single", "multi", "ignore"]
|
||||
|
||||
- name: "entity_name"
|
||||
type: "string"
|
||||
required: false
|
||||
default: "auto"
|
||||
description: "实体类名称(默认根据表名自动生成)"
|
||||
|
||||
- name: "feature"
|
||||
type: "string"
|
||||
required: false
|
||||
default: "auto"
|
||||
description: "功能子目录名(默认根据实体名推断)"
|
||||
|
||||
# ============================================
|
||||
# 设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
business_position: "实体类(DO)是数据库表与业务逻辑之间的桥梁,将关系型数据映射为对象"
|
||||
|
||||
design_principles:
|
||||
- "继承体系:多租户继承 TenantBaseDO,单租户继承 BaseDO"
|
||||
- "审计字段:由基类提供,业务实体不重复定义"
|
||||
- "字段注释:使用 JavaDoc 格式,不使用 Swagger 注解"
|
||||
- "JSON 字段:使用 JacksonTypeHandler 处理复杂类型"
|
||||
- "命名规范:实体类以 DO 结尾,与表名保持映射关系"
|
||||
- "枚举引用:在注释中说明枚举类,字段使用 Integer 类型"
|
||||
|
||||
inheritance_model:
|
||||
base_hierarchy:
|
||||
BaseDO:
|
||||
package: "cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO"
|
||||
fields: ["createTime", "updateTime", "creator", "updater", "deleted"]
|
||||
use_case: "单租户业务表"
|
||||
note: "deleted 字段为 Boolean 类型"
|
||||
TenantBaseDO:
|
||||
package: "cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO"
|
||||
parent: "BaseDO"
|
||||
extra_fields: ["tenantId"]
|
||||
use_case: "多租户业务表(绝大多数场景)"
|
||||
special_annotation:
|
||||
TenantIgnore:
|
||||
package: "cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore"
|
||||
use_case: "租户表、套餐表等需要忽略多租户过滤的实体"
|
||||
|
||||
# ============================================
|
||||
# 命名转换规则
|
||||
# ============================================
|
||||
naming:
|
||||
# 表名 -> 实体类名转换
|
||||
table_to_class:
|
||||
rules:
|
||||
- "去除表前缀(如 mes_、erp_、system_、crm_、bpm_、pay_)"
|
||||
- "snake_case 转 PascalCase"
|
||||
- "添加 DO 后缀"
|
||||
- "特殊表名特殊处理"
|
||||
examples:
|
||||
- table: "mes_work_order"
|
||||
class: "WorkOrderDO"
|
||||
- table: "erp_product"
|
||||
class: "ProductDO"
|
||||
- table: "system_users"
|
||||
class: "AdminUserDO"
|
||||
- table: "system_role"
|
||||
class: "RoleDO"
|
||||
- table: "crm_customer"
|
||||
class: "CustomerDO"
|
||||
|
||||
# 字段名 -> 属性名转换
|
||||
column_to_field:
|
||||
rules:
|
||||
- "snake_case 转 camelCase"
|
||||
- "保持与数据库字段映射"
|
||||
examples:
|
||||
- column: "work_order_no"
|
||||
field: "workOrderNo"
|
||||
- column: "create_time"
|
||||
field: "createTime"
|
||||
- column: "tenant_id"
|
||||
field: "tenantId"
|
||||
|
||||
# 特殊表名映射
|
||||
special_mappings:
|
||||
system_users: "AdminUserDO"
|
||||
system_menu: "MenuDO"
|
||||
system_tenant: "TenantDO"
|
||||
system_dict_data: "DictDataDO"
|
||||
system_dict_type: "DictTypeDO"
|
||||
|
||||
# 表前缀映射(模块编码)
|
||||
prefix_mapping:
|
||||
sys: "system"
|
||||
mes: "mes"
|
||||
erp: "erp"
|
||||
crm: "crm"
|
||||
bpm: "bpm"
|
||||
pay: "pay"
|
||||
member: "member"
|
||||
infra: "infra"
|
||||
|
||||
# ============================================
|
||||
# 类型映射规则
|
||||
# ============================================
|
||||
type_mapping:
|
||||
postgresql_to_java:
|
||||
- postgresql: "BIGINT"
|
||||
java: "Long"
|
||||
note: "主键、外键、数量类字段"
|
||||
|
||||
- postgresql: "INT / INTEGER"
|
||||
java: "Integer"
|
||||
note: "状态、排序、计数类字段"
|
||||
|
||||
- postgresql: "TINYINT"
|
||||
java: "Integer"
|
||||
note: "枚举字段、开关字段"
|
||||
|
||||
- postgresql: "SMALLINT"
|
||||
java: "Integer"
|
||||
note: "小范围数值"
|
||||
|
||||
- postgresql: "DECIMAL(p,s)"
|
||||
java: "BigDecimal"
|
||||
note: "金额、精确数值"
|
||||
import: "java.math.BigDecimal"
|
||||
|
||||
- postgresql: "FLOAT / DOUBLE"
|
||||
java: "Double"
|
||||
note: "浮点数"
|
||||
|
||||
- postgresql: "VARCHAR / CHAR"
|
||||
java: "String"
|
||||
note: "字符串"
|
||||
|
||||
- postgresql: "TEXT / LONGTEXT"
|
||||
java: "String"
|
||||
note: "大文本"
|
||||
|
||||
- postgresql: "TIMESTAMP / TIMESTAMPTZ"
|
||||
java: "LocalDateTime"
|
||||
note: "日期时间"
|
||||
import: "java.time.LocalDateTime"
|
||||
|
||||
- postgresql: "DATE"
|
||||
java: "LocalDate"
|
||||
note: "日期"
|
||||
import: "java.time.LocalDate"
|
||||
|
||||
- postgresql: "TIME"
|
||||
java: "LocalTime"
|
||||
note: "时间"
|
||||
import: "java.time.LocalTime"
|
||||
|
||||
- postgresql: "BOOLEAN"
|
||||
java: "Boolean"
|
||||
note: "布尔值"
|
||||
|
||||
- postgresql: "BLOB / LONGBLOB"
|
||||
java: "byte[]"
|
||||
note: "二进制数据"
|
||||
|
||||
- postgresql: "JSON"
|
||||
java: "复杂类型"
|
||||
handler: "JacksonTypeHandler"
|
||||
note: "JSON 数据,需特殊处理"
|
||||
|
||||
# JSON 字段类型映射
|
||||
json_type_mapping:
|
||||
- pattern: "ID数组"
|
||||
java_type: "Set<Long>"
|
||||
import: "java.util.Set"
|
||||
- pattern: "字符串数组"
|
||||
java_type: "List<String>"
|
||||
import: "java.util.List"
|
||||
- pattern: "键值对对象"
|
||||
java_type: "Map<String, Object>"
|
||||
import: "java.util.Map"
|
||||
|
||||
# ============================================
|
||||
# 注解规范
|
||||
# ============================================
|
||||
annotations:
|
||||
# 类级别注解
|
||||
class_annotations:
|
||||
required:
|
||||
- annotation: "@TableName"
|
||||
format: "@TableName(value = \"{table_name}\", autoResultMap = true)"
|
||||
import: "com.baomidou.mybatisplus.annotation.TableName"
|
||||
note: "autoResultMap = true 用于支持 TypeHandler"
|
||||
|
||||
- annotation: "@KeySequence"
|
||||
format: "@KeySequence(\"{table_name}_seq\")"
|
||||
import: "com.baomidou.mybatisplus.annotation.KeySequence"
|
||||
note: "PostgreSQL 主键约定;与项目既有 @KeySequence 规范保持一致"
|
||||
|
||||
- annotation: "@Data"
|
||||
import: "lombok.Data"
|
||||
note: "Lombok getter/setter"
|
||||
|
||||
- annotation: "@EqualsAndHashCode"
|
||||
format: "@EqualsAndHashCode(callSuper = true)"
|
||||
import: "lombok.EqualsAndHashCode"
|
||||
note: "必须 callSuper = true 以包含基类字段"
|
||||
|
||||
optional:
|
||||
- annotation: "@Builder"
|
||||
import: "lombok.Builder"
|
||||
note: "构建器模式"
|
||||
|
||||
- annotation: "@NoArgsConstructor"
|
||||
import: "lombok.NoArgsConstructor"
|
||||
note: "无参构造(与 Builder 配合)"
|
||||
|
||||
- annotation: "@AllArgsConstructor"
|
||||
import: "lombok.AllArgsConstructor"
|
||||
note: "全参构造(与 Builder 配合)"
|
||||
|
||||
- annotation: "@TenantIgnore"
|
||||
import: "cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore"
|
||||
note: "忽略多租户过滤(仅用于租户表等特殊场景)"
|
||||
|
||||
# 字段级别注解
|
||||
field_annotations:
|
||||
primary_key:
|
||||
- annotation: "@TableId"
|
||||
import: "com.baomidou.mybatisplus.annotation.TableId"
|
||||
note: "主键字段,默认使用雪花算法"
|
||||
|
||||
json_field:
|
||||
- annotation: "@TableField"
|
||||
format: "@TableField(typeHandler = JacksonTypeHandler.class)"
|
||||
import: "com.baomidou.mybatisplus.annotation.TableField"
|
||||
handler_import: "com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"
|
||||
note: "JSON 类型字段,需要 @TableName 包含 autoResultMap = true"
|
||||
|
||||
# ============================================
|
||||
# 代码模板
|
||||
# ============================================
|
||||
templates:
|
||||
# 多租户实体模板(最常用)
|
||||
tenant_entity:
|
||||
description: "多租户业务表实体类"
|
||||
base_class: "TenantBaseDO"
|
||||
code: |
|
||||
package cn.iocoder.yudao.module.{module}.dal.dataobject.{feature};
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
{additional_imports}
|
||||
|
||||
/**
|
||||
* {table_comment} DO
|
||||
*
|
||||
* @author {author}
|
||||
*/
|
||||
@TableName(value = "{table_name}", autoResultMap = true)
|
||||
@KeySequence("{table_name}_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class {EntityName}DO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
{business_fields}
|
||||
|
||||
}
|
||||
|
||||
# 单租户实体模板
|
||||
single_entity:
|
||||
description: "单租户业务表实体类"
|
||||
base_class: "BaseDO"
|
||||
code: |
|
||||
package cn.iocoder.yudao.module.{module}.dal.dataobject.{feature};
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
{additional_imports}
|
||||
|
||||
/**
|
||||
* {table_comment} DO
|
||||
*
|
||||
* @author {author}
|
||||
*/
|
||||
@TableName(value = "{table_name}", autoResultMap = true)
|
||||
@KeySequence("{table_name}_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class {EntityName}DO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
{business_fields}
|
||||
|
||||
}
|
||||
|
||||
# 忽略多租户实体模板
|
||||
tenant_ignore_entity:
|
||||
description: "需要忽略多租户过滤的实体类(如租户表、套餐表)"
|
||||
base_class: "BaseDO"
|
||||
extra_annotation: "@TenantIgnore"
|
||||
code: |
|
||||
package cn.iocoder.yudao.module.{module}.dal.dataobject.{feature};
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
{additional_imports}
|
||||
|
||||
/**
|
||||
* {table_comment} DO
|
||||
*
|
||||
* @author {author}
|
||||
*/
|
||||
@TenantIgnore
|
||||
@TableName(value = "{table_name}", autoResultMap = true)
|
||||
@KeySequence("{table_name}_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class {EntityName}DO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
{business_fields}
|
||||
|
||||
}
|
||||
|
||||
# 业务字段模板
|
||||
business_field:
|
||||
normal: |
|
||||
/**
|
||||
* {字段注释}
|
||||
{enum_reference}
|
||||
*/
|
||||
private {JavaType} {fieldName};
|
||||
|
||||
json_field: |
|
||||
/**
|
||||
* {字段注释}
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private {JavaType} {fieldName};
|
||||
|
||||
# ============================================
|
||||
# 输出规范
|
||||
# ============================================
|
||||
output:
|
||||
# 文件输出位置
|
||||
file_location:
|
||||
path_pattern: "yudao-module-{module}/src/main/java/cn/iocoder/yudao/module/{module}/dal/dataobject/{feature}/{EntityName}DO.java"
|
||||
example: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/workorder/WorkOrderDO.java"
|
||||
|
||||
# 导入语句规范
|
||||
imports:
|
||||
base_imports:
|
||||
- "com.baomidou.mybatisplus.annotation.TableName"
|
||||
- "com.baomidou.mybatisplus.annotation.TableId"
|
||||
- "com.baomidou.mybatisplus.annotation.KeySequence"
|
||||
- "lombok.Data"
|
||||
- "lombok.EqualsAndHashCode"
|
||||
|
||||
tenant_imports:
|
||||
- "cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO"
|
||||
|
||||
single_imports:
|
||||
- "cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO"
|
||||
|
||||
json_handler_import:
|
||||
- "com.baomidou.mybatisplus.annotation.TableField"
|
||||
- "com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"
|
||||
|
||||
builder_imports:
|
||||
- "lombok.Builder"
|
||||
- "lombok.NoArgsConstructor"
|
||||
- "lombok.AllArgsConstructor"
|
||||
|
||||
# 产物清单
|
||||
artifacts:
|
||||
- name: "实体类文件"
|
||||
type: "code"
|
||||
path: "dal/dataobject/{feature}/{EntityName}DO.java"
|
||||
|
||||
# ============================================
|
||||
# 执行流程
|
||||
# ============================================
|
||||
workflow:
|
||||
phases:
|
||||
- name: "表结构解析"
|
||||
description: "解析数据库表结构,提取元数据"
|
||||
steps:
|
||||
- "识别表名、表前缀、表注释"
|
||||
- "提取业务字段列表(排除通用字段:id, tenant_id, creator, create_time, updater, update_time, deleted)"
|
||||
- "识别主键、索引信息"
|
||||
- "识别 JSON 字段、枚举字段"
|
||||
|
||||
- name: "命名转换"
|
||||
description: "将数据库命名转换为 Java 命名"
|
||||
steps:
|
||||
- "表名 -> 实体类名(去除前缀 + PascalCase + DO 后缀)"
|
||||
- "字段名 -> 属性名(camelCase)"
|
||||
- "处理特殊表名映射"
|
||||
|
||||
- name: "类型映射"
|
||||
description: "将 MySQL 类型映射为 Java 类型"
|
||||
steps:
|
||||
- "标准类型映射(BIGINT -> Long 等)"
|
||||
- "JSON 字段识别和处理"
|
||||
- "枚举字段处理(Integer + 注释引用)"
|
||||
|
||||
- name: "继承选择"
|
||||
description: "确定实体类继承的基类"
|
||||
steps:
|
||||
- "判断租户类型(multi/single/ignore)"
|
||||
- "选择对应基类(TenantBaseDO/BaseDO)"
|
||||
- "判断是否需要 @TenantIgnore"
|
||||
|
||||
- name: "代码生成"
|
||||
description: "生成实体类代码"
|
||||
steps:
|
||||
- "生成类注释和注解"
|
||||
- "生成主键字段"
|
||||
- "生成业务字段(含 JavaDoc 注释)"
|
||||
- "生成导入语句(按规范分组排序)"
|
||||
- "格式化代码"
|
||||
|
||||
- name: "质量检查"
|
||||
description: "验证生成的实体类"
|
||||
steps:
|
||||
- "检查注解完整性"
|
||||
- "检查字段注释格式"
|
||||
- "检查继承正确性"
|
||||
- "检查命名规范"
|
||||
|
||||
# ============================================
|
||||
# 质量检查清单
|
||||
# ============================================
|
||||
quality_check:
|
||||
before_generate:
|
||||
- "确认表名符合命名规范"
|
||||
- "确认业务字段列表完整"
|
||||
- "确认租户类型选择正确"
|
||||
- "确认字段类型映射正确"
|
||||
|
||||
after_generate:
|
||||
- "实体类名称以 DO 结尾"
|
||||
- "@TableName 注解包含 autoResultMap = true"
|
||||
- "@EqualsAndHashCode 包含 callSuper = true"
|
||||
- "继承正确的基类"
|
||||
- "主键字段使用 @TableId 注解"
|
||||
- "所有字段使用 JavaDoc 注释格式"
|
||||
- "JSON 字段使用 JacksonTypeHandler"
|
||||
- "枚举字段注释包含枚举引用"
|
||||
- "不包含 Swagger @Schema 注解"
|
||||
- "不重复定义审计字段(已在基类中)"
|
||||
|
||||
# ============================================
|
||||
# 示例
|
||||
# ============================================
|
||||
examples:
|
||||
# 多租户实体示例
|
||||
work_order:
|
||||
input:
|
||||
module_code: "mes"
|
||||
table_name: "mes_work_order"
|
||||
table_comment: "生产工单"
|
||||
tenant_type: "multi"
|
||||
business_fields:
|
||||
- name: "order_no"
|
||||
type: "VARCHAR(64)"
|
||||
comment: "工单编号"
|
||||
unique: true
|
||||
- name: "product_id"
|
||||
type: "BIGINT"
|
||||
comment: "产品ID"
|
||||
- name: "status"
|
||||
type: "TINYINT"
|
||||
comment: "状态"
|
||||
enum_ref: "WorkOrderStatusEnum"
|
||||
- name: "plan_qty"
|
||||
type: "INT"
|
||||
comment: "计划数量"
|
||||
|
||||
output_class_name: "WorkOrderDO"
|
||||
output_file: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/workorder/WorkOrderDO.java"
|
||||
|
||||
output_code: |
|
||||
package cn.iocoder.yudao.module.mes.dal.dataobject.workorder;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import cn.iocoder.yudao.module.mes.enums.WorkOrderStatusEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 生产工单 DO
|
||||
*
|
||||
* @author yudao
|
||||
*/
|
||||
@TableName(value = "mes_work_order", autoResultMap = true)
|
||||
@KeySequence("mes_work_order_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class WorkOrderDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 工单编号
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 产品ID
|
||||
*/
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* 枚举 {@link WorkOrderStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 计划数量
|
||||
*/
|
||||
private Integer planQty;
|
||||
|
||||
}
|
||||
|
||||
# JSON 字段实体示例
|
||||
role_entity:
|
||||
input:
|
||||
module_code: "system"
|
||||
table_name: "system_role"
|
||||
table_comment: "角色"
|
||||
tenant_type: "multi"
|
||||
business_fields:
|
||||
- name: "name"
|
||||
type: "VARCHAR(100)"
|
||||
comment: "角色名称"
|
||||
- name: "code"
|
||||
type: "VARCHAR(50)"
|
||||
comment: "角色标识"
|
||||
- name: "data_scope_dept_ids"
|
||||
type: "JSON"
|
||||
comment: "数据范围部门ID数组"
|
||||
json_field: true
|
||||
java_type: "Set<Long>"
|
||||
|
||||
output_code: |
|
||||
package cn.iocoder.yudao.module.system.dal.dataobject.permission;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 角色 DO
|
||||
*
|
||||
* @author yudao
|
||||
*/
|
||||
@TableName(value = "system_role", autoResultMap = true)
|
||||
@KeySequence("system_role_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RoleDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 角色名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 角色标识
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 数据范围(指定部门数组)
|
||||
*/
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private Set<Long> dataScopeDeptIds;
|
||||
|
||||
}
|
||||
|
||||
# 忽略租户实体示例
|
||||
tenant_entity:
|
||||
input:
|
||||
module_code: "system"
|
||||
table_name: "system_tenant"
|
||||
table_comment: "租户"
|
||||
tenant_type: "ignore"
|
||||
business_fields:
|
||||
- name: "name"
|
||||
type: "VARCHAR(100)"
|
||||
comment: "租户名称"
|
||||
- name: "contact_name"
|
||||
type: "VARCHAR(50)"
|
||||
comment: "联系人"
|
||||
- name: "status"
|
||||
type: "TINYINT"
|
||||
comment: "状态"
|
||||
enum_ref: "CommonStatusEnum"
|
||||
|
||||
output_code: |
|
||||
package cn.iocoder.yudao.module.system.dal.dataobject.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 租户 DO
|
||||
*
|
||||
* @author yudao
|
||||
*/
|
||||
@TenantIgnore
|
||||
@TableName(value = "system_tenant", autoResultMap = true)
|
||||
@KeySequence("system_tenant_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TenantDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 租户名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 联系人
|
||||
*/
|
||||
private String contactName;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* 枚举 {@link CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 与其他 Skill 的关系
|
||||
# ============================================
|
||||
related_skills:
|
||||
upstream:
|
||||
- skill: "db-designer"
|
||||
relationship: "依赖"
|
||||
description: "依赖数据库表结构设计,从 SQL 解析表信息"
|
||||
path: "skills/design/db-designer.yaml"
|
||||
|
||||
downstream:
|
||||
- skill: "api-designer"
|
||||
relationship: "输出"
|
||||
description: "实体类作为 API 设计的输入"
|
||||
path: "skills/design/api-designer.yaml"
|
||||
|
||||
collaboration:
|
||||
- path: "skills/usage/entity-implementation.md"
|
||||
relationship: "协同"
|
||||
description: "entity-designer 专注实体类设计规范,entity-implementation 提供完整实现流程(含 Mapper、Service 等)"
|
||||
|
||||
# ============================================
|
||||
# 错误处理
|
||||
# ============================================
|
||||
error_handling:
|
||||
errors:
|
||||
- code: "ENT001"
|
||||
message: "表名不符合命名规范"
|
||||
solution: "检查表名前缀和格式,确保包含模块前缀(如 mes_、erp_)"
|
||||
|
||||
- code: "ENT002"
|
||||
message: "缺少必需的表注释"
|
||||
solution: "提供 table_comment 参数"
|
||||
|
||||
- code: "ENT003"
|
||||
message: "字段类型无法映射"
|
||||
solution: "使用默认 String 类型,手动调整"
|
||||
|
||||
- code: "ENT004"
|
||||
message: "JSON 字段缺少 java_type"
|
||||
solution: "为 JSON 字段指定 java_type,如 Set<Long>"
|
||||
|
||||
- code: "ENT005"
|
||||
message: "租户类型选择错误"
|
||||
solution: "multi 使用 TenantBaseDO,single 使用 BaseDO,ignore 使用 @TenantIgnore"
|
||||
|
||||
- code: "ENT006"
|
||||
message: "枚举字段缺少 enum_ref"
|
||||
solution: "为枚举字段提供 enum_ref 参数,在注释中引用枚举类"
|
||||
@@ -1,152 +0,0 @@
|
||||
# Skill 索引文件
|
||||
# 记录所有模块的Skill文档位置和状态
|
||||
|
||||
skills:
|
||||
# 核心模块
|
||||
system:
|
||||
name: "系统管理模块"
|
||||
path: "skills/modules/system/skill-system.yaml"
|
||||
status: "completed"
|
||||
priority: 1
|
||||
lines: 839
|
||||
|
||||
infra:
|
||||
name: "基础设施模块"
|
||||
path: "skills/modules/infra/skill-infra.yaml"
|
||||
status: "completed"
|
||||
priority: 1
|
||||
lines: 700+
|
||||
|
||||
pay:
|
||||
name: "支付模块"
|
||||
path: "skills/modules/pay/skill-pay.yaml"
|
||||
status: "completed"
|
||||
priority: 1
|
||||
lines: 654
|
||||
|
||||
# 业务模块
|
||||
member:
|
||||
name: "会员模块"
|
||||
path: "skills/modules/member/skill-member.yaml"
|
||||
status: "completed"
|
||||
priority: 2
|
||||
lines: 608
|
||||
|
||||
mall:
|
||||
name: "商城模块"
|
||||
path: "skills/modules/mall/skill-mall.yaml"
|
||||
status: "completed"
|
||||
priority: 2
|
||||
lines: 650+
|
||||
|
||||
crm:
|
||||
name: "CRM模块"
|
||||
path: "skills/modules/crm/skill-crm.yaml"
|
||||
status: "completed"
|
||||
priority: 2
|
||||
lines: 850+
|
||||
|
||||
erp:
|
||||
name: "ERP模块"
|
||||
path: "skills/modules/erp/skill-erp.yaml"
|
||||
status: "completed"
|
||||
priority: 3
|
||||
lines: 900+
|
||||
|
||||
# 技术/平台模块
|
||||
bpm:
|
||||
name: "工作流模块"
|
||||
path: "skills/modules/bpm/skill-bpm.yaml"
|
||||
status: "completed"
|
||||
priority: 2
|
||||
lines: 650+
|
||||
|
||||
ai:
|
||||
name: "AI模块"
|
||||
path: "skills/modules/ai/skill-ai.yaml"
|
||||
status: "completed"
|
||||
priority: 3
|
||||
lines: 670+
|
||||
|
||||
iot:
|
||||
name: "物联网模块"
|
||||
path: "skills/modules/iot/skill-iot.yaml"
|
||||
status: "completed"
|
||||
priority: 3
|
||||
lines: 721
|
||||
|
||||
mp:
|
||||
name: "微信公众号模块"
|
||||
path: "skills/modules/mp/skill-mp.yaml"
|
||||
status: "completed"
|
||||
priority: 3
|
||||
lines: 660+
|
||||
|
||||
report:
|
||||
name: "报表模块"
|
||||
path: "skills/modules/report/skill-report.yaml"
|
||||
status: "completed"
|
||||
priority: 3
|
||||
lines: 320+
|
||||
|
||||
# 设计规范
|
||||
design:
|
||||
db:
|
||||
name: "数据库设计"
|
||||
path: "skills/design/db-designer.yaml"
|
||||
status: "completed"
|
||||
description: "数据库表结构设计规范、SQL文件规范、编写规范"
|
||||
lines: 450
|
||||
entity:
|
||||
name: "实体类设计"
|
||||
path: "skills/design/entity-designer.yaml"
|
||||
status: "completed"
|
||||
description: "yudao 实体类(DO)设计规范,包括继承体系、注解规范、命名转换"
|
||||
lines: 520
|
||||
api:
|
||||
name: "API 设计"
|
||||
path: "skills/design/api-designer.yaml"
|
||||
status: "completed"
|
||||
description: "REST API 设计规范、注解、请求响应格式、权限标识"
|
||||
crud:
|
||||
name: "CRUD 代码生成"
|
||||
path: "skills/design/crud-designer.yaml"
|
||||
status: "completed"
|
||||
description: "根据数据库表结构自动生成完整CRUD功能代码"
|
||||
lines: 517
|
||||
|
||||
# 使用样例自动引用说明
|
||||
# 使用样例文档(usage/目录)通过 YAML front matter 声明需要引用的规范文件
|
||||
# AI 读取使用样例时会自动加载相关规范,确保生成的代码符合项目标准
|
||||
usage_auto_reference:
|
||||
description: "使用样例通过 YAML front matter 自动引用设计规范"
|
||||
mechanism: |
|
||||
每个使用样例文档头部包含 references 配置,声明该场景需要引用的规范文件:
|
||||
- design: 设计规范(db-designer, entity-designer, api-designer, crud-designer)
|
||||
- module_guide: 模块引用提示(用户指定模块后加载对应 skill)
|
||||
- patterns: 设计模式引用(factory, strategy, template-method)
|
||||
- templates: 模板文件引用
|
||||
example: |
|
||||
---
|
||||
references:
|
||||
design:
|
||||
- skills/design/db-designer.yaml
|
||||
- skills/design/entity-designer.yaml
|
||||
module_guide:
|
||||
prompt: "请指定目标模块"
|
||||
mapping:
|
||||
mes: skills/modules/mes/skill-mes.yaml
|
||||
---
|
||||
|
||||
# 提取进度
|
||||
progress:
|
||||
total: 12
|
||||
completed: 12
|
||||
in_progress: 0
|
||||
pending: 0
|
||||
|
||||
# 最后更新时间
|
||||
metadata:
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-31"
|
||||
completed_at: "2026-03-18"
|
||||
@@ -1,706 +0,0 @@
|
||||
# Skill 文件 - AI 模块
|
||||
# 用于提取模块知识的标准格式
|
||||
|
||||
skill:
|
||||
id: "skill-ai"
|
||||
name: "AI Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-ai"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
AI 模块是系统的智能化核心,提供大模型集成能力,支持多种 AI 能力:
|
||||
1. 聊天对话:支持多模型切换、流式响应、知识库增强、联网搜索
|
||||
2. 图像生成:支持 OpenAI DALL-E、Stable Diffusion、Midjourney、硅基流动等
|
||||
3. 音乐生成:支持 Suno AI 音乐创作
|
||||
4. 知识库:RAG 检索增强生成,支持文档分片、向量存储、语义检索
|
||||
5. 写作助手:AI 辅助写作
|
||||
6. 思维导图:AI 生成思维导图
|
||||
7. 工作流:可视化 AI 工作流编排
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "策略模式:通过 AiModelFactory 统一创建不同平台的 AI 模型实例"
|
||||
- "工厂模式:AiModelFactoryImpl 负责创建 ChatModel、ImageModel、EmbeddingModel 等"
|
||||
- "单例缓存:使用 Hutool Singleton 缓存 AI 客户端实例,避免重复创建"
|
||||
- "Spring AI 集成:基于 Spring AI 框架,实现多模型统一抽象"
|
||||
- "开放-封闭原则:新增 AI 平台只需扩展 AiPlatformEnum 和工厂方法"
|
||||
- "接口隔离:Service 层接口按领域划分,职责单一"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "聊天会话"
|
||||
type: "聚合根"
|
||||
entities: ["AiChatMessageDO"]
|
||||
description: "用户发起的对话,包含多条消息"
|
||||
- name: "知识库"
|
||||
type: "聚合根"
|
||||
entities: ["AiKnowledgeDocumentDO", "AiKnowledgeSegmentDO"]
|
||||
description: "知识库包含多个文档,文档包含多个分段"
|
||||
- name: "AI 模型配置"
|
||||
type: "聚合根"
|
||||
entities: ["AiApiKeyDO"]
|
||||
description: "模型配置关联 API 密钥"
|
||||
|
||||
value_objects:
|
||||
- name: "AiPlatformEnum"
|
||||
description: "AI 平台枚举,定义支持的 AI 服务商"
|
||||
- name: "AiModelTypeEnum"
|
||||
description: "模型类型枚举:对话、图像、语音、视频、向量、重排序"
|
||||
|
||||
services:
|
||||
- name: "AiChatMessageService"
|
||||
description: "聊天消息服务,处理对话生成"
|
||||
- name: "AiImageService"
|
||||
description: "图像生成服务"
|
||||
- name: "AiKnowledgeService"
|
||||
description: "知识库管理服务"
|
||||
- name: "AiModelService"
|
||||
description: "模型配置管理服务"
|
||||
- name: "AiMusicService"
|
||||
description: "音乐生成服务"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "controller"
|
||||
purpose: "HTTP 接口层"
|
||||
components:
|
||||
- "AiChatMessageController: 聊天消息接口"
|
||||
- "AiChatConversationController: 对话管理接口"
|
||||
- "AiImageController: 图像生成接口"
|
||||
- "AiKnowledgeController: 知识库管理接口"
|
||||
- "AiModelController: 模型配置接口"
|
||||
- "AiMusicController: 音乐生成接口"
|
||||
- "AiWriteController: 写作助手接口"
|
||||
- "AiMindMapController: 思维导图接口"
|
||||
- "AiWorkflowController: 工作流接口"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "chat/: 聊天相关服务"
|
||||
- "image/: 图像生成服务"
|
||||
- "knowledge/: 知识库服务"
|
||||
- "model/: 模型配置服务"
|
||||
- "music/: 音乐生成服务"
|
||||
- "write/: 写作服务"
|
||||
- "mindmap/: 思维导图服务"
|
||||
- "workflow/: 工作流服务"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject/: DO 实体类"
|
||||
- "mysql/: MyBatis Mapper 接口"
|
||||
|
||||
- name: "framework/ai"
|
||||
purpose: "AI 框架集成层"
|
||||
components:
|
||||
- "core/model/: 模型工厂和多平台适配"
|
||||
- "core/webserch/: 联网搜索客户端"
|
||||
- "config/: 自动配置类"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "工厂模式"
|
||||
location: "framework/ai/core/model/AiModelFactory.java"
|
||||
purpose: "统一创建各平台 AI 模型实例,屏蔽创建细节"
|
||||
- pattern: "策略模式"
|
||||
location: "framework/ai/core/model/AiModelFactoryImpl.java"
|
||||
purpose: "根据平台类型选择不同的模型创建策略"
|
||||
- pattern: "单例模式"
|
||||
location: "framework/ai/core/model/AiModelFactoryImpl.java"
|
||||
purpose: "使用 Hutool Singleton 缓存 AI 客户端,避免重复创建连接"
|
||||
- pattern: "模板方法"
|
||||
location: "service/chat/AiChatMessageServiceImpl.java"
|
||||
purpose: "聊天消息处理的统一流程"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: []
|
||||
# 暂无对外暴露的 RPC API
|
||||
consumers:
|
||||
- module: "system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息"
|
||||
mq: []
|
||||
# 暂未使用消息队列
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有实体继承 BaseDO,包含 id、creator、createTime、updater、updateTime、deleted 字段"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 模型配置 ==========
|
||||
- name: "ai_api_key"
|
||||
comment: "AI API 密钥表"
|
||||
entity: "AiApiKeyDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "名称" }
|
||||
- { name: "api_key", type: "String", comment: "密钥" }
|
||||
- { name: "platform", type: "String", comment: "平台,枚举 AiPlatformEnum" }
|
||||
- { name: "url", type: "String", comment: "API 地址" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
- name: "ai_model"
|
||||
comment: "AI 模型配置表"
|
||||
entity: "AiModelDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "key_id", type: "Long", comment: "API 密钥编号" }
|
||||
- { name: "name", type: "String", comment: "模型名称" }
|
||||
- { name: "model", type: "String", comment: "模型标识" }
|
||||
- { name: "platform", type: "String", comment: "平台" }
|
||||
- { name: "type", type: "Integer", comment: "类型:1对话 2图片 3语音 4视频 5向量 6重排序" }
|
||||
- { name: "sort", type: "Integer", comment: "排序值" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
- { name: "temperature", type: "Double", comment: "温度参数" }
|
||||
- { name: "max_tokens", type: "Integer", comment: "最大 Token 数" }
|
||||
- { name: "max_contexts", type: "Integer", comment: "最大上下文数" }
|
||||
|
||||
- name: "ai_chat_role"
|
||||
comment: "AI 聊天角色表"
|
||||
entity: "AiChatRoleDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "角色名称" }
|
||||
- { name: "avatar", type: "String", comment: "角色头像" }
|
||||
- { name: "category", type: "String", comment: "角色分类" }
|
||||
- { name: "description", type: "String", comment: "角色描述" }
|
||||
- { name: "system_message", type: "String", comment: "角色设定" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "model_id", type: "Long", comment: "模型编号" }
|
||||
- { name: "knowledge_ids", type: "List<Long>", comment: "知识库编号列表" }
|
||||
- { name: "tool_ids", type: "List<Long>", comment: "工具编号列表" }
|
||||
- { name: "mcp_client_names", type: "List<String>", comment: "MCP Client 名字列表" }
|
||||
- { name: "public_status", type: "Boolean", comment: "是否公开" }
|
||||
- { name: "sort", type: "Integer", comment: "排序值" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
- name: "ai_tool"
|
||||
comment: "AI 工具表"
|
||||
entity: "AiToolDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "工具名称(Bean 名字)" }
|
||||
- { name: "description", type: "String", comment: "工具描述" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
# ========== 聊天对话 ==========
|
||||
- name: "ai_chat_conversation"
|
||||
comment: "AI 聊天对话表"
|
||||
entity: "AiChatConversationDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "title", type: "String", comment: "对话标题" }
|
||||
- { name: "pinned", type: "Boolean", comment: "是否置顶" }
|
||||
- { name: "pinned_time", type: "LocalDateTime", comment: "置顶时间" }
|
||||
- { name: "role_id", type: "Long", comment: "角色编号" }
|
||||
- { name: "model_id", type: "Long", comment: "模型编号" }
|
||||
- { name: "model", type: "String", comment: "模型标识" }
|
||||
- { name: "system_message", type: "String", comment: "角色设定" }
|
||||
- { name: "temperature", type: "Double", comment: "温度参数" }
|
||||
- { name: "max_tokens", type: "Integer", comment: "最大 Token 数" }
|
||||
- { name: "max_contexts", type: "Integer", comment: "最大上下文数" }
|
||||
|
||||
- name: "ai_chat_message"
|
||||
comment: "AI 聊天消息表"
|
||||
entity: "AiChatMessageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "conversation_id", type: "Long", comment: "对话编号" }
|
||||
- { name: "reply_id", type: "Long", comment: "回复消息编号" }
|
||||
- { name: "type", type: "String", comment: "消息类型(USER/ASSISTANT/SYSTEM)" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "role_id", type: "Long", comment: "角色编号" }
|
||||
- { name: "model", type: "String", comment: "模型标识" }
|
||||
- { name: "model_id", type: "Long", comment: "模型编号" }
|
||||
- { name: "content", type: "String", comment: "聊天内容" }
|
||||
- { name: "reasoning_content", type: "String", comment: "推理内容" }
|
||||
- { name: "use_context", type: "Boolean", comment: "是否携带上下文" }
|
||||
- { name: "segment_ids", type: "List<Long>", comment: "知识库段落编号数组" }
|
||||
- { name: "web_search_pages", type: "List<WebPage>", comment: "联网搜索网页内容" }
|
||||
- { name: "attachment_urls", type: "List<String>", comment: "附件 URL 数组" }
|
||||
|
||||
# ========== 图像生成 ==========
|
||||
- name: "ai_image"
|
||||
comment: "AI 绘画表"
|
||||
entity: "AiImageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "prompt", type: "String", comment: "提示词" }
|
||||
- { name: "platform", type: "String", comment: "平台" }
|
||||
- { name: "model_id", type: "Long", comment: "模型编号" }
|
||||
- { name: "model", type: "String", comment: "模型标识" }
|
||||
- { name: "width", type: "Integer", comment: "图片宽度" }
|
||||
- { name: "height", type: "Integer", comment: "图片高度" }
|
||||
- { name: "status", type: "Integer", comment: "生成状态" }
|
||||
- { name: "finish_time", type: "LocalDateTime", comment: "完成时间" }
|
||||
- { name: "error_message", type: "String", comment: "错误信息" }
|
||||
- { name: "pic_url", type: "String", comment: "图片地址" }
|
||||
- { name: "public_status", type: "Boolean", comment: "是否公开" }
|
||||
- { name: "options", type: "Map<String,Object>", comment: "绘制参数" }
|
||||
- { name: "buttons", type: "List<Button>", comment: "MJ 按钮" }
|
||||
- { name: "task_id", type: "String", comment: "任务编号" }
|
||||
|
||||
# ========== 知识库 ==========
|
||||
- name: "ai_knowledge"
|
||||
comment: "AI 知识库表"
|
||||
entity: "AiKnowledgeDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "知识库名称" }
|
||||
- { name: "description", type: "String", comment: "知识库描述" }
|
||||
- { name: "embedding_model_id", type: "Long", comment: "向量模型编号" }
|
||||
- { name: "embedding_model", type: "String", comment: "模型标识" }
|
||||
- { name: "top_k", type: "Integer", comment: "TopK" }
|
||||
- { name: "similarity_threshold", type: "Double", comment: "相似度阈值" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
- name: "ai_knowledge_document"
|
||||
comment: "AI 知识库文档表"
|
||||
entity: "AiKnowledgeDocumentDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "knowledge_id", type: "Long", comment: "知识库编号" }
|
||||
- { name: "name", type: "String", comment: "文档名称" }
|
||||
- { name: "url", type: "String", comment: "文件 URL" }
|
||||
- { name: "content", type: "String", comment: "内容" }
|
||||
- { name: "content_length", type: "Integer", comment: "文档长度" }
|
||||
- { name: "tokens", type: "Integer", comment: "Token 数量" }
|
||||
- { name: "segment_max_tokens", type: "Integer", comment: "分片最大 Token 数" }
|
||||
- { name: "retrieval_count", type: "Integer", comment: "召回次数" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
- name: "ai_knowledge_segment"
|
||||
comment: "AI 知识库分段表"
|
||||
entity: "AiKnowledgeSegmentDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "knowledge_id", type: "Long", comment: "知识库编号" }
|
||||
- { name: "document_id", type: "Long", comment: "文档编号" }
|
||||
- { name: "content", type: "String", comment: "切片内容" }
|
||||
- { name: "content_length", type: "Integer", comment: "内容长度" }
|
||||
- { name: "vector_id", type: "String", comment: "向量库编号" }
|
||||
- { name: "tokens", type: "Integer", comment: "Token 数量" }
|
||||
- { name: "retrieval_count", type: "Integer", comment: "召回次数" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
# ========== 音乐生成 ==========
|
||||
- name: "ai_music"
|
||||
comment: "AI 音乐表"
|
||||
entity: "AiMusicDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "title", type: "String", comment: "音乐名称" }
|
||||
- { name: "lyric", type: "String", comment: "歌词" }
|
||||
- { name: "image_url", type: "String", comment: "图片地址" }
|
||||
- { name: "audio_url", type: "String", comment: "音频地址" }
|
||||
- { name: "video_url", type: "String", comment: "视频地址" }
|
||||
- { name: "status", type: "Integer", comment: "音乐状态" }
|
||||
- { name: "generate_mode", type: "Integer", comment: "生成模式" }
|
||||
- { name: "description", type: "String", comment: "描述词" }
|
||||
- { name: "platform", type: "String", comment: "平台" }
|
||||
- { name: "model", type: "String", comment: "模型" }
|
||||
- { name: "tags", type: "List<String>", comment: "音乐风格标签" }
|
||||
- { name: "duration", type: "Double", comment: "音乐时长" }
|
||||
- { name: "public_status", type: "Boolean", comment: "是否公开" }
|
||||
- { name: "task_id", type: "String", comment: "任务编号" }
|
||||
- { name: "error_message", type: "String", comment: "错误信息" }
|
||||
|
||||
# ========== 写作 ==========
|
||||
- name: "ai_write"
|
||||
comment: "AI 写作表"
|
||||
entity: "AiWriteDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "type", type: "Integer", comment: "写作类型" }
|
||||
- { name: "platform", type: "String", comment: "平台" }
|
||||
- { name: "model_id", type: "Long", comment: "模型编号" }
|
||||
- { name: "model", type: "String", comment: "模型" }
|
||||
- { name: "prompt", type: "String", comment: "生成内容提示" }
|
||||
- { name: "generated_content", type: "String", comment: "生成的内容" }
|
||||
- { name: "original_content", type: "String", comment: "原文" }
|
||||
- { name: "length", type: "Integer", comment: "长度提示词" }
|
||||
- { name: "format", type: "Integer", comment: "格式提示词" }
|
||||
- { name: "tone", type: "Integer", comment: "语气提示词" }
|
||||
- { name: "language", type: "Integer", comment: "语言提示词" }
|
||||
- { name: "error_message", type: "String", comment: "错误信息" }
|
||||
|
||||
# ========== 思维导图 ==========
|
||||
- name: "ai_mind_map"
|
||||
comment: "AI 思维导图表"
|
||||
entity: "AiMindMapDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "platform", type: "String", comment: "平台" }
|
||||
- { name: "model_id", type: "Long", comment: "模型编号" }
|
||||
- { name: "model", type: "String", comment: "模型" }
|
||||
- { name: "prompt", type: "String", comment: "生成内容提示" }
|
||||
- { name: "generated_content", type: "String", comment: "生成的内容" }
|
||||
- { name: "error_message", type: "String", comment: "错误信息" }
|
||||
|
||||
# ========== 工作流 ==========
|
||||
- name: "ai_workflow"
|
||||
comment: "AI 工作流表"
|
||||
entity: "AiWorkflowDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "工作流名称" }
|
||||
- { name: "code", type: "String", comment: "工作流标识" }
|
||||
- { name: "graph", type: "String", comment: "工作流模型 JSON 数据" }
|
||||
- { name: "remark", type: "String", comment: "备注" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "ai_chat_conversation"
|
||||
to: "ai_chat_message"
|
||||
type: "1:N"
|
||||
foreign_key: "conversation_id"
|
||||
- from: "ai_knowledge"
|
||||
to: "ai_knowledge_document"
|
||||
type: "1:N"
|
||||
foreign_key: "knowledge_id"
|
||||
- from: "ai_knowledge_document"
|
||||
to: "ai_knowledge_segment"
|
||||
type: "1:N"
|
||||
foreign_key: "document_id"
|
||||
- from: "ai_model"
|
||||
to: "ai_api_key"
|
||||
type: "N:1"
|
||||
foreign_key: "key_id"
|
||||
- from: "ai_chat_role"
|
||||
to: "ai_model"
|
||||
type: "N:1"
|
||||
foreign_key: "model_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - XXX')"
|
||||
- "@RestController"
|
||||
- "@RequestMapping('/ai/xxx')"
|
||||
- "@Operation(summary = 'xxx')"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 聊天消息")
|
||||
@RestController
|
||||
@RequestMapping("/ai/chat/message")
|
||||
public class AiChatMessageController {
|
||||
|
||||
@Resource
|
||||
private AiChatMessageService chatMessageService;
|
||||
|
||||
@Operation(summary = "发送消息(流式)")
|
||||
@PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(
|
||||
@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
|
||||
return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "定义接口 + Impl 实现类,接口用于解耦"
|
||||
impl_pattern: "@Service 注解,使用 @Resource 注入依赖"
|
||||
example: |
|
||||
public interface AiChatMessageService {
|
||||
// 同步发送消息
|
||||
AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId);
|
||||
// 流式发送消息
|
||||
Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(
|
||||
AiChatMessageSendReqVO sendReqVO, Long userId);
|
||||
}
|
||||
|
||||
@Service
|
||||
public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||
@Resource
|
||||
private AiModelService modelService;
|
||||
|
||||
@Override
|
||||
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(
|
||||
AiChatMessageSendReqVO sendReqVO, Long userId) {
|
||||
// 1. 获取对话和模型配置
|
||||
// 2. 构建消息上下文
|
||||
// 3. 调用 ChatModel 流式生成
|
||||
// 4. 保存消息记录
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: "继承 BaseMapperX,使用 LambdaQueryWrapper 查询"
|
||||
example: |
|
||||
@Mapper
|
||||
public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> {
|
||||
|
||||
default List<AiChatMessageDO> selectListByConversationId(Long conversationId) {
|
||||
return selectList(new LambdaQueryWrapper<AiChatMessageDO>()
|
||||
.eq(AiChatMessageDO::getConversationId, conversationId)
|
||||
.orderByAsc(AiChatMessageDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# AI 模型调用规范
|
||||
ai_model_usage:
|
||||
chat_model: |
|
||||
// 获取 ChatModel
|
||||
ChatModel chatModel = modelService.getChatModel(modelId);
|
||||
|
||||
// 同步调用
|
||||
ChatResponse response = chatModel.call(new Prompt(messages));
|
||||
|
||||
// 流式调用
|
||||
Flux<ChatResponse> stream = chatModel.stream(new Prompt(messages));
|
||||
|
||||
image_model: |
|
||||
// 获取 ImageModel
|
||||
ImageModel imageModel = modelService.getImageModel(modelId);
|
||||
|
||||
// 生成图片
|
||||
ImageResponse response = imageModel.call(new ImagePrompt(prompt, options));
|
||||
|
||||
embedding_model: |
|
||||
// 获取 EmbeddingModel
|
||||
EmbeddingModel embeddingModel = modelService.getOrCreateVectorStore(modelId, metadataFields);
|
||||
|
||||
// 生成向量
|
||||
EmbeddingResponse response = embeddingModel.embedForResponse(texts);
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-040-XXX-XXX"
|
||||
examples:
|
||||
- code: "1_040_000_000"
|
||||
message: "API 密钥不存在"
|
||||
- code: "1_040_001_000"
|
||||
message: "模型不存在"
|
||||
- code: "1_040_003_000"
|
||||
message: "对话不存在"
|
||||
- code: "1_040_004_001"
|
||||
message: "对话生成异常"
|
||||
- code: "1_040_009_000"
|
||||
message: "知识库不存在"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "CommonResult.error(ErrorCode)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增 AI 平台提供商
|
||||
new_platform:
|
||||
title: "新增 AI 平台提供商"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "在 AiPlatformEnum 中添加新平台枚举值"
|
||||
example: |
|
||||
MY_PLATFORM("MyPlatform", "我的平台"),
|
||||
|
||||
- step: 2
|
||||
action: "在 AiModelFactoryImpl 中添加模型创建方法"
|
||||
example: |
|
||||
// 在 getOrCreateChatModel 方法的 switch 中添加
|
||||
case MY_PLATFORM:
|
||||
return buildMyPlatformChatModel(apiKey, url);
|
||||
|
||||
// 新增构建方法
|
||||
private static ChatModel buildMyPlatformChatModel(String apiKey, String url) {
|
||||
// 参考 Spring AI 对应平台的 AutoConfiguration 实现
|
||||
MyPlatformApi api = MyPlatformApi.builder()
|
||||
.apiKey(apiKey)
|
||||
.baseUrl(url)
|
||||
.build();
|
||||
return MyPlatformChatModel.builder()
|
||||
.myPlatformApi(api)
|
||||
.toolCallingManager(getToolCallingManager())
|
||||
.build();
|
||||
}
|
||||
|
||||
- step: 3
|
||||
action: "如需自定义模型类,在 framework/ai/core/model/ 下创建"
|
||||
example: |
|
||||
// 参考 BaiChuanChatModel、GeminiChatModel 等
|
||||
|
||||
- step: 4
|
||||
action: "在 YudaoAiProperties 中添加平台配置属性"
|
||||
example: |
|
||||
@Data
|
||||
public static class MyPlatform {
|
||||
private String apiKey;
|
||||
private String baseUrl;
|
||||
}
|
||||
|
||||
- step: 5
|
||||
action: "在 AiAutoConfiguration 中添加自动配置"
|
||||
example: |
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "yudao.ai.my-platform", name = "api-key")
|
||||
public ChatModel myPlatformChatModel(YudaoAiProperties properties) {
|
||||
return buildMyPlatformChatModel(properties.getMyPlatform());
|
||||
}
|
||||
|
||||
# 新增 AI 能力类型
|
||||
new_capability:
|
||||
title: "新增 AI 能力类型"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "在 AiModelTypeEnum 中添加新类型"
|
||||
example: |
|
||||
MY_CAPABILITY(7, "新能力"),
|
||||
|
||||
- step: 2
|
||||
action: "创建对应的 DO 实体类"
|
||||
example: |
|
||||
@TableName("ai_my_capability")
|
||||
@Data
|
||||
public class AiMyCapabilityDO extends BaseDO {
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private String prompt;
|
||||
private String result;
|
||||
// ...
|
||||
}
|
||||
|
||||
- step: 3
|
||||
action: "创建 Controller、Service、Mapper"
|
||||
example: |
|
||||
// Controller
|
||||
@Tag(name = "管理后台 - 新能力")
|
||||
@RestController
|
||||
@RequestMapping("/ai/my-capability")
|
||||
public class AiMyCapabilityController { ... }
|
||||
|
||||
// Service
|
||||
public interface AiMyCapabilityService { ... }
|
||||
|
||||
// Mapper
|
||||
public interface AiMyCapabilityMapper extends BaseMapperX<AiMyCapabilityDO> { ... }
|
||||
|
||||
- step: 4
|
||||
action: "在 AiModelFactory 中添加对应的模型获取方法"
|
||||
example: |
|
||||
MyCapabilityModel getOrCreateMyCapabilityModel(AiPlatformEnum platform,
|
||||
String apiKey, String url);
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "模型实例缓存:使用 Singleton.get() 缓存,避免重复创建连接"
|
||||
- "流式响应优先:聊天推荐使用流式接口,用户体验更好"
|
||||
- "错误处理:捕获 AI 调用异常,记录 errorMessage 字段"
|
||||
- "向量存储选择:生产环境推荐 Milvus、Qdrant,测试可用 SimpleVectorStore"
|
||||
- "知识库分片:根据文档特点选择分片策略(语义分片、Markdown QA 分片)"
|
||||
- "多模态支持:ChatMessage 支持携带 attachmentUrls 实现多模态对话"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "spring-ai-core"
|
||||
version: "1.0.x"
|
||||
purpose: "Spring AI 核心框架,提供统一的 AI 模型抽象"
|
||||
- name: "spring-ai-openai"
|
||||
version: "1.0.x"
|
||||
purpose: "OpenAI 模型集成"
|
||||
- name: "spring-ai-anthropic"
|
||||
version: "1.0.x"
|
||||
purpose: "Anthropic Claude 模型集成"
|
||||
- name: "spring-ai-ollama"
|
||||
version: "1.0.x"
|
||||
purpose: "Ollama 本地模型集成"
|
||||
- name: "spring-ai-zhipuai"
|
||||
version: "1.0.x"
|
||||
purpose: "智谱 AI 模型集成"
|
||||
- name: "spring-ai-deepseek"
|
||||
version: "1.0.x"
|
||||
purpose: "DeepSeek 模型集成"
|
||||
- name: "spring-ai-minimax"
|
||||
version: "1.0.x"
|
||||
purpose: "MiniMax 模型集成"
|
||||
- name: "spring-ai-milvus"
|
||||
version: "1.0.x"
|
||||
purpose: "Milvus 向量数据库集成"
|
||||
- name: "spring-ai-qdrant"
|
||||
version: "1.0.x"
|
||||
purpose: "Qdrant 向量数据库集成"
|
||||
- name: "spring-ai-redis"
|
||||
version: "1.0.x"
|
||||
purpose: "Redis 向量存储集成"
|
||||
- name: "hutool"
|
||||
version: "5.x"
|
||||
purpose: "Singleton 缓存、工具类"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java"
|
||||
purpose: "AI 模型工厂接口,定义统一的模型创建方法"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java"
|
||||
purpose: "AI 模型工厂实现,包含各平台模型创建逻辑"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java"
|
||||
purpose: "AI 平台枚举,定义支持的 20+ AI 服务商"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java"
|
||||
purpose: "AI 模型类型枚举"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java"
|
||||
purpose: "模型配置服务,管理 API Key、模型配置"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java"
|
||||
purpose: "聊天消息服务,核心业务逻辑"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java"
|
||||
purpose: "知识库服务"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java"
|
||||
purpose: "聊天消息 Controller,提供 HTTP 接口"
|
||||
- path: "yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java"
|
||||
purpose: "AI 自动配置类"
|
||||
@@ -1,719 +0,0 @@
|
||||
# Skill 文件 - BPM 工作流模块
|
||||
# 用于提取模块知识的标准格式
|
||||
|
||||
skill:
|
||||
id: "skill-bpm"
|
||||
name: "BPM 工作流 Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-bpm"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
BPM (Business Process Management) 模块是工作流引擎核心模块,基于 Flowable 6 实现完整的业务流程管理功能。
|
||||
主要解决企业内部审批流程、业务流程自动化等问题。
|
||||
|
||||
核心功能包括:
|
||||
1. 流程定义管理 - 支持 BPMN 2.0 标准设计器和仿钉钉/飞书的 Simple 设计器
|
||||
2. 流程表单管理 - 动态表单配置,支持与流程绑定
|
||||
3. 流程实例管理 - 发起、取消、查询流程实例
|
||||
4. 任务审批管理 - 待办、已办、审批通过/拒绝、退回、委派、转办、加签/减签
|
||||
5. 流程抄送 - 支持流程抄送功能
|
||||
6. 流程监听器 - 支持执行监听器和任务监听器扩展
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "领域驱动设计 (DDD): 按流程定义(Definition)、流程实例(Instance)、任务(Task)划分限界上下文"
|
||||
- "策略模式: 审批人策略(BpmTaskCandidateStrategy)使用策略模式,支持多种审批人分配方式"
|
||||
- "事件驱动: 流程状态变更通过 Spring ApplicationEvent 发布事件,实现解耦"
|
||||
- "模板方法模式: BpmTaskCandidateStrategy 接口定义审批人计算模板"
|
||||
- "适配器模式: 封装 Flowable API,隔离底层工作流引擎实现"
|
||||
- "监听器模式: 通过 Flowable Listener 扩展流程行为"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "流程定义 (ProcessDefinition)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "BpmFormDO - 流程表单"
|
||||
- "BpmCategoryDO - 流程分类"
|
||||
- "BpmUserGroupDO - 审批用户组"
|
||||
- "BpmProcessDefinitionInfoDO - 流程定义扩展信息"
|
||||
- "BpmProcessListenerDO - 流程监听器"
|
||||
- "BpmProcessExpressionDO - 流程表达式"
|
||||
description: "流程定义是流程的模板,包含流程结构、表单配置、审批规则等元数据"
|
||||
|
||||
- name: "流程实例 (ProcessInstance)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "BpmProcessInstanceCopyDO - 流程抄送记录"
|
||||
description: "流程实例是流程定义的一次执行,由 Flowable 原生表管理,扩展信息存储在 DO 中"
|
||||
|
||||
- name: "流程任务 (Task)"
|
||||
type: "实体"
|
||||
entities: []
|
||||
description: "流程任务是流程实例中的审批节点,由 Flowable 原生表管理"
|
||||
|
||||
value_objects:
|
||||
- "BpmProcessInstanceCreateReqDTO - 流程实例创建请求"
|
||||
- "BpmModelMetaInfoVO - 流程模型元信息"
|
||||
- "BpmSimpleModelNodeVO - Simple 设计器节点定义"
|
||||
|
||||
services:
|
||||
- "BpmModelService - 流程模型管理服务"
|
||||
- "BpmProcessDefinitionService - 流程定义管理服务"
|
||||
- "BpmProcessInstanceService - 流程实例管理服务"
|
||||
- "BpmTaskService - 流程任务管理服务"
|
||||
- "BpmFormService - 表单管理服务"
|
||||
- "BpmMessageService - 流程消息服务"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口,供其他模块调用"
|
||||
components:
|
||||
- "BpmProcessInstanceApi - 流程实例创建接口"
|
||||
- "BpmProcessTaskApi - 流程任务操作接口"
|
||||
- "BpmProcessInstanceStatusEvent - 流程状态变更事件"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口,供前端调用"
|
||||
components:
|
||||
- "BpmModelController - 流程模型管理"
|
||||
- "BpmProcessDefinitionController - 流程定义管理"
|
||||
- "BpmProcessInstanceController - 流程实例管理"
|
||||
- "BpmTaskController - 流程任务管理"
|
||||
- "BpmFormController - 表单管理"
|
||||
- "BpmCategoryController - 分类管理"
|
||||
- "BpmUserGroupController - 用户组管理"
|
||||
- "BpmOALeaveController - 请假申请示例"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层,封装核心业务"
|
||||
components:
|
||||
- "definition/ - 流程定义相关服务"
|
||||
- "task/ - 流程任务相关服务"
|
||||
- "message/ - 消息通知服务"
|
||||
- "oa/ - OA 业务示例(请假)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层,与数据库交互"
|
||||
components:
|
||||
- "dataobject/definition/ - 定义相关 DO"
|
||||
- "dataobject/task/ - 任务相关 DO"
|
||||
- "dataobject/oa/ - OA 相关 DO"
|
||||
- "mysql/ - MyBatis Mapper"
|
||||
- "redis/ - Redis 缓存"
|
||||
|
||||
- name: "framework/flowable"
|
||||
purpose: "Flowable 框架集成层"
|
||||
components:
|
||||
- "config/BpmFlowableConfiguration - Flowable 配置"
|
||||
- "core/candidate/ - 审批人策略实现"
|
||||
- "core/listener/ - 流程监听器"
|
||||
- "core/behavior/ - 自定义行为"
|
||||
- "core/util/ - 工具类"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "策略模式 (Strategy Pattern)"
|
||||
location: "framework/flowable/core/candidate/BpmTaskCandidateStrategy.java"
|
||||
purpose: "审批人分配策略,支持角色、部门、用户、表达式等多种分配方式"
|
||||
|
||||
- pattern: "事件驱动 (Event-Driven)"
|
||||
location: "api/event/BpmProcessInstanceStatusEvent.java"
|
||||
purpose: "流程状态变更事件,通知其他模块流程结果"
|
||||
|
||||
- pattern: "监听器模式 (Listener Pattern)"
|
||||
location: "framework/flowable/core/listener/BpmTaskEventListener.java"
|
||||
purpose: "监听 Flowable 任务事件,执行自定义逻辑"
|
||||
|
||||
- pattern: "工厂模式 (Factory Pattern)"
|
||||
location: "framework/flowable/core/behavior/BpmActivityBehaviorFactory.java"
|
||||
purpose: "创建自定义 ActivityBehavior,扩展 Flowable 行为"
|
||||
|
||||
- pattern: "模板方法模式 (Template Method)"
|
||||
location: "service/task/trigger/BpmTrigger.java"
|
||||
purpose: "流程触发器抽象,支持 HTTP 请求、表单操作等触发类型"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "BpmProcessInstanceApi"
|
||||
method: "createProcessInstance"
|
||||
purpose: "供其他模块发起流程实例"
|
||||
- name: "BpmProcessTaskApi"
|
||||
method: "triggerTask"
|
||||
purpose: "触发流程任务执行"
|
||||
- name: "BpmProcessInstanceStatusEvent"
|
||||
purpose: "流程状态变更事件,供其他模块监听"
|
||||
|
||||
consumers:
|
||||
- module: "system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息"
|
||||
- module: "system"
|
||||
api: "DeptApi"
|
||||
purpose: "获取部门信息"
|
||||
- module: "system"
|
||||
api: "PermissionApi"
|
||||
purpose: "获取用户角色、权限"
|
||||
|
||||
mq: []
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有 DO 继承 BaseDO,包含 creator、createTime、updater、updateTime、deleted 字段"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
- name: "bpm_category"
|
||||
comment: "流程分类表"
|
||||
entity: "BpmCategoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "分类编号" }
|
||||
- { name: "name", type: "String", comment: "分类名" }
|
||||
- { name: "code", type: "String", comment: "分类标志" }
|
||||
- { name: "description", type: "String", comment: "分类描述" }
|
||||
- { name: "status", type: "Integer", comment: "分类状态" }
|
||||
- { name: "sort", type: "Integer", comment: "分类排序" }
|
||||
|
||||
- name: "bpm_form"
|
||||
comment: "流程表单表"
|
||||
entity: "BpmFormDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "表单名" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
- { name: "conf", type: "String", comment: "表单配置 JSON" }
|
||||
- { name: "fields", type: "List<String>", comment: "表单项数组" }
|
||||
- { name: "remark", type: "String", comment: "备注" }
|
||||
|
||||
- name: "bpm_user_group"
|
||||
comment: "审批用户组表"
|
||||
entity: "BpmUserGroupDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "组名" }
|
||||
- { name: "description", type: "String", comment: "描述" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
- { name: "user_ids", type: "Set<Long>", comment: "成员用户编号数组" }
|
||||
|
||||
- name: "bpm_process_definition_info"
|
||||
comment: "流程定义扩展信息表"
|
||||
entity: "BpmProcessDefinitionInfoDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "process_definition_id", type: "String", comment: "流程定义编号" }
|
||||
- { name: "model_id", type: "String", comment: "流程模型编号" }
|
||||
- { name: "model_type", type: "Integer", comment: "模型类型(10:BPMN, 20:Simple)" }
|
||||
- { name: "category", type: "String", comment: "流程分类编码" }
|
||||
- { name: "form_type", type: "Integer", comment: "表单类型" }
|
||||
- { name: "form_id", type: "Long", comment: "动态表单编号" }
|
||||
- { name: "form_conf", type: "String", comment: "表单配置" }
|
||||
- { name: "form_fields", type: "List<String>", comment: "表单字段" }
|
||||
- { name: "start_user_ids", type: "List<Long>", comment: "可发起用户" }
|
||||
- { name: "manager_user_ids", type: "List<Long>", comment: "可管理用户" }
|
||||
- { name: "simple_model", type: "String", comment: "Simple设计器模型数据" }
|
||||
|
||||
- name: "bpm_process_listener"
|
||||
comment: "流程监听器表"
|
||||
entity: "BpmProcessListenerDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "name", type: "String", comment: "监听器名字" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
- { name: "type", type: "String", comment: "监听类型(execution/task)" }
|
||||
- { name: "event", type: "String", comment: "监听事件" }
|
||||
- { name: "value_type", type: "String", comment: "值类型(class/delegateExpression/expression)" }
|
||||
- { name: "value", type: "String", comment: "值" }
|
||||
|
||||
- name: "bpm_process_expression"
|
||||
comment: "流程表达式表"
|
||||
entity: "BpmProcessExpressionDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "name", type: "String", comment: "表达式名字" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
- { name: "expression", type: "String", comment: "表达式" }
|
||||
|
||||
- name: "bpm_process_instance_copy"
|
||||
comment: "流程抄送表"
|
||||
entity: "BpmProcessInstanceCopyDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "编号" }
|
||||
- { name: "start_user_id", type: "Long", comment: "发起人ID" }
|
||||
- { name: "process_instance_id", type: "String", comment: "流程实例编号" }
|
||||
- { name: "process_definition_id", type: "String", comment: "流程定义编号" }
|
||||
- { name: "activity_id", type: "String", comment: "流程活动编号" }
|
||||
- { name: "task_id", type: "String", comment: "任务编号" }
|
||||
- { name: "user_id", type: "Long", comment: "被抄送用户编号" }
|
||||
- { name: "reason", type: "String", comment: "抄送意见" }
|
||||
|
||||
- name: "bpm_oa_leave"
|
||||
comment: "OA请假申请表(示例业务表)"
|
||||
entity: "BpmOALeaveDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "请假表单主键" }
|
||||
- { name: "user_id", type: "Long", comment: "申请人用户编号" }
|
||||
- { name: "type", type: "String", comment: "请假类型" }
|
||||
- { name: "reason", type: "String", comment: "原因" }
|
||||
- { name: "start_time", type: "LocalDateTime", comment: "开始时间" }
|
||||
- { name: "end_time", type: "LocalDateTime", comment: "结束时间" }
|
||||
- { name: "day", type: "Long", comment: "请假天数" }
|
||||
- { name: "status", type: "Integer", comment: "审批结果" }
|
||||
- { name: "process_instance_id", type: "String", comment: "流程编号" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "bpm_process_definition_info"
|
||||
to: "bpm_category"
|
||||
type: "N:1"
|
||||
foreign_key: "category -> code"
|
||||
- from: "bpm_process_definition_info"
|
||||
to: "bpm_form"
|
||||
type: "N:1"
|
||||
foreign_key: "form_id -> id"
|
||||
- from: "bpm_process_instance_copy"
|
||||
to: "Flowable ProcessInstance"
|
||||
type: "N:1"
|
||||
foreign_key: "process_instance_id -> id"
|
||||
- from: "bpm_oa_leave"
|
||||
to: "Flowable ProcessInstance"
|
||||
type: "1:1"
|
||||
foreign_key: "process_instance_id -> id"
|
||||
|
||||
# Flowable 原生表说明
|
||||
flowable_tables:
|
||||
- name: "ACT_RE_DEPLOYMENT"
|
||||
comment: "部署信息表"
|
||||
- name: "ACT_RE_PROCDEF"
|
||||
comment: "流程定义表"
|
||||
- name: "ACT_RE_MODEL"
|
||||
comment: "流程模型表"
|
||||
- name: "ACT_RU_EXECUTION"
|
||||
comment: "运行时执行实例表"
|
||||
- name: "ACT_RU_TASK"
|
||||
comment: "运行时任务表"
|
||||
- name: "ACT_HI_PROCINST"
|
||||
comment: "历史流程实例表"
|
||||
- name: "ACT_HI_TASKINST"
|
||||
comment: "历史任务实例表"
|
||||
- name: "ACT_HI_VARINST"
|
||||
comment: "历史变量表"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - XXX')"
|
||||
- "@RestController"
|
||||
- "@RequestMapping('/bpm/xxx')"
|
||||
- "@Validated"
|
||||
- "@PreAuthorize('@ss.hasPermission(xxx)')"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 流程任务实例")
|
||||
@RestController
|
||||
@RequestMapping("/bpm/task")
|
||||
@Validated
|
||||
public class BpmTaskController {
|
||||
|
||||
@Resource
|
||||
private BpmTaskService taskService;
|
||||
|
||||
@PutMapping("/approve")
|
||||
@Operation(summary = "通过任务")
|
||||
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
|
||||
public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
|
||||
taskService.approveTask(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: |
|
||||
接口定义在 service/ 目录下,命名规则:Bpm{Entity}Service
|
||||
|
||||
核心服务接口:
|
||||
- BpmModelService: 流程模型管理
|
||||
- BpmProcessDefinitionService: 流程定义管理
|
||||
- BpmProcessInstanceService: 流程实例管理
|
||||
- BpmTaskService: 流程任务管理
|
||||
|
||||
方法分类:
|
||||
1. Query 查询相关方法 - 查询操作
|
||||
2. Update 写入相关方法 - 修改操作
|
||||
3. Event 事件相关方法 - 事件处理
|
||||
|
||||
impl_pattern: |
|
||||
实现类放在同目录下,命名规则:Bpm{Entity}ServiceImpl
|
||||
使用 @Service 和 @Validated 注解
|
||||
|
||||
注意事项:
|
||||
1. 循环依赖使用 @Lazy 注解
|
||||
2. 事务操作使用 @Transactional
|
||||
3. 数据权限使用 @DataPermission
|
||||
|
||||
example: |
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
|
||||
|
||||
@Resource
|
||||
private RuntimeService runtimeService; // Flowable 运行时服务
|
||||
@Resource
|
||||
private HistoryService historyService; // Flowable 历史服务
|
||||
|
||||
@Override
|
||||
public String createProcessInstance(Long userId, BpmProcessInstanceCreateReqDTO createReqDTO) {
|
||||
// 1. 获取流程定义
|
||||
ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
|
||||
createReqDTO.getProcessDefinitionKey());
|
||||
// 2. 构建流程实例
|
||||
ProcessInstanceBuilder builder = runtimeService.createProcessInstanceBuilder()
|
||||
.processDefinitionKey(createReqDTO.getProcessDefinitionKey())
|
||||
.businessKey(createReqDTO.getBusinessKey())
|
||||
.variables(createReqDTO.getVariables());
|
||||
// 3. 启动流程实例
|
||||
return builder.start().getId();
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
Mapper 继承 BaseMapperX,提供基础 CRUD 操作
|
||||
使用 @Mapper 注解
|
||||
|
||||
特殊查询:
|
||||
- 使用 MyBatis-Plus 的 LambdaQueryWrapper
|
||||
- 复杂查询使用自定义 XML 或 @Select 注解
|
||||
|
||||
example: |
|
||||
@Mapper
|
||||
public interface BpmFormMapper extends BaseMapperX<BpmFormDO> {
|
||||
|
||||
default PageResult<BpmFormDO> selectPage(BpmFormPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<BpmFormDO>()
|
||||
.likeIfPresent(BpmFormDO::getName, reqVO.getName())
|
||||
.orderByDesc(BpmFormDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-009-XXX-XXX"
|
||||
examples:
|
||||
- code: "1_009_002_000"
|
||||
message: "已经存在流程标识为【{}】的流程"
|
||||
- code: "1_009_003_002"
|
||||
message: "流程定义不存在"
|
||||
- code: "1_009_004_000"
|
||||
message: "流程实例不存在"
|
||||
- code: "1_009_005_001"
|
||||
message: "操作失败,原因:该任务的审批人不是你"
|
||||
- code: "1_009_005_002"
|
||||
message: "流程任务不存在"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE, params...)"
|
||||
|
||||
# 流程发起代码示例
|
||||
process_start_example: |
|
||||
// 方式一:通过 API 接口发起流程(推荐)
|
||||
@Resource
|
||||
private BpmProcessInstanceApi processInstanceApi;
|
||||
|
||||
public void submitLeave(LeaveCreateReqVO createReqVO) {
|
||||
// 1. 创建业务数据
|
||||
BpmOALeaveDO leave = new BpmOALeaveDO();
|
||||
// ... 设置属性
|
||||
leaveMapper.insert(leave);
|
||||
|
||||
// 2. 发起流程
|
||||
BpmProcessInstanceCreateReqDTO reqDTO = new BpmProcessInstanceCreateReqDTO();
|
||||
reqDTO.setProcessDefinitionKey("oa_leave"); // 流程定义 Key
|
||||
reqDTO.setBusinessKey(leave.getId().toString()); // 业务主键
|
||||
reqDTO.setVariables(BeanUtil.beanToMap(createReqVO)); // 流程变量
|
||||
String processInstanceId = processInstanceApi.createProcessInstance(userId, reqDTO);
|
||||
|
||||
// 3. 关联流程实例
|
||||
leave.setProcessInstanceId(processInstanceId);
|
||||
leaveMapper.updateById(leave);
|
||||
}
|
||||
|
||||
// 方式二:通过 Controller 接口发起流程(前端调用)
|
||||
@PostMapping("/create")
|
||||
public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
|
||||
return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
|
||||
}
|
||||
|
||||
# 任务审批代码示例
|
||||
task_approve_example: |
|
||||
// 审批通过
|
||||
@PutMapping("/approve")
|
||||
public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
|
||||
taskService.approveTask(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// 审批拒绝
|
||||
@PutMapping("/reject")
|
||||
public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
|
||||
taskService.rejectTask(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// 任务退回
|
||||
@PutMapping("/return")
|
||||
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
|
||||
taskService.returnTask(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// 任务委派
|
||||
@PutMapping("/delegate")
|
||||
public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {
|
||||
taskService.delegateTask(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增审批类型
|
||||
new_approval_type:
|
||||
title: "新增审批类型(如报销、采购等)"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建业务 DO 实体"
|
||||
description: |
|
||||
在 dal/dataobject/ 下创建业务实体类,如 BpmExpenseDO
|
||||
需要包含 processInstanceId 字段关联流程实例
|
||||
|
||||
- step: 2
|
||||
action: "创建业务表"
|
||||
description: |
|
||||
创建对应数据库表,包含业务字段和 process_instance_id 字段
|
||||
|
||||
- step: 3
|
||||
action: "创建业务服务"
|
||||
description: |
|
||||
创建 Service 接口和实现类
|
||||
实现业务逻辑,调用 BpmProcessInstanceApi 发起流程
|
||||
|
||||
- step: 4
|
||||
action: "实现流程状态监听"
|
||||
description: |
|
||||
实现 BpmProcessInstanceStatusEventListener
|
||||
监听流程状态变更,更新业务表状态
|
||||
|
||||
示例:
|
||||
@Component
|
||||
public class BpmExpenseStatusListener extends BpmProcessInstanceStatusEventListener {
|
||||
@Override
|
||||
protected void onEvent(BpmProcessInstanceStatusEvent event) {
|
||||
if ("expense".equals(event.getProcessDefinitionKey())) {
|
||||
// 更新报销单状态
|
||||
expenseMapper.updateStatus(event.getBusinessKey(), event.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- step: 5
|
||||
action: "配置流程定义"
|
||||
description: |
|
||||
在管理后台配置流程定义
|
||||
1. 创建流程分类
|
||||
2. 创建流程表单
|
||||
3. 设计流程图(BPMN 或 Simple)
|
||||
4. 配置审批人策略
|
||||
|
||||
# 自定义审批人策略
|
||||
new_candidate_strategy:
|
||||
title: "自定义审批人策略"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "添加策略枚举"
|
||||
description: |
|
||||
在 BpmTaskCandidateStrategyEnum 中添加新的策略类型
|
||||
|
||||
- step: 2
|
||||
action: "实现策略接口"
|
||||
description: |
|
||||
实现 BpmTaskCandidateStrategy 接口
|
||||
|
||||
示例:
|
||||
@Component
|
||||
public class BpmTaskCandidateCustomStrategy implements BpmTaskCandidateStrategy {
|
||||
|
||||
@Override
|
||||
public BpmTaskCandidateStrategyEnum getStrategy() {
|
||||
return BpmTaskCandidateStrategyEnum.CUSTOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParam(String param) {
|
||||
// 校验参数
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> calculateUsersByTask(DelegateExecution execution, String param) {
|
||||
// 计算审批人
|
||||
return calculateUsers(param);
|
||||
}
|
||||
}
|
||||
|
||||
- step: 3
|
||||
action: "注册策略 Bean"
|
||||
description: |
|
||||
使用 @Component 注解,Spring 自动注册到策略工厂
|
||||
|
||||
# 自定义流程监听器
|
||||
new_process_listener:
|
||||
title: "自定义流程监听器"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建监听器类"
|
||||
description: |
|
||||
实现 ExecutionListener 或 TaskListener 接口
|
||||
|
||||
示例(执行监听器):
|
||||
@Component
|
||||
public class CustomExecutionListener implements ExecutionListener {
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
// 监听逻辑
|
||||
}
|
||||
}
|
||||
|
||||
示例(任务监听器):
|
||||
@Component
|
||||
public class CustomTaskListener implements TaskListener {
|
||||
@Override
|
||||
public void notify(DelegateTask delegateTask) {
|
||||
// 监听逻辑
|
||||
}
|
||||
}
|
||||
|
||||
- step: 2
|
||||
action: "注册监听器"
|
||||
description: |
|
||||
方式一:通过数据库配置(推荐)
|
||||
在 bpm_process_listener 表中添加监听器配置
|
||||
|
||||
方式二:在 BPMN XML 中配置
|
||||
<extensionElements>
|
||||
<flowable:executionListener event="start" delegateExpression="${customExecutionListener}"/>
|
||||
</extensionElements>
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "流程设计原则:流程定义和业务数据分离,通过 businessKey 关联"
|
||||
- "审批人策略:优先使用已有的审批人策略,如角色、部门负责人、用户组等"
|
||||
- "流程变量:使用流程变量传递表单数据,避免过度依赖业务查询"
|
||||
- "事件监听:通过监听流程状态变更事件更新业务状态,实现解耦"
|
||||
- "Simple 设计器:对于简单审批流程,推荐使用 Simple 设计器,降低学习成本"
|
||||
- "流程版本:同一流程定义可以部署多个版本,已运行的实例使用旧版本"
|
||||
- "权限控制:通过 managerUserIds 配置流程管理员,实现流程管理权限分离"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息,用于审批人计算和流程发起人信息"
|
||||
- module: "yudao-module-system"
|
||||
api: "DeptApi"
|
||||
purpose: "获取部门信息,用于部门负责人审批策略"
|
||||
- module: "yudao-module-system"
|
||||
api: "PermissionApi"
|
||||
purpose: "获取用户角色,用于角色审批策略"
|
||||
- module: "yudao-module-system"
|
||||
api: "PostApi"
|
||||
purpose: "获取岗位信息,用于岗位审批策略"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "flowable-spring-boot-starter-process"
|
||||
version: "6.x"
|
||||
purpose: "Flowable 工作流引擎核心"
|
||||
- name: "flowable-spring-boot-starter-actuator"
|
||||
version: "6.x"
|
||||
purpose: "Flowable 监控端点"
|
||||
- name: "yudao-spring-boot-starter-mybatis"
|
||||
version: "${revision}"
|
||||
purpose: "MyBatis-Plus 数据访问"
|
||||
- name: "yudao-spring-boot-starter-biz-tenant"
|
||||
version: "${revision}"
|
||||
purpose: "多租户支持"
|
||||
- name: "yudao-spring-boot-starter-biz-data-permission"
|
||||
version: "${revision}"
|
||||
purpose: "数据权限控制"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java"
|
||||
purpose: "流程实例服务接口,定义流程实例的核心操作"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java"
|
||||
purpose: "流程任务服务接口,定义任务审批的核心操作"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java"
|
||||
purpose: "流程模型服务接口,定义流程模型管理操作"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java"
|
||||
purpose: "流程实例 API 接口,供其他模块调用发起流程"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/api/event/BpmProcessInstanceStatusEvent.java"
|
||||
purpose: "流程状态变更事件,实现模块间解耦"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java"
|
||||
purpose: "审批人策略接口,定义审批人计算的标准"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java"
|
||||
purpose: "审批人策略枚举,列出所有支持的审批人分配方式"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量,定义 BPM 模块所有错误码"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmModelTypeEnum.java"
|
||||
purpose: "流程模型类型枚举,区分 BPMN 和 Simple 设计器"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java"
|
||||
purpose: "流程实例状态枚举,定义流程的所有状态"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java"
|
||||
purpose: "任务管理 Controller,提供任务审批相关 API"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java"
|
||||
purpose: "流程实例 Controller,提供流程实例管理 API"
|
||||
- path: "yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java"
|
||||
purpose: "请假流程状态监听示例,展示如何监听流程状态变更"
|
||||
- path: "yudao-module-bpm/pom.xml"
|
||||
purpose: "Maven 依赖配置,包含 Flowable 等核心依赖"
|
||||
@@ -1,861 +0,0 @@
|
||||
# Skill 文档 - yudao-module-crm 模块
|
||||
# 客户关系管理模块完整技术文档
|
||||
|
||||
skill:
|
||||
id: "skill-crm"
|
||||
name: "CRM 客户关系管理 Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-crm"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
CRM(Customer Relationship Management)模块是客户关系管理系统的核心,解决企业销售过程中的客户管理、商机跟进、合同签订、回款管理等全流程业务问题。
|
||||
|
||||
核心业务场景:
|
||||
1. 线索管理:潜在客户信息的录入与转化
|
||||
2. 客户管理:客户信息维护、公海池机制、客户分配与转移
|
||||
3. 联系人管理:客户关键联系人信息管理
|
||||
4. 商机管理:销售机会的全生命周期管理(销售漏斗)
|
||||
5. 合同管理:合同签订与审批流程
|
||||
6. 回款管理:回款计划与实际回款跟踪
|
||||
7. 产品管理:产品信息与分类管理
|
||||
8. 数据统计:销售漏斗、业绩排名等统计分析
|
||||
|
||||
在整个系统中的定位:
|
||||
- 作为独立的业务模块,提供完整的 CRM 业务功能
|
||||
- 依赖 System 模块(用户、部门)和 BPM 模块(审批流程)
|
||||
- 不对外暴露 API,通过 Controller 层提供 HTTP 接口
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- name: "领域驱动设计(DDD)"
|
||||
description: "以客户为聚合根,商机、联系人、合同等为关联实体,形成清晰的领域模型边界"
|
||||
- name: "数据权限控制"
|
||||
description: "基于 CrmPermissionDO 实现细粒度的数据权限控制,支持负责人、只读、读写三级权限"
|
||||
- name: "分层架构"
|
||||
description: "严格的 Controller -> Service -> DAL 三层架构,职责清晰"
|
||||
- name: "销售漏斗模型"
|
||||
description: "商机状态组(CrmBusinessStatusTypeDO)+ 商机状态(CrmBusinessStatusDO)实现可配置的销售漏斗"
|
||||
- name: "公海池机制"
|
||||
description: "客户公海池配置(CrmCustomerPoolConfigDO)实现客户自动回收与分配机制"
|
||||
- name: "AOP 权限校验"
|
||||
description: "通过 @CrmPermission 注解实现声明式的数据权限校验"
|
||||
- name: "操作日志记录"
|
||||
description: "通过 @LogRecord 注解记录关键业务操作日志"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "客户(Customer)"
|
||||
type: "聚合根"
|
||||
description: "CRM 系统的核心聚合根,关联商机、联系人、合同、回款等所有业务对象"
|
||||
entities:
|
||||
- "CrmBusinessDO(商机)"
|
||||
- "CrmContactDO(联系人)"
|
||||
- "CrmContractDO(合同)"
|
||||
- "CrmReceivableDO(回款)"
|
||||
- "CrmReceivablePlanDO(回款计划)"
|
||||
- "CrmFollowUpRecordDO(跟进记录)"
|
||||
|
||||
- name: "线索(Clue)"
|
||||
type: "聚合根"
|
||||
description: "潜在客户信息,可转化为正式客户"
|
||||
entities: []
|
||||
|
||||
- name: "产品(Product)"
|
||||
type: "聚合根"
|
||||
description: "产品信息管理,被商机和合同引用"
|
||||
entities:
|
||||
- "CrmProductCategoryDO(产品分类)"
|
||||
|
||||
value_objects:
|
||||
- name: "CrmPermissionDO"
|
||||
description: "数据权限值对象,关联业务类型与权限级别"
|
||||
- name: "CrmBusinessStatusDO"
|
||||
description: "商机状态值对象,属于商机状态组"
|
||||
- name: "CrmBusinessStatusTypeDO"
|
||||
description: "商机状态组配置"
|
||||
- name: "CrmCustomerPoolConfigDO"
|
||||
description: "客户公海池配置"
|
||||
- name: "CrmCustomerLimitConfigDO"
|
||||
description: "客户限制配置(拥有上限、锁定上限)"
|
||||
|
||||
services:
|
||||
- name: "CrmCustomerService"
|
||||
description: "客户管理领域服务,处理客户创建、转移、公海等核心业务"
|
||||
- name: "CrmBusinessService"
|
||||
description: "商机管理领域服务,处理商机创建、状态变更等业务"
|
||||
- name: "CrmPermissionService"
|
||||
description: "数据权限领域服务,处理权限创建、转移、校验等业务"
|
||||
- name: "CrmContractService"
|
||||
description: "合同管理领域服务,处理合同签订、审批等业务"
|
||||
- name: "CrmReceivableService"
|
||||
description: "回款管理领域服务,处理回款录入、审批等业务"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口(当前模块未对外暴露API)"
|
||||
components:
|
||||
- "package-info.java"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口层,处理请求响应"
|
||||
components:
|
||||
- "CrmCustomerController - 客户管理接口"
|
||||
- "CrmClueController - 线索管理接口"
|
||||
- "CrmContactController - 联系人管理接口"
|
||||
- "CrmBusinessController - 商机管理接口"
|
||||
- "CrmBusinessStatusController - 商机状态管理接口"
|
||||
- "CrmContractController - 合同管理接口"
|
||||
- "CrmContractConfigController - 合同配置接口"
|
||||
- "CrmReceivableController - 回款管理接口"
|
||||
- "CrmReceivablePlanController - 回款计划接口"
|
||||
- "CrmProductController - 产品管理接口"
|
||||
- "CrmProductCategoryController - 产品分类接口"
|
||||
- "CrmPermissionController - 数据权限接口"
|
||||
- "CrmFollowUpRecordController - 跟进记录接口"
|
||||
- "CrmOperateLogController - 操作日志接口"
|
||||
- "CrmCustomerLimitConfigController - 客户限制配置接口"
|
||||
- "CrmCustomerPoolConfigController - 公海池配置接口"
|
||||
- "CrmStatisticsCustomerController - 客户统计接口"
|
||||
- "CrmStatisticsFunnelController - 漏斗统计接口"
|
||||
- "CrmStatisticsPerformanceController - 业绩统计接口"
|
||||
- "CrmStatisticsPortraitController - 客户画像统计接口"
|
||||
- "CrmStatisticsRankController - 排行榜统计接口"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层,处理核心业务"
|
||||
components:
|
||||
- "customer/ - 客户相关服务"
|
||||
- "clue/ - 线索相关服务"
|
||||
- "contact/ - 联系人相关服务"
|
||||
- "business/ - 商机相关服务"
|
||||
- "contract/ - 合同相关服务"
|
||||
- "receivable/ - 回款相关服务"
|
||||
- "product/ - 产品相关服务"
|
||||
- "permission/ - 权限相关服务"
|
||||
- "followup/ - 跟进记录服务"
|
||||
- "statistics/ - 统计服务"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层,处理数据库操作"
|
||||
components:
|
||||
- "dataobject/ - DO 实体类"
|
||||
- "mysql/ - MyBatis Mapper 接口"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "AOP 权限校验"
|
||||
location: "CrmPermission 注解 + CrmPermissionAspect"
|
||||
purpose: "通过注解声明数据权限要求,AOP 自动校验"
|
||||
- pattern: "策略模式"
|
||||
location: "CrmBizTypeEnum 业务类型枚举"
|
||||
purpose: "统一处理不同业务类型的权限校验逻辑"
|
||||
- pattern: "模板方法"
|
||||
location: "各 Service 实现类的校验方法"
|
||||
purpose: "统一的业务校验流程"
|
||||
- pattern: "观察者模式"
|
||||
location: "CrmContractStatusListener、CrmReceivableStatusListener"
|
||||
purpose: "监听 BPM 审批状态变更,更新业务状态"
|
||||
- pattern: "Builder 模式"
|
||||
location: "所有 DO 实体类使用 @Builder"
|
||||
purpose: "简化复杂对象的构建"
|
||||
- pattern: "代理模式"
|
||||
location: "CrmCustomerServiceImpl.getSelf()"
|
||||
purpose: "解决 AOP 事务代理问题"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: [] # 当前模块未对外暴露 API
|
||||
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息、校验用户"
|
||||
- module: "yudao-module-system"
|
||||
api: "DeptApi"
|
||||
purpose: "获取部门信息"
|
||||
- module: "yudao-module-bpm"
|
||||
api: "流程审批"
|
||||
purpose: "合同、回款的审批流程"
|
||||
|
||||
mq: [] # 当前模块未使用消息队列
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有 CRM 实体继承 BaseDO,包含 id、creator、create_time、updater、update_time、deleted 字段。注意:CRM 模块未使用 TenantBaseDO,不涉及多租户"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ==================== 客户相关 ====================
|
||||
- name: "crm_customer"
|
||||
comment: "客户表"
|
||||
entity: "CrmCustomerDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "客户名称" }
|
||||
- { name: "follow_up_status", type: "TINYINT", comment: "跟进状态" }
|
||||
- { name: "contact_last_time", type: "DATETIME", comment: "最后跟进时间" }
|
||||
- { name: "contact_last_content", type: "VARCHAR", comment: "最后跟进内容" }
|
||||
- { name: "contact_next_time", type: "DATETIME", comment: "下次联系时间" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "owner_time", type: "DATETIME", comment: "成为负责人时间" }
|
||||
- { name: "lock_status", type: "TINYINT", comment: "锁定状态" }
|
||||
- { name: "deal_status", type: "TINYINT", comment: "成交状态" }
|
||||
- { name: "mobile", type: "VARCHAR", comment: "手机号" }
|
||||
- { name: "telephone", type: "VARCHAR", comment: "电话" }
|
||||
- { name: "qq", type: "VARCHAR", comment: "QQ" }
|
||||
- { name: "wechat", type: "VARCHAR", comment: "微信" }
|
||||
- { name: "email", type: "VARCHAR", comment: "邮箱" }
|
||||
- { name: "area_id", type: "INT", comment: "地区ID" }
|
||||
- { name: "detail_address", type: "VARCHAR", comment: "详细地址" }
|
||||
- { name: "industry_id", type: "INT", comment: "所属行业" }
|
||||
- { name: "level", type: "INT", comment: "客户等级" }
|
||||
- { name: "source", type: "INT", comment: "客户来源" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_owner_user_id", columns: ["owner_user_id"] }
|
||||
- { name: "idx_name", columns: ["name"] }
|
||||
|
||||
- name: "crm_customer_pool_config"
|
||||
comment: "客户公海池配置表"
|
||||
entity: "CrmCustomerPoolConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "enabled", type: "TINYINT", comment: "是否启用客户公海" }
|
||||
- { name: "contact_expire_days", type: "INT", comment: "未跟进放入公海天数" }
|
||||
- { name: "deal_expire_days", type: "INT", comment: "未成交放入公海天数" }
|
||||
- { name: "notify_enabled", type: "TINYINT", comment: "是否开启提前提醒" }
|
||||
- { name: "notify_days", type: "INT", comment: "提前提醒天数" }
|
||||
|
||||
- name: "crm_customer_limit_config"
|
||||
comment: "客户限制配置表"
|
||||
entity: "CrmCustomerLimitConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "type", type: "INT", comment: "限制类型(拥有上限/锁定上限)" }
|
||||
- { name: "max_count", type: "INT", comment: "最大数量" }
|
||||
- { name: "deal_count_enabled", type: "TINYINT", comment: "是否计入成交客户" }
|
||||
- { name: "user_ids", type: "VARCHAR", comment: "适用用户ID列表" }
|
||||
- { name: "dept_ids", type: "VARCHAR", comment: "适用部门ID列表" }
|
||||
|
||||
# ==================== 线索相关 ====================
|
||||
- name: "crm_clue"
|
||||
comment: "线索表"
|
||||
entity: "CrmClueDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "线索名称" }
|
||||
- { name: "follow_up_status", type: "TINYINT", comment: "跟进状态" }
|
||||
- { name: "contact_last_time", type: "DATETIME", comment: "最后跟进时间" }
|
||||
- { name: "contact_last_content", type: "VARCHAR", comment: "最后跟进内容" }
|
||||
- { name: "contact_next_time", type: "DATETIME", comment: "下次联系时间" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "transform_status", type: "TINYINT", comment: "转化状态" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "转化后的客户ID" }
|
||||
- { name: "mobile", type: "VARCHAR", comment: "手机号" }
|
||||
- { name: "telephone", type: "VARCHAR", comment: "电话" }
|
||||
- { name: "qq", type: "VARCHAR", comment: "QQ" }
|
||||
- { name: "wechat", type: "VARCHAR", comment: "微信" }
|
||||
- { name: "email", type: "VARCHAR", comment: "邮箱" }
|
||||
- { name: "area_id", type: "INT", comment: "地区ID" }
|
||||
- { name: "detail_address", type: "VARCHAR", comment: "详细地址" }
|
||||
- { name: "industry_id", type: "INT", comment: "所属行业" }
|
||||
- { name: "level", type: "INT", comment: "客户等级" }
|
||||
- { name: "source", type: "INT", comment: "客户来源" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
# ==================== 联系人相关 ====================
|
||||
- name: "crm_contact"
|
||||
comment: "联系人表"
|
||||
entity: "CrmContactDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "联系人姓名" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "contact_last_time", type: "DATETIME", comment: "最后跟进时间" }
|
||||
- { name: "contact_last_content", type: "VARCHAR", comment: "最后跟进内容" }
|
||||
- { name: "contact_next_time", type: "DATETIME", comment: "下次联系时间" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "mobile", type: "VARCHAR", comment: "手机号" }
|
||||
- { name: "telephone", type: "VARCHAR", comment: "电话" }
|
||||
- { name: "email", type: "VARCHAR", comment: "邮箱" }
|
||||
- { name: "qq", type: "BIGINT", comment: "QQ" }
|
||||
- { name: "wechat", type: "VARCHAR", comment: "微信" }
|
||||
- { name: "area_id", type: "INT", comment: "地区ID" }
|
||||
- { name: "detail_address", type: "VARCHAR", comment: "详细地址" }
|
||||
- { name: "sex", type: "INT", comment: "性别" }
|
||||
- { name: "master", type: "TINYINT", comment: "是否关键决策人" }
|
||||
- { name: "post", type: "VARCHAR", comment: "职位" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "直属上级ID" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
- name: "crm_contact_business"
|
||||
comment: "联系人商机关联表"
|
||||
entity: "CrmContactBusinessDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "contact_id", type: "BIGINT", comment: "联系人ID" }
|
||||
- { name: "business_id", type: "BIGINT", comment: "商机ID" }
|
||||
|
||||
# ==================== 商机相关 ====================
|
||||
- name: "crm_business"
|
||||
comment: "商机表"
|
||||
entity: "CrmBusinessDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "商机名称" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "follow_up_status", type: "TINYINT", comment: "跟进状态" }
|
||||
- { name: "contact_last_time", type: "DATETIME", comment: "最后跟进时间" }
|
||||
- { name: "contact_next_time", type: "DATETIME", comment: "下次联系时间" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "status_type_id", type: "BIGINT", comment: "商机状态组ID" }
|
||||
- { name: "status_id", type: "BIGINT", comment: "商机状态ID" }
|
||||
- { name: "end_status", type: "INT", comment: "结束状态(赢单/输单/无效)" }
|
||||
- { name: "end_remark", type: "VARCHAR", comment: "结束备注" }
|
||||
- { name: "deal_time", type: "DATETIME", comment: "预计成交日期" }
|
||||
- { name: "total_product_price", type: "DECIMAL", comment: "产品总金额" }
|
||||
- { name: "discount_percent", type: "DECIMAL", comment: "整单折扣" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "商机总金额" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
- name: "crm_business_status_type"
|
||||
comment: "商机状态组表(配置表)"
|
||||
entity: "CrmBusinessStatusTypeDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "状态组名称" }
|
||||
- { name: "dept_ids", type: "VARCHAR", comment: "使用的部门ID列表" }
|
||||
|
||||
- name: "crm_business_status"
|
||||
comment: "商机状态表(配置表)"
|
||||
entity: "CrmBusinessStatusDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "type_id", type: "BIGINT", comment: "状态组ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "状态名称" }
|
||||
- { name: "percent", type: "INT", comment: "赢单率百分比" }
|
||||
- { name: "sort", type: "INT", comment: "排序" }
|
||||
|
||||
- name: "crm_business_product"
|
||||
comment: "商机产品关联表"
|
||||
entity: "CrmBusinessProductDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "business_id", type: "BIGINT", comment: "商机ID" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品ID" }
|
||||
- { name: "count", type: "DECIMAL", comment: "数量" }
|
||||
- { name: "price", type: "DECIMAL", comment: "单价" }
|
||||
|
||||
# ==================== 合同相关 ====================
|
||||
- name: "crm_contract"
|
||||
comment: "合同表"
|
||||
entity: "CrmContractDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "合同名称" }
|
||||
- { name: "no", type: "VARCHAR", comment: "合同编号" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "business_id", type: "BIGINT", comment: "商机ID" }
|
||||
- { name: "contact_last_time", type: "DATETIME", comment: "最后跟进时间" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "process_instance_id", type: "VARCHAR", comment: "工作流实例ID" }
|
||||
- { name: "audit_status", type: "INT", comment: "审批状态" }
|
||||
- { name: "order_date", type: "DATETIME", comment: "下单日期" }
|
||||
- { name: "start_time", type: "DATETIME", comment: "开始时间" }
|
||||
- { name: "end_time", type: "DATETIME", comment: "结束时间" }
|
||||
- { name: "total_product_price", type: "DECIMAL", comment: "产品总金额" }
|
||||
- { name: "discount_percent", type: "DECIMAL", comment: "整单折扣" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合同总金额" }
|
||||
- { name: "sign_contact_id", type: "BIGINT", comment: "客户签约人ID" }
|
||||
- { name: "sign_user_id", type: "BIGINT", comment: "公司签约人ID" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
- name: "crm_contract_config"
|
||||
comment: "合同配置表"
|
||||
entity: "CrmContractConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "deposit_enabled", type: "TINYINT", comment: "是否开启押金" }
|
||||
- { name: "deposit_percent", type: "DECIMAL", comment: "押金比例" }
|
||||
|
||||
- name: "crm_contract_product"
|
||||
comment: "合同产品关联表"
|
||||
entity: "CrmContractProductDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "contract_id", type: "BIGINT", comment: "合同ID" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品ID" }
|
||||
- { name: "count", type: "DECIMAL", comment: "数量" }
|
||||
- { name: "price", type: "DECIMAL", comment: "单价" }
|
||||
|
||||
# ==================== 回款相关 ====================
|
||||
- name: "crm_receivable"
|
||||
comment: "回款表"
|
||||
entity: "CrmReceivableDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "no", type: "VARCHAR", comment: "回款编号" }
|
||||
- { name: "plan_id", type: "BIGINT", comment: "回款计划ID" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "contract_id", type: "BIGINT", comment: "合同ID" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "return_time", type: "DATETIME", comment: "回款日期" }
|
||||
- { name: "return_type", type: "INT", comment: "回款方式" }
|
||||
- { name: "price", type: "DECIMAL", comment: "回款金额" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
- { name: "process_instance_id", type: "VARCHAR", comment: "工作流实例ID" }
|
||||
- { name: "audit_status", type: "INT", comment: "审批状态" }
|
||||
|
||||
- name: "crm_receivable_plan"
|
||||
comment: "回款计划表"
|
||||
entity: "CrmReceivablePlanDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "period", type: "INT", comment: "期数" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "contract_id", type: "BIGINT", comment: "合同ID" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
- { name: "return_time", type: "DATETIME", comment: "计划回款日期" }
|
||||
- { name: "return_type", type: "INT", comment: "计划回款类型" }
|
||||
- { name: "price", type: "DECIMAL", comment: "计划回款金额" }
|
||||
- { name: "receivable_id", type: "BIGINT", comment: "实际回款ID" }
|
||||
- { name: "remind_days", type: "INT", comment: "提前提醒天数" }
|
||||
- { name: "remind_time", type: "DATETIME", comment: "提醒日期" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
# ==================== 产品相关 ====================
|
||||
- name: "crm_product"
|
||||
comment: "产品表"
|
||||
entity: "CrmProductDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "产品名称" }
|
||||
- { name: "no", type: "VARCHAR", comment: "产品编码" }
|
||||
- { name: "unit", type: "INT", comment: "单位" }
|
||||
- { name: "price", type: "DECIMAL", comment: "价格" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "category_id", type: "BIGINT", comment: "分类ID" }
|
||||
- { name: "description", type: "VARCHAR", comment: "产品描述" }
|
||||
- { name: "owner_user_id", type: "BIGINT", comment: "负责人用户编号" }
|
||||
|
||||
- name: "crm_product_category"
|
||||
comment: "产品分类表"
|
||||
entity: "CrmProductCategoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "分类名称" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "父分类ID" }
|
||||
- { name: "sort", type: "INT", comment: "排序" }
|
||||
|
||||
# ==================== 权限与跟进 ====================
|
||||
- name: "crm_permission"
|
||||
comment: "数据权限表"
|
||||
entity: "CrmPermissionDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "biz_type", type: "INT", comment: "业务类型" }
|
||||
- { name: "biz_id", type: "BIGINT", comment: "业务ID" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户ID" }
|
||||
- { name: "level", type: "INT", comment: "权限级别" }
|
||||
indexes:
|
||||
- { name: "idx_biz", columns: ["biz_type", "biz_id"] }
|
||||
- { name: "idx_user", columns: ["user_id"] }
|
||||
|
||||
- name: "crm_follow_up_record"
|
||||
comment: "跟进记录表"
|
||||
entity: "CrmFollowUpRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID" }
|
||||
- { name: "biz_type", type: "INT", comment: "业务类型" }
|
||||
- { name: "biz_id", type: "BIGINT", comment: "业务ID" }
|
||||
- { name: "type", type: "INT", comment: "跟进类型" }
|
||||
- { name: "content", type: "VARCHAR", comment: "跟进内容" }
|
||||
- { name: "next_time", type: "DATETIME", comment: "下次联系时间" }
|
||||
- { name: "pic_urls", type: "VARCHAR", comment: "图片URL列表" }
|
||||
- { name: "file_urls", type: "VARCHAR", comment: "附件URL列表" }
|
||||
- { name: "business_ids", type: "VARCHAR", comment: "关联商机ID列表" }
|
||||
- { name: "contact_ids", type: "VARCHAR", comment: "关联联系人ID列表" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "crm_customer"
|
||||
to: "crm_business"
|
||||
type: "1:N"
|
||||
foreign_key: "customer_id"
|
||||
description: "一个客户可以有多个商机"
|
||||
- from: "crm_customer"
|
||||
to: "crm_contact"
|
||||
type: "1:N"
|
||||
foreign_key: "customer_id"
|
||||
description: "一个客户可以有多个联系人"
|
||||
- from: "crm_customer"
|
||||
to: "crm_contract"
|
||||
type: "1:N"
|
||||
foreign_key: "customer_id"
|
||||
description: "一个客户可以签订多个合同"
|
||||
- from: "crm_customer"
|
||||
to: "crm_receivable"
|
||||
type: "1:N"
|
||||
foreign_key: "customer_id"
|
||||
description: "一个客户可以有多个回款"
|
||||
- from: "crm_business"
|
||||
to: "crm_contract"
|
||||
type: "1:N"
|
||||
foreign_key: "business_id"
|
||||
description: "一个商机可以签订多个合同"
|
||||
- from: "crm_contract"
|
||||
to: "crm_receivable"
|
||||
type: "1:N"
|
||||
foreign_key: "contract_id"
|
||||
description: "一个合同可以有多个回款"
|
||||
- from: "crm_contract"
|
||||
to: "crm_receivable_plan"
|
||||
type: "1:N"
|
||||
foreign_key: "contract_id"
|
||||
description: "一个合同可以有多个回款计划"
|
||||
- from: "crm_receivable_plan"
|
||||
to: "crm_receivable"
|
||||
type: "1:1"
|
||||
foreign_key: "receivable_id"
|
||||
description: "一个回款计划对应一个实际回款"
|
||||
- from: "crm_business_status_type"
|
||||
to: "crm_business_status"
|
||||
type: "1:N"
|
||||
foreign_key: "type_id"
|
||||
description: "一个状态组包含多个状态"
|
||||
- from: "crm_product_category"
|
||||
to: "crm_product"
|
||||
type: "1:N"
|
||||
foreign_key: "category_id"
|
||||
description: "一个分类下有多个产品"
|
||||
- from: "crm_clue"
|
||||
to: "crm_customer"
|
||||
type: "N:1"
|
||||
foreign_key: "customer_id"
|
||||
description: "线索转化为客户"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - CRM xxx')"
|
||||
- "@RestController"
|
||||
- "@RequestMapping('/crm/xxx')"
|
||||
- "@Validated"
|
||||
- "@PreAuthorize('@ss.hasPermission('crm:xxx:action')')"
|
||||
- "@Operation(summary = 'xxx')"
|
||||
- "@Parameter(name = 'id', description = '编号', required = true)"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - CRM 客户")
|
||||
@RestController
|
||||
@RequestMapping("/crm/customer")
|
||||
@Validated
|
||||
public class CrmCustomerController {
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:create')")
|
||||
public CommonResult<Long> createCustomer(@Valid @RequestBody CrmCustomerSaveReqVO createReqVO) {
|
||||
return success(customerService.createCustomer(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新客户")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:update')")
|
||||
public CommonResult<Boolean> updateCustomer(@Valid @RequestBody CrmCustomerSaveReqVO updateReqVO) {
|
||||
customerService.updateCustomer(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得客户分页")
|
||||
@PreAuthorize("@ss.hasPermission('crm:customer:query')")
|
||||
public CommonResult<PageResult<CrmCustomerRespVO>> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) {
|
||||
PageResult<CrmCustomerDO> pageResult = customerService.getCustomerPage(pageVO, getLoginUserId());
|
||||
return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "接口定义业务方法,使用 @Valid 注解校验参数"
|
||||
impl_pattern: |
|
||||
- @Service 注解标记服务类
|
||||
- @Validated 启用方法参数校验
|
||||
- @Transactional(rollbackFor = Exception.class) 声明事务
|
||||
- @LogRecord 记录操作日志
|
||||
- @CrmPermission 声明数据权限要求
|
||||
example: |
|
||||
@Service
|
||||
@Validated
|
||||
public class CrmCustomerServiceImpl implements CrmCustomerService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE,
|
||||
bizNo = "{{#customer.id}}", success = CRM_CUSTOMER_CREATE_SUCCESS)
|
||||
public Long createCustomer(CrmCustomerSaveReqVO createReqVO, Long userId) {
|
||||
// 1. 校验拥有客户是否到达上限
|
||||
validateCustomerExceedOwnerLimit(createReqVO.getOwnerUserId(), 1);
|
||||
|
||||
// 2. 插入客户
|
||||
CrmCustomerDO customer = initCustomer(createReqVO, userId);
|
||||
customerMapper.insert(customer);
|
||||
|
||||
// 3. 创建数据权限
|
||||
permissionService.createPermission(new CrmPermissionCreateReqBO()
|
||||
.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
|
||||
.setBizId(customer.getId())
|
||||
.setUserId(userId)
|
||||
.setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
|
||||
|
||||
// 4. 记录操作日志上下文
|
||||
LogRecordContext.putVariable("customer", customer);
|
||||
return customer.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id",
|
||||
level = CrmPermissionLevelEnum.READ)
|
||||
public CrmCustomerDO getCustomer(Long id) {
|
||||
return customerMapper.selectById(id);
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
- 继承 BaseMapperX<T> 获得通用 CRUD 方法
|
||||
- 使用 @Mapper 注解标记
|
||||
- 复杂查询使用 LambdaQueryWrapperX
|
||||
example: |
|
||||
@Mapper
|
||||
public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> {
|
||||
|
||||
default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
|
||||
return selectPage(pageReqVO, new LambdaQueryWrapperX<CrmCustomerDO>()
|
||||
.likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
|
||||
.eqIfPresent(CrmCustomerDO::getOwnerUserId, pageReqVO.getOwnerUserId())
|
||||
.orderByDesc(CrmCustomerDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 数据权限控制
|
||||
permission:
|
||||
annotation_pattern: "@CrmPermission(bizType = CrmBizTypeEnum.XXX, bizId = '#id', level = CrmPermissionLevelEnum.XXX)"
|
||||
levels:
|
||||
- level: "OWNER"
|
||||
code: 1
|
||||
description: "负责人权限,拥有所有操作权限,可以删除、转移"
|
||||
- level: "READ"
|
||||
code: 2
|
||||
description: "只读权限,只能查看数据"
|
||||
- level: "WRITE"
|
||||
code: 3
|
||||
description: "读写权限,可以编辑数据但不能删除"
|
||||
example: |
|
||||
// 查询需要 READ 权限
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.READ)
|
||||
public CrmCustomerDO getCustomer(Long id)
|
||||
|
||||
// 更新需要 WRITE 权限
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
|
||||
public void updateCustomer(CrmCustomerSaveReqVO updateReqVO)
|
||||
|
||||
// 删除需要 OWNER 权限
|
||||
@CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
|
||||
public void deleteCustomer(Long id)
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-020-xxx-xxx"
|
||||
examples:
|
||||
- code: "1_020_006_000"
|
||||
message: "客户不存在"
|
||||
constant: "CUSTOMER_NOT_EXISTS"
|
||||
- code: "1_020_002_000"
|
||||
message: "商机不存在"
|
||||
constant: "BUSINESS_NOT_EXISTS"
|
||||
- code: "1_020_007_001"
|
||||
message: "{}操作失败,原因:没有权限"
|
||||
constant: "CRM_PERMISSION_DENIED"
|
||||
usage: |
|
||||
// 抛出业务异常
|
||||
throw exception(CUSTOMER_NOT_EXISTS);
|
||||
throw exception(CUSTOMER_OWNER_EXISTS, customer.getName());
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "CommonResult.error(errorCode)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增 CRM 业务功能"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建 DO 实体类"
|
||||
description: "在 dal/dataobject 包下创建实体类,继承 BaseDO,添加 @TableName 注解"
|
||||
- step: 2
|
||||
action: "创建 Mapper 接口"
|
||||
description: "在 dal/mysql 包下创建 Mapper 接口,继承 BaseMapperX"
|
||||
- step: 3
|
||||
action: "创建 Service 接口和实现类"
|
||||
description: "在 service 包下创建接口和实现类,添加 @CrmPermission 注解控制数据权限"
|
||||
- step: 4
|
||||
action: "创建 Controller 和 VO"
|
||||
description: "在 controller 包下创建 Controller 和 VO 类,添加权限注解和 Swagger 注解"
|
||||
- step: 5
|
||||
action: "添加业务类型枚举"
|
||||
description: "在 CrmBizTypeEnum 中添加新的业务类型,确保权限系统正确识别"
|
||||
- step: 6
|
||||
action: "添加错误码"
|
||||
description: "在 ErrorCodeConstants 中添加相关错误码"
|
||||
|
||||
# 新增商机阶段示例
|
||||
new_business_stage:
|
||||
title: "自定义商机阶段"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建商机状态组"
|
||||
description: "通过 CrmBusinessStatusService.createBusinessStatusType() 创建新的状态组"
|
||||
- step: 2
|
||||
action: "添加状态阶段"
|
||||
description: "为状态组添加多个状态,设置赢单率和排序"
|
||||
- step: 3
|
||||
action: "关联部门"
|
||||
description: "设置状态组适用的部门列表"
|
||||
- step: 4
|
||||
action: "使用状态组"
|
||||
description: "创建商机时选择对应的状态组"
|
||||
|
||||
# 新增客户类型示例
|
||||
new_customer_type:
|
||||
title: "新增客户类型/行业"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "修改字典数据"
|
||||
description: "在 CRM_CUSTOMER_INDUSTRY 字典中添加新的行业类型"
|
||||
- step: 2
|
||||
action: "前端适配"
|
||||
description: "前端页面会自动读取字典数据显示下拉选项"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "数据权限校验"
|
||||
description: "所有涉及数据操作的方法都必须添加 @CrmPermission 注解,确保数据安全"
|
||||
- practice: "操作日志记录"
|
||||
description: "关键业务操作(创建、更新、删除、转移)使用 @LogRecord 记录操作日志"
|
||||
- practice: "事务管理"
|
||||
description: "涉及多表操作的方法添加 @Transactional 注解,确保数据一致性"
|
||||
- practice: "参数校验"
|
||||
description: "VO 类使用 @Valid 注解进行参数校验,Service 层使用 @Validated"
|
||||
- practice: "公海池机制"
|
||||
description: "客户放入公海时,需要同时更新联系人的负责人为空"
|
||||
- practice: "客户转移"
|
||||
description: "客户转移时,可选择同时转移联系人、商机、合同"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息、校验用户存在性"
|
||||
- module: "yudao-module-system"
|
||||
api: "DeptApi"
|
||||
purpose: "获取部门信息,用于商机状态组关联"
|
||||
- module: "yudao-module-bpm"
|
||||
api: "流程审批"
|
||||
purpose: "合同、回款的审批流程管理"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
version: "3.x"
|
||||
purpose: "ORM 框架,提供 CRUD 操作"
|
||||
- name: "Spring Security"
|
||||
version: "5.x"
|
||||
purpose: "权限校验"
|
||||
- name: "Swagger/OpenAPI"
|
||||
version: "3.x"
|
||||
purpose: "API 文档生成"
|
||||
- name: "EasyExcel"
|
||||
version: "3.x"
|
||||
purpose: "Excel 导入导出"
|
||||
- name: "LogRecord"
|
||||
version: "mzt-biz-log"
|
||||
purpose: "操作日志记录"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java"
|
||||
purpose: "客户实体类,CRM 核心聚合根"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/business/CrmBusinessDO.java"
|
||||
purpose: "商机实体类,销售漏斗核心"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/permission/CrmPermissionDO.java"
|
||||
purpose: "数据权限实体,权限控制核心"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java"
|
||||
purpose: "客户服务接口,定义客户核心业务方法"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java"
|
||||
purpose: "客户服务实现,包含公海池、转移等复杂业务逻辑"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionService.java"
|
||||
purpose: "数据权限服务接口"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java"
|
||||
purpose: "客户控制器,HTTP 接口入口"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java"
|
||||
purpose: "业务类型枚举,权限控制的基础"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionLevelEnum.java"
|
||||
purpose: "权限级别枚举"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义"
|
||||
- path: "yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/annotations/CrmPermission.java"
|
||||
purpose: "数据权限注解定义"
|
||||
@@ -1,968 +0,0 @@
|
||||
# ERP 模块 Skill 文档
|
||||
# 企业资源计划模块,涵盖采购、销售、库存、财务等核心业务
|
||||
|
||||
skill:
|
||||
id: "skill-erp"
|
||||
name: "ERP Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-erp"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
ERP(企业资源计划)模块是企业管理核心系统,实现进销存一体化管理。
|
||||
主要业务场景:
|
||||
1. 采购管理:采购订单 -> 采购入库 -> 采购退货 -> 付款
|
||||
2. 销售管理:销售订单 -> 销售出库 -> 销售退货 -> 收款
|
||||
3. 库存管理:其它入库/出库、库存调拨、库存盘点、库存查询
|
||||
4. 财务管理:结算账户、付款单、收款单
|
||||
5. 基础资料:产品、产品分类、产品单位、供应商、客户、仓库
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "单据状态机:所有业务单据采用 PROCESS(未审核) -> APPROVE(已审核) 状态流转"
|
||||
- "主子表设计:订单类单据采用 Order + OrderItem 主子表结构,支持明细项增删改差量更新"
|
||||
- "库存增量更新:库存变动采用 updateCountIncrement 增量更新,避免并发问题"
|
||||
- "单据号生成器:基于 Redis 的分布式单据号生成,格式:前缀 + yyyyMMdd + 6位序号"
|
||||
- "审核权限控制:已审核单据不可修改/删除,需反审核后才能操作"
|
||||
- "业务关联校验:单据之间存在关联关系,删除/反审核时需检查关联单据"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
# 采购聚合
|
||||
- name: "采购订单 (ErpPurchaseOrderDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "采购订单项 (ErpPurchaseOrderItemDO)"
|
||||
- "供应商 (ErpSupplierDO)"
|
||||
description: "采购域核心聚合,管理采购订单及其明细项"
|
||||
|
||||
- name: "采购入库 (ErpPurchaseInDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "采购入库项 (ErpPurchaseInItemDO)"
|
||||
description: "关联采购订单,记录采购入库明细"
|
||||
|
||||
- name: "采购退货 (ErpPurchaseReturnDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "采购退货项 (ErpPurchaseReturnItemDO)"
|
||||
description: "关联采购订单,记录采购退货明细"
|
||||
|
||||
# 销售聚合
|
||||
- name: "销售订单 (ErpSaleOrderDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "销售订单项 (ErpSaleOrderItemDO)"
|
||||
- "客户 (ErpCustomerDO)"
|
||||
description: "销售域核心聚合,管理销售订单及其明细项"
|
||||
|
||||
- name: "销售出库 (ErpSaleOutDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "销售出库项 (ErpSaleOutItemDO)"
|
||||
description: "关联销售订单,记录销售出库明细"
|
||||
|
||||
- name: "销售退货 (ErpSaleReturnDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "销售退货项 (ErpSaleReturnItemDO)"
|
||||
description: "关联销售订单,记录销售退货明细"
|
||||
|
||||
# 库存聚合
|
||||
- name: "库存 (ErpStockDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "仓库 (ErpWarehouseDO)"
|
||||
- "产品 (ErpProductDO)"
|
||||
description: "库存域核心,记录产品在各仓库的实时库存"
|
||||
|
||||
- name: "库存记录 (ErpStockRecordDO)"
|
||||
type: "实体"
|
||||
description: "记录所有库存变动明细,支持追溯"
|
||||
|
||||
# 财务聚合
|
||||
- name: "付款单 (ErpFinancePaymentDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "付款单项 (ErpFinancePaymentItemDO)"
|
||||
- "结算账户 (ErpAccountDO)"
|
||||
description: "财务域付款管理"
|
||||
|
||||
- name: "收款单 (ErpFinanceReceiptDO)"
|
||||
type: "聚合根"
|
||||
entities:
|
||||
- "收款单项 (ErpFinanceReceiptItemDO)"
|
||||
- "结算账户 (ErpAccountDO)"
|
||||
description: "财务域收款管理"
|
||||
|
||||
value_objects:
|
||||
- name: "ErpStockRecordCreateReqBO"
|
||||
type: "值对象"
|
||||
description: "库存记录创建请求BO,封装库存变动所需的业务信息"
|
||||
|
||||
services:
|
||||
- name: "ErpPurchaseOrderService"
|
||||
type: "领域服务"
|
||||
description: "采购订单领域服务,管理采购订单生命周期"
|
||||
- name: "ErpSaleOrderService"
|
||||
type: "领域服务"
|
||||
description: "销售订单领域服务,管理销售订单生命周期"
|
||||
- name: "ErpStockService"
|
||||
type: "领域服务"
|
||||
description: "库存领域服务,管理库存变动"
|
||||
- name: "ErpStockRecordService"
|
||||
type: "领域服务"
|
||||
description: "库存记录领域服务,记录库存变动明细"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口层,处理请求响应、参数校验、权限控制"
|
||||
components:
|
||||
- "ErpPurchaseOrderController - 采购订单接口"
|
||||
- "ErpPurchaseInController - 采购入库接口"
|
||||
- "ErpPurchaseReturnController - 采购退货接口"
|
||||
- "ErpSaleOrderController - 销售订单接口"
|
||||
- "ErpSaleOutController - 销售出库接口"
|
||||
- "ErpSaleReturnController - 销售退货接口"
|
||||
- "ErpStockController - 库存查询接口"
|
||||
- "ErpStockInController - 其它入库接口"
|
||||
- "ErpStockOutController - 其它出库接口"
|
||||
- "ErpStockMoveController - 库存调拨接口"
|
||||
- "ErpStockCheckController - 库存盘点接口"
|
||||
- "ErpFinancePaymentController - 付款单接口"
|
||||
- "ErpFinanceReceiptController - 收款单接口"
|
||||
- "ErpProductController - 产品接口"
|
||||
- "ErpSupplierController - 供应商接口"
|
||||
- "ErpCustomerController - 客户接口"
|
||||
- "ErpWarehouseController - 仓库接口"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层,实现领域服务、事务管理、业务规则校验"
|
||||
components:
|
||||
- "purchase/ - 采购域服务(订单、入库、退货、供应商)"
|
||||
- "sale/ - 销售域服务(订单、出库、退货、客户)"
|
||||
- "stock/ - 库存域服务(库存、仓库、入库、出库、调拨、盘点、记录)"
|
||||
- "finance/ - 财务域服务(账户、付款、收款)"
|
||||
- "product/ - 产品域服务(产品、分类、单位)"
|
||||
- "statistics/ - 统计服务(采购统计、销售统计)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层,封装数据库操作、Redis操作"
|
||||
components:
|
||||
- "dataobject/ - DO实体类(按业务域分包)"
|
||||
- "mysql/ - MyBatis Mapper接口"
|
||||
- "redis/ - Redis操作(单据号生成)"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "状态机模式 (State Machine)"
|
||||
location: "cn.iocoder.yudao.module.erp.enums.ErpAuditStatus"
|
||||
purpose: "定义单据审核状态 PROCESS(10-未审核) -> APPROVE(20-已审核),控制单据生命周期"
|
||||
|
||||
- pattern: "主子表模式 (Master-Detail)"
|
||||
location: "purchase/ErpPurchaseOrderDO + ErpPurchaseOrderItemDO"
|
||||
purpose: "订单类单据采用主子表设计,支持明细项差量更新"
|
||||
|
||||
- pattern: "策略模式 (Strategy)"
|
||||
location: "cn.iocoder.yudao.module.erp.enums.stock.ErpStockRecordBizTypeEnum"
|
||||
purpose: "库存记录业务类型枚举,定义不同业务场景的库存变动类型"
|
||||
|
||||
- pattern: "分布式ID生成"
|
||||
location: "cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO"
|
||||
purpose: "基于Redis的分布式单据号生成,保证唯一性和有序性"
|
||||
|
||||
- pattern: "增量更新模式"
|
||||
location: "ErpStockMapper.updateCountIncrement"
|
||||
purpose: "库存增量更新,使用乐观锁避免并发问题"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: [] # ERP模块不对外暴露API接口
|
||||
consumers:
|
||||
- module: "system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息,用于显示操作人姓名"
|
||||
mq: [] # 暂无消息队列使用
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有DO继承BaseDO,包含id、creator、create_time、updater、update_time、deleted字段"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 产品相关表 ==========
|
||||
- name: "erp_product"
|
||||
comment: "产品表"
|
||||
entity: "ErpProductDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "产品编号" }
|
||||
- { name: "name", type: "VARCHAR", comment: "产品名称" }
|
||||
- { name: "bar_code", type: "VARCHAR", comment: "产品条码" }
|
||||
- { name: "category_id", type: "BIGINT", comment: "产品分类ID" }
|
||||
- { name: "unit_id", type: "BIGINT", comment: "单位ID" }
|
||||
- { name: "status", type: "INT", comment: "状态(0正常 1停用)" }
|
||||
- { name: "standard", type: "VARCHAR", comment: "规格" }
|
||||
- { name: "purchase_price", type: "DECIMAL", comment: "采购价" }
|
||||
- { name: "sale_price", type: "DECIMAL", comment: "销售价" }
|
||||
- { name: "min_price", type: "DECIMAL", comment: "最低价" }
|
||||
|
||||
- name: "erp_product_category"
|
||||
comment: "产品分类表"
|
||||
entity: "ErpProductCategoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "分类编号" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "父分类ID" }
|
||||
- { name: "name", type: "VARCHAR", comment: "分类名称" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
|
||||
- name: "erp_product_unit"
|
||||
comment: "产品单位表"
|
||||
entity: "ErpProductUnitDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "单位编号" }
|
||||
- { name: "name", type: "VARCHAR", comment: "单位名称" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
|
||||
# ========== 采购相关表 ==========
|
||||
- name: "erp_supplier"
|
||||
comment: "供应商表"
|
||||
entity: "ErpSupplierDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "供应商编号" }
|
||||
- { name: "name", type: "VARCHAR", comment: "供应商名称" }
|
||||
- { name: "contact", type: "VARCHAR", comment: "联系人" }
|
||||
- { name: "mobile", type: "VARCHAR", comment: "手机号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
|
||||
- name: "erp_purchase_order"
|
||||
comment: "采购订单表"
|
||||
entity: "ErpPurchaseOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "采购订单号(CGDD+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态(10未审核 20已审核)" }
|
||||
- { name: "supplier_id", type: "BIGINT", comment: "供应商ID" }
|
||||
- { name: "account_id", type: "BIGINT", comment: "结算账户ID" }
|
||||
- { name: "order_time", type: "DATETIME", comment: "下单时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "最终合计价格" }
|
||||
- { name: "total_product_price", type: "DECIMAL", comment: "合计产品价格" }
|
||||
- { name: "total_tax_price", type: "DECIMAL", comment: "合计税额" }
|
||||
- { name: "discount_percent", type: "DECIMAL", comment: "优惠率(%)" }
|
||||
- { name: "discount_price", type: "DECIMAL", comment: "优惠金额" }
|
||||
- { name: "in_count", type: "DECIMAL", comment: "已入库数量" }
|
||||
- { name: "return_count", type: "DECIMAL", comment: "已退货数量" }
|
||||
|
||||
- name: "erp_purchase_order_item"
|
||||
comment: "采购订单项表"
|
||||
entity: "ErpPurchaseOrderItemDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "采购订单ID" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品ID" }
|
||||
- { name: "product_unit_id", type: "BIGINT", comment: "产品单位ID" }
|
||||
- { name: "count", type: "DECIMAL", comment: "数量" }
|
||||
- { name: "product_price", type: "DECIMAL", comment: "产品单价" }
|
||||
- { name: "tax_percent", type: "DECIMAL", comment: "税率(%)" }
|
||||
- { name: "in_count", type: "DECIMAL", comment: "已入库数量" }
|
||||
- { name: "return_count", type: "DECIMAL", comment: "已退货数量" }
|
||||
|
||||
- name: "erp_purchase_in"
|
||||
comment: "采购入库表"
|
||||
entity: "ErpPurchaseInDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "入库单号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "supplier_id", type: "BIGINT", comment: "供应商ID" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "采购订单ID" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "in_time", type: "DATETIME", comment: "入库时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计价格" }
|
||||
- { name: "payment_price", type: "DECIMAL", comment: "已付款金额" }
|
||||
|
||||
- name: "erp_purchase_return"
|
||||
comment: "采购退货表"
|
||||
entity: "ErpPurchaseReturnDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "退货单号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "supplier_id", type: "BIGINT", comment: "供应商ID" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "采购订单ID" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "return_time", type: "DATETIME", comment: "退货时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计价格" }
|
||||
- { name: "refund_price", type: "DECIMAL", comment: "已退款金额" }
|
||||
|
||||
# ========== 销售相关表 ==========
|
||||
- name: "erp_customer"
|
||||
comment: "客户表"
|
||||
entity: "ErpCustomerDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "客户编号" }
|
||||
- { name: "name", type: "VARCHAR", comment: "客户名称" }
|
||||
- { name: "contact", type: "VARCHAR", comment: "联系人" }
|
||||
- { name: "mobile", type: "VARCHAR", comment: "手机号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
|
||||
- name: "erp_sale_order"
|
||||
comment: "销售订单表"
|
||||
entity: "ErpSaleOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "销售订单号(XSDD+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态(10未审核 20已审核)" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "account_id", type: "BIGINT", comment: "结算账户ID" }
|
||||
- { name: "sale_user_id", type: "BIGINT", comment: "销售员ID" }
|
||||
- { name: "order_time", type: "DATETIME", comment: "下单时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "最终合计价格" }
|
||||
- { name: "out_count", type: "DECIMAL", comment: "已出库数量" }
|
||||
- { name: "return_count", type: "DECIMAL", comment: "已退货数量" }
|
||||
|
||||
- name: "erp_sale_order_item"
|
||||
comment: "销售订单项表"
|
||||
entity: "ErpSaleOrderItemDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "销售订单ID" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品ID" }
|
||||
- { name: "count", type: "DECIMAL", comment: "数量" }
|
||||
- { name: "product_price", type: "DECIMAL", comment: "产品单价" }
|
||||
- { name: "out_count", type: "DECIMAL", comment: "已出库数量" }
|
||||
- { name: "return_count", type: "DECIMAL", comment: "已退货数量" }
|
||||
|
||||
- name: "erp_sale_out"
|
||||
comment: "销售出库表"
|
||||
entity: "ErpSaleOutDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "出库单号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "销售订单ID" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "out_time", type: "DATETIME", comment: "出库时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计价格" }
|
||||
- { name: "receipt_price", type: "DECIMAL", comment: "已收款金额" }
|
||||
|
||||
- name: "erp_sale_return"
|
||||
comment: "销售退货表"
|
||||
entity: "ErpSaleReturnDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "退货单号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "销售订单ID" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "return_time", type: "DATETIME", comment: "退货时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计价格" }
|
||||
- { name: "refund_price", type: "DECIMAL", comment: "已退款金额" }
|
||||
|
||||
# ========== 库存相关表 ==========
|
||||
- name: "erp_warehouse"
|
||||
comment: "仓库表"
|
||||
entity: "ErpWarehouseDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "仓库编号" }
|
||||
- { name: "name", type: "VARCHAR", comment: "仓库名称" }
|
||||
- { name: "address", type: "VARCHAR", comment: "仓库地址" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
|
||||
- name: "erp_stock"
|
||||
comment: "产品库存表"
|
||||
entity: "ErpStockDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品ID" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "count", type: "DECIMAL", comment: "库存数量" }
|
||||
|
||||
- name: "erp_stock_record"
|
||||
comment: "库存明细表"
|
||||
entity: "ErpStockRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品ID" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "count", type: "DECIMAL", comment: "出入库数量(正数入库/负数出库)" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "变动后库存量" }
|
||||
- { name: "biz_type", type: "INT", comment: "业务类型" }
|
||||
- { name: "biz_id", type: "BIGINT", comment: "业务编号" }
|
||||
- { name: "biz_item_id", type: "BIGINT", comment: "业务项编号" }
|
||||
- { name: "biz_no", type: "VARCHAR", comment: "业务单号" }
|
||||
|
||||
- name: "erp_stock_in"
|
||||
comment: "其它入库表"
|
||||
entity: "ErpStockInDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "入库单号(QTRK+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "in_time", type: "DATETIME", comment: "入库时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计价格" }
|
||||
|
||||
- name: "erp_stock_out"
|
||||
comment: "其它出库表"
|
||||
entity: "ErpStockOutDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "出库单号(QCKD+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "out_time", type: "DATETIME", comment: "出库时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计价格" }
|
||||
|
||||
- name: "erp_stock_move"
|
||||
comment: "库存调拨表"
|
||||
entity: "ErpStockMoveDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "调拨单号(QCDB+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "from_warehouse_id", type: "BIGINT", comment: "调出仓库ID" }
|
||||
- { name: "to_warehouse_id", type: "BIGINT", comment: "调入仓库ID" }
|
||||
- { name: "move_time", type: "DATETIME", comment: "调拨时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
|
||||
- name: "erp_stock_check"
|
||||
comment: "库存盘点表"
|
||||
entity: "ErpStockCheckDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "盘点单号(QCPD+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库ID" }
|
||||
- { name: "check_time", type: "DATETIME", comment: "盘点时间" }
|
||||
- { name: "total_count", type: "DECIMAL", comment: "合计数量" }
|
||||
|
||||
# ========== 财务相关表 ==========
|
||||
- name: "erp_account"
|
||||
comment: "结算账户表"
|
||||
entity: "ErpAccountDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "账户编号" }
|
||||
- { name: "name", type: "VARCHAR", comment: "账户名称" }
|
||||
- { name: "no", type: "VARCHAR", comment: "账户编号" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
|
||||
- name: "erp_finance_payment"
|
||||
comment: "付款单表"
|
||||
entity: "ErpFinancePaymentDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "付款单号(FKD+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "supplier_id", type: "BIGINT", comment: "供应商ID" }
|
||||
- { name: "account_id", type: "BIGINT", comment: "付款账户ID" }
|
||||
- { name: "payment_time", type: "DATETIME", comment: "付款时间" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计金额" }
|
||||
- { name: "discount_price", type: "DECIMAL", comment: "优惠金额" }
|
||||
- { name: "payment_price", type: "DECIMAL", comment: "实付金额" }
|
||||
|
||||
- name: "erp_finance_receipt"
|
||||
comment: "收款单表"
|
||||
entity: "ErpFinanceReceiptDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "no", type: "VARCHAR", comment: "收款单号(SKD+yyyyMMdd+6位序号)" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "customer_id", type: "BIGINT", comment: "客户ID" }
|
||||
- { name: "account_id", type: "BIGINT", comment: "收款账户ID" }
|
||||
- { name: "receipt_time", type: "DATETIME", comment: "收款时间" }
|
||||
- { name: "total_price", type: "DECIMAL", comment: "合计金额" }
|
||||
- { name: "discount_price", type: "DECIMAL", comment: "优惠金额" }
|
||||
- { name: "receipt_price", type: "DECIMAL", comment: "实收金额" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
# 产品相关
|
||||
- from: "erp_product"
|
||||
to: "erp_product_category"
|
||||
type: "N:1"
|
||||
foreign_key: "category_id"
|
||||
- from: "erp_product"
|
||||
to: "erp_product_unit"
|
||||
type: "N:1"
|
||||
foreign_key: "unit_id"
|
||||
|
||||
# 采购相关
|
||||
- from: "erp_purchase_order"
|
||||
to: "erp_supplier"
|
||||
type: "N:1"
|
||||
foreign_key: "supplier_id"
|
||||
- from: "erp_purchase_order_item"
|
||||
to: "erp_purchase_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "erp_purchase_in"
|
||||
to: "erp_purchase_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "erp_purchase_return"
|
||||
to: "erp_purchase_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
|
||||
# 销售相关
|
||||
- from: "erp_sale_order"
|
||||
to: "erp_customer"
|
||||
type: "N:1"
|
||||
foreign_key: "customer_id"
|
||||
- from: "erp_sale_order_item"
|
||||
to: "erp_sale_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "erp_sale_out"
|
||||
to: "erp_sale_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "erp_sale_return"
|
||||
to: "erp_sale_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
|
||||
# 库存相关
|
||||
- from: "erp_stock"
|
||||
to: "erp_product"
|
||||
type: "N:1"
|
||||
foreign_key: "product_id"
|
||||
- from: "erp_stock"
|
||||
to: "erp_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
- from: "erp_stock_record"
|
||||
to: "erp_product"
|
||||
type: "N:1"
|
||||
foreign_key: "product_id"
|
||||
- from: "erp_stock_record"
|
||||
to: "erp_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - ERP XXX') - Swagger接口文档标签"
|
||||
- "@RestController - RESTful控制器"
|
||||
- "@RequestMapping('/erp/xxx') - 请求路径前缀"
|
||||
- "@PreAuthorize('@ss.hasPermission('erp:xxx:create')') - 权限控制"
|
||||
- "@Validated - 参数校验"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - ERP 采购订单")
|
||||
@RestController
|
||||
@RequestMapping("/erp/purchase-order")
|
||||
@Validated
|
||||
public class ErpPurchaseOrderController {
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建采购订单")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-order:create')")
|
||||
public CommonResult<Long> createPurchaseOrder(@Valid @RequestBody ErpPurchaseOrderSaveReqVO createReqVO) {
|
||||
return success(purchaseOrderService.createPurchaseOrder(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update-status")
|
||||
@Operation(summary = "更新采购订单的状态")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-order:update-status')")
|
||||
public CommonResult<Boolean> updatePurchaseOrderStatus(@RequestParam("id") Long id,
|
||||
@RequestParam("status") Integer status) {
|
||||
purchaseOrderService.updatePurchaseOrderStatus(id, status);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得采购订单分页")
|
||||
@PreAuthorize("@ss.hasPermission('erp:purchase-order:query')")
|
||||
public CommonResult<PageResult<ErpPurchaseOrderRespVO>> getPurchaseOrderPage(@Valid ErpPurchaseOrderPageReqVO pageReqVO) {
|
||||
PageResult<ErpPurchaseOrderDO> pageResult = purchaseOrderService.getPurchaseOrderPage(pageReqVO);
|
||||
return success(buildPurchaseOrderVOPageResult(pageResult));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: |
|
||||
public interface ErpPurchaseOrderService {
|
||||
// 基础CRUD
|
||||
Long createPurchaseOrder(ErpPurchaseOrderSaveReqVO createReqVO);
|
||||
void updatePurchaseOrder(ErpPurchaseOrderSaveReqVO updateReqVO);
|
||||
void deletePurchaseOrder(List<Long> ids);
|
||||
ErpPurchaseOrderDO getPurchaseOrder(Long id);
|
||||
PageResult<ErpPurchaseOrderDO> getPurchaseOrderPage(ErpPurchaseOrderPageReqVO pageReqVO);
|
||||
|
||||
// 状态更新
|
||||
void updatePurchaseOrderStatus(Long id, Integer status);
|
||||
|
||||
// 关联更新
|
||||
void updatePurchaseOrderInCount(Long id, Map<Long, BigDecimal> inCountMap);
|
||||
void updatePurchaseOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap);
|
||||
|
||||
// 校验方法
|
||||
ErpPurchaseOrderDO validatePurchaseOrder(Long id);
|
||||
|
||||
// 订单项查询
|
||||
List<ErpPurchaseOrderItemDO> getPurchaseOrderItemListByOrderId(Long orderId);
|
||||
}
|
||||
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService {
|
||||
|
||||
@Resource
|
||||
private ErpPurchaseOrderMapper purchaseOrderMapper;
|
||||
@Resource
|
||||
private ErpPurchaseOrderItemMapper purchaseOrderItemMapper;
|
||||
@Resource
|
||||
private ErpNoRedisDAO noRedisDAO;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createPurchaseOrder(ErpPurchaseOrderSaveReqVO createReqVO) {
|
||||
// 1. 校验业务数据
|
||||
List<ErpPurchaseOrderItemDO> items = validatePurchaseOrderItems(createReqVO.getItems());
|
||||
supplierService.validateSupplier(createReqVO.getSupplierId());
|
||||
|
||||
// 2. 生成单据号
|
||||
String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX);
|
||||
|
||||
// 3. 计算价格
|
||||
ErpPurchaseOrderDO order = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class);
|
||||
order.setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus());
|
||||
calculateTotalPrice(order, items);
|
||||
|
||||
// 4. 保存主表和子表
|
||||
purchaseOrderMapper.insert(order);
|
||||
items.forEach(item -> item.setOrderId(order.getId()));
|
||||
purchaseOrderItemMapper.insertBatch(items);
|
||||
|
||||
return order.getId();
|
||||
}
|
||||
}
|
||||
|
||||
example: |
|
||||
// 采购入库审核流程
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePurchaseInStatus(Long id, Integer status) {
|
||||
boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
|
||||
// 1. 校验存在
|
||||
ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id);
|
||||
// 2. 校验状态
|
||||
if (purchaseIn.getStatus().equals(status)) {
|
||||
throw exception(approve ? PURCHASE_IN_APPROVE_FAIL : PURCHASE_IN_PROCESS_FAIL);
|
||||
}
|
||||
|
||||
// 3. 更新库存
|
||||
if (approve) {
|
||||
List<ErpPurchaseInItemDO> items = purchaseInItemMapper.selectListByInId(id);
|
||||
items.forEach(item -> {
|
||||
// 创建库存记录
|
||||
ErpStockRecordCreateReqBO reqBO = new ErpStockRecordCreateReqBO();
|
||||
reqBO.setProductId(item.getProductId());
|
||||
reqBO.setWarehouseId(purchaseIn.getWarehouseId());
|
||||
reqBO.setCount(item.getCount());
|
||||
reqBO.setBizType(ErpStockRecordBizTypeEnum.PURCHASE_IN.getType());
|
||||
reqBO.setBizId(id);
|
||||
reqBO.setBizItemId(item.getId());
|
||||
reqBO.setBizNo(purchaseIn.getNo());
|
||||
// 更新库存
|
||||
stockRecordService.createStockRecord(reqBO);
|
||||
});
|
||||
} else {
|
||||
// 反审核,扣减库存
|
||||
stockRecordService.deleteStockRecord(id, ErpStockRecordBizTypeEnum.PURCHASE_IN_CANCEL.getType());
|
||||
}
|
||||
|
||||
// 4. 更新状态
|
||||
purchaseInMapper.updateById(new ErpPurchaseInDO().setId(id).setStatus(status));
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
@Mapper
|
||||
public interface ErpPurchaseOrderMapper extends BaseMapperX<ErpPurchaseOrderDO> {
|
||||
|
||||
default ErpPurchaseOrderDO selectByNo(String no) {
|
||||
return selectOne(ErpPurchaseOrderDO::getNo, no);
|
||||
}
|
||||
|
||||
default PageResult<ErpPurchaseOrderDO> selectPage(ErpPurchaseOrderPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<ErpPurchaseOrderDO>()
|
||||
.likeIfPresent(ErpPurchaseOrderDO::getNo, reqVO.getNo())
|
||||
.eqIfPresent(ErpPurchaseOrderDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(ErpPurchaseOrderDO::getSupplierId, reqVO.getSupplierId())
|
||||
.betweenIfPresent(ErpPurchaseOrderDO::getOrderTime, reqVO.getOrderTime())
|
||||
.orderByDesc(ErpPurchaseOrderDO::getId));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Long id, Integer status, ErpPurchaseOrderDO update) {
|
||||
return update(update, new LambdaQueryWrapperX<ErpPurchaseOrderDO>()
|
||||
.eq(ErpPurchaseOrderDO::getId, id)
|
||||
.eq(ErpPurchaseOrderDO::getStatus, status));
|
||||
}
|
||||
}
|
||||
|
||||
example: |
|
||||
// 库存增量更新 - 使用乐观锁避免并发问题
|
||||
@Mapper
|
||||
public interface ErpStockMapper extends BaseMapperX<ErpStockDO> {
|
||||
|
||||
default int updateCountIncrement(Long id, BigDecimal count, Boolean negativeEnable) {
|
||||
// 如果不允许负库存,需要检查库存是否充足
|
||||
if (!negativeEnable) {
|
||||
return update(null, new LambdaUpdateWrapper<ErpStockDO>()
|
||||
.setSql("count = count + " + count)
|
||||
.eq(ErpStockDO::getId, id)
|
||||
.ge(count.compareTo(BigDecimal.ZERO) < 0, ErpStockDO::getCount, count.abs()));
|
||||
}
|
||||
return update(null, new LambdaUpdateWrapper<ErpStockDO>()
|
||||
.setSql("count = count + " + count)
|
||||
.eq(ErpStockDO::getId, id));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-030-xxx-xxx"
|
||||
examples:
|
||||
- code: "1_030_101_000"
|
||||
message: "采购订单不存在"
|
||||
constant: "PURCHASE_ORDER_NOT_EXISTS"
|
||||
- code: "1_030_101_001"
|
||||
message: "采购订单({})已审核,无法删除"
|
||||
constant: "PURCHASE_ORDER_DELETE_FAIL_APPROVE"
|
||||
- code: "1_030_101_003"
|
||||
message: "审核失败,只有未审核的采购订单才能审核"
|
||||
constant: "PURCHASE_ORDER_APPROVE_FAIL"
|
||||
- code: "1_030_404_000"
|
||||
message: "操作失败,产品({})所在仓库({})的库存:{},小于变更数量:{}"
|
||||
constant: "STOCK_COUNT_NEGATIVE"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE, args...)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增业务单据类型"
|
||||
steps:
|
||||
- step: "1. 创建DO实体类"
|
||||
detail: |
|
||||
在 dal/dataobject/{domain}/ 下创建主表DO和子表DO
|
||||
继承 BaseDO,使用 @TableName、@KeySequence 注解
|
||||
定义字段并使用 @TableId 标注主键
|
||||
|
||||
- step: "2. 创建Mapper接口"
|
||||
detail: |
|
||||
在 dal/mysql/{domain}/ 下创建Mapper接口
|
||||
继承 BaseMapperX<DO>,添加自定义查询方法
|
||||
实现分页查询、按ID查询等方法
|
||||
|
||||
- step: "3. 创建Service层"
|
||||
detail: |
|
||||
在 service/{domain}/ 下创建Service接口和实现类
|
||||
实现CRUD、状态更新、业务校验等方法
|
||||
使用 @Transactional 注解保证事务
|
||||
|
||||
- step: "4. 创建Controller层"
|
||||
detail: |
|
||||
在 controller/admin/{domain}/ 下创建Controller
|
||||
添加 @Tag、@RestController、@PreAuthorize 注解
|
||||
实现创建、更新、删除、查询、导出等接口
|
||||
|
||||
- step: "5. 创建VO类"
|
||||
detail: |
|
||||
在 controller/admin/{domain}/vo/ 下创建:
|
||||
- SaveReqVO: 创建/更新请求VO
|
||||
- PageReqVO: 分页查询请求VO
|
||||
- RespVO: 响应VO
|
||||
|
||||
- step: "6. 添加单据号前缀"
|
||||
detail: |
|
||||
在 ErpNoRedisDAO 中添加单据号前缀常量
|
||||
格式:X + X + XX(如:CGDD-采购订单)
|
||||
|
||||
- step: "7. 添加错误码"
|
||||
detail: |
|
||||
在 ErrorCodeConstants 中添加错误码
|
||||
格式:1_030_xxx_xxx
|
||||
|
||||
# 新增渠道/类型示例
|
||||
new_channel:
|
||||
title: "新增库存业务类型"
|
||||
steps:
|
||||
- step: "1. 添加业务类型枚举"
|
||||
detail: |
|
||||
在 ErpStockRecordBizTypeEnum 中添加新类型:
|
||||
NEW_TYPE(90, "新业务类型"),
|
||||
NEW_TYPE_CANCEL(91, "新业务类型(作废)")
|
||||
|
||||
- step: "2. 创建业务单据"
|
||||
detail: "参考采购入库/销售出库创建对应的业务单据"
|
||||
|
||||
- step: "3. 在业务审核时创建库存记录"
|
||||
detail: |
|
||||
审核通过时调用 stockRecordService.createStockRecord()
|
||||
反审核时调用 stockRecordService.deleteStockRecord()
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "单据状态管理"
|
||||
description: |
|
||||
所有业务单据使用 ErpAuditStatus 枚举管理状态
|
||||
PROCESS(10) - 未审核/审核中
|
||||
APPROVE(20) - 已审核
|
||||
状态变更需要校验前置状态和关联数据
|
||||
|
||||
- practice: "主子表更新"
|
||||
description: |
|
||||
使用 diffList 方法对比新旧数据
|
||||
分别处理新增、修改、删除的子表记录
|
||||
批量操作使用 insertBatch、updateBatch、deleteByIds
|
||||
|
||||
- practice: "库存操作"
|
||||
description: |
|
||||
使用 updateCountIncrement 增量更新库存
|
||||
通过乐观锁避免并发问题
|
||||
每次库存变动都创建 stock_record 记录
|
||||
|
||||
- practice: "单据号生成"
|
||||
description: |
|
||||
使用 ErpNoRedisDAO.generate() 生成唯一单据号
|
||||
格式:前缀 + yyyyMMdd + 6位自增序号
|
||||
基于Redis保证分布式唯一性
|
||||
|
||||
- practice: "数据关联查询"
|
||||
description: |
|
||||
分页查询时批量查询关联数据
|
||||
使用 convertMap、convertMultiMap 构建映射
|
||||
避免N+1查询问题
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息,显示操作人姓名"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
version: "3.x"
|
||||
purpose: "ORM框架,简化数据库操作"
|
||||
- name: "Hutool"
|
||||
version: "5.x"
|
||||
purpose: "Java工具类库,日期、集合等工具"
|
||||
- name: "Spring Redis"
|
||||
version: "2.x"
|
||||
purpose: "Redis操作,单据号生成"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
# 枚举和常量
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java"
|
||||
purpose: "审核状态枚举,定义单据状态流转"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量,定义所有业务异常"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/stock/ErpStockRecordBizTypeEnum.java"
|
||||
purpose: "库存记录业务类型枚举"
|
||||
|
||||
# 核心服务
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java"
|
||||
purpose: "采购订单服务实现,展示标准业务流程"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java"
|
||||
purpose: "库存服务实现,展示库存增量更新"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java"
|
||||
purpose: "库存记录服务,管理库存变动明细"
|
||||
|
||||
# 数据访问
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java"
|
||||
purpose: "单据号生成器,分布式唯一ID"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java"
|
||||
purpose: "采购订单DO,展示主表设计"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockDO.java"
|
||||
purpose: "库存DO,展示库存表设计"
|
||||
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/stock/ErpStockRecordDO.java"
|
||||
purpose: "库存记录DO,展示库存明细设计"
|
||||
|
||||
# 业务对象
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java"
|
||||
purpose: "库存记录创建请求BO,展示值对象设计"
|
||||
|
||||
# 控制器示例
|
||||
- path: "yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java"
|
||||
purpose: "采购订单控制器,展示标准Controller设计"
|
||||
@@ -1,748 +0,0 @@
|
||||
# Skill 文件 - yudao-module-im 模块
|
||||
# 即时通讯模块知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-im"
|
||||
name: "Instant Messaging Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-im"
|
||||
created_at: "2026-06-08"
|
||||
updated_at: "2026-06-08"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
im 模块是系统的"即时通讯层",提供全链路即时通讯能力:
|
||||
1. 好友关系管理 - 双向好友关系,支持拉黑、免打扰、置顶、备注名
|
||||
2. 群组管理 - 创建/解散群组,三级角色体系(群主/管理员/普通成员),审批入群
|
||||
3. 私聊消息 - 一对一聊天,支持多种消息类型,clientMessageId 幂等发送
|
||||
4. 群聊消息 - 群组聊天,支持 @提醒、已读回执统计、消息置顶
|
||||
5. 频道广播 - 单向广播频道,管理员发布素材推送给指定用户
|
||||
6. RTC 实时通话 - 音视频通话,集成 LiveKit,支持呼叫/接听/拒绝/取消/挂断
|
||||
7. 表情系统 - 系统表情包 + 用户私人表情
|
||||
8. 敏感词过滤 - 基于 Trie 树的敏感词检测和替换
|
||||
|
||||
定位:面向终端用户的即时通讯模块,支持单聊、群聊、频道广播及 RTC 音视频通话。
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- principle: "双层 Controller 模式"
|
||||
description: "用户端 Controller(无 RBAC,通过登录态鉴权)+ Manager 端 Controller(RBAC 权限控制),分离普通用户操作和管理后台操作"
|
||||
- principle: "双向好友模型"
|
||||
description: "每对好友在 im_friend 表中存储 2 行记录(A->B 和 B->A),各自拥有独立的 silent/pinned/blocked/displayName 属性"
|
||||
- principle: "幂等消息发送"
|
||||
description: "通过 clientMessageId 字段实现客户端消息去重,避免网络重试导致重复消息"
|
||||
- principle: "事务感知异步推送"
|
||||
description: "消息推送使用 afterCommit 回调,确保数据库事务提交后再推送,避免读到未提交数据"
|
||||
- principle: "Redis 已读游标"
|
||||
description: "使用 Redis 存储每个用户在每个会话中的最大已读消息 ID,避免频繁更新数据库"
|
||||
- principle: "LiveKit Webhook 安全"
|
||||
description: "LiveKit Webhook 接口使用 JWT + SHA256 签名验证,同时标记 @PermitAll + @TenantIgnore"
|
||||
- principle: "Trie 树敏感词过滤"
|
||||
description: "使用 sensitive-word 库实现基于 Trie 树的高效敏感词检测和替换"
|
||||
- principle: "OpenIM 兼容消息类型"
|
||||
description: "消息类型编号兼容 OpenIM 协议,便于与 OpenIM 生态对接"
|
||||
- principle: "配置驱动"
|
||||
description: "模块配置统一在 yudao.im.* 命名空间下,通过 ImProperties 类管理"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "频道聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ChannelDO", "ChannelMaterialDO", "ChannelMessageDO"]
|
||||
description: "ChannelDO 是频道聚合根,ChannelMaterialDO 是频道素材,ChannelMessageDO 是频道广播消息"
|
||||
- name: "表情聚合"
|
||||
type: "聚合根"
|
||||
entities: ["FacePackDO", "FacePackItemDO", "FaceUserItemDO"]
|
||||
description: "FacePackDO 是表情包聚合根,FacePackItemDO 是表情项,FaceUserItemDO 是用户私人表情"
|
||||
- name: "好友聚合"
|
||||
type: "聚合根"
|
||||
entities: ["FriendDO", "FriendRequestDO"]
|
||||
description: "FriendDO 是好友关系记录(双向 2 行),FriendRequestDO 是好友请求记录"
|
||||
- name: "群组聚合"
|
||||
type: "聚合根"
|
||||
entities: ["GroupDO", "GroupMemberDO", "GroupRequestDO"]
|
||||
description: "GroupDO 是群组聚合根,GroupMemberDO 是群成员,GroupRequestDO 是入群请求"
|
||||
- name: "消息聚合"
|
||||
type: "聚合根"
|
||||
entities: ["PrivateMessageDO", "GroupMessageDO"]
|
||||
description: "PrivateMessageDO 是私聊消息,GroupMessageDO 是群聊消息"
|
||||
- name: "RTC 通话聚合"
|
||||
type: "聚合根"
|
||||
entities: ["RtcCallDO", "RtcParticipantDO"]
|
||||
description: "RtcCallDO 是通话会话聚合根,RtcParticipantDO 是通话参与者"
|
||||
|
||||
value_objects:
|
||||
- name: "ImProperties"
|
||||
description: "IM 模块配置属性,yudao.im.* 命名空间下的配置项"
|
||||
- name: "GroupMemberRoleEnum"
|
||||
description: "群成员角色枚举:OWNER/ADMIN/NORMAL"
|
||||
- name: "MessageTypeEnum"
|
||||
description: "消息类型枚举,兼容 OpenIM 协议编号"
|
||||
|
||||
services:
|
||||
- name: "FriendService"
|
||||
description: "好友关系服务,管理好友的增删改查、拉黑/取消拉黑"
|
||||
- name: "FriendRequestService"
|
||||
description: "好友请求服务,管理申请、同意、拒绝流程"
|
||||
- name: "GroupService"
|
||||
description: "群组核心服务,管理群组生命周期"
|
||||
- name: "GroupMemberService"
|
||||
description: "群成员服务,管理成员的加入/退出/角色变更"
|
||||
- name: "GroupRequestService"
|
||||
description: "入群请求服务,管理申请和审批流程"
|
||||
- name: "PrivateMessageService"
|
||||
description: "私聊消息服务,管理消息的发送、拉取、已读、撤回"
|
||||
- name: "GroupMessageService"
|
||||
description: "群聊消息服务,管理群消息的发送、拉取、已读、撤回"
|
||||
- name: "ChannelService"
|
||||
description: "频道管理服务,管理频道的 CRUD 和状态"
|
||||
- name: "ChannelMaterialService"
|
||||
description: "素材管理服务,管理素材的创建、编辑、查询"
|
||||
- name: "ChannelMessageService"
|
||||
description: "频道消息服务,管理广播消息的发送和拉取"
|
||||
- name: "RtcService"
|
||||
description: "RTC 通话核心服务,管理通话生命周期"
|
||||
- name: "LiveKitService"
|
||||
description: "LiveKit 集成服务,管理房间和 Token 生成"
|
||||
- name: "FacePackService"
|
||||
description: "表情包服务,管理表情包的 CRUD 和状态"
|
||||
- name: "FaceUserItemService"
|
||||
description: "用户表情服务,管理用户的私人表情"
|
||||
- name: "SensitiveWordService"
|
||||
description: "敏感词服务,管理敏感词库的 CRUD"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间 API 接口,供其他模块 RPC 调用"
|
||||
components: []
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP 接口层,提供 RESTful API"
|
||||
components:
|
||||
- "admin/friend - 好友管理接口(用户端)"
|
||||
- "admin/friend-request - 好友请求接口(用户端)"
|
||||
- "admin/group - 群组管理接口(用户端)"
|
||||
- "admin/group-member - 群成员管理接口(用户端)"
|
||||
- "admin/group-request - 群组请求接口(用户端)"
|
||||
- "admin/message/private - 私聊消息接口(用户端)"
|
||||
- "admin/message/group - 群聊消息接口(用户端)"
|
||||
- "admin/message/channel - 频道消息接口(用户端)"
|
||||
- "admin/channel/material - 频道素材接口(用户端)"
|
||||
- "admin/face-pack - 表情包接口(用户端)"
|
||||
- "admin/face-user-item - 用户表情接口(用户端)"
|
||||
- "admin/rtc - RTC 通话接口(用户端)"
|
||||
- "admin/livekit - LiveKit Webhook 接口"
|
||||
- "admin/manager/* - 管理后台接口(RBAC 保护)"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "friend - 好友服务(FriendService, FriendRequestService)"
|
||||
- "group - 群组服务(GroupService, GroupMemberService, GroupRequestService)"
|
||||
- "message - 消息服务(PrivateMessageService, GroupMessageService, ChannelMessageService)"
|
||||
- "channel - 频道服务(ChannelService, ChannelMaterialService)"
|
||||
- "face - 表情服务(FacePackService, FacePackItemService, FaceUserItemService)"
|
||||
- "rtc - RTC 服务(RtcService, LiveKitService)"
|
||||
- "sensitive - 敏感词服务(SensitiveWordService)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject - DO 实体类定义"
|
||||
- "mysql - MyBatis Mapper 接口"
|
||||
|
||||
- name: "framework"
|
||||
purpose: "框架层,模块内部基础设施"
|
||||
components:
|
||||
- "config - 模块配置(ImProperties)"
|
||||
- "redis - Redis 已读游标操作"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "双层 Controller 模式 (Two-Tier Controller)"
|
||||
location: "controller/admin/ + controller/admin/manager/"
|
||||
purpose: "用户端 Controller 通过登录态鉴权,管理端 Controller 通过 @PreAuthorize 进行 RBAC 权限校验"
|
||||
- pattern: "幂等模式 (Idempotency Pattern)"
|
||||
location: "service/message/"
|
||||
purpose: "通过 clientMessageId 字段和数据库唯一索引实现消息幂等发送"
|
||||
- pattern: "事务感知异步推送"
|
||||
location: "service/message/"
|
||||
purpose: "使用 TransactionSynchronizationManager.registerSynchronization(afterCommit) 确保事务提交后再推送"
|
||||
- pattern: "游标模式 (Cursor Pattern)"
|
||||
location: "service/message/ + framework/redis/"
|
||||
purpose: "Redis 存储每个用户在每个会话中的最大已读消息 ID,支持增量拉取"
|
||||
- pattern: "安全验证模式"
|
||||
location: "controller/admin/livekit/"
|
||||
purpose: "LiveKit Webhook 使用 JWT + SHA256 签名验证,@PermitAll + @TenantIgnore 组合"
|
||||
- pattern: "Trie 树过滤模式"
|
||||
location: "service/sensitive/"
|
||||
purpose: "基于 sensitive-word 库的 Trie 树实现高效敏感词检测"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "AdminUserApi"
|
||||
method: "getUser()"
|
||||
description: "查询用户基本信息,用于好友信息、群成员信息展示"
|
||||
- name: "AdminUserApi"
|
||||
method: "getUsers()"
|
||||
description: "批量查询用户信息,用于消息列表中展示发送者信息"
|
||||
- name: "FileApi"
|
||||
method: "createFile()"
|
||||
description: "上传文件(头像、图片消息、文件消息)"
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "查询用户信息(好友、群成员、消息发送者)"
|
||||
- module: "yudao-module-infra"
|
||||
api: "FileApi"
|
||||
purpose: "上传头像、图片消息、文件消息"
|
||||
mq: []
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有 DO 继承 BaseDO,包含 creator, createTime, updater, updateTime, deleted 字段。"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 频道相关 ==========
|
||||
- name: "im_channel"
|
||||
comment: "频道表"
|
||||
entity: "ChannelDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "频道编号,主键" }
|
||||
- { name: "code", type: "VARCHAR", comment: "频道编码,唯一" }
|
||||
- { name: "name", type: "VARCHAR", comment: "频道名称" }
|
||||
- { name: "avatar", type: "VARCHAR", comment: "频道头像 URL" }
|
||||
- { name: "sort", type: "INT", comment: "排序值" }
|
||||
- { name: "status", type: "TINYINT", comment: "频道状态(启用/禁用)" }
|
||||
indexes:
|
||||
- { name: "uk_code", columns: ["code"], unique: true }
|
||||
|
||||
- name: "im_channel_material"
|
||||
comment: "频道素材表"
|
||||
entity: "ChannelMaterialDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "素材编号,主键" }
|
||||
- { name: "title", type: "VARCHAR", comment: "素材标题" }
|
||||
- { name: "cover_url", type: "VARCHAR", comment: "封面图片 URL" }
|
||||
- { name: "summary", type: "VARCHAR", comment: "内容摘要" }
|
||||
- { name: "content", type: "TEXT", comment: "内容 HTML" }
|
||||
- { name: "url", type: "VARCHAR", comment: "原文链接" }
|
||||
|
||||
- name: "im_channel_message"
|
||||
comment: "频道消息表"
|
||||
entity: "ChannelMessageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "消息编号,主键" }
|
||||
- { name: "channel_id", type: "BIGINT", comment: "频道编号" }
|
||||
- { name: "material_id", type: "BIGINT", comment: "关联素材编号" }
|
||||
- { name: "receiver_user_ids", type: "VARCHAR", comment: "接收用户 ID 列表(JSON 数组)" }
|
||||
- { name: "send_time", type: "DATETIME", comment: "发送时间" }
|
||||
indexes:
|
||||
- { name: "idx_channel_id", columns: ["channel_id"] }
|
||||
|
||||
# ========== 表情相关 ==========
|
||||
- name: "im_face_pack"
|
||||
comment: "表情包表"
|
||||
entity: "FacePackDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "表情包编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "表情包名称" }
|
||||
- { name: "icon", type: "VARCHAR", comment: "表情包图标 URL" }
|
||||
- { name: "sort", type: "INT", comment: "排序值" }
|
||||
- { name: "status", type: "TINYINT", comment: "启用状态" }
|
||||
|
||||
- name: "im_face_pack_item"
|
||||
comment: "表情项表"
|
||||
entity: "FacePackItemDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "表情项编号,主键" }
|
||||
- { name: "pack_id", type: "BIGINT", comment: "所属表情包编号" }
|
||||
- { name: "url", type: "VARCHAR", comment: "表情图片 URL" }
|
||||
- { name: "name", type: "VARCHAR", comment: "表情名称" }
|
||||
- { name: "width", type: "INT", comment: "图片宽度" }
|
||||
- { name: "height", type: "INT", comment: "图片高度" }
|
||||
indexes:
|
||||
- { name: "idx_pack_id", columns: ["pack_id"] }
|
||||
|
||||
- name: "im_face_user_item"
|
||||
comment: "用户表情表"
|
||||
entity: "FaceUserItemDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "表情编号,主键" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "url", type: "VARCHAR", comment: "表情图片 URL" }
|
||||
- { name: "name", type: "VARCHAR", comment: "表情名称" }
|
||||
- { name: "width", type: "INT", comment: "图片宽度" }
|
||||
- { name: "height", type: "INT", comment: "图片高度" }
|
||||
indexes:
|
||||
- { name: "idx_user_id", columns: ["user_id"] }
|
||||
|
||||
# ========== 好友相关 ==========
|
||||
- name: "im_friend"
|
||||
comment: "好友关系表"
|
||||
entity: "FriendDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "关系编号,主键" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "friend_id", type: "BIGINT", comment: "好友用户编号" }
|
||||
- { name: "silent", type: "BIT", comment: "是否免打扰" }
|
||||
- { name: "pinned", type: "BIT", comment: "是否置顶" }
|
||||
- { name: "blocked", type: "BIT", comment: "是否拉黑" }
|
||||
- { name: "display_name", type: "VARCHAR", comment: "好友备注名" }
|
||||
indexes:
|
||||
- { name: "uk_user_friend", columns: ["user_id", "friend_id"], unique: true }
|
||||
|
||||
- name: "im_friend_request"
|
||||
comment: "好友请求表"
|
||||
entity: "FriendRequestDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "请求编号,主键" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "申请人用户编号" }
|
||||
- { name: "friend_id", type: "BIGINT", comment: "目标用户编号" }
|
||||
- { name: "apply_content", type: "VARCHAR", comment: "申请附言" }
|
||||
- { name: "display_name", type: "VARCHAR", comment: "对好友的备注名" }
|
||||
- { name: "add_source", type: "TINYINT", comment: "添加来源" }
|
||||
- { name: "handle_result", type: "TINYINT", comment: "处理结果" }
|
||||
- { name: "handle_time", type: "DATETIME", comment: "处理时间" }
|
||||
- { name: "handle_user_id", type: "BIGINT", comment: "处理人用户编号" }
|
||||
indexes:
|
||||
- { name: "idx_user_id", columns: ["user_id"] }
|
||||
- { name: "idx_friend_id", columns: ["friend_id"] }
|
||||
|
||||
# ========== 群组相关 ==========
|
||||
- name: "im_group"
|
||||
comment: "群组表"
|
||||
entity: "GroupDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "群组编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "群组名称" }
|
||||
- { name: "owner_id", type: "BIGINT", comment: "群主用户编号" }
|
||||
- { name: "avatar", type: "VARCHAR", comment: "群组头像 URL" }
|
||||
- { name: "notice", type: "VARCHAR", comment: "群公告" }
|
||||
- { name: "join_approval", type: "BIT", comment: "是否开启入群审批" }
|
||||
- { name: "banned", type: "BIT", comment: "是否被封禁" }
|
||||
- { name: "muted_all", type: "BIT", comment: "是否全员禁言" }
|
||||
- { name: "pinned_message_ids", type: "VARCHAR", comment: "置顶消息 ID 列表(JSON 数组)" }
|
||||
indexes:
|
||||
- { name: "idx_owner_id", columns: ["owner_id"] }
|
||||
|
||||
- name: "im_group_member"
|
||||
comment: "群成员表"
|
||||
entity: "GroupMemberDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "成员编号,主键" }
|
||||
- { name: "group_id", type: "BIGINT", comment: "群组编号" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "role", type: "TINYINT", comment: "角色(OWNER/ADMIN/NORMAL)" }
|
||||
- { name: "nickname", type: "VARCHAR", comment: "群内昵称" }
|
||||
- { name: "mute_end_time", type: "DATETIME", comment: "禁言结束时间" }
|
||||
- { name: "add_source", type: "TINYINT", comment: "加入来源" }
|
||||
- { name: "inviter", type: "BIGINT", comment: "邀请人用户编号" }
|
||||
indexes:
|
||||
- { name: "uk_group_user", columns: ["group_id", "user_id"], unique: true }
|
||||
|
||||
- name: "im_group_request"
|
||||
comment: "群组请求表"
|
||||
entity: "GroupRequestDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "请求编号,主键" }
|
||||
- { name: "group_id", type: "BIGINT", comment: "群组编号" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "申请人/被邀请人用户编号" }
|
||||
- { name: "type", type: "TINYINT", comment: "请求类型(用户申请/成员邀请)" }
|
||||
- { name: "apply_content", type: "VARCHAR", comment: "申请附言" }
|
||||
- { name: "handle_result", type: "TINYINT", comment: "处理结果" }
|
||||
- { name: "handle_time", type: "DATETIME", comment: "处理时间" }
|
||||
- { name: "handle_user_id", type: "BIGINT", comment: "处理人用户编号" }
|
||||
indexes:
|
||||
- { name: "idx_group_id", columns: ["group_id"] }
|
||||
- { name: "idx_user_id", columns: ["user_id"] }
|
||||
|
||||
# ========== 消息相关 ==========
|
||||
- name: "im_private_message"
|
||||
comment: "私聊消息表"
|
||||
entity: "PrivateMessageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "消息编号,主键" }
|
||||
- { name: "client_message_id", type: "VARCHAR", comment: "客户端消息 ID(幂等键)" }
|
||||
- { name: "sender_id", type: "BIGINT", comment: "发送者用户编号" }
|
||||
- { name: "receiver_id", type: "BIGINT", comment: "接收者用户编号" }
|
||||
- { name: "type", type: "TINYINT", comment: "消息类型" }
|
||||
- { name: "content", type: "VARCHAR", comment: "消息内容(JSON 格式)" }
|
||||
- { name: "status", type: "TINYINT", comment: "消息状态(正常/已撤回)" }
|
||||
indexes:
|
||||
- { name: "uk_client_message_id", columns: ["client_message_id"], unique: true }
|
||||
- { name: "idx_sender_id", columns: ["sender_id"] }
|
||||
- { name: "idx_receiver_id", columns: ["receiver_id"] }
|
||||
|
||||
- name: "im_group_message"
|
||||
comment: "群聊消息表"
|
||||
entity: "GroupMessageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "消息编号,主键" }
|
||||
- { name: "client_message_id", type: "VARCHAR", comment: "客户端消息 ID(幂等键)" }
|
||||
- { name: "sender_id", type: "BIGINT", comment: "发送者用户编号" }
|
||||
- { name: "group_id", type: "BIGINT", comment: "群组编号" }
|
||||
- { name: "type", type: "TINYINT", comment: "消息类型" }
|
||||
- { name: "content", type: "VARCHAR", comment: "消息内容(JSON 格式)" }
|
||||
- { name: "at_user_ids", type: "VARCHAR", comment: "@的用户 ID 列表(JSON 数组)" }
|
||||
- { name: "status", type: "TINYINT", comment: "消息状态(正常/已撤回)" }
|
||||
- { name: "receipt_status", type: "TINYINT", comment: "已读回执状态" }
|
||||
- { name: "read_count", type: "INT", comment: "已读人数" }
|
||||
indexes:
|
||||
- { name: "uk_client_message_id", columns: ["client_message_id"], unique: true }
|
||||
- { name: "idx_group_id", columns: ["group_id"] }
|
||||
- { name: "idx_sender_id", columns: ["sender_id"] }
|
||||
|
||||
# ========== RTC 相关 ==========
|
||||
- name: "im_rtc_call"
|
||||
comment: "RTC 通话表"
|
||||
entity: "RtcCallDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "通话编号,主键" }
|
||||
- { name: "room_id", type: "VARCHAR", comment: "LiveKit 房间 UUID" }
|
||||
- { name: "conversation_type", type: "TINYINT", comment: "会话类型(私聊/群聊)" }
|
||||
- { name: "media_type", type: "TINYINT", comment: "媒体类型(音频/视频)" }
|
||||
- { name: "inviter_uid", type: "BIGINT", comment: "发起者用户编号" }
|
||||
- { name: "status", type: "TINYINT", comment: "通话状态" }
|
||||
- { name: "start_time", type: "DATETIME", comment: "开始时间" }
|
||||
- { name: "accept_time", type: "DATETIME", comment: "接听时间" }
|
||||
- { name: "end_time", type: "DATETIME", comment: "结束时间" }
|
||||
indexes:
|
||||
- { name: "uk_room_id", columns: ["room_id"], unique: true }
|
||||
- { name: "idx_inviter_uid", columns: ["inviter_uid"] }
|
||||
|
||||
- name: "im_rtc_participant"
|
||||
comment: "RTC 参与者表"
|
||||
entity: "RtcParticipantDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "参与者编号,主键" }
|
||||
- { name: "call_id", type: "BIGINT", comment: "通话编号" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "role", type: "TINYINT", comment: "角色(发起者/参与者)" }
|
||||
- { name: "status", type: "TINYINT", comment: "状态(INVITING/JOINED/LEFT/REJECTED/NO_ANSWER)" }
|
||||
- { name: "join_time", type: "DATETIME", comment: "加入时间" }
|
||||
- { name: "leave_time", type: "DATETIME", comment: "离开时间" }
|
||||
indexes:
|
||||
- { name: "idx_call_id", columns: ["call_id"] }
|
||||
- { name: "idx_user_id", columns: ["user_id"] }
|
||||
|
||||
# ========== 敏感词相关 ==========
|
||||
- name: "im_sensitive_word"
|
||||
comment: "敏感词表"
|
||||
entity: "SensitiveWordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "敏感词编号,主键" }
|
||||
- { name: "word", type: "VARCHAR", comment: "敏感词内容" }
|
||||
- { name: "status", type: "TINYINT", comment: "启用状态" }
|
||||
indexes:
|
||||
- { name: "uk_word", columns: ["word"], unique: true }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "im_channel_message"
|
||||
to: "im_channel"
|
||||
type: "N:1"
|
||||
foreign_key: "channel_id"
|
||||
- from: "im_channel_message"
|
||||
to: "im_channel_material"
|
||||
type: "N:1"
|
||||
foreign_key: "material_id"
|
||||
- from: "im_face_pack_item"
|
||||
to: "im_face_pack"
|
||||
type: "N:1"
|
||||
foreign_key: "pack_id"
|
||||
- from: "im_group_member"
|
||||
to: "im_group"
|
||||
type: "N:1"
|
||||
foreign_key: "group_id"
|
||||
- from: "im_group_request"
|
||||
to: "im_group"
|
||||
type: "N:1"
|
||||
foreign_key: "group_id"
|
||||
- from: "im_group_message"
|
||||
to: "im_group"
|
||||
type: "N:1"
|
||||
foreign_key: "group_id"
|
||||
- from: "im_rtc_participant"
|
||||
to: "im_rtc_call"
|
||||
type: "N:1"
|
||||
foreign_key: "call_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = 'xxx') - Swagger 文档标签"
|
||||
- "@RestController - REST 控制器"
|
||||
- "@RequestMapping('/im/xxx') - 请求路径前缀"
|
||||
- "@Validated - 参数校验"
|
||||
- "@PreAuthorize('@ss.hasPermission('im:xxx:action')') - 权限控制(仅管理端)"
|
||||
example: |
|
||||
@Tag(name = "用户端 - 好友管理")
|
||||
@RestController
|
||||
@RequestMapping("/im/friend")
|
||||
@Validated
|
||||
public class ImFriendController {
|
||||
@Resource
|
||||
private FriendService friendService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取好友列表")
|
||||
public CommonResult<List<FriendDO>> getFriendList() {
|
||||
return success(friendService.getFriendList(LoginContextHolder.getLoginUserId()));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "XxxService 接口定义业务方法,XxxServiceImpl 实现类"
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class XxxServiceImpl implements XxxService {
|
||||
@Resource
|
||||
private XxxMapper xxxMapper;
|
||||
|
||||
// 使用 static final 定义常量
|
||||
// 使用 @Transactional 注解控制事务
|
||||
// 使用 validateXxxExists 方法校验存在性
|
||||
// 使用 exception(ErrorCode) 抛出业务异常
|
||||
}
|
||||
example: |
|
||||
@Service
|
||||
public class PrivateMessageServiceImpl implements PrivateMessageService {
|
||||
@Resource
|
||||
private ImPrivateMessageMapper privateMessageMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PrivateMessageDO sendPrivateMessage(PrivateMessageSendReqDTO reqDTO) {
|
||||
// 1. 校验接收者存在
|
||||
// 2. 检查 clientMessageId 幂等
|
||||
// 3. 敏感词过滤
|
||||
// 4. 保存消息
|
||||
PrivateMessageDO message = ...;
|
||||
privateMessageMapper.insert(message);
|
||||
// 5. 事务提交后异步推送
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
// 推送消息给接收者
|
||||
}
|
||||
});
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
public interface XxxMapper extends BaseMapperX<XxxDO> {
|
||||
// 继承 BaseMapperX 获得通用 CRUD 方法
|
||||
// 自定义查询方法使用 @Select 注解或 XML
|
||||
// 分页查询返回 PageResult<XxxDO>
|
||||
}
|
||||
example: |
|
||||
public interface ImPrivateMessageMapper extends BaseMapperX<PrivateMessageDO> {
|
||||
default PageResult<PrivateMessageDO> selectPage(PrivateMessagePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PrivateMessageDO>()
|
||||
.eqIfPresent(PrivateMessageDO::getSenderId, reqVO.getSenderId())
|
||||
.eqIfPresent(PrivateMessageDO::getReceiverId, reqVO.getReceiverId())
|
||||
.betweenIfPresent(PrivateMessageDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(PrivateMessageDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-009-xxx-xxx"
|
||||
examples:
|
||||
- code: "好友已存在"
|
||||
- code: "不是好友关系"
|
||||
- code: "群组不存在"
|
||||
- code: "不是群成员"
|
||||
- code: "已被禁言"
|
||||
- code: "消息已撤回"
|
||||
- code: "撤回超时"
|
||||
- code: "通话不存在"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ErrorCode)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增消息类型
|
||||
new_channel:
|
||||
title: "新增消息类型"
|
||||
steps:
|
||||
- step: "1. 定义消息类型枚举"
|
||||
description: "在 MessageTypeEnum 中添加新的消息类型编号,兼容 OpenIM 协议"
|
||||
- step: "2. 定义消息内容 DTO"
|
||||
description: "创建 XxxContentDTO 类定义该消息类型的 content JSON 结构"
|
||||
code: |
|
||||
@Data
|
||||
public class VideoContentDTO {
|
||||
private String url;
|
||||
private Integer duration;
|
||||
private Integer width;
|
||||
private Integer height;
|
||||
private String thumbUrl;
|
||||
}
|
||||
- step: "3. 实现消息渲染"
|
||||
description: "在客户端实现该消息类型的渲染逻辑"
|
||||
- step: "4. 敏感词处理"
|
||||
description: "如果消息类型包含文本内容,需要在发送时进行敏感词过滤"
|
||||
|
||||
# 新增管理接口
|
||||
new_feature:
|
||||
title: "新增管理后台接口"
|
||||
steps:
|
||||
- step: "1. 创建 Manager Controller"
|
||||
description: "在 controller/admin/manager/ 下创建新的 Controller 类"
|
||||
code: |
|
||||
@Tag(name = "管理后台 - XXX 管理")
|
||||
@RestController
|
||||
@RequestMapping("/im/manager/xxx")
|
||||
@Validated
|
||||
@PreAuthorize("@ss.hasPermission('im:xxx:query')")
|
||||
public class ImXxxManagerController {
|
||||
// 注入 Service
|
||||
// 定义 CRUD 接口
|
||||
}
|
||||
- step: "2. 定义权限标识"
|
||||
description: "在权限系统中注册 im:xxx:query/create/update/delete 权限"
|
||||
- step: "3. 实现 Service 方法"
|
||||
description: "在对应的 Service 中实现管理端的业务逻辑"
|
||||
- step: "4. 添加数据权限"
|
||||
description: "根据需要添加数据权限控制"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "clientMessageId 幂等发送"
|
||||
description: "客户端生成 UUID 作为 clientMessageId,服务端通过唯一索引保证幂等,避免网络重试导致重复消息"
|
||||
- practice: "事务感知异步推送"
|
||||
description: "使用 afterCommit 回调确保事务提交后再推送消息,避免接收方读到未提交数据"
|
||||
- practice: "Redis 已读游标"
|
||||
description: "使用 Redis 存储已读游标,避免频繁更新数据库,提高已读状态查询性能"
|
||||
- practice: "双向好友独立属性"
|
||||
description: "每对好友的 2 行记录拥有独立属性,更新时只修改当前用户方向的记录"
|
||||
- practice: "群组角色权限校验"
|
||||
description: "通过 GroupMemberDO.role 字段判断操作权限,注意权限边界(管理员不可踢管理员)"
|
||||
- practice: "LiveKit Webhook 安全"
|
||||
description: "使用 JWT + SHA256 签名验证 Webhook 请求,不要在未验证签名的情况下处理事件"
|
||||
- practice: "敏感词过滤时机"
|
||||
description: "敏感词过滤在消息发送时执行,过滤后直接存储,已发送消息不受后续词库更新影响"
|
||||
- practice: "消息增量拉取"
|
||||
description: "使用 maxMessageId 作为游标进行增量拉取,避免使用 offset 分页的性能问题"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
purpose: "AdminUserApi - 查询用户信息、校验用户存在性"
|
||||
- module: "yudao-module-infra"
|
||||
purpose: "文件存储(头像、图片、文件消息等)"
|
||||
- module: "yudao-framework-common"
|
||||
purpose: "通用工具类、CommonResult、PageResult 等"
|
||||
- module: "yudao-framework-mybatis"
|
||||
purpose: "MyBatis-Plus 封装、BaseMapperX、BaseDO"
|
||||
- module: "yudao-framework-redis"
|
||||
purpose: "Redis 操作封装,用于已读游标存储"
|
||||
- module: "yudao-framework-tenant"
|
||||
purpose: "租户支持"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
purpose: "ORM 框架,提供 CRUD 封装"
|
||||
- name: "sensitive-word"
|
||||
purpose: "基于 Trie 树的敏感词过滤库"
|
||||
- name: "pinyin4j"
|
||||
purpose: "汉字转拼音,用于敏感词拼音匹配"
|
||||
- name: "LiveKit"
|
||||
purpose: "开源实时音视频通信框架,RTC 能力支撑"
|
||||
- name: "Swagger/OpenAPI"
|
||||
purpose: "API 文档注解"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/ImFriendController.java"
|
||||
purpose: "好友管理用户端 Controller"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java"
|
||||
purpose: "群组管理用户端 Controller"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java"
|
||||
purpose: "私聊消息用户端 Controller"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java"
|
||||
purpose: "群聊消息用户端 Controller"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/ImRtcController.java"
|
||||
purpose: "RTC 通话用户端 Controller"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/livekit/ImLivekitController.java"
|
||||
purpose: "LiveKit Webhook Controller"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/FriendService.java"
|
||||
purpose: "好友关系服务接口"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/GroupService.java"
|
||||
purpose: "群组核心服务接口"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/PrivateMessageService.java"
|
||||
purpose: "私聊消息服务接口"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/GroupMessageService.java"
|
||||
purpose: "群聊消息服务接口"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/RtcService.java"
|
||||
purpose: "RTC 通话服务接口"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/sensitive/SensitiveWordService.java"
|
||||
purpose: "敏感词服务接口"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/GroupDO.java"
|
||||
purpose: "群组实体类"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/PrivateMessageDO.java"
|
||||
purpose: "私聊消息实体类"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/rtc/RtcCallDO.java"
|
||||
purpose: "RTC 通话实体类"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义"
|
||||
- path: "yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/config/ImProperties.java"
|
||||
purpose: "IM 模块配置属性类"
|
||||
@@ -1,662 +0,0 @@
|
||||
# Skill 文件 - yudao-module-infra 模块
|
||||
# 基础设施模块知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-infra"
|
||||
name: "Infrastructure Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-infra"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
infra 模块是整个系统的"基础设施层",提供跨业务模块的通用能力支撑:
|
||||
1. 文件存储服务 - 统一的文件上传、下载、管理能力,支持多存储渠道(本地/S3/OSS/FTP等)
|
||||
2. 代码生成器 - 基于数据库表结构自动生成 CRUD 代码,提升开发效率
|
||||
3. 定时任务管理 - 基于 Quartz 的任务调度管理,支持动态配置和监控
|
||||
4. API 日志审计 - 记录 API 访问日志和异常日志,支持问题追踪和分析
|
||||
5. 系统配置管理 - 键值对形式的系统参数配置
|
||||
6. 数据源管理 - 多数据源配置和管理,支持代码生成器连接不同数据库
|
||||
|
||||
定位:作为底层基础设施模块,不直接面向终端用户业务,而是为其他业务模块提供技术能力支撑。
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- principle: "策略模式 + 工厂模式"
|
||||
description: "文件存储采用策略模式,通过 FileClientFactory 创建不同的 FileClient 实现,支持本地、S3、FTP、SFTP、数据库等多种存储方式"
|
||||
- principle: "模板方法模式"
|
||||
description: "AbstractFileClient 抽象类定义文件客户端的通用流程,子类实现具体的初始化和操作逻辑"
|
||||
- principle: "多态配置"
|
||||
description: "FileClientConfig 使用 Jackson @JsonTypeInfo 实现多态序列化,配置类与存储类型一一对应"
|
||||
- principle: "Builder 模式"
|
||||
description: "CodegenBuilder 负责构建代码生成的表和列定义对象,CodegenEngine 负责执行代码生成"
|
||||
- principle: "模板引擎"
|
||||
description: "代码生成使用 Velocity 模板引擎,支持多前端框架(Vue2/Vue3/Vben/UniApp)"
|
||||
- principle: "缓存优化"
|
||||
description: "FileClient 使用 Guava LoadingCache 实现10秒异步刷新,避免频繁查询数据库"
|
||||
- principle: "租户隔离忽略"
|
||||
description: "infra 模块大部分表使用 @TenantIgnore 注解,基础设施不参与租户隔离"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "文件存储聚合"
|
||||
type: "聚合根"
|
||||
entities: ["FileDO", "FileConfigDO", "FileContentDO"]
|
||||
description: "FileConfigDO 是配置聚合根,FileDO 是文件记录,FileContentDO 是数据库存储的文件内容"
|
||||
- name: "代码生成聚合"
|
||||
type: "聚合根"
|
||||
entities: ["CodegenTableDO", "CodegenColumnDO"]
|
||||
description: "CodegenTableDO 是表定义聚合根,CodegenColumnDO 是列定义,两者是一对多关系"
|
||||
- name: "定时任务聚合"
|
||||
type: "聚合根"
|
||||
entities: ["JobDO", "JobLogDO"]
|
||||
description: "JobDO 是任务定义聚合根,JobLogDO 是执行日志,两者是一对多关系"
|
||||
- name: "API日志聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ApiAccessLogDO", "ApiErrorLogDO"]
|
||||
description: "独立的日志实体,无关联关系"
|
||||
|
||||
value_objects:
|
||||
- name: "FileClientConfig"
|
||||
description: "文件客户端配置接口,有多种实现(LocalFileClientConfig, S3FileClientConfig 等)"
|
||||
- name: "FileStorageEnum"
|
||||
description: "文件存储类型枚举,定义存储类型与配置类、客户端类的映射关系"
|
||||
|
||||
services:
|
||||
- name: "FileService"
|
||||
description: "文件管理服务,提供文件上传、下载、删除等功能"
|
||||
- name: "FileConfigService"
|
||||
description: "文件配置服务,管理存储渠道配置"
|
||||
- name: "CodegenService"
|
||||
description: "代码生成服务,提供表导入、代码生成等功能"
|
||||
- name: "JobService"
|
||||
description: "定时任务服务,与 Quartz 集成管理任务调度"
|
||||
- name: "ConfigService"
|
||||
description: "系统配置服务,管理键值对配置"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间 API 接口,供其他模块 RPC 调用"
|
||||
components:
|
||||
- "FileApi - 文件上传接口"
|
||||
- "ConfigApi - 配置获取接口"
|
||||
- "WebSocketSenderApi - WebSocket 消息发送接口"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP 接口层,提供 RESTful API"
|
||||
components:
|
||||
- "admin/codegen - 代码生成器接口"
|
||||
- "admin/file - 文件管理接口"
|
||||
- "admin/job - 定时任务管理接口"
|
||||
- "admin/logger - API 日志管理接口"
|
||||
- "admin/config - 系统配置接口"
|
||||
- "admin/db - 数据源管理接口"
|
||||
- "admin/redis - Redis 监控接口"
|
||||
- "app/file - App 端文件上传接口"
|
||||
- "admin/demo - 代码生成示例(Demo01/Demo02/Demo03)"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "codegen - 代码生成服务(CodegenService, CodegenBuilder, CodegenEngine)"
|
||||
- "file - 文件服务(FileService, FileConfigService)"
|
||||
- "job - 定时任务服务(JobService, JobLogService)"
|
||||
- "logger - 日志服务(ApiAccessLogService, ApiErrorLogService)"
|
||||
- "config - 配置服务(ConfigService)"
|
||||
- "db - 数据源服务(DataSourceConfigService, DatabaseTableService)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject - DO 实体类定义"
|
||||
- "mysql - MyBatis Mapper 接口"
|
||||
|
||||
- name: "framework"
|
||||
purpose: "框架层,模块内部基础设施"
|
||||
components:
|
||||
- "file/core/client - 文件客户端抽象和实现"
|
||||
- "file/core/enums - 文件存储枚举"
|
||||
- "codegen/config - 代码生成配置"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "策略模式 (Strategy Pattern)"
|
||||
location: "framework/file/core/client/"
|
||||
purpose: "FileClient 接口定义统一操作,不同存储实现(LocalFileClient, S3FileClient, FtpFileClient 等)"
|
||||
- pattern: "工厂模式 (Factory Pattern)"
|
||||
location: "framework/file/core/client/FileClientFactoryImpl.java"
|
||||
purpose: "根据存储类型动态创建对应的 FileClient 实例"
|
||||
- pattern: "模板方法模式 (Template Method Pattern)"
|
||||
location: "framework/file/core/client/AbstractFileClient.java"
|
||||
purpose: "定义文件客户端的初始化和刷新模板流程,子类实现 doInit()"
|
||||
- pattern: "Builder 模式"
|
||||
location: "service/codegen/inner/CodegenBuilder.java"
|
||||
purpose: "构建 CodegenTableDO 和 CodegenColumnDO 对象"
|
||||
- pattern: "模板引擎模式"
|
||||
location: "service/codegen/inner/CodegenEngine.java"
|
||||
purpose: "使用 Velocity 模板生成代码,支持多前端框架"
|
||||
- pattern: "缓存模式"
|
||||
location: "service/file/FileConfigServiceImpl.java"
|
||||
purpose: "使用 Guava LoadingCache 缓存 FileClient,10秒异步刷新"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "FileApi"
|
||||
method: "createFile()"
|
||||
description: "其他模块调用上传文件,返回文件 URL"
|
||||
- name: "FileApi"
|
||||
method: "presignGetUrl()"
|
||||
description: "获取文件预签名访问地址"
|
||||
- name: "ConfigApi"
|
||||
method: "getConfigValueByKey()"
|
||||
description: "其他模块获取系统配置值"
|
||||
- name: "WebSocketSenderApi"
|
||||
method: "send()"
|
||||
description: "发送 WebSocket 消息"
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
api: "FileApi"
|
||||
purpose: "用户头像上传"
|
||||
- module: "yudao-module-member"
|
||||
api: "FileApi"
|
||||
purpose: "会员头像上传"
|
||||
- module: "所有模块"
|
||||
api: "ConfigApi"
|
||||
purpose: "获取系统配置"
|
||||
mq: []
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有 DO 继承 BaseDO,包含 creator, createTime, updater, updateTime, deleted 字段。大部分表使用 @TenantIgnore 忽略租户隔离。"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 文件存储相关 ==========
|
||||
- name: "infra_file_config"
|
||||
comment: "文件配置表"
|
||||
entity: "FileConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "配置编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "配置名称" }
|
||||
- { name: "storage", type: "TINYINT", comment: "存储器类型,枚举 FileStorageEnum" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
- { name: "master", type: "BIT", comment: "是否主配置" }
|
||||
- { name: "config", type: "JSON", comment: "存储配置,JSON 格式" }
|
||||
indexes:
|
||||
- { name: "idx_master", columns: ["master"] }
|
||||
|
||||
- name: "infra_file"
|
||||
comment: "文件表"
|
||||
entity: "FileDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "文件编号,主键" }
|
||||
- { name: "config_id", type: "BIGINT", comment: "配置编号,关联 FileConfigDO" }
|
||||
- { name: "name", type: "VARCHAR", comment: "原文件名" }
|
||||
- { name: "path", type: "VARCHAR", comment: "文件路径" }
|
||||
- { name: "url", type: "VARCHAR", comment: "访问地址" }
|
||||
- { name: "type", type: "VARCHAR", comment: "MIME 类型" }
|
||||
- { name: "size", type: "BIGINT", comment: "文件大小(字节)" }
|
||||
indexes:
|
||||
- { name: "idx_config_id", columns: ["config_id"] }
|
||||
|
||||
- name: "infra_file_content"
|
||||
comment: "文件内容表(数据库存储方式)"
|
||||
entity: "FileContentDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号,主键" }
|
||||
- { name: "config_id", type: "BIGINT", comment: "配置编号" }
|
||||
- { name: "path", type: "VARCHAR", comment: "文件路径" }
|
||||
- { name: "content", type: "LONGBLOB", comment: "文件内容" }
|
||||
|
||||
# ========== 代码生成相关 ==========
|
||||
- name: "infra_codegen_table"
|
||||
comment: "代码生成表定义"
|
||||
entity: "CodegenTableDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号,主键" }
|
||||
- { name: "data_source_config_id", type: "BIGINT", comment: "数据源编号" }
|
||||
- { name: "scene", type: "TINYINT", comment: "生成场景" }
|
||||
- { name: "table_name", type: "VARCHAR", comment: "表名称" }
|
||||
- { name: "table_comment", type: "VARCHAR", comment: "表描述" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
- { name: "module_name", type: "VARCHAR", comment: "模块名" }
|
||||
- { name: "business_name", type: "VARCHAR", comment: "业务名" }
|
||||
- { name: "class_name", type: "VARCHAR", comment: "类名称" }
|
||||
- { name: "class_comment", type: "VARCHAR", comment: "类描述" }
|
||||
- { name: "author", type: "VARCHAR", comment: "作者" }
|
||||
- { name: "template_type", type: "TINYINT", comment: "模板类型" }
|
||||
- { name: "front_type", type: "TINYINT", comment: "前端类型" }
|
||||
- { name: "parent_menu_id", type: "BIGINT", comment: "父菜单编号" }
|
||||
- { name: "master_table_id", type: "BIGINT", comment: "主表编号(子表时使用)" }
|
||||
- { name: "sub_join_column_id", type: "BIGINT", comment: "子表关联字段编号" }
|
||||
- { name: "sub_join_many", type: "BIT", comment: "是否一对多" }
|
||||
- { name: "tree_parent_column_id", type: "BIGINT", comment: "树表父字段编号" }
|
||||
- { name: "tree_name_column_id", type: "BIGINT", comment: "树表名称字段编号" }
|
||||
|
||||
- name: "infra_codegen_column"
|
||||
comment: "代码生成列定义"
|
||||
entity: "CodegenColumnDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号,主键" }
|
||||
- { name: "table_id", type: "BIGINT", comment: "表编号,关联 CodegenTableDO" }
|
||||
- { name: "column_name", type: "VARCHAR", comment: "字段名" }
|
||||
- { name: "data_type", type: "VARCHAR", comment: "数据库类型" }
|
||||
- { name: "column_comment", type: "VARCHAR", comment: "字段描述" }
|
||||
- { name: "nullable", type: "BIT", comment: "是否允许空" }
|
||||
- { name: "primary_key", type: "BIT", comment: "是否主键" }
|
||||
- { name: "ordinal_position", type: "INT", comment: "排序" }
|
||||
- { name: "java_type", type: "VARCHAR", comment: "Java 类型" }
|
||||
- { name: "java_field", type: "VARCHAR", comment: "Java 属性名" }
|
||||
- { name: "dict_type", type: "VARCHAR", comment: "字典类型" }
|
||||
- { name: "example", type: "VARCHAR", comment: "数据示例" }
|
||||
- { name: "create_operation", type: "BIT", comment: "是否 Create 操作字段" }
|
||||
- { name: "update_operation", type: "BIT", comment: "是否 Update 操作字段" }
|
||||
- { name: "list_operation", type: "BIT", comment: "是否 List 查询字段" }
|
||||
- { name: "list_operation_condition", type: "VARCHAR", comment: "List 查询条件" }
|
||||
- { name: "list_operation_result", type: "BIT", comment: "是否 List 返回字段" }
|
||||
- { name: "html_type", type: "VARCHAR", comment: "显示类型" }
|
||||
|
||||
# ========== 定时任务相关 ==========
|
||||
- name: "infra_job"
|
||||
comment: "定时任务表"
|
||||
entity: "JobDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "任务编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "任务名称" }
|
||||
- { name: "status", type: "TINYINT", comment: "任务状态" }
|
||||
- { name: "handler_name", type: "VARCHAR", comment: "处理器名称" }
|
||||
- { name: "handler_param", type: "VARCHAR", comment: "处理器参数" }
|
||||
- { name: "cron_expression", type: "VARCHAR", comment: "CRON 表达式" }
|
||||
- { name: "retry_count", type: "INT", comment: "重试次数" }
|
||||
- { name: "retry_interval", type: "INT", comment: "重试间隔(毫秒)" }
|
||||
- { name: "monitor_timeout", type: "INT", comment: "监控超时时间(毫秒)" }
|
||||
|
||||
- name: "infra_job_log"
|
||||
comment: "定时任务日志表"
|
||||
entity: "JobLogDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "日志编号,主键" }
|
||||
- { name: "job_id", type: "BIGINT", comment: "任务编号,关联 JobDO" }
|
||||
- { name: "handler_name", type: "VARCHAR", comment: "处理器名称" }
|
||||
- { name: "handler_param", type: "VARCHAR", comment: "处理器参数" }
|
||||
- { name: "execute_index", type: "INT", comment: "第几次执行" }
|
||||
- { name: "begin_time", type: "DATETIME", comment: "开始时间" }
|
||||
- { name: "end_time", type: "DATETIME", comment: "结束时间" }
|
||||
- { name: "duration", type: "INT", comment: "执行时长(毫秒)" }
|
||||
- { name: "status", type: "TINYINT", comment: "执行状态" }
|
||||
- { name: "result", type: "VARCHAR", comment: "执行结果" }
|
||||
|
||||
# ========== API 日志相关 ==========
|
||||
- name: "infra_api_access_log"
|
||||
comment: "API 访问日志表"
|
||||
entity: "ApiAccessLogDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号,主键" }
|
||||
- { name: "trace_id", type: "VARCHAR", comment: "链路追踪编号" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "user_type", type: "TINYINT", comment: "用户类型" }
|
||||
- { name: "application_name", type: "VARCHAR", comment: "应用名" }
|
||||
- { name: "request_method", type: "VARCHAR", comment: "请求方法" }
|
||||
- { name: "request_url", type: "VARCHAR", comment: "请求地址" }
|
||||
- { name: "request_params", type: "VARCHAR", comment: "请求参数" }
|
||||
- { name: "response_body", type: "VARCHAR", comment: "响应结果" }
|
||||
- { name: "user_ip", type: "VARCHAR", comment: "用户 IP" }
|
||||
- { name: "user_agent", type: "VARCHAR", comment: "浏览器 UA" }
|
||||
- { name: "operate_module", type: "VARCHAR", comment: "操作模块" }
|
||||
- { name: "operate_name", type: "VARCHAR", comment: "操作名" }
|
||||
- { name: "operate_type", type: "TINYINT", comment: "操作分类" }
|
||||
- { name: "begin_time", type: "DATETIME", comment: "开始时间" }
|
||||
- { name: "end_time", type: "DATETIME", comment: "结束时间" }
|
||||
- { name: "duration", type: "INT", comment: "执行时长(毫秒)" }
|
||||
- { name: "result_code", type: "INT", comment: "结果码" }
|
||||
- { name: "result_msg", type: "VARCHAR", comment: "结果提示" }
|
||||
|
||||
- name: "infra_api_error_log"
|
||||
comment: "API 异常日志表"
|
||||
entity: "ApiErrorLogDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号,主键" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "trace_id", type: "VARCHAR", comment: "链路追踪编号" }
|
||||
- { name: "user_type", type: "TINYINT", comment: "用户类型" }
|
||||
- { name: "application_name", type: "VARCHAR", comment: "应用名" }
|
||||
- { name: "request_method", type: "VARCHAR", comment: "请求方法" }
|
||||
- { name: "request_url", type: "VARCHAR", comment: "请求地址" }
|
||||
- { name: "request_params", type: "VARCHAR", comment: "请求参数" }
|
||||
- { name: "user_ip", type: "VARCHAR", comment: "用户 IP" }
|
||||
- { name: "user_agent", type: "VARCHAR", comment: "浏览器 UA" }
|
||||
- { name: "exception_time", type: "DATETIME", comment: "异常时间" }
|
||||
- { name: "exception_name", type: "VARCHAR", comment: "异常名" }
|
||||
- { name: "exception_message", type: "VARCHAR", comment: "异常消息" }
|
||||
- { name: "exception_root_cause_message", type: "VARCHAR", comment: "异常根消息" }
|
||||
- { name: "exception_stack_trace", type: "VARCHAR", comment: "异常栈轨迹" }
|
||||
- { name: "exception_class_name", type: "VARCHAR", comment: "异常类名" }
|
||||
- { name: "exception_file_name", type: "VARCHAR", comment: "异常文件名" }
|
||||
- { name: "exception_method_name", type: "VARCHAR", comment: "异常方法名" }
|
||||
- { name: "exception_line_number", type: "INT", comment: "异常行号" }
|
||||
- { name: "process_status", type: "TINYINT", comment: "处理状态" }
|
||||
- { name: "process_time", type: "DATETIME", comment: "处理时间" }
|
||||
- { name: "process_user_id", type: "BIGINT", comment: "处理用户编号" }
|
||||
|
||||
# ========== 系统配置相关 ==========
|
||||
- name: "infra_config"
|
||||
comment: "参数配置表"
|
||||
entity: "ConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "参数主键" }
|
||||
- { name: "category", type: "VARCHAR", comment: "参数分类" }
|
||||
- { name: "name", type: "VARCHAR", comment: "参数名称" }
|
||||
- { name: "config_key", type: "VARCHAR", comment: "参数键名" }
|
||||
- { name: "value", type: "VARCHAR", comment: "参数键值" }
|
||||
- { name: "type", type: "TINYINT", comment: "参数类型" }
|
||||
- { name: "visible", type: "BIT", comment: "是否可见" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
# ========== 数据源配置相关 ==========
|
||||
- name: "infra_data_source_config"
|
||||
comment: "数据源配置表"
|
||||
entity: "DataSourceConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键编号,0 表示 Master 数据源" }
|
||||
- { name: "name", type: "VARCHAR", comment: "连接名" }
|
||||
- { name: "url", type: "VARCHAR", comment: "数据源连接" }
|
||||
- { name: "username", type: "VARCHAR", comment: "用户名" }
|
||||
- { name: "password", type: "VARCHAR", comment: "密码(加密存储)" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "infra_file"
|
||||
to: "infra_file_config"
|
||||
type: "N:1"
|
||||
foreign_key: "config_id"
|
||||
- from: "infra_codegen_column"
|
||||
to: "infra_codegen_table"
|
||||
type: "N:1"
|
||||
foreign_key: "table_id"
|
||||
- from: "infra_job_log"
|
||||
to: "infra_job"
|
||||
type: "N:1"
|
||||
foreign_key: "job_id"
|
||||
- from: "infra_codegen_table"
|
||||
to: "infra_codegen_table"
|
||||
type: "N:1"
|
||||
foreign_key: "master_table_id"
|
||||
comment: "主子表关联"
|
||||
- from: "infra_codegen_table"
|
||||
to: "infra_data_source_config"
|
||||
type: "N:1"
|
||||
foreign_key: "data_source_config_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - xxx') - Swagger 文档标签"
|
||||
- "@RestController - REST 控制器"
|
||||
- "@RequestMapping('/infra/xxx') - 请求路径前缀"
|
||||
- "@Validated - 参数校验"
|
||||
- "@PreAuthorize('@ss.hasPermission('infra:xxx:action')') - 权限控制"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 文件存储")
|
||||
@RestController
|
||||
@RequestMapping("/infra/file")
|
||||
@Validated
|
||||
public class FileController {
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
@PostMapping("/upload")
|
||||
@Operation(summary = "上传文件")
|
||||
public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {
|
||||
MultipartFile file = uploadReqVO.getFile();
|
||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||
return success(fileService.createFile(content, file.getOriginalFilename(),
|
||||
uploadReqVO.getDirectory(), file.getContentType()));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "XxxService 接口定义业务方法,XxxServiceImpl 实现类"
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class XxxServiceImpl implements XxxService {
|
||||
@Resource
|
||||
private XxxMapper xxxMapper;
|
||||
|
||||
// 使用 static final 定义常量
|
||||
// 使用 @Transactional 注解控制事务
|
||||
// 使用 validateXxxExists 方法校验存在性
|
||||
// 使用 exception(ErrorCode) 抛出业务异常
|
||||
}
|
||||
example: |
|
||||
@Service
|
||||
public class FileServiceImpl implements FileService {
|
||||
@Resource
|
||||
private FileConfigService fileConfigService;
|
||||
@Resource
|
||||
private FileMapper fileMapper;
|
||||
|
||||
@Override
|
||||
public String createFile(byte[] content, String name, String directory, String type) {
|
||||
// 1. 处理参数
|
||||
// 2. 生成上传路径
|
||||
String path = generateUploadPath(name, directory);
|
||||
// 3. 上传到文件存储器
|
||||
FileClient client = fileConfigService.getMasterFileClient();
|
||||
String url = client.upload(content, path, type);
|
||||
// 4. 保存到数据库
|
||||
fileMapper.insert(new FileDO()...);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
public interface XxxMapper extends BaseMapperX<XxxDO> {
|
||||
// 继承 BaseMapperX 获得通用 CRUD 方法
|
||||
// 自定义查询方法使用 @Select 注解或 XML
|
||||
// 分页查询返回 PageResult<XxxDO>
|
||||
}
|
||||
example: |
|
||||
public interface FileMapper extends BaseMapperX<FileDO> {
|
||||
default PageResult<FileDO> selectPage(FilePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<FileDO>()
|
||||
.likeIfPresent(FileDO::getPath, reqVO.getPath())
|
||||
.likeIfPresent(FileDO::getName, reqVO.getName())
|
||||
.betweenIfPresent(FileDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(FileDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-001-xxx-xxx"
|
||||
examples:
|
||||
- code: "1_001_000_001"
|
||||
message: "参数配置不存在"
|
||||
- code: "1_001_001_000"
|
||||
message: "定时任务不存在"
|
||||
- code: "1_001_003_001"
|
||||
message: "文件不存在"
|
||||
- code: "1_001_004_002"
|
||||
message: "表定义已经存在"
|
||||
- code: "1_001_006_000"
|
||||
message: "文件配置不存在"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ErrorCode)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增文件存储类型
|
||||
new_channel:
|
||||
title: "新增文件存储类型"
|
||||
steps:
|
||||
- step: "1. 创建配置类"
|
||||
description: "在 framework/file/core/client/ 下创建 XxxFileClientConfig 实现 FileClientConfig"
|
||||
code: |
|
||||
@Data
|
||||
public class XxxFileClientConfig implements FileClientConfig {
|
||||
private String endpoint;
|
||||
private String accessKey;
|
||||
private String secretKey;
|
||||
}
|
||||
- step: "2. 创建客户端类"
|
||||
description: "创建 XxxFileClient 继承 AbstractFileClient<XxxFileClientConfig>"
|
||||
code: |
|
||||
public class XxxFileClient extends AbstractFileClient<XxxFileClientConfig> {
|
||||
public XxxFileClient(Long id, XxxFileClientConfig config) {
|
||||
super(id, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
// 初始化客户端
|
||||
}
|
||||
|
||||
@Override
|
||||
public String upload(byte[] content, String path, String type) {
|
||||
// 实现上传逻辑
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String path) {
|
||||
// 实现删除逻辑
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getContent(String path) {
|
||||
// 实现获取内容逻辑
|
||||
}
|
||||
}
|
||||
- step: "3. 注册枚举"
|
||||
description: "在 FileStorageEnum 中添加新存储类型"
|
||||
code: |
|
||||
XXX(30, XxxFileClientConfig.class, XxxFileClient.class);
|
||||
- step: "4. 测试验证"
|
||||
description: "编写单元测试验证功能正确性"
|
||||
|
||||
# 新增代码生成模板
|
||||
new_feature:
|
||||
title: "自定义代码生成模板"
|
||||
steps:
|
||||
- step: "1. 创建模板文件"
|
||||
description: "在 resources/codegen/ 下创建 .vm 模板文件"
|
||||
- step: "2. 注册模板路径"
|
||||
description: "在 CodegenEngine 的 SERVER_TEMPLATES 或 FRONT_TEMPLATES 中添加映射"
|
||||
code: |
|
||||
.put(javaTemplatePath("custom/template"), javaModuleImplMainFilePath("custom/${table.className}Custom"))
|
||||
- step: "3. 添加模板变量"
|
||||
description: "在 initBindingMap() 方法中添加模板需要的全局变量"
|
||||
- step: "4. 测试生成结果"
|
||||
description: "使用预览接口验证生成代码"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "文件上传路径规范"
|
||||
description: "使用 generateUploadPath() 方法生成唯一路径,包含日期前缀和时间戳后缀,避免文件名冲突"
|
||||
- practice: "文件配置缓存"
|
||||
description: "使用 Guava LoadingCache 缓存 FileClient,避免频繁查询数据库,支持异步刷新"
|
||||
- practice: "代码生成同步"
|
||||
description: "使用 syncCodegenFromDB() 同步数据库表结构变更,保持代码生成定义与数据库一致"
|
||||
- practice: "定时任务幂等"
|
||||
description: "JobHandler.execute() 应实现幂等性,避免重复执行产生副作用"
|
||||
- practice: "错误日志追踪"
|
||||
description: "通过 traceId 关联 API 访问日志和错误日志,便于问题排查"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-framework-common"
|
||||
purpose: "通用工具类、CommonResult、PageResult 等"
|
||||
- module: "yudao-framework-mybatis"
|
||||
purpose: "MyBatis-Plus 封装、BaseMapperX、BaseDO"
|
||||
- module: "yudao-framework-quartz"
|
||||
purpose: "Quartz 封装、JobHandler、SchedulerManager"
|
||||
- module: "yudao-framework-tenant"
|
||||
purpose: "租户支持、@TenantIgnore"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
purpose: "ORM 框架,提供 CRUD 封装"
|
||||
- name: "Quartz"
|
||||
purpose: "定时任务调度框架"
|
||||
- name: "Velocity"
|
||||
purpose: "模板引擎,用于代码生成"
|
||||
- name: "Hutool"
|
||||
purpose: "Java 工具类库"
|
||||
- name: "Guava"
|
||||
purpose: "Google 工具库,使用 Cache、Table 等"
|
||||
- name: "MinIO/S3 SDK"
|
||||
purpose: "对象存储客户端"
|
||||
- name: "Swagger/OpenAPI"
|
||||
purpose: "API 文档注解"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java"
|
||||
purpose: "文件服务实现,核心上传下载逻辑"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java"
|
||||
purpose: "文件配置服务,FileClient 缓存管理"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java"
|
||||
purpose: "文件客户端接口定义"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java"
|
||||
purpose: "文件客户端抽象类,模板方法模式"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java"
|
||||
purpose: "文件存储类型枚举,配置类与客户端类映射"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java"
|
||||
purpose: "代码生成服务实现"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java"
|
||||
purpose: "代码生成引擎,模板渲染核心"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java"
|
||||
purpose: "代码生成对象构建器"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java"
|
||||
purpose: "定时任务服务实现,Quartz 集成"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java"
|
||||
purpose: "文件 API 接口,供其他模块调用"
|
||||
- path: "yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义"
|
||||
@@ -1,722 +0,0 @@
|
||||
# Skill 文档 - yudao-module-iot 模块
|
||||
# 物联网平台模块知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-iot"
|
||||
name: "IoT Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-iot"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:物联网平台核心模块,提供设备接入、管理、数据采集、规则引擎、OTA升级等能力
|
||||
business_position: |
|
||||
IoT 物联网平台模块,作为整个系统的设备接入和管理中心:
|
||||
1. 设备管理:设备注册、认证、状态管理、分组管理
|
||||
2. 产品管理:产品定义、物模型配置、协议设置
|
||||
3. 协议适配:支持 MQTT、CoAP、HTTP、TCP、UDP、WebSocket、Modbus、EMQX 等多种协议
|
||||
4. 规则引擎:场景联动、数据流转、告警触发
|
||||
5. OTA升级:固件管理、升级任务、升级记录
|
||||
6. 数据处理:设备属性存储、消息记录、统计分析
|
||||
|
||||
# 设计原则:协议抽象、设备模型化、规则驱动
|
||||
design_principles:
|
||||
- "协议适配器模式:通过 IotProtocol 接口抽象不同传输协议,支持灵活扩展"
|
||||
- "产品-设备模型:产品定义设备模板,设备继承产品配置,支持一型一密动态注册"
|
||||
- "物模型驱动:通过 ThingModel 定义设备属性、事件、服务,实现标准化数据交互"
|
||||
- "规则引擎:基于触发器-条件-动作模型,实现场景联动自动化"
|
||||
- "多租户设计:设备、产品、规则等都支持租户隔离"
|
||||
- "分层架构:gateway 网关层处理协议,biz 业务层处理逻辑,core 核心层定义模型"
|
||||
|
||||
# 领域模型:核心领域对象及其关系
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "设备聚合"
|
||||
type: "聚合根"
|
||||
entities: ["IotDeviceDO", "IotDeviceGroupDO", "IotDevicePropertyDO", "IotDeviceMessageDO"]
|
||||
description: "设备是 IoT 平台的核心实体,包含设备基本信息、状态、属性、消息等"
|
||||
|
||||
- name: "产品聚合"
|
||||
type: "聚合根"
|
||||
entities: ["IotProductDO", "IotProductCategoryDO", "IotThingModelDO"]
|
||||
description: "产品是设备的模板,定义物模型(属性、事件、服务)和协议配置"
|
||||
|
||||
- name: "规则引擎聚合"
|
||||
type: "聚合根"
|
||||
entities: ["IotSceneRuleDO", "IotDataRuleDO", "IotDataSinkDO", "IotAlertConfigDO", "IotAlertRecordDO"]
|
||||
description: "规则引擎处理设备数据流转、场景联动、告警触发"
|
||||
|
||||
- name: "OTA升级聚合"
|
||||
type: "聚合根"
|
||||
entities: ["IotOtaFirmwareDO", "IotOtaTaskDO", "IotOtaTaskRecordDO"]
|
||||
description: "OTA 升级管理固件版本、升级任务和升级记录"
|
||||
|
||||
value_objects:
|
||||
- name: "ThingModelProperty"
|
||||
description: "物模型属性定义,包含数据类型、单位、范围等"
|
||||
- name: "ThingModelEvent"
|
||||
description: "物模型事件定义,包含事件类型、参数等"
|
||||
- name: "ThingModelService"
|
||||
description: "物模型服务定义,包含输入输出参数"
|
||||
- name: "IotDeviceIdentity"
|
||||
description: "设备身份标识,包含 productKey、deviceName"
|
||||
|
||||
services:
|
||||
- name: "IotDeviceService"
|
||||
description: "设备管理服务,处理设备 CRUD、认证、状态更新"
|
||||
- name: "IotDeviceMessageService"
|
||||
description: "设备消息服务,处理消息收发、记录存储"
|
||||
- name: "IotSceneRuleService"
|
||||
description: "场景规则服务,执行场景联动逻辑"
|
||||
- name: "IotProtocol"
|
||||
description: "协议接口,定义协议生命周期(启动、停止、运行状态)"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "yudao-module-iot-gateway"
|
||||
purpose: "协议网关层,处理设备连接、协议解析"
|
||||
components:
|
||||
- "protocol/mqtt/IotMqttProtocol - MQTT 协议实现"
|
||||
- "protocol/coap/IotCoapProtocol - CoAP 协议实现"
|
||||
- "protocol/http/IotHttpProtocol - HTTP 协议实现"
|
||||
- "protocol/tcp/IotTcpProtocol - TCP 协议实现"
|
||||
- "protocol/udp/IotUdpProtocol - UDP 协议实现"
|
||||
- "protocol/websocket/IotWebSocketProtocol - WebSocket 协议实现"
|
||||
- "protocol/modbus/IotModbusTcpClientProtocol - Modbus TCP 客户端"
|
||||
- "protocol/modbus/IotModbusTcpServerProtocol - Modbus TCP 服务端"
|
||||
- "protocol/emqx/IotEmqxProtocol - EMQX 协议桥接"
|
||||
- "handler/upstream - 上行消息处理器"
|
||||
- "handler/downstream - 下行消息处理器"
|
||||
|
||||
- name: "yudao-module-iot-biz"
|
||||
purpose: "业务逻辑层,处理设备、产品、规则等核心业务"
|
||||
components:
|
||||
- "service/device - 设备管理服务"
|
||||
- "service/product - 产品管理服务"
|
||||
- "service/thingmodel - 物模型管理服务"
|
||||
- "service/rule - 规则引擎服务"
|
||||
- "service/ota - OTA 升级服务"
|
||||
- "service/alert - 告警服务"
|
||||
- "controller/admin - 管理后台接口"
|
||||
|
||||
- name: "yudao-module-iot-core"
|
||||
purpose: "核心层,定义公共模型、枚举、工具类"
|
||||
components:
|
||||
- "enums - 协议类型、设备状态、消息方法等枚举"
|
||||
- "mq/message - 设备消息模型"
|
||||
- "biz/dto - 业务 DTO"
|
||||
- "topic - Topic 定义和工具"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "策略模式 / 适配器模式"
|
||||
location: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocol.java"
|
||||
purpose: "定义协议接口,不同协议实现统一接口,支持灵活扩展新协议"
|
||||
|
||||
- pattern: "模板方法模式"
|
||||
location: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/AbstractIotProtocolDownstreamSubscriber.java"
|
||||
purpose: "抽象下行消息订阅者,子类实现具体的消息处理逻辑"
|
||||
|
||||
- pattern: "观察者模式"
|
||||
location: "yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/"
|
||||
purpose: "场景规则引擎,设备状态变化触发规则执行"
|
||||
|
||||
- pattern: "责任链模式"
|
||||
location: "yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/"
|
||||
purpose: "规则匹配器链,依次判断触发条件、执行条件是否满足"
|
||||
|
||||
- pattern: "工厂模式"
|
||||
location: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/IotMessageSerializerManager.java"
|
||||
purpose: "消息序列化器工厂,根据序列化类型获取对应的序列化器"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "IotDeviceCommonApi"
|
||||
path: "yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java"
|
||||
description: "设备通用 API,提供设备认证、查询、注册等接口"
|
||||
methods:
|
||||
- "authDevice - 设备认证"
|
||||
- "getDevice - 获取设备信息"
|
||||
- "getModbusDeviceConfigList - 获取 Modbus 设备配置"
|
||||
- "registerDevice - 设备动态注册"
|
||||
- "registerSubDevices - 子设备动态注册"
|
||||
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取告警接收用户信息"
|
||||
|
||||
mq:
|
||||
- type: "IotMessageBus"
|
||||
description: "内部消息总线,用于设备消息在 gateway 和 biz 之间传递"
|
||||
topics:
|
||||
- "device:message:upstream - 设备上行消息"
|
||||
- "device:message:downstream - 设备下行消息"
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "TenantBaseDO | BaseDO"
|
||||
description: |
|
||||
- TenantBaseDO: 设备、产品、场景规则等需要租户隔离的实体
|
||||
- BaseDO: 设备分组、固件、告警记录等不需要租户隔离的实体
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# 设备相关表
|
||||
- name: "iot_device"
|
||||
comment: "IoT 设备表"
|
||||
entity: "IotDeviceDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "设备 ID,主键" }
|
||||
- { name: "device_name", type: "VARCHAR(64)", comment: "设备名称,产品内唯一" }
|
||||
- { name: "nickname", type: "VARCHAR(64)", comment: "设备备注名称" }
|
||||
- { name: "serial_number", type: "VARCHAR(64)", comment: "设备序列号" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品编号" }
|
||||
- { name: "product_key", type: "VARCHAR(32)", comment: "产品标识(冗余)" }
|
||||
- { name: "device_type", type: "INT", comment: "设备类型(冗余)" }
|
||||
- { name: "gateway_id", type: "BIGINT", comment: "网关设备编号(子设备)" }
|
||||
- { name: "state", type: "INT", comment: "设备状态:0未激活、1在线、2离线" }
|
||||
- { name: "device_secret", type: "VARCHAR(64)", comment: "设备密钥" }
|
||||
- { name: "group_ids", type: "VARCHAR(255)", comment: "设备分组编号集合(JSON)" }
|
||||
- { name: "firmware_id", type: "BIGINT", comment: "固件编号" }
|
||||
- { name: "latitude", type: "DECIMAL(10,6)", comment: "纬度" }
|
||||
- { name: "longitude", type: "DECIMAL(10,6)", comment: "经度" }
|
||||
- { name: "online_time", type: "DATETIME", comment: "最后上线时间" }
|
||||
- { name: "offline_time", type: "DATETIME", comment: "最后离线时间" }
|
||||
- { name: "active_time", type: "DATETIME", comment: "激活时间" }
|
||||
indexes:
|
||||
- { name: "idx_product_id", columns: ["product_id"] }
|
||||
- { name: "idx_product_key_device_name", columns: ["product_key", "device_name"], unique: true }
|
||||
|
||||
- name: "iot_device_group"
|
||||
comment: "IoT 设备分组表"
|
||||
entity: "IotDeviceGroupDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "分组 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "分组名称" }
|
||||
- { name: "status", type: "INT", comment: "分组状态" }
|
||||
- { name: "description", type: "VARCHAR(255)", comment: "分组描述" }
|
||||
|
||||
- name: "iot_device_message"
|
||||
comment: "IoT 设备消息表"
|
||||
entity: "IotDeviceMessageDO"
|
||||
extends: "BaseDO"
|
||||
storage: "TDengine 时序数据库"
|
||||
columns:
|
||||
- { name: "device_id", type: "BIGINT", comment: "设备编号" }
|
||||
- { name: "product_key", type: "VARCHAR(32)", comment: "产品标识" }
|
||||
- { name: "device_name", type: "VARCHAR(64)", comment: "设备名称" }
|
||||
- { name: "method", type: "VARCHAR(64)", comment: "消息方法" }
|
||||
- { name: "request_id", type: "VARCHAR(64)", comment: "请求 ID" }
|
||||
- { name: "payload", type: "TEXT", comment: "消息内容" }
|
||||
|
||||
# 产品相关表
|
||||
- name: "iot_product"
|
||||
comment: "IoT 产品表"
|
||||
entity: "IotProductDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "产品 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "产品名称" }
|
||||
- { name: "product_key", type: "VARCHAR(32)", comment: "产品标识" }
|
||||
- { name: "product_secret", type: "VARCHAR(64)", comment: "产品密钥" }
|
||||
- { name: "category_id", type: "BIGINT", comment: "产品分类编号" }
|
||||
- { name: "device_type", type: "INT", comment: "设备类型" }
|
||||
- { name: "net_type", type: "INT", comment: "联网方式" }
|
||||
- { name: "protocol_type", type: "VARCHAR(32)", comment: "协议类型" }
|
||||
- { name: "serialize_type", type: "VARCHAR(32)", comment: "序列化类型" }
|
||||
- { name: "status", type: "INT", comment: "产品状态" }
|
||||
- { name: "register_enabled", type: "BOOLEAN", comment: "是否开启动态注册" }
|
||||
|
||||
- name: "iot_product_category"
|
||||
comment: "IoT 产品分类表"
|
||||
entity: "IotProductCategoryDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "分类 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "分类名称" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "父分类 ID" }
|
||||
- { name: "sort", type: "INT", comment: "排序" }
|
||||
|
||||
- name: "iot_thing_model"
|
||||
comment: "IoT 产品物模型功能表"
|
||||
entity: "IotThingModelDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "物模型功能 ID" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品编号" }
|
||||
- { name: "product_key", type: "VARCHAR(32)", comment: "产品标识" }
|
||||
- { name: "identifier", type: "VARCHAR(64)", comment: "功能标识符" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "功能名称" }
|
||||
- { name: "type", type: "INT", comment: "功能类型:1属性、2事件、3服务" }
|
||||
- { name: "property", type: "JSON", comment: "属性定义" }
|
||||
- { name: "event", type: "JSON", comment: "事件定义" }
|
||||
- { name: "service", type: "JSON", comment: "服务定义" }
|
||||
|
||||
# 规则引擎相关表
|
||||
- name: "iot_scene_rule"
|
||||
comment: "IoT 场景联动规则表"
|
||||
entity: "IotSceneRuleDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "场景联动 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "场景名称" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "triggers", type: "JSON", comment: "触发器配置" }
|
||||
- { name: "actions", type: "JSON", comment: "动作配置" }
|
||||
- { name: "last_trigger_time", type: "DATETIME", comment: "最后触发时间" }
|
||||
|
||||
- name: "iot_data_sink"
|
||||
comment: "IoT 数据流转目的地表"
|
||||
entity: "IotDataSinkDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "数据目的地 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "名称" }
|
||||
- { name: "type", type: "VARCHAR(32)", comment: "类型:HTTP/MQTT/Kafka/RabbitMQ/Redis/RocketMQ/TCP/WebSocket" }
|
||||
- { name: "config", type: "JSON", comment: "配置信息" }
|
||||
|
||||
- name: "iot_alert_config"
|
||||
comment: "IoT 告警配置表"
|
||||
entity: "IotAlertConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "告警配置 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "告警名称" }
|
||||
- { name: "level", type: "INT", comment: "告警级别" }
|
||||
- { name: "status", type: "INT", comment: "状态" }
|
||||
- { name: "scene_rule_ids", type: "VARCHAR(255)", comment: "关联的场景规则 ID 列表" }
|
||||
- { name: "receive_user_ids", type: "VARCHAR(255)", comment: "接收用户 ID 列表" }
|
||||
|
||||
# OTA 升级相关表
|
||||
- name: "iot_ota_firmware"
|
||||
comment: "IoT OTA 固件表"
|
||||
entity: "IotOtaFirmwareDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "固件 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "固件名称" }
|
||||
- { name: "version", type: "VARCHAR(32)", comment: "版本号" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品编号" }
|
||||
- { name: "file_url", type: "VARCHAR(512)", comment: "固件文件 URL" }
|
||||
- { name: "file_size", type: "BIGINT", comment: "文件大小" }
|
||||
- { name: "file_digest_algorithm", type: "VARCHAR(32)", comment: "签名算法" }
|
||||
- { name: "file_digest_value", type: "VARCHAR(128)", comment: "签名值" }
|
||||
|
||||
- name: "iot_ota_task"
|
||||
comment: "IoT OTA 升级任务表"
|
||||
entity: "IotOtaTaskDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "任务 ID" }
|
||||
- { name: "name", type: "VARCHAR(64)", comment: "任务名称" }
|
||||
- { name: "firmware_id", type: "BIGINT", comment: "固件编号" }
|
||||
- { name: "product_id", type: "BIGINT", comment: "产品编号" }
|
||||
- { name: "status", type: "INT", comment: "任务状态" }
|
||||
|
||||
- name: "iot_ota_task_record"
|
||||
comment: "IoT OTA 升级任务记录表"
|
||||
entity: "IotOtaTaskRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "记录 ID" }
|
||||
- { name: "task_id", type: "BIGINT", comment: "任务 ID" }
|
||||
- { name: "device_id", type: "BIGINT", comment: "设备 ID" }
|
||||
- { name: "status", type: "INT", comment: "升级状态" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "iot_device"
|
||||
to: "iot_product"
|
||||
type: "N:1"
|
||||
foreign_key: "product_id"
|
||||
description: "设备属于某个产品"
|
||||
|
||||
- from: "iot_device"
|
||||
to: "iot_device"
|
||||
type: "N:1"
|
||||
foreign_key: "gateway_id"
|
||||
description: "子设备关联网关设备"
|
||||
|
||||
- from: "iot_product"
|
||||
to: "iot_product_category"
|
||||
type: "N:1"
|
||||
foreign_key: "category_id"
|
||||
description: "产品属于某个分类"
|
||||
|
||||
- from: "iot_thing_model"
|
||||
to: "iot_product"
|
||||
type: "N:1"
|
||||
foreign_key: "product_id"
|
||||
description: "物模型属于某个产品"
|
||||
|
||||
- from: "iot_ota_firmware"
|
||||
to: "iot_product"
|
||||
type: "N:1"
|
||||
foreign_key: "product_id"
|
||||
description: "固件属于某个产品"
|
||||
|
||||
- from: "iot_ota_task"
|
||||
to: "iot_ota_firmware"
|
||||
type: "N:1"
|
||||
foreign_key: "firmware_id"
|
||||
description: "升级任务使用某个固件"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = \"管理后台 - IoT 设备\")"
|
||||
- "@RestController"
|
||||
- "@RequestMapping(\"/iot/device\")"
|
||||
- "@PreAuthorize(\"@ss.hasPermission('iot:device:create')\")"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - IoT 设备")
|
||||
@RestController
|
||||
@RequestMapping("/iot/device")
|
||||
@Validated
|
||||
public class IotDeviceController {
|
||||
@Resource
|
||||
private IotDeviceService deviceService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建设备")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:create')")
|
||||
public CommonResult<Long> createDevice(@Valid @RequestBody IotDeviceSaveReqVO createReqVO) {
|
||||
return success(deviceService.createDevice(createReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得设备分页")
|
||||
@PreAuthorize("@ss.hasPermission('iot:device:query')")
|
||||
public CommonResult<PageResult<IotDeviceRespVO>> getDevicePage(@Valid IotDevicePageReqVO pageReqVO) {
|
||||
PageResult<IotDeviceDO> pageResult = deviceService.getDevicePage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: |
|
||||
public interface IotDeviceService {
|
||||
// CRUD 操作
|
||||
Long createDevice(@Valid IotDeviceSaveReqVO createReqVO);
|
||||
void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
|
||||
void deleteDevice(Long id);
|
||||
IotDeviceDO getDevice(Long id);
|
||||
PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
|
||||
|
||||
// 业务操作
|
||||
boolean authDevice(@Valid IotDeviceAuthReqDTO authReqDTO);
|
||||
void updateDeviceState(Long id, Integer state);
|
||||
IotDeviceRegisterRespDTO registerDevice(@Valid IotDeviceRegisterReqDTO reqDTO);
|
||||
|
||||
// 缓存操作
|
||||
IotDeviceDO getDeviceFromCache(Long id);
|
||||
IotDeviceDO getDeviceFromCache(String productKey, String deviceName);
|
||||
}
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
@Resource
|
||||
private IotDeviceMapper deviceMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createDevice(IotDeviceSaveReqVO createReqVO) {
|
||||
// 1. 校验产品存在
|
||||
IotProductDO product = productService.validateProductExists(createReqVO.getProductId());
|
||||
// 2. 生成设备密钥
|
||||
String deviceSecret = generateDeviceSecret();
|
||||
// 3. 插入设备
|
||||
IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class)
|
||||
.setProductKey(product.getProductKey())
|
||||
.setDeviceType(product.getDeviceType())
|
||||
.setDeviceSecret(deviceSecret)
|
||||
.setState(IotDeviceStateEnum.INACTIVE.getState());
|
||||
deviceMapper.insert(device);
|
||||
return device.getId();
|
||||
}
|
||||
}
|
||||
example: |
|
||||
// 设备认证示例
|
||||
@Override
|
||||
public boolean authDevice(IotDeviceAuthReqDTO authReqDTO) {
|
||||
// 1. 获取设备信息
|
||||
IotDeviceDO device = getDeviceFromCache(authReqDTO.getProductKey(), authReqDTO.getDeviceName());
|
||||
if (device == null) {
|
||||
return false;
|
||||
}
|
||||
// 2. 校验设备密钥
|
||||
if (!StrUtil.equals(device.getDeviceSecret(), authReqDTO.getDeviceSecret())) {
|
||||
return false;
|
||||
}
|
||||
// 3. 更新设备状态为在线
|
||||
updateDeviceState(device, IotDeviceStateEnum.ONLINE.getState());
|
||||
return true;
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
@Mapper
|
||||
public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
|
||||
default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()
|
||||
.likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())
|
||||
.eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())
|
||||
.eqIfPresent(IotDeviceDO::getState, reqVO.getState())
|
||||
.orderByDesc(IotDeviceDO::getId));
|
||||
}
|
||||
}
|
||||
example: |
|
||||
// 设备属性存储在 Redis 中
|
||||
@Resource
|
||||
private DevicePropertyRedisDAO devicePropertyRedisDAO;
|
||||
|
||||
public void updateDeviceProperty(String productKey, String deviceName, String identifier, Object value) {
|
||||
String key = formatDevicePropertyKey(productKey, deviceName, identifier);
|
||||
IotDevicePropertyDO property = IotDevicePropertyDO.builder()
|
||||
.value(value)
|
||||
.updateTime(LocalDateTime.now())
|
||||
.build();
|
||||
devicePropertyRedisDAO.set(key, property);
|
||||
}
|
||||
|
||||
# 协议扩展示例
|
||||
protocol:
|
||||
interface_definition: |
|
||||
public interface IotProtocol {
|
||||
String getId(); // 协议实例 ID
|
||||
String getServerId(); // 服务器 ID(消息追踪)
|
||||
IotProtocolTypeEnum getType(); // 协议类型
|
||||
void start(); // 启动协议服务
|
||||
void stop(); // 停止协议服务
|
||||
boolean isRunning(); // 检查运行状态
|
||||
}
|
||||
example: |
|
||||
// MQTT 协议实现示例
|
||||
public class IotMqttProtocol implements IotProtocol {
|
||||
private MqttServer mqttServer;
|
||||
private IotMqttConnectionManager connectionManager;
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
// 1. 创建 MQTT 服务器
|
||||
mqttServer = MqttServer.create(vertx, options);
|
||||
mqttServer.endpointHandler(this::handleEndpoint);
|
||||
|
||||
// 2. 启动服务器
|
||||
mqttServer.listen();
|
||||
running = true;
|
||||
|
||||
// 3. 启动下行消息订阅者
|
||||
downstreamSubscriber.start();
|
||||
}
|
||||
|
||||
private void handleEndpoint(MqttEndpoint endpoint) {
|
||||
// 1. 处理认证
|
||||
if (!authHandler.handleAuthenticationRequest(endpoint)) {
|
||||
endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD);
|
||||
return;
|
||||
}
|
||||
// 2. 设置消息处理器
|
||||
endpoint.publishHandler(message -> processMessage(endpoint, message));
|
||||
// 3. 接受连接
|
||||
endpoint.accept(false);
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-011"
|
||||
examples:
|
||||
- code: "1-011-000-000"
|
||||
message: "设备不存在"
|
||||
- code: "1-011-000-001"
|
||||
message: "设备认证失败"
|
||||
- code: "1-011-000-002"
|
||||
message: "设备已存在"
|
||||
- code: "1-011-001-000"
|
||||
message: "产品不存在"
|
||||
- code: "1-011-002-000"
|
||||
message: "物模型不存在"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "CommonResult.error(ErrorCodeConstants.DEVICE_NOT_EXISTS)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增协议类型
|
||||
new_protocol:
|
||||
title: "新增协议类型"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "添加协议类型枚举"
|
||||
file: "yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotProtocolTypeEnum.java"
|
||||
description: "在枚举中添加新的协议类型,如 GOPHER(\"gopher\")"
|
||||
|
||||
- step: 2
|
||||
action: "创建协议配置类"
|
||||
file: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/{protocol}/Iot{Protocol}Config.java"
|
||||
description: "定义协议特有的配置参数,如端口、超时时间、SSL 配置等"
|
||||
|
||||
- step: 3
|
||||
action: "实现协议接口"
|
||||
file: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/{protocol}/Iot{Protocol}Protocol.java"
|
||||
description: |
|
||||
实现 IotProtocol 接口:
|
||||
- start(): 启动协议服务,创建服务器/客户端
|
||||
- stop(): 停止协议服务,释放资源
|
||||
- handleUpstream(): 处理上行消息
|
||||
- handleDownstream(): 处理下行消息
|
||||
|
||||
- step: 4
|
||||
action: "实现上行消息处理器"
|
||||
file: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/{protocol}/handler/upstream/Iot{Protocol}UpstreamHandler.java"
|
||||
description: "处理设备上报的消息,解析后发送到消息总线"
|
||||
|
||||
- step: 5
|
||||
action: "实现下行消息订阅者"
|
||||
file: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/{protocol}/handler/downstream/Iot{Protocol}DownstreamSubscriber.java"
|
||||
description: "订阅下行消息,下发给设备"
|
||||
|
||||
- step: 6
|
||||
action: "注册协议到管理器"
|
||||
file: "yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocolManager.java"
|
||||
description: "在配置中添加新协议的实例化逻辑"
|
||||
|
||||
# 新增设备类型
|
||||
new_device_type:
|
||||
title: "新增设备类型"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "添加设备类型枚举"
|
||||
file: "yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductDeviceTypeEnum.java"
|
||||
description: "在枚举中添加新的设备类型"
|
||||
|
||||
- step: 2
|
||||
action: "定义物模型"
|
||||
file: "通过管理后台配置"
|
||||
description: "为新产品定义物模型(属性、事件、服务)"
|
||||
|
||||
- step: 3
|
||||
action: "配置协议"
|
||||
file: "产品管理页面"
|
||||
description: "设置产品使用的协议类型和序列化方式"
|
||||
|
||||
# 新增规则动作
|
||||
new_rule_action:
|
||||
title: "新增场景联动动作类型"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "添加动作类型枚举"
|
||||
file: "yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/rule/IotSceneRuleActionTypeEnum.java"
|
||||
description: "添加新的动作类型枚举值"
|
||||
|
||||
- step: 2
|
||||
action: "实现动作执行器"
|
||||
file: "yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/Iot{Action}SceneRuleAction.java"
|
||||
description: "实现 IotSceneRuleAction 接口,定义动作执行逻辑"
|
||||
|
||||
- step: 3
|
||||
action: "注册动作执行器"
|
||||
description: "在 Spring 容器中注册为 Bean,自动被规则引擎发现和调用"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "设备密钥安全性:设备密钥应使用强随机数生成,避免硬编码或简单规则"
|
||||
- "协议扩展:新协议应继承 AbstractIotProtocolDownstreamSubscriber,复用下行消息处理逻辑"
|
||||
- "消息存储:设备消息使用 TDengine 时序数据库存储,支持高效查询和聚合"
|
||||
- "设备缓存:频繁访问的设备信息使用 Redis 缓存,减少数据库压力"
|
||||
- "规则引擎:场景规则应设置合理的触发条件,避免频繁触发导致性能问题"
|
||||
- "OTA升级:大规模升级应分批进行,避免网络拥塞和服务器压力"
|
||||
- "租户隔离:所有设备操作都应考虑租户隔离,防止越权访问"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取告警接收用户信息"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "vertx-mqtt"
|
||||
version: "4.x"
|
||||
purpose: "MQTT 服务器实现"
|
||||
- name: "californium"
|
||||
version: "3.x"
|
||||
purpose: "CoAP 服务器实现"
|
||||
- name: "jmodbus"
|
||||
version: "1.x"
|
||||
purpose: "Modbus 协议实现"
|
||||
- name: "netty"
|
||||
version: "4.x"
|
||||
purpose: "TCP/UDP/WebSocket 网络框架"
|
||||
- name: "tdengine-jdbc"
|
||||
version: "3.x"
|
||||
purpose: "TDengine 时序数据库驱动"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotProtocolTypeEnum.java"
|
||||
purpose: "协议类型枚举定义"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/IotProtocol.java"
|
||||
purpose: "协议接口定义,所有协议必须实现此接口"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotMqttProtocol.java"
|
||||
purpose: "MQTT 协议实现,最常用的设备接入协议"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java"
|
||||
purpose: "设备管理服务接口"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/message/IotDeviceMessageService.java"
|
||||
purpose: "设备消息服务接口"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleService.java"
|
||||
purpose: "场景联动规则服务"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java"
|
||||
purpose: "设备实体类定义"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java"
|
||||
purpose: "产品实体类定义"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/thingmodel/IotThingModelDO.java"
|
||||
purpose: "物模型实体类定义"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java"
|
||||
purpose: "设备管理 Controller"
|
||||
|
||||
- path: "yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/IotDeviceCommonApi.java"
|
||||
purpose: "设备通用 API 接口"
|
||||
@@ -1,654 +0,0 @@
|
||||
# Skill 模板文件
|
||||
# 用于提取模块知识的标准格式
|
||||
|
||||
skill:
|
||||
id: "skill-mall"
|
||||
name: "Mall Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-mall"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
商城系统(Mall)是 ruoyi-vue-pro 项目的电商核心模块,提供完整的电商解决方案。
|
||||
包含商品管理、促销活动、交易订单、数据统计四大核心能力:
|
||||
- 商品管理:SPU/SKU 商品模型、商品分类、品牌、属性规格管理
|
||||
- 促销活动:优惠券、秒杀、拼团、砍价、满减送、积分商城等
|
||||
- 交易订单:购物车、订单创建/支付/发货/收货、售后退款
|
||||
- 数据统计:交易统计、商品统计、会员统计等
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "SPU-SKU 模型:标准电商商品模型,SPU 为商品标准化单元,SKU 为库存单位"
|
||||
- "订单状态机:订单状态流转严格遵循状态机模式,确保状态变更可追溯"
|
||||
- "领域驱动设计:按业务领域划分子模块(product/promotion/trade/statistics)"
|
||||
- "分层架构:Controller -> Service -> DAL 三层架构,职责清晰"
|
||||
- "聚合根设计:订单(TradeOrder)和商品(ProductSpu)作为聚合根"
|
||||
- "API 模块分离:trade-api 独立模块,提供跨模块调用接口"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "ProductSpu(商品SPU)"
|
||||
type: "聚合根"
|
||||
entities: ["ProductSku(商品SKU)", "ProductCategory(分类)", "ProductBrand(品牌)", "ProductProperty(属性)"]
|
||||
description: "商品SPU作为聚合根,管理SKU、分类、品牌、属性等关联实体"
|
||||
- name: "TradeOrder(交易订单)"
|
||||
type: "聚合根"
|
||||
entities: ["TradeOrderItem(订单项)", "AfterSale(售后单)", "Cart(购物车)"]
|
||||
description: "订单作为交易核心聚合根,管理订单项、售后、购物车等"
|
||||
- name: "CouponTemplate(优惠券模板)"
|
||||
type: "聚合根"
|
||||
entities: ["Coupon(优惠券)"]
|
||||
description: "优惠券模板管理优惠券的发放和使用"
|
||||
- name: "CombinationActivity(拼团活动)"
|
||||
type: "聚合根"
|
||||
entities: ["CombinationProduct(拼团商品)", "CombinationRecord(拼团记录)"]
|
||||
description: "拼团活动管理拼团商品和拼团参与记录"
|
||||
|
||||
value_objects:
|
||||
- "ProductSkuDO.Property(SKU属性值对象)"
|
||||
- "TradeOrderDO.Address(收货地址值对象)"
|
||||
- "DeliveryExpressTemplateCharge(运费模板计费规则)"
|
||||
|
||||
services:
|
||||
- "ProductSpuService:商品SPU业务逻辑"
|
||||
- "TradeOrderUpdateService:订单创建和状态更新"
|
||||
- "TradePriceService:价格计算服务"
|
||||
- "AfterSaleService:售后服务"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口,提供RPC调用能力"
|
||||
components:
|
||||
- "ProductSpuApi/ProductSkuApi:商品信息查询、库存更新"
|
||||
- "TradeOrderApi:订单信息查询"
|
||||
- "CouponApi:优惠券使用"
|
||||
- "SeckillActivityApi/CombinationRecordApi:营销活动验证"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口,分为管理后台(admin)和用户端(app)"
|
||||
components:
|
||||
- "admin/spu/ProductSpuController:商品SPU管理"
|
||||
- "admin/order/TradeOrderController:订单管理"
|
||||
- "app/spu/AppProductSpuController:商品展示"
|
||||
- "app/order/AppTradeOrderController:用户下单"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层,核心业务处理"
|
||||
components:
|
||||
- "ProductSpuService/ProductSkuService:商品业务"
|
||||
- "TradeOrderUpdateService/TradeOrderQueryService:订单业务"
|
||||
- "AfterSaleService:售后业务"
|
||||
- "CartService:购物车业务"
|
||||
- "CouponService:优惠券业务"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层,MyBatis-Plus Mapper"
|
||||
components:
|
||||
- "ProductSpuMapper/ProductSkuMapper:商品数据访问"
|
||||
- "TradeOrderMapper/TradeOrderItemMapper:订单数据访问"
|
||||
- "CouponMapper/CouponTemplateMapper:优惠券数据访问"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "状态模式"
|
||||
location: "TradeOrderStatusEnum, ProductSpuStatusEnum"
|
||||
purpose: "订单状态和商品状态的状态机管理,通过枚举定义状态流转"
|
||||
- pattern: "策略模式"
|
||||
location: "TradePriceService"
|
||||
purpose: "价格计算策略,支持不同促销活动的价格计算"
|
||||
- pattern: "模板方法模式"
|
||||
location: "BaseDO, TenantBaseDO"
|
||||
purpose: "数据实体基类,统一处理创建时间、更新时间、删除标记等"
|
||||
- pattern: "工厂模式"
|
||||
location: "ProductSpuConvert, TradeOrderConvert"
|
||||
purpose: "MapStruct转换器,VO与DO之间的对象转换"
|
||||
- pattern: "观察者模式"
|
||||
location: "TradeOrderLogService"
|
||||
purpose: "订单操作日志记录,订单状态变更时自动记录日志"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "ProductSpuApi"
|
||||
module: "yudao-module-product"
|
||||
purpose: "查询商品信息、更新库存"
|
||||
- name: "ProductSkuApi"
|
||||
module: "yudao-module-product"
|
||||
purpose: "查询SKU信息、更新库存"
|
||||
- name: "CouponApi"
|
||||
module: "yudao-module-promotion"
|
||||
purpose: "优惠券使用、回收"
|
||||
- name: "SeckillActivityApi"
|
||||
module: "yudao-module-promotion"
|
||||
purpose: "秒杀活动校验、库存扣减"
|
||||
|
||||
consumers:
|
||||
- module: "yudao-module-trade"
|
||||
api: "ProductSpuApi"
|
||||
purpose: "下单时查询商品信息、扣减库存"
|
||||
- module: "yudao-module-trade"
|
||||
api: "CouponApi"
|
||||
purpose: "下单时使用优惠券"
|
||||
- module: "yudao-module-statistics"
|
||||
api: "TradeOrderApi"
|
||||
purpose: "统计交易数据"
|
||||
|
||||
mq:
|
||||
- topic: "order.paid"
|
||||
purpose: "订单支付成功消息,触发积分发放、优惠券赠送等"
|
||||
- topic: "order.delivered"
|
||||
purpose: "订单发货消息,通知用户"
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有DO实体继承BaseDO,包含id、creator、createTime、updater、updateTime、deleted字段"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 商品模块 ==========
|
||||
- name: "product_spu"
|
||||
comment: "商品SPU表"
|
||||
entity: "ProductSpuDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "商品SPU编号" }
|
||||
- { name: "name", type: "String", comment: "商品名称" }
|
||||
- { name: "keyword", type: "String", comment: "关键字" }
|
||||
- { name: "introduction", type: "String", comment: "商品简介" }
|
||||
- { name: "description", type: "String", comment: "商品详情" }
|
||||
- { name: "category_id", type: "Long", comment: "分类编号" }
|
||||
- { name: "brand_id", type: "Long", comment: "品牌编号" }
|
||||
- { name: "pic_url", type: "String", comment: "商品封面图" }
|
||||
- { name: "slider_pic_urls", type: "List<String>", comment: "轮播图" }
|
||||
- { name: "status", type: "Integer", comment: "商品状态:-1回收站 0下架 1上架" }
|
||||
- { name: "spec_type", type: "Boolean", comment: "规格类型:false单规格 true多规格" }
|
||||
- { name: "price", type: "Integer", comment: "最低价格(分)" }
|
||||
- { name: "market_price", type: "Integer", comment: "市场价(分)" }
|
||||
- { name: "cost_price", type: "Integer", comment: "成本价(分)" }
|
||||
- { name: "stock", type: "Integer", comment: "库存总量" }
|
||||
- { name: "sales_count", type: "Integer", comment: "销量" }
|
||||
- { name: "delivery_types", type: "List<Integer>", comment: "配送方式" }
|
||||
- { name: "delivery_template_id", type: "Long", comment: "运费模板编号" }
|
||||
|
||||
- name: "product_sku"
|
||||
comment: "商品SKU表"
|
||||
entity: "ProductSkuDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "SKU编号" }
|
||||
- { name: "spu_id", type: "Long", comment: "SPU编号" }
|
||||
- { name: "properties", type: "List<Property>", comment: "属性数组JSON" }
|
||||
- { name: "price", type: "Integer", comment: "商品价格(分)" }
|
||||
- { name: "market_price", type: "Integer", comment: "市场价(分)" }
|
||||
- { name: "cost_price", type: "Integer", comment: "成本价(分)" }
|
||||
- { name: "bar_code", type: "String", comment: "商品条码" }
|
||||
- { name: "pic_url", type: "String", comment: "图片地址" }
|
||||
- { name: "stock", type: "Integer", comment: "库存" }
|
||||
- { name: "weight", type: "Double", comment: "重量(kg)" }
|
||||
- { name: "volume", type: "Double", comment: "体积(m^3)" }
|
||||
- { name: "first_brokerage_price", type: "Integer", comment: "一级分销佣金" }
|
||||
- { name: "second_brokerage_price", type: "Integer", comment: "二级分销佣金" }
|
||||
|
||||
- name: "product_category"
|
||||
comment: "商品分类表"
|
||||
entity: "ProductCategoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "分类编号" }
|
||||
- { name: "parent_id", type: "Long", comment: "父分类编号" }
|
||||
- { name: "name", type: "String", comment: "分类名称" }
|
||||
- { name: "pic_url", type: "String", comment: "分类图片" }
|
||||
- { name: "sort", type: "Integer", comment: "排序" }
|
||||
- { name: "status", type: "Integer", comment: "状态:0启用 1禁用" }
|
||||
|
||||
- name: "product_brand"
|
||||
comment: "商品品牌表"
|
||||
entity: "ProductBrandDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "品牌编号" }
|
||||
- { name: "name", type: "String", comment: "品牌名称" }
|
||||
- { name: "pic_url", type: "String", comment: "品牌图片" }
|
||||
- { name: "sort", type: "Integer", comment: "排序" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
# ========== 交易模块 ==========
|
||||
- name: "trade_order"
|
||||
comment: "交易订单表"
|
||||
entity: "TradeOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "订单编号" }
|
||||
- { name: "no", type: "String", comment: "订单流水号" }
|
||||
- { name: "type", type: "Integer", comment: "订单类型" }
|
||||
- { name: "terminal", type: "Integer", comment: "订单来源终端" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "status", type: "Integer", comment: "订单状态" }
|
||||
- { name: "product_count", type: "Integer", comment: "商品数量" }
|
||||
- { name: "total_price", type: "Integer", comment: "商品原价(分)" }
|
||||
- { name: "discount_price", type: "Integer", comment: "优惠金额(分)" }
|
||||
- { name: "delivery_price", type: "Integer", comment: "运费金额(分)" }
|
||||
- { name: "pay_price", type: "Integer", comment: "应付金额(分)" }
|
||||
- { name: "pay_order_id", type: "Long", comment: "支付订单编号" }
|
||||
- { name: "pay_status", type: "Boolean", comment: "是否已支付" }
|
||||
- { name: "pay_time", type: "LocalDateTime", comment: "付款时间" }
|
||||
- { name: "delivery_type", type: "Integer", comment: "配送方式" }
|
||||
- { name: "logistics_id", type: "Long", comment: "物流公司编号" }
|
||||
- { name: "logistics_no", type: "String", comment: "物流单号" }
|
||||
- { name: "receiver_name", type: "String", comment: "收件人名称" }
|
||||
- { name: "receiver_mobile", type: "String", comment: "收件人手机" }
|
||||
- { name: "receiver_detail_address", type: "String", comment: "收件人详细地址" }
|
||||
- { name: "refund_status", type: "Integer", comment: "售后状态" }
|
||||
- { name: "coupon_id", type: "Long", comment: "优惠券编号" }
|
||||
- { name: "coupon_price", type: "Integer", comment: "优惠券减免金额" }
|
||||
|
||||
- name: "trade_order_item"
|
||||
comment: "交易订单项表"
|
||||
entity: "TradeOrderItemDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "订单项编号" }
|
||||
- { name: "order_id", type: "Long", comment: "订单编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "spu_id", type: "Long", comment: "SPU编号" }
|
||||
- { name: "sku_id", type: "Long", comment: "SKU编号" }
|
||||
- { name: "spu_name", type: "String", comment: "商品名称" }
|
||||
- { name: "sku_name", type: "String", comment: "SKU名称" }
|
||||
- { name: "price", type: "Integer", comment: "商品原价(分)" }
|
||||
- { name: "count", type: "Integer", comment: "购买数量" }
|
||||
- { name: "pay_price", type: "Integer", comment: "子订单实付金额" }
|
||||
- { name: "after_sale_status", type: "Integer", comment: "售后状态" }
|
||||
|
||||
- name: "trade_after_sale"
|
||||
comment: "售后订单表"
|
||||
entity: "AfterSaleDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "售后编号" }
|
||||
- { name: "no", type: "String", comment: "售后流水号" }
|
||||
- { name: "order_id", type: "Long", comment: "订单编号" }
|
||||
- { name: "order_item_id", type: "Long", comment: "订单项编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "type", type: "Integer", comment: "售后类型:0退款1退货退款" }
|
||||
- { name: "way", type: "Integer", comment: "退款方式" }
|
||||
- { name: "status", type: "Integer", comment: "售后状态" }
|
||||
- { name: "refund_price", type: "Integer", comment: "退款金额" }
|
||||
- { name: "apply_reason", type: "String", comment: "申请原因" }
|
||||
|
||||
# ========== 促销模块 ==========
|
||||
- name: "promotion_coupon_template"
|
||||
comment: "优惠券模板表"
|
||||
entity: "CouponTemplateDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "模板编号" }
|
||||
- { name: "name", type: "String", comment: "优惠券名称" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
- { name: "total_count", type: "Integer", comment: "发放数量" }
|
||||
- { name: "take_limit_count", type: "Integer", comment: "每人限领数量" }
|
||||
- { name: "take_type", type: "Integer", comment: "领取方式" }
|
||||
- { name: "product_scope", type: "Integer", comment: "商品范围" }
|
||||
- { name: "discount_type", type: "Integer", comment: "折扣类型" }
|
||||
|
||||
- name: "promotion_coupon"
|
||||
comment: "优惠券表"
|
||||
entity: "CouponDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "优惠券编号" }
|
||||
- { name: "template_id", type: "Long", comment: "模板编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "status", type: "Integer", comment: "状态:1未使用2已使用3已过期" }
|
||||
|
||||
- name: "promotion_seckill_activity"
|
||||
comment: "秒杀活动表"
|
||||
entity: "SeckillActivityDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "活动编号" }
|
||||
- { name: "name", type: "String", comment: "活动名称" }
|
||||
- { name: "spu_id", type: "Long", comment: "SPU编号" }
|
||||
- { name: "status", type: "Integer", comment: "活动状态" }
|
||||
- { name: "start_time", type: "LocalDateTime", comment: "开始时间" }
|
||||
- { name: "end_time", type: "LocalDateTime", comment: "结束时间" }
|
||||
- { name: "total_limit_count", type: "Integer", comment: "总限购数量" }
|
||||
- { name: "single_limit_count", type: "Integer", comment: "单次限购数量" }
|
||||
|
||||
- name: "promotion_combination_activity"
|
||||
comment: "拼团活动表"
|
||||
entity: "CombinationActivityDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "活动编号" }
|
||||
- { name: "name", type: "String", comment: "活动名称" }
|
||||
- { name: "spu_id", type: "Long", comment: "SPU编号" }
|
||||
- { name: "status", type: "Integer", comment: "活动状态" }
|
||||
- { name: "user_size", type: "Integer", comment: "成团人数" }
|
||||
- { name: "limit_duration", type: "Integer", comment: "拼团时长(小时)" }
|
||||
|
||||
- name: "promotion_bargain_activity"
|
||||
comment: "砍价活动表"
|
||||
entity: "BargainActivityDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "活动编号" }
|
||||
- { name: "name", type: "String", comment: "活动名称" }
|
||||
- { name: "spu_id", type: "Long", comment: "SPU编号" }
|
||||
- { name: "status", type: "Integer", comment: "活动状态" }
|
||||
- { name: "bargain_first_price", type: "Integer", comment: "砍价起始价格" }
|
||||
- { name: "bargain_min_price", type: "Integer", comment: "砍价最低价格" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "product_spu"
|
||||
to: "product_sku"
|
||||
type: "1:N"
|
||||
foreign_key: "spu_id"
|
||||
comment: "一个SPU对应多个SKU"
|
||||
- from: "product_spu"
|
||||
to: "product_category"
|
||||
type: "N:1"
|
||||
foreign_key: "category_id"
|
||||
comment: "多个SPU属于一个分类"
|
||||
- from: "product_spu"
|
||||
to: "product_brand"
|
||||
type: "N:1"
|
||||
foreign_key: "brand_id"
|
||||
comment: "多个SPU属于一个品牌"
|
||||
- from: "trade_order"
|
||||
to: "trade_order_item"
|
||||
type: "1:N"
|
||||
foreign_key: "order_id"
|
||||
comment: "一个订单包含多个订单项"
|
||||
- from: "trade_order_item"
|
||||
to: "product_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
comment: "订单项关联SKU"
|
||||
- from: "trade_order"
|
||||
to: "trade_after_sale"
|
||||
type: "1:N"
|
||||
foreign_key: "order_id"
|
||||
comment: "一个订单可能有多个售后单"
|
||||
- from: "promotion_coupon_template"
|
||||
to: "promotion_coupon"
|
||||
type: "1:N"
|
||||
foreign_key: "template_id"
|
||||
comment: "一个模板发放多张优惠券"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@RestController"
|
||||
- "@RequestMapping"
|
||||
- "@Tag(name = \"模块名称\")"
|
||||
- "@Operation(summary = \"接口描述\")"
|
||||
- "@PreAuthorize(\"@ss.hasPermission('权限标识')\")"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 商品 SPU")
|
||||
@RestController
|
||||
@RequestMapping("/product/spu")
|
||||
@Validated
|
||||
public class ProductSpuController {
|
||||
|
||||
@Resource
|
||||
private ProductSpuService productSpuService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建商品 SPU")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:create')")
|
||||
public CommonResult<Long> createProductSpu(@Valid @RequestBody ProductSpuSaveReqVO createReqVO) {
|
||||
return success(productSpuService.createSpu(createReqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得商品 SPU 分页")
|
||||
@PreAuthorize("@ss.hasPermission('product:spu:query')")
|
||||
public CommonResult<PageResult<ProductSpuRespVO>> getSpuPage(@Valid ProductSpuPageReqVO pageVO) {
|
||||
PageResult<ProductSpuDO> pageResult = productSpuService.getSpuPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, ProductSpuRespVO.class));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: |
|
||||
public interface ProductSpuService {
|
||||
Long createSpu(@Valid ProductSpuSaveReqVO createReqVO);
|
||||
void updateSpu(@Valid ProductSpuSaveReqVO updateReqVO);
|
||||
void deleteSpu(Long id);
|
||||
ProductSpuDO getSpu(Long id);
|
||||
PageResult<ProductSpuDO> getSpuPage(ProductSpuPageReqVO pageReqVO);
|
||||
}
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class ProductSpuServiceImpl implements ProductSpuService {
|
||||
|
||||
@Resource
|
||||
private ProductSpuMapper productSpuMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createSpu(ProductSpuSaveReqVO createReqVO) {
|
||||
// 1. 校验分类、品牌
|
||||
validateCategory(createReqVO.getCategoryId());
|
||||
brandService.validateProductBrand(createReqVO.getBrandId());
|
||||
// 2. 校验 SKU
|
||||
productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());
|
||||
// 3. 插入 SPU
|
||||
ProductSpuDO spu = BeanUtils.toBean(createReqVO, ProductSpuDO.class);
|
||||
productSpuMapper.insert(spu);
|
||||
// 4. 插入 SKU
|
||||
productSkuService.createSkuList(spu.getId(), skuSaveReqList);
|
||||
return spu.getId();
|
||||
}
|
||||
}
|
||||
example: |
|
||||
// 商品创建流程
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createSpu(ProductSpuSaveReqVO createReqVO) {
|
||||
// 1. 校验分类、品牌
|
||||
validateCategory(createReqVO.getCategoryId());
|
||||
brandService.validateProductBrand(createReqVO.getBrandId());
|
||||
// 2. 校验 SKU
|
||||
List<ProductSkuSaveReqVO> skuSaveReqList = createReqVO.getSkus();
|
||||
productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());
|
||||
// 3. 初始化 SPU 信息
|
||||
ProductSpuDO spu = BeanUtils.toBean(createReqVO, ProductSpuDO.class);
|
||||
initSpuFromSkus(spu, skuSaveReqList);
|
||||
// 4. 插入 SPU
|
||||
productSpuMapper.insert(spu);
|
||||
// 5. 插入 SKU
|
||||
productSkuService.createSkuList(spu.getId(), skuSaveReqList);
|
||||
return spu.getId();
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
@Mapper
|
||||
public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
|
||||
|
||||
default PageResult<ProductSpuDO> selectPage(ProductSpuPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<ProductSpuDO>()
|
||||
.likeIfPresent(ProductSpuDO::getName, reqVO.getName())
|
||||
.eqIfPresent(ProductSpuDO::getCategoryId, reqVO.getCategoryId())
|
||||
.eqIfPresent(ProductSpuDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(ProductSpuDO::getSort));
|
||||
}
|
||||
}
|
||||
example: |
|
||||
// 使用 MyBatis-Plus 查询
|
||||
List<ProductSpuDO> list = productSpuMapper.selectList(
|
||||
new LambdaQueryWrapper<ProductSpuDO>()
|
||||
.eq(ProductSpuDO::getStatus, ProductSpuStatusEnum.ENABLE.getStatus())
|
||||
.orderByDesc(ProductSpuDO::getSort)
|
||||
);
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix:
|
||||
product: "1-008-xxx-xxx"
|
||||
trade: "1-011-xxx-xxx"
|
||||
promotion: "1-013-xxx-xxx"
|
||||
examples:
|
||||
- code: "1_008_005_000"
|
||||
message: "商品 SPU 不存在"
|
||||
module: "product"
|
||||
- code: "1_008_006_004"
|
||||
message: "商品 SKU 库存不足"
|
||||
module: "product"
|
||||
- code: "1_011_000_011"
|
||||
message: "交易订单不存在"
|
||||
module: "trade"
|
||||
- code: "1_011_000_017"
|
||||
message: "交易订单发货失败,订单不是【待发货】状态"
|
||||
module: "trade"
|
||||
- code: "1_013_004_000"
|
||||
message: "优惠劵模板不存在"
|
||||
module: "promotion"
|
||||
- code: "1_013_008_006"
|
||||
message: "秒杀失败,原因:秒杀库存不足"
|
||||
module: "promotion"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE, args)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增业务功能(以商品为例)"
|
||||
steps:
|
||||
- "1. 创建 DO 实体类:在 dal/dataobject 目录下创建实体类,继承 BaseDO"
|
||||
- "2. 创建 Mapper 接口:在 dal/mysql 目录下创建 Mapper,继承 BaseMapperX"
|
||||
- "3. 创建 Service 接口和实现:在 service 目录下创建接口和实现类"
|
||||
- "4. 创建 VO 类:在 controller/vo 目录下创建请求和响应 VO"
|
||||
- "5. 创建 Controller:在 controller 目录下创建控制器"
|
||||
- "6. 创建错误码:在 enums/ErrorCodeConstants.java 中定义错误码"
|
||||
- "7. 编写单元测试:验证业务逻辑正确性"
|
||||
|
||||
# 新增渠道/类型示例
|
||||
new_channel:
|
||||
title: "新增促销活动类型"
|
||||
steps:
|
||||
- "1. 创建活动 DO 实体:如 XxxActivityDO,关联商品和规则"
|
||||
- "2. 创建 API 接口:提供活动校验、库存扣减等跨模块调用能力"
|
||||
- "3. 创建 Service 层:处理活动创建、状态流转、库存管理"
|
||||
- "4. 集成到订单流程:在 TradePriceService 中增加价格计算逻辑"
|
||||
- "5. 集成到下单流程:在 TradeOrderUpdateService 中处理活动商品下单"
|
||||
- "6. 创建管理后台接口:活动创建、编辑、上下架等"
|
||||
- "7. 创建用户端接口:活动列表、详情、参与等"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "商品状态管理:使用 ProductSpuStatusEnum 枚举,状态变更通过 updateSpuStatus 方法"
|
||||
- "订单状态流转:严格遵循状态机模式,状态变更记录日志"
|
||||
- "库存扣减:使用乐观锁或 Redis 原子操作,防止超卖"
|
||||
- "价格计算:统一使用分为单位,避免浮点精度问题"
|
||||
- "事务管理:涉及多表操作时使用 @Transactional 注解"
|
||||
- "异常处理:使用框架提供的 exception() 方法抛出业务异常"
|
||||
- "API 设计:跨模块调用通过 API 接口,避免直接依赖实现类"
|
||||
- "并发控制:使用分布式锁处理秒杀、拼团等高并发场景"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-member"
|
||||
api: "MemberUserApi"
|
||||
purpose: "获取会员信息,用于订单关联用户"
|
||||
- module: "yudao-module-pay"
|
||||
api: "PayOrderApi"
|
||||
purpose: "创建支付订单,处理支付回调"
|
||||
- module: "yudao-module-system"
|
||||
api: "AreaApi"
|
||||
purpose: "获取地区信息,用于收货地址"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
version: "3.x"
|
||||
purpose: "ORM 框架,简化数据库操作"
|
||||
- name: "MapStruct"
|
||||
version: "1.x"
|
||||
purpose: "对象映射,VO 与 DO 转换"
|
||||
- name: "Spring Validation"
|
||||
version: "2.x"
|
||||
purpose: "参数校验"
|
||||
- name: "Swagger/OpenAPI"
|
||||
version: "3.x"
|
||||
purpose: "API 文档生成"
|
||||
- name: "Redis"
|
||||
version: "6.x"
|
||||
purpose: "缓存、分布式锁、库存扣减"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
# 商品模块
|
||||
- path: "yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java"
|
||||
purpose: "商品SPU实体类,电商核心领域模型"
|
||||
- path: "yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/sku/ProductSkuDO.java"
|
||||
purpose: "商品SKU实体类,库存管理单元"
|
||||
- path: "yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java"
|
||||
purpose: "商品SPU服务接口,定义商品CRUD操作"
|
||||
- path: "yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java"
|
||||
purpose: "商品SPU管理接口,后台商品管理入口"
|
||||
- path: "yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java"
|
||||
purpose: "商品模块错误码定义"
|
||||
|
||||
# 交易模块
|
||||
- path: "yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java"
|
||||
purpose: "交易订单实体类,订单聚合根"
|
||||
- path: "yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateService.java"
|
||||
purpose: "订单更新服务,订单创建和状态流转"
|
||||
- path: "yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/service/price/TradePriceService.java"
|
||||
purpose: "价格计算服务,订单金额计算核心"
|
||||
- path: "yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java"
|
||||
purpose: "售后服务,退款退货处理"
|
||||
- path: "yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java"
|
||||
purpose: "交易模块错误码定义"
|
||||
|
||||
# 促销模块
|
||||
- path: "yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/coupon/CouponTemplateDO.java"
|
||||
purpose: "优惠券模板实体类"
|
||||
- path: "yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/seckill/SeckillActivityDO.java"
|
||||
purpose: "秒杀活动实体类"
|
||||
- path: "yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/combination/CombinationActivityDO.java"
|
||||
purpose: "拼团活动实体类"
|
||||
- path: "yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java"
|
||||
purpose: "促销模块错误码定义"
|
||||
|
||||
# 统计模块
|
||||
- path: "yudao-module-mall/yudao-module-statistics/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsService.java"
|
||||
purpose: "交易统计服务,订单数据汇总"
|
||||
@@ -1,609 +0,0 @@
|
||||
# Skill 文件 - yudao-module-member 会员模块
|
||||
# 用于提取模块知识的标准格式
|
||||
|
||||
skill:
|
||||
id: "skill-member"
|
||||
name: "Member Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-member"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
会员中心模块,负责管理C端用户的会员体系,包括:
|
||||
1. 会员用户管理:注册、登录、个人信息管理
|
||||
2. 会员等级体系:等级配置、经验累积、自动升降级
|
||||
3. 积分系统:积分获取、消费、记录追踪
|
||||
4. 签到功能:每日签到、连续签到奖励
|
||||
5. 会员标签与分组:用户分类管理
|
||||
6. 收货地址管理:多地址支持、默认地址
|
||||
|
||||
在整个系统中的定位:
|
||||
- 作为C端用户的核心数据层,与system模块的AdminUser区分
|
||||
- 为mall模块提供会员信息、积分抵扣、等级折扣等支持
|
||||
- 通过API接口对外暴露会员服务,支持跨模块调用
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "领域驱动设计(DDD):会员(MemberUser)作为聚合根,关联等级、积分、标签等实体"
|
||||
- "分层架构:Controller -> Service -> DAL 清晰分层,API层提供跨模块调用"
|
||||
- "单一职责:每个Service专注一个业务领域,如MemberUserService、MemberLevelService"
|
||||
- "开闭原则:通过枚举定义业务类型(MemberPointBizTypeEnum),便于扩展新的积分/经验来源"
|
||||
- "事务一致性:积分变更、等级变更使用@Transactional保证数据一致性"
|
||||
- "TenantBaseDO继承:会员数据支持多租户隔离"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "MemberUser"
|
||||
type: "聚合根"
|
||||
description: "会员用户,包含账号信息、基础信息、积分、等级、标签等"
|
||||
entities:
|
||||
- "MemberLevelDO (会员等级)"
|
||||
- "MemberGroupDO (会员分组)"
|
||||
- "MemberTagDO (会员标签)"
|
||||
- "MemberAddressDO (收货地址)"
|
||||
|
||||
value_objects:
|
||||
- name: "MemberPointRecord"
|
||||
type: "值对象"
|
||||
description: "积分变更记录,不可变的历史记录"
|
||||
- name: "MemberExperienceRecord"
|
||||
type: "值对象"
|
||||
description: "经验变更记录"
|
||||
- name: "MemberLevelRecord"
|
||||
type: "值对象"
|
||||
description: "等级变更记录"
|
||||
- name: "MemberSignInRecord"
|
||||
type: "值对象"
|
||||
description: "签到记录"
|
||||
|
||||
services:
|
||||
- name: "MemberUserService"
|
||||
description: "会员用户管理服务"
|
||||
- name: "MemberAuthService"
|
||||
description: "会员认证服务(登录、登出、token刷新)"
|
||||
- name: "MemberLevelService"
|
||||
description: "会员等级服务(等级配置、经验管理、自动升级)"
|
||||
- name: "MemberPointRecordService"
|
||||
description: "积分记录服务"
|
||||
- name: "MemberSignInRecordService"
|
||||
description: "签到服务"
|
||||
- name: "AddressService"
|
||||
description: "收货地址服务"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口,供其他模块调用"
|
||||
components:
|
||||
- "MemberUserApi - 会员用户查询接口"
|
||||
- "MemberPointApi - 积分增减接口"
|
||||
- "MemberLevelApi - 等级查询、经验增减接口"
|
||||
- "MemberAddressApi - 收货地址查询接口"
|
||||
- "MemberConfigApi - 会员配置查询接口"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口,分为admin(管理后台)和app(C端应用)"
|
||||
components:
|
||||
- "admin/user/MemberUserController - 会员用户管理"
|
||||
- "admin/level/MemberLevelController - 等级配置管理"
|
||||
- "admin/point/MemberPointRecordController - 积分记录查询"
|
||||
- "admin/signin/MemberSignInConfigController - 签到规则配置"
|
||||
- "admin/tag/MemberTagController - 标签管理"
|
||||
- "admin/group/MemberGroupController - 分组管理"
|
||||
- "app/auth/AppAuthController - C端认证(登录、注册、登出)"
|
||||
- "app/address/AppAddressController - C端收货地址"
|
||||
- "app/signin/AppMemberSignInRecordController - C端签到"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "user/MemberUserService - 会员用户核心服务"
|
||||
- "auth/MemberAuthService - 认证服务"
|
||||
- "level/MemberLevelService - 等级服务"
|
||||
- "level/MemberLevelRecordService - 等级变更记录"
|
||||
- "level/MemberExperienceRecordService - 经验记录"
|
||||
- "point/MemberPointRecordService - 积分记录"
|
||||
- "signin/MemberSignInRecordService - 签到服务"
|
||||
- "signin/MemberSignInConfigService - 签到配置"
|
||||
- "tag/MemberTagService - 标签服务"
|
||||
- "group/MemberGroupService - 分组服务"
|
||||
- "address/AddressService - 地址服务"
|
||||
- "config/MemberConfigService - 配置服务"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject/user/MemberUserDO - 会员用户DO"
|
||||
- "dataobject/level/MemberLevelDO - 等级DO"
|
||||
- "dataobject/level/MemberLevelRecordDO - 等级记录DO"
|
||||
- "dataobject/level/MemberExperienceRecordDO - 经验记录DO"
|
||||
- "dataobject/point/MemberPointRecordDO - 积分记录DO"
|
||||
- "dataobject/signin/MemberSignInConfigDO - 签到配置DO"
|
||||
- "dataobject/signin/MemberSignInRecordDO - 签到记录DO"
|
||||
- "dataobject/tag/MemberTagDO - 标签DO"
|
||||
- "dataobject/group/MemberGroupDO - 分组DO"
|
||||
- "dataobject/address/MemberAddressDO - 地址DO"
|
||||
- "dataobject/config/MemberConfigDO - 配置DO"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "DTO转换模式"
|
||||
location: "api/*/dto/*.java"
|
||||
purpose: "API层使用DTO隔离内部DO,保护领域模型"
|
||||
- pattern: "策略模式(枚举实现)"
|
||||
location: "enums/point/MemberPointBizTypeEnum.java"
|
||||
purpose: "定义积分业务类型,支持多种积分来源"
|
||||
- pattern: "模板方法"
|
||||
location: "service/level/MemberLevelServiceImpl.java"
|
||||
purpose: "等级变更流程:记录变更 -> 更新经验 -> 计算新等级 -> 通知用户"
|
||||
- pattern: "Facade模式"
|
||||
location: "api/*ApiImpl.java"
|
||||
purpose: "API实现类封装Service调用,简化跨模块交互"
|
||||
- pattern: "观察者模式(MQ)"
|
||||
location: "mq/producer/user/MemberUserProducer.java"
|
||||
purpose: "用户创建后发送消息,解耦后续处理逻辑"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "MemberUserApi"
|
||||
methods: ["getUser", "getUserList", "getUserByMobile", "validateUser"]
|
||||
consumers: ["pay", "mall-trade", "mall-promotion"]
|
||||
- name: "MemberPointApi"
|
||||
methods: ["addPoint", "reducePoint"]
|
||||
consumers: ["mall-trade", "mall-promotion"]
|
||||
- name: "MemberLevelApi"
|
||||
methods: ["getMemberLevel", "addExperience", "reduceExperience"]
|
||||
consumers: ["mall-trade"]
|
||||
- name: "MemberAddressApi"
|
||||
methods: ["getAddress", "getDefaultAddress"]
|
||||
consumers: ["mall-trade"]
|
||||
- name: "MemberConfigApi"
|
||||
methods: ["getConfig"]
|
||||
consumers: ["mall-trade"]
|
||||
|
||||
consumers:
|
||||
- module: "system"
|
||||
api: "SmsCodeApi"
|
||||
purpose: "短信验证码校验"
|
||||
- module: "system"
|
||||
api: "SocialClientApi"
|
||||
purpose: "社交登录、微信小程序登录"
|
||||
- module: "infra"
|
||||
api: "文件存储"
|
||||
purpose: "头像上传"
|
||||
|
||||
mq:
|
||||
- name: "MemberUserCreateMessage"
|
||||
direction: "producer"
|
||||
purpose: "用户创建后发送消息,通知其他模块"
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "TenantBaseDO"
|
||||
description: "会员核心表(MemberUserDO)继承TenantBaseDO支持多租户,其他表继承BaseDO"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
- name: "member_user"
|
||||
comment: "会员用户表"
|
||||
entity: "MemberUserDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "用户ID" }
|
||||
- { name: "mobile", type: "String", comment: "手机号(唯一索引)" }
|
||||
- { name: "password", type: "String", comment: "加密密码" }
|
||||
- { name: "status", type: "Integer", comment: "状态(0启用1禁用)" }
|
||||
- { name: "register_ip", type: "String", comment: "注册IP" }
|
||||
- { name: "register_terminal", type: "Integer", comment: "注册终端" }
|
||||
- { name: "login_ip", type: "String", comment: "最后登录IP" }
|
||||
- { name: "login_date", type: "LocalDateTime", comment: "最后登录时间" }
|
||||
- { name: "nickname", type: "String", comment: "昵称" }
|
||||
- { name: "avatar", type: "String", comment: "头像" }
|
||||
- { name: "name", type: "String", comment: "真实姓名" }
|
||||
- { name: "sex", type: "Integer", comment: "性别" }
|
||||
- { name: "birthday", type: "LocalDateTime", comment: "生日" }
|
||||
- { name: "area_id", type: "Integer", comment: "地区ID" }
|
||||
- { name: "mark", type: "String", comment: "用户备注" }
|
||||
- { name: "point", type: "Integer", comment: "当前积分" }
|
||||
- { name: "tag_ids", type: "List<Long>", comment: "标签ID列表(JSON存储)" }
|
||||
- { name: "level_id", type: "Long", comment: "等级ID" }
|
||||
- { name: "experience", type: "Integer", comment: "当前经验" }
|
||||
- { name: "group_id", type: "Long", comment: "分组ID" }
|
||||
indexes:
|
||||
- { name: "uk_mobile", columns: ["mobile"] }
|
||||
|
||||
- name: "member_level"
|
||||
comment: "会员等级表"
|
||||
entity: "MemberLevelDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "等级ID" }
|
||||
- { name: "name", type: "String", comment: "等级名称" }
|
||||
- { name: "level", type: "Integer", comment: "等级值(用于排序)" }
|
||||
- { name: "experience", type: "Integer", comment: "升级所需经验" }
|
||||
- { name: "discount_percent", type: "Integer", comment: "享受折扣百分比" }
|
||||
- { name: "icon", type: "String", comment: "等级图标" }
|
||||
- { name: "background_url", type: "String", comment: "等级背景图" }
|
||||
- { name: "status", type: "Integer", comment: "状态(0启用1禁用)" }
|
||||
|
||||
- name: "member_point_record"
|
||||
comment: "积分记录表"
|
||||
entity: "MemberPointRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "记录ID" }
|
||||
- { name: "user_id", type: "Long", comment: "用户ID" }
|
||||
- { name: "biz_id", type: "String", comment: "业务编码" }
|
||||
- { name: "biz_type", type: "Integer", comment: "业务类型(枚举)" }
|
||||
- { name: "title", type: "String", comment: "积分标题" }
|
||||
- { name: "description", type: "String", comment: "积分描述" }
|
||||
- { name: "point", type: "Integer", comment: "变动积分(正负)" }
|
||||
- { name: "total_point", type: "Integer", comment: "变动后积分" }
|
||||
|
||||
- name: "member_experience_record"
|
||||
comment: "经验记录表"
|
||||
entity: "MemberExperienceRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "记录ID" }
|
||||
- { name: "user_id", type: "Long", comment: "用户ID" }
|
||||
- { name: "biz_type", type: "Integer", comment: "业务类型(枚举)" }
|
||||
- { name: "biz_id", type: "String", comment: "业务编号" }
|
||||
- { name: "title", type: "String", comment: "标题" }
|
||||
- { name: "description", type: "String", comment: "描述" }
|
||||
- { name: "experience", type: "Integer", comment: "变动经验(正负)" }
|
||||
- { name: "total_experience", type: "Integer", comment: "变动后经验" }
|
||||
|
||||
- name: "member_level_record"
|
||||
comment: "等级变更记录表"
|
||||
entity: "MemberLevelRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "记录ID" }
|
||||
- { name: "user_id", type: "Long", comment: "用户ID" }
|
||||
- { name: "level_id", type: "Long", comment: "等级ID" }
|
||||
- { name: "level", type: "Integer", comment: "等级值(冗余)" }
|
||||
- { name: "discount_percent", type: "Integer", comment: "折扣(冗余)" }
|
||||
- { name: "experience", type: "Integer", comment: "升级经验(冗余)" }
|
||||
- { name: "user_experience", type: "Integer", comment: "用户变更后经验" }
|
||||
- { name: "remark", type: "String", comment: "备注" }
|
||||
- { name: "description", type: "String", comment: "描述" }
|
||||
|
||||
- name: "member_sign_in_config"
|
||||
comment: "签到配置表"
|
||||
entity: "MemberSignInConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "配置ID" }
|
||||
- { name: "day", type: "Integer", comment: "签到第N天" }
|
||||
- { name: "point", type: "Integer", comment: "奖励积分" }
|
||||
- { name: "experience", type: "Integer", comment: "奖励经验" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
- name: "member_sign_in_record"
|
||||
comment: "签到记录表"
|
||||
entity: "MemberSignInRecordDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "记录ID" }
|
||||
- { name: "user_id", type: "Long", comment: "用户ID" }
|
||||
- { name: "day", type: "Integer", comment: "第几天签到" }
|
||||
- { name: "point", type: "Integer", comment: "签到获得积分" }
|
||||
- { name: "experience", type: "Integer", comment: "签到获得经验" }
|
||||
|
||||
- name: "member_tag"
|
||||
comment: "会员标签表"
|
||||
entity: "MemberTagDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "标签ID" }
|
||||
- { name: "name", type: "String", comment: "标签名称" }
|
||||
|
||||
- name: "member_group"
|
||||
comment: "会员分组表"
|
||||
entity: "MemberGroupDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "分组ID" }
|
||||
- { name: "name", type: "String", comment: "分组名称" }
|
||||
- { name: "remark", type: "String", comment: "备注" }
|
||||
- { name: "status", type: "Integer", comment: "状态" }
|
||||
|
||||
- name: "member_address"
|
||||
comment: "收货地址表"
|
||||
entity: "MemberAddressDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "地址ID" }
|
||||
- { name: "user_id", type: "Long", comment: "用户ID" }
|
||||
- { name: "name", type: "String", comment: "收件人名称" }
|
||||
- { name: "mobile", type: "String", comment: "手机号" }
|
||||
- { name: "area_id", type: "Long", comment: "地区ID" }
|
||||
- { name: "detail_address", type: "String", comment: "详细地址" }
|
||||
- { name: "default_status", type: "Boolean", comment: "是否默认" }
|
||||
|
||||
- name: "member_config"
|
||||
comment: "会员配置表"
|
||||
entity: "MemberConfigDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "配置ID" }
|
||||
- { name: "point_trade_deduct_enable", type: "Boolean", comment: "积分抵扣开关" }
|
||||
- { name: "point_trade_deduct_unit_price", type: "Integer", comment: "积分抵扣单位(1积分=多少分)" }
|
||||
- { name: "point_trade_deduct_max_price", type: "Integer", comment: "积分抵扣上限(分)" }
|
||||
- { name: "point_trade_give_point", type: "Integer", comment: "1元赠送积分" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "member_user"
|
||||
to: "member_level"
|
||||
type: "N:1"
|
||||
foreign_key: "level_id"
|
||||
- from: "member_user"
|
||||
to: "member_group"
|
||||
type: "N:1"
|
||||
foreign_key: "group_id"
|
||||
- from: "member_user"
|
||||
to: "member_tag"
|
||||
type: "N:N"
|
||||
foreign_key: "tag_ids(JSON数组)"
|
||||
- from: "member_point_record"
|
||||
to: "member_user"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
- from: "member_experience_record"
|
||||
to: "member_user"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
- from: "member_level_record"
|
||||
to: "member_user"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
- from: "member_level_record"
|
||||
to: "member_level"
|
||||
type: "N:1"
|
||||
foreign_key: "level_id"
|
||||
- from: "member_sign_in_record"
|
||||
to: "member_user"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
- from: "member_address"
|
||||
to: "member_user"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - XXX') - Swagger分组"
|
||||
- "@RestController @RequestMapping('/member/xxx')"
|
||||
- "@PreAuthorize('@ss.hasPermission(\\'member:xxx:action\\')') - 权限控制"
|
||||
- "@PermitAll - 无需登录(如登录接口)"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 会员用户")
|
||||
@RestController
|
||||
@RequestMapping("/member/user")
|
||||
@Validated
|
||||
public class MemberUserController {
|
||||
@Resource
|
||||
private MemberUserService memberUserService;
|
||||
|
||||
@PutMapping("/update-point")
|
||||
@Operation(summary = "更新会员用户积分")
|
||||
@PreAuthorize("@ss.hasPermission('member:user:update-point')")
|
||||
public CommonResult<Boolean> updateUserPoint(@Valid @RequestBody MemberUserUpdatePointReqVO updateReqVO) {
|
||||
memberPointRecordService.createPointRecord(updateReqVO.getId(), updateReqVO.getPoint(),
|
||||
MemberPointBizTypeEnum.ADMIN, String.valueOf(getLoginUserId()));
|
||||
return success(true);
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "定义在service包下,以Service结尾"
|
||||
impl_pattern: "实现类以ServiceImpl结尾,使用@Service注解"
|
||||
example: |
|
||||
// Service接口
|
||||
public interface MemberPointRecordService {
|
||||
void createPointRecord(Long userId, Integer point, MemberPointBizTypeEnum bizType, String bizId);
|
||||
}
|
||||
|
||||
// Service实现
|
||||
@Service
|
||||
@Validated
|
||||
public class MemberPointRecordServiceImpl implements MemberPointRecordService {
|
||||
@Resource
|
||||
private MemberPointRecordMapper memberPointRecordMapper;
|
||||
@Resource
|
||||
private MemberUserService memberUserService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createPointRecord(Long userId, Integer point, MemberPointBizTypeEnum bizType, String bizId) {
|
||||
// 1. 校验用户积分余额
|
||||
MemberUserDO user = memberUserService.getUser(userId);
|
||||
int totalPoint = ObjectUtil.defaultIfNull(user.getPoint(), 0) + point;
|
||||
if (totalPoint < 0) {
|
||||
throw exception(USER_POINT_NOT_ENOUGH);
|
||||
}
|
||||
// 2. 更新用户积分
|
||||
memberUserService.updateUserPoint(userId, point);
|
||||
// 3. 增加积分记录
|
||||
MemberPointRecordDO record = new MemberPointRecordDO()
|
||||
.setUserId(userId).setBizId(bizId).setBizType(bizType.getType())
|
||||
.setPoint(point).setTotalPoint(totalPoint);
|
||||
memberPointRecordMapper.insert(record);
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: "继承BaseMapperX,在mysql包下"
|
||||
example: |
|
||||
@Mapper
|
||||
public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
|
||||
default MemberUserDO selectByMobile(String mobile) {
|
||||
return selectOne(MemberUserDO::getMobile, mobile);
|
||||
}
|
||||
default int updatePointIncr(Long id, Integer point) {
|
||||
return update(new MemberUserDO().setPoint(point),
|
||||
new LambdaQueryWrapper<MemberUserDO>().eq(MemberUserDO::getId, id));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1_004_XXX_XXX"
|
||||
examples:
|
||||
- code: "1_004_001_000"
|
||||
message: "用户不存在"
|
||||
- code: "1_004_001_003"
|
||||
message: "用户积分余额不足"
|
||||
- code: "1_004_003_000"
|
||||
message: "登录失败,账号密码不正确"
|
||||
- code: "1_004_006_000"
|
||||
message: "用户标签不存在"
|
||||
- code: "1_004_008_000"
|
||||
message: "用户积分记录业务类型不支持"
|
||||
- code: "1_004_010_000"
|
||||
message: "今日已签到,请勿重复签到"
|
||||
- code: "1_004_011_000"
|
||||
message: "用户等级不存在"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE, args...)"
|
||||
|
||||
# 枚举定义
|
||||
enums:
|
||||
- name: "MemberPointBizTypeEnum"
|
||||
description: "积分业务类型"
|
||||
values:
|
||||
- { type: 1, name: "SIGN", desc: "签到", add: true }
|
||||
- { type: 2, name: "ADMIN", desc: "管理员修改", add: true }
|
||||
- { type: 11, name: "ORDER_USE", desc: "订单积分抵扣", add: false }
|
||||
- { type: 21, name: "ORDER_GIVE", desc: "订单积分奖励", add: true }
|
||||
|
||||
- name: "MemberExperienceBizTypeEnum"
|
||||
description: "经验业务类型"
|
||||
values:
|
||||
- { type: 0, name: "ADMIN", desc: "管理员调整", add: true }
|
||||
- { type: 1, name: "INVITE_REGISTER", desc: "邀新奖励", add: true }
|
||||
- { type: 4, name: "SIGN_IN", desc: "签到奖励", add: true }
|
||||
- { type: 11, name: "ORDER_GIVE", desc: "下单奖励", add: true }
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增会员权益功能"
|
||||
steps:
|
||||
- "1. 在dal/dataobject下创建MemberBenefitDO实体类"
|
||||
- "2. 创建MemberBenefitMapper接口"
|
||||
- "3. 创建MemberBenefitService接口和实现类"
|
||||
- "4. 创建admin和app的Controller"
|
||||
- "5. 在ErrorCodeConstants中添加错误码"
|
||||
- "6. 如需对外暴露API,在api包下创建MemberBenefitApi"
|
||||
|
||||
# 新增会员等级步骤
|
||||
new_channel:
|
||||
title: "新增会员等级"
|
||||
steps:
|
||||
- "1. 通过管理后台或直接插入member_level表"
|
||||
- "2. 设置等级名称、等级值、升级所需经验"
|
||||
- "3. 配置享受折扣百分比"
|
||||
- "4. 上传等级图标和背景图"
|
||||
- "5. 确保升级经验值在相邻等级之间"
|
||||
|
||||
# 新增积分来源
|
||||
new_point_source:
|
||||
title: "新增积分来源类型"
|
||||
steps:
|
||||
- "1. 在MemberPointBizTypeEnum中添加新的枚举值"
|
||||
- "2. 设置type(唯一)、name、description、add(是否增加)"
|
||||
- "3. 在对应业务场景调用MemberPointApi.addPoint或reducePoint"
|
||||
- "4. 示例:MemberPointApi.addPoint(userId, 10, MemberPointBizTypeEnum.NEW_TYPE.getType(), bizId)"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "积分变更使用MemberPointApi,保证记录完整性"
|
||||
- "经验变更使用MemberLevelApi,自动触发等级计算"
|
||||
- "用户创建后发送MQ消息,便于其他模块处理"
|
||||
- "等级经验配置需确保递增顺序,系统会自动校验"
|
||||
- "积分抵扣配置通过MemberConfigApi获取,支持动态调整"
|
||||
- "签到规则配置支持连续签到奖励递增"
|
||||
- "地址管理支持多地址,需处理默认地址切换逻辑"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "system"
|
||||
api: "SmsCodeApi"
|
||||
purpose: "短信验证码校验(登录、修改手机、重置密码)"
|
||||
- module: "system"
|
||||
api: "SocialClientApi"
|
||||
purpose: "社交登录(微信、QQ等)、微信小程序登录"
|
||||
- module: "infra"
|
||||
api: "文件存储"
|
||||
purpose: "头像上传存储"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "spring-security-crypto"
|
||||
version: "Spring Boot自带"
|
||||
purpose: "密码加密(BCryptPasswordEncoder)"
|
||||
- name: "mybatis-plus"
|
||||
version: "3.x"
|
||||
purpose: "ORM框架,数据访问增强"
|
||||
- name: "hutool"
|
||||
version: "5.x"
|
||||
purpose: "工具类库"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java"
|
||||
purpose: "会员用户实体,聚合根"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java"
|
||||
purpose: "会员用户服务接口"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/level/MemberLevelService.java"
|
||||
purpose: "会员等级服务,含经验管理和自动升级"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java"
|
||||
purpose: "积分API接口,供其他模块调用"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java"
|
||||
purpose: "积分业务类型枚举"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java"
|
||||
purpose: "C端认证控制器"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordService.java"
|
||||
purpose: "签到服务"
|
||||
- path: "yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/config/MemberConfigDO.java"
|
||||
purpose: "会员配置(积分抵扣设置)"
|
||||
@@ -1,964 +0,0 @@
|
||||
# Skill 文件 - yudao-module-mes 模块
|
||||
# 制造执行系统模块知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-mes"
|
||||
name: "Manufacturing Execution System Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-mes"
|
||||
created_at: "2026-06-08"
|
||||
updated_at: "2026-06-08"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
mes 模块是制造执行系统(MES),连接 ERP 计划层与车间执行层,实现生产过程的数字化管理:
|
||||
1. 主数据管理 - 物料、BOM、工艺路线、SOP/SIP、客户/供应商、车间/工位、自动编码规则
|
||||
2. 生产管理 - 工单(草稿->确认->完工/取消)、工序任务(甘特图)、流转卡、报工反馈、安灯呼叫
|
||||
3. 质量管理 - 检验模板、检验指标、缺陷管理、IQC/IPQC/OQC/RQC 四大检验
|
||||
4. 设备管理 - 设备台账、点检计划/记录、保养记录、维修管理
|
||||
5. 工具管理 - 工具台账、工具类型
|
||||
6. 仓库管理 - 仓库/库区/库位、物料库存、批次、条码/SN、各类出入库、调拨、盘点
|
||||
7. 排班日历 - 工厂日历、节假日、排班计划、班组管理
|
||||
8. 首页看板 - 订单汇总、工单状态、生产趋势统计
|
||||
|
||||
定位:面向离散制造和流程制造,提供从生产计划下达到成品入库的全流程管理。
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- principle: "状态机模式"
|
||||
description: "工单/流转卡/报工/维修/调拨/排班计划等核心实体使用状态机管理状态流转,变更前校验前置状态,使用 CAS 防止并发覆盖"
|
||||
- principle: "策略模式"
|
||||
description: "自动编码规则支持日期、固定字符、输入字符、序列号等零件组合,不同零件类型有不同的生成策略"
|
||||
- principle: "VO Assembly 模式"
|
||||
description: "查询接口批量获取关联实体后组装 VO,避免 N+1 查询,提升查询性能"
|
||||
- principle: "标准 CRUD 模式"
|
||||
description: "全部 Controller/Service/Mapper 遵循统一的增删改查规范,配合 @PreAuthorize 权限控制"
|
||||
- principle: "主子表结构"
|
||||
description: "BOM、检验单、出入库单、维修单等使用主子表结构,子表通过外键关联主表"
|
||||
- principle: "Oracle/PG 兼容"
|
||||
description: "使用 @KeySequence 注解兼容 Oracle 和 PostgreSQL 的主键生成方式"
|
||||
- principle: "跨模块 API 集成"
|
||||
description: "通过 AdminUserApi、RoleApi 等接口与 system 模块集成,不直接依赖实现类"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "物料聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ItemDO", "ItemBatchConfigDO"]
|
||||
description: "ItemDO 是物料聚合根,BatchConfigDO 定义物料的批次生成规则"
|
||||
- name: "BOM 聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ProductBomDO", "Bom子表"]
|
||||
description: "ProductBomDO 是 BOM 聚合根,定义产品由哪些子物料组成及用量"
|
||||
- name: "工艺路线聚合"
|
||||
type: "聚合根"
|
||||
entities: ["RouteDO", "RouteProcessDO", "RouteProductDO"]
|
||||
description: "RouteDO 是工艺路线聚合根,包含工序列表和适用产品"
|
||||
- name: "工单聚合"
|
||||
type: "聚合根"
|
||||
entities: ["WorkOrderDO", "WorkOrderBomDO"]
|
||||
description: "WorkOrderDO 是工单聚合根,关联 BOM 和工艺路线"
|
||||
- name: "任务聚合"
|
||||
type: "聚合根"
|
||||
entities: ["TaskDO", "TaskIssueDO"]
|
||||
description: "TaskDO 是生产任务聚合根,关联工序、工位、设备、操作员"
|
||||
- name: "流转卡聚合"
|
||||
type: "聚合根"
|
||||
entities: ["CardDO", "CardProcessDO"]
|
||||
description: "CardDO 是流转卡聚合根,跟踪各工序的执行情况"
|
||||
- name: "检验单聚合"
|
||||
type: "聚合根"
|
||||
entities: ["QcIqcDO/QcIpqcDO/QcOqcDO/QcRqcDO", "检验行"]
|
||||
description: "四大检验单各自为聚合根,主子表结构"
|
||||
- name: "设备聚合"
|
||||
type: "聚合根"
|
||||
entities: ["MachineryDO", "CheckPlanDO", "CheckRecordDO", "MaintenRecordDO", "RepairDO"]
|
||||
description: "MachineryDO 是设备聚合根,关联点检、保养、维修"
|
||||
- name: "仓库聚合"
|
||||
type: "聚合根"
|
||||
entities: ["WarehouseDO", "WarehouseAreaDO", "WarehouseLocationDO"]
|
||||
description: "WarehouseDO 是仓库聚合根,包含库区和库位的层级结构"
|
||||
- name: "库存聚合"
|
||||
type: "聚合根"
|
||||
entities: ["MaterialStockDO", "BatchDO", "BarcodeDO", "SnDO"]
|
||||
description: "MaterialStockDO 是库存聚合根,按物料/仓库/库位/批次维度管理"
|
||||
- name: "排班聚合"
|
||||
type: "聚合根"
|
||||
entities: ["CalPlanDO", "CalPlanShiftDO", "CalPlanTeamDO", "CalTeamDO"]
|
||||
description: "CalPlanDO 是排班计划聚合根,关联班次和班组"
|
||||
- name: "自动编码聚合"
|
||||
type: "聚合根"
|
||||
entities: ["AutoCodeRuleDO", "AutoCodePartDO", "AutoCodeRecordDO"]
|
||||
description: "AutoCodeRuleDO 是编码规则聚合根,由零件组合定义编码格式"
|
||||
|
||||
services:
|
||||
- name: "ItemService"
|
||||
description: "物料管理服务,提供物料的增删改查"
|
||||
- name: "ProductBomService"
|
||||
description: "BOM 管理服务,管理产品物料清单"
|
||||
- name: "RouteService"
|
||||
description: "工艺路线服务,管理生产工序流程"
|
||||
- name: "WorkOrderService"
|
||||
description: "工单管理服务,工单状态机流转"
|
||||
- name: "TaskService"
|
||||
description: "生产任务服务,支持甘特图排产"
|
||||
- name: "CardService"
|
||||
description: "流转卡服务,跟踪生产工序执行"
|
||||
- name: "FeedbackService"
|
||||
description: "报工反馈服务,提交/审批报工"
|
||||
- name: "QcIqcService/QcIpqcService/QcOqcService/QcRqcService"
|
||||
description: "四大检验服务,管理来料/过程/出货/退货检验"
|
||||
- name: "MachineryService"
|
||||
description: "设备管理服务,设备台账维护"
|
||||
- name: "RepairService"
|
||||
description: "维修管理服务,维修单状态流转"
|
||||
- name: "WarehouseService"
|
||||
description: "仓库管理服务,仓库/库区/库位层级管理"
|
||||
- name: "MaterialStockService"
|
||||
description: "库存管理服务,出入库、调拨、盘点"
|
||||
- name: "AutoCodeService"
|
||||
description: "自动编码服务,根据规则生成业务单号"
|
||||
- name: "CalPlanService"
|
||||
description: "排班计划服务,工厂日历和班组排班"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "controller"
|
||||
purpose: "HTTP 接口层,提供 RESTful API"
|
||||
components:
|
||||
- "admin/home - 首页看板接口"
|
||||
- "admin/cal - 排班日历接口(cal/holiday, cal/plan, cal/team 等)"
|
||||
- "admin/md - 主数据接口(item, bom, sop, sip, client, vendor, workshop, workstation, auto-code)"
|
||||
- "admin/pro - 生产管理接口(work-order, task, route, process, card, feedback, work-record, andon)"
|
||||
- "admin/qc - 质量管理接口(template, indicator, defect, iqc, ipqc, oqc, rqc, pending-inspect)"
|
||||
- "admin/dv - 设备管理接口(machinery, machinery-type, check-plan, check-record, mainten-record, repair, subject)"
|
||||
- "admin/tm - 工具管理接口(tool, tool-type)"
|
||||
- "admin/wm - 仓库管理接口(warehouse, area, location, material-stock, batch, barcode, sn, arrival-notice, item-receipt, product-receipt, product-issue, return-issue, misc-issue, misc-receipt, product-sales, return-sales, outsource-issue, outsource-receipt, return-vendor, transfer, package, stock-taking, sales-notice)"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "md - 主数据服务(ItemService, ProductBomService, RouteService, WorkshopService, WorkstationService, AutoCodeService)"
|
||||
- "pro - 生产服务(WorkOrderService, TaskService, CardService, FeedbackService, WorkRecordService, AndonService)"
|
||||
- "qc - 质量服务(QcTemplateService, QcIndicatorService, QcDefectService, QcIqcService, QcIpqcService, QcOqcService, QcRqcService)"
|
||||
- "dv - 设备服务(MachineryService, CheckPlanService, CheckRecordService, MaintenRecordService, RepairService)"
|
||||
- "tm - 工具服务(ToolService)"
|
||||
- "wm - 仓库服务(WarehouseService, MaterialStockService, BatchService, BarcodeService, 各出入库Service, TransferService, PackageService, StockTakingService)"
|
||||
- "cal - 日历服务(CalPlanService, CalHolidayService, CalTeamService)"
|
||||
- "home - 首页服务(HomeService)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject - DO 实体类定义,按域组织(md/pro/qc/dv/tm/wm/cal)"
|
||||
- "mysql - MyBatis Mapper 接口,继承 BaseMapperX"
|
||||
|
||||
- name: "enums"
|
||||
purpose: "枚举和错误码"
|
||||
components:
|
||||
- "MesBizTypeConstants - 集中管理业务类型常量"
|
||||
- "各状态枚举 - WorkOrderStatusEnum, CardStatusEnum, FeedbackStatusEnum, RepairStatusEnum 等"
|
||||
- "ErrorCodeConstants - 错误码常量定义"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "状态机模式 (State Machine Pattern)"
|
||||
location: "service 层各核心实体的状态管理"
|
||||
purpose: "工单/流转卡/报工/维修/调拨/排班计划的状态流转管理,变更前校验前置状态"
|
||||
- pattern: "策略模式 (Strategy Pattern)"
|
||||
location: "service/md/AutoCodeService"
|
||||
purpose: "自动编码规则支持日期、固定字符、输入字符、序列号等零件组合,不同零件类型有不同的生成策略"
|
||||
- pattern: "VO Assembly 模式"
|
||||
location: "controller 层的 get/page 接口"
|
||||
purpose: "批量获取关联实体后组装 VO,避免 N+1 查询"
|
||||
- pattern: "标准 CRUD 模式"
|
||||
location: "全部 Controller/Service/Mapper"
|
||||
purpose: "统一的增删改查 + @PreAuthorize 权限控制"
|
||||
- pattern: "主子表模式"
|
||||
location: "BOM、检验单、出入库单、维修单等"
|
||||
purpose: "主表记录基本信息,子表记录明细,通过外键关联"
|
||||
- pattern: "CAS 更新模式"
|
||||
location: "状态变更、库存扣减"
|
||||
purpose: "使用 WHERE status = #{oldStatus} 防止并发覆盖"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "AdminUserApi"
|
||||
method: "getUser()"
|
||||
description: "获取用户信息,用于工位操作员、报工人等关联"
|
||||
- name: "RoleApi"
|
||||
method: "hasAnyRole()"
|
||||
description: "角色权限校验"
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息(工位操作员名称、报工人名称等)"
|
||||
- module: "yudao-module-system"
|
||||
api: "RoleApi"
|
||||
purpose: "角色权限校验"
|
||||
mq: []
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有 DO 继承 BaseDO,包含 id, creator, createTime, updater, updateTime, deleted 字段。部分表使用 @KeySequence 注解兼容 Oracle/PostgreSQL。"
|
||||
|
||||
# 核心数据表(按域组织)
|
||||
tables:
|
||||
# ========== 主数据域 (MD) - 17 张表 ==========
|
||||
- name: "mes_md_item"
|
||||
comment: "物料表"
|
||||
entity: "MesItemDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_item_type"
|
||||
comment: "物料分类表"
|
||||
entity: "MesItemTypeDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_item_batch_config"
|
||||
comment: "物料批次配置表"
|
||||
entity: "MesItemBatchConfigDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_product_bom"
|
||||
comment: "产品 BOM 表"
|
||||
entity: "MesProductBomDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_product_sip"
|
||||
comment: "产品 SIP 表(标准检验程序)"
|
||||
entity: "MesProductSipDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_product_sop"
|
||||
comment: "产品 SOP 表(标准作业指导书)"
|
||||
entity: "MesProductSopDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_client"
|
||||
comment: "客户表"
|
||||
entity: "MesClientDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_vendor"
|
||||
comment: "供应商表"
|
||||
entity: "MesVendorDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_unit_measure"
|
||||
comment: "计量单位表"
|
||||
entity: "MesUnitMeasureDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_workshop"
|
||||
comment: "车间表"
|
||||
entity: "MesWorkshopDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_workstation"
|
||||
comment: "工位表"
|
||||
entity: "MesWorkstationDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_workstation_machine"
|
||||
comment: "工位机器关联表"
|
||||
entity: "MesWorkstationMachineDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_workstation_tool"
|
||||
comment: "工位工具关联表"
|
||||
entity: "MesWorkstationToolDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_workstation_worker"
|
||||
comment: "工位工人关联表"
|
||||
entity: "MesWorkstationWorkerDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_auto_code_rule"
|
||||
comment: "自动编码规则表"
|
||||
entity: "MesAutoCodeRuleDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_auto_code_part"
|
||||
comment: "编码规则零件表"
|
||||
entity: "MesAutoCodePartDO"
|
||||
domain: "MD"
|
||||
- name: "mes_md_auto_code_record"
|
||||
comment: "编码记录表"
|
||||
entity: "MesAutoCodeRecordDO"
|
||||
domain: "MD"
|
||||
|
||||
# ========== 生产域 (PRO) - 17 张表 ==========
|
||||
- name: "mes_pro_work_order"
|
||||
comment: "生产工单表"
|
||||
entity: "MesWorkOrderDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_work_order_bom"
|
||||
comment: "工单 BOM 表"
|
||||
entity: "MesWorkOrderBomDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_task"
|
||||
comment: "生产任务表"
|
||||
entity: "MesTaskDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_task_issue"
|
||||
comment: "任务领料表"
|
||||
entity: "MesTaskIssueDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_route"
|
||||
comment: "工艺路线表"
|
||||
entity: "MesRouteDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_route_process"
|
||||
comment: "工艺路线工序表"
|
||||
entity: "MesRouteProcessDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_route_product"
|
||||
comment: "工艺路线产品关联表"
|
||||
entity: "MesRouteProductDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_route_product_bom"
|
||||
comment: "工艺路线产品 BOM 关联表"
|
||||
entity: "MesRouteProductBomDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_process"
|
||||
comment: "工序表"
|
||||
entity: "MesProcessDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_process_content"
|
||||
comment: "工序内容表"
|
||||
entity: "MesProcessContentDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_card"
|
||||
comment: "流转卡表"
|
||||
entity: "MesCardDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_card_process"
|
||||
comment: "流转卡工序表"
|
||||
entity: "MesCardProcessDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_feedback"
|
||||
comment: "报工反馈表"
|
||||
entity: "MesFeedbackDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_work_record"
|
||||
comment: "报工记录表"
|
||||
entity: "MesWorkRecordDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_work_record_log"
|
||||
comment: "报工日志表"
|
||||
entity: "MesWorkRecordLogDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_andon_config"
|
||||
comment: "安灯配置表"
|
||||
entity: "MesAndonConfigDO"
|
||||
domain: "PRO"
|
||||
- name: "mes_pro_andon_record"
|
||||
comment: "安灯记录表"
|
||||
entity: "MesAndonRecordDO"
|
||||
domain: "PRO"
|
||||
|
||||
# ========== 质量域 (QC) - 17 张表 ==========
|
||||
- name: "mes_qc_template"
|
||||
comment: "检验模板表"
|
||||
entity: "MesQcTemplateDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_template_item"
|
||||
comment: "检验模板检验项表"
|
||||
entity: "MesQcTemplateItemDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_template_indicator"
|
||||
comment: "检验模板指标表"
|
||||
entity: "MesQcTemplateIndicatorDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_indicator"
|
||||
comment: "检验指标表"
|
||||
entity: "MesQcIndicatorDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_indicator_result"
|
||||
comment: "指标结果表"
|
||||
entity: "MesQcIndicatorResultDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_indicator_result_detail"
|
||||
comment: "指标结果明细表"
|
||||
entity: "MesQcIndicatorResultDetailDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_defect"
|
||||
comment: "缺陷类型表"
|
||||
entity: "MesQcDefectDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_defect_record"
|
||||
comment: "缺陷记录表"
|
||||
entity: "MesQcDefectRecordDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_iqc"
|
||||
comment: "来料检验单表"
|
||||
entity: "MesQcIqcDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_iqc_line"
|
||||
comment: "来料检验行表"
|
||||
entity: "MesQcIqcLineDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_ipqc"
|
||||
comment: "过程检验单表"
|
||||
entity: "MesQcIpqcDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_ipqc_line"
|
||||
comment: "过程检验行表"
|
||||
entity: "MesQcIpqcLineDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_oqc"
|
||||
comment: "出货检验单表"
|
||||
entity: "MesQcOqcDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_oqc_line"
|
||||
comment: "出货检验行表"
|
||||
entity: "MesQcOqcLineDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_rqc"
|
||||
comment: "退货检验单表"
|
||||
entity: "MesQcRqcDO"
|
||||
domain: "QC"
|
||||
- name: "mes_qc_rqc_line"
|
||||
comment: "退货检验行表"
|
||||
entity: "MesQcRqcLineDO"
|
||||
domain: "QC"
|
||||
|
||||
# ========== 设备域 (DV) - 12 张表 ==========
|
||||
- name: "mes_dv_machinery"
|
||||
comment: "设备台账表"
|
||||
entity: "MesMachineryDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_machinery_type"
|
||||
comment: "设备类型表"
|
||||
entity: "MesMachineryTypeDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_check_plan"
|
||||
comment: "点检计划表"
|
||||
entity: "MesCheckPlanDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_check_plan_machinery"
|
||||
comment: "点检计划设备关联表"
|
||||
entity: "MesCheckPlanMachineryDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_check_plan_subject"
|
||||
comment: "点检计划检查项关联表"
|
||||
entity: "MesCheckPlanSubjectDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_check_record"
|
||||
comment: "点检记录表"
|
||||
entity: "MesCheckRecordDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_check_record_line"
|
||||
comment: "点检记录行表"
|
||||
entity: "MesCheckRecordLineDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_mainten_record"
|
||||
comment: "保养记录表"
|
||||
entity: "MesMaintenRecordDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_mainten_record_line"
|
||||
comment: "保养记录行表"
|
||||
entity: "MesMaintenRecordLineDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_repair"
|
||||
comment: "维修单表"
|
||||
entity: "MesRepairDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_repair_line"
|
||||
comment: "维修行表"
|
||||
entity: "MesRepairLineDO"
|
||||
domain: "DV"
|
||||
- name: "mes_dv_subject"
|
||||
comment: "检查项目表"
|
||||
entity: "MesSubjectDO"
|
||||
domain: "DV"
|
||||
|
||||
# ========== 工具域 (TM) - 2 张表 ==========
|
||||
- name: "mes_tm_tool"
|
||||
comment: "工具台账表"
|
||||
entity: "MesToolDO"
|
||||
domain: "TM"
|
||||
- name: "mes_tm_tool_type"
|
||||
comment: "工具类型表"
|
||||
entity: "MesToolTypeDO"
|
||||
domain: "TM"
|
||||
|
||||
# ========== 仓库域 (WM) - 40+ 张表 ==========
|
||||
- name: "mes_wm_warehouse"
|
||||
comment: "仓库表"
|
||||
entity: "MesWarehouseDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_warehouse_area"
|
||||
comment: "库区表"
|
||||
entity: "MesWarehouseAreaDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_warehouse_location"
|
||||
comment: "库位表"
|
||||
entity: "MesWarehouseLocationDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_material_stock"
|
||||
comment: "物料库存表"
|
||||
entity: "MesMaterialStockDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_batch"
|
||||
comment: "批次表"
|
||||
entity: "MesBatchDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_barcode"
|
||||
comment: "条码表"
|
||||
entity: "MesBarcodeDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_barcode_config"
|
||||
comment: "条码配置表"
|
||||
entity: "MesBarcodeConfigDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_sn"
|
||||
comment: "SN 序列号表"
|
||||
entity: "MesSnDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_transaction"
|
||||
comment: "库存事务表"
|
||||
entity: "MesTransactionDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_arrival_notice"
|
||||
comment: "到货通知表"
|
||||
entity: "MesArrivalNoticeDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_arrival_notice_line"
|
||||
comment: "到货通知行表"
|
||||
entity: "MesArrivalNoticeLineDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_item_receipt"
|
||||
comment: "采购入库表"
|
||||
entity: "MesItemReceiptDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_item_receipt_line"
|
||||
comment: "采购入库行表"
|
||||
entity: "MesItemReceiptLineDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_item_receipt_detail"
|
||||
comment: "采购入库详情表"
|
||||
entity: "MesItemReceiptDetailDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_product_receipt"
|
||||
comment: "产品入库表"
|
||||
entity: "MesProductReceiptDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_product_issue"
|
||||
comment: "生产领料表"
|
||||
entity: "MesProductIssueDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_return_issue"
|
||||
comment: "生产退料表"
|
||||
entity: "MesReturnIssueDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_misc_issue"
|
||||
comment: "其他出库表"
|
||||
entity: "MesMiscIssueDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_misc_receipt"
|
||||
comment: "其他入库表"
|
||||
entity: "MesMiscReceiptDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_product_sales"
|
||||
comment: "销售出库表"
|
||||
entity: "MesProductSalesDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_return_sales"
|
||||
comment: "销售退货表"
|
||||
entity: "MesReturnSalesDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_outsource_issue"
|
||||
comment: "委外发料表"
|
||||
entity: "MesOutsourceIssueDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_outsource_receipt"
|
||||
comment: "委外收料表"
|
||||
entity: "MesOutsourceReceiptDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_return_vendor"
|
||||
comment: "供应商退货表"
|
||||
entity: "MesReturnVendorDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_transfer"
|
||||
comment: "库存调拨表"
|
||||
entity: "MesTransferDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_package"
|
||||
comment: "包装表"
|
||||
entity: "MesPackageDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_package_line"
|
||||
comment: "包装行表"
|
||||
entity: "MesPackageLineDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_stock_taking_plan"
|
||||
comment: "盘点计划表"
|
||||
entity: "MesStockTakingPlanDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_stock_taking_task"
|
||||
comment: "盘点任务表"
|
||||
entity: "MesStockTakingTaskDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_stock_taking_task_result"
|
||||
comment: "盘点结果表"
|
||||
entity: "MesStockTakingTaskResultDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_item_consume"
|
||||
comment: "物料消耗表"
|
||||
entity: "MesItemConsumeDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_product_produce"
|
||||
comment: "产品产出表"
|
||||
entity: "MesProductProduceDO"
|
||||
domain: "WM"
|
||||
- name: "mes_wm_sales_notice"
|
||||
comment: "销售通知表"
|
||||
entity: "MesSalesNoticeDO"
|
||||
domain: "WM"
|
||||
|
||||
# ========== 日历域 (CAL) - 7 张表 ==========
|
||||
- name: "mes_cal_holiday"
|
||||
comment: "节假日表"
|
||||
entity: "MesCalHolidayDO"
|
||||
domain: "CAL"
|
||||
- name: "mes_cal_plan"
|
||||
comment: "排班计划表"
|
||||
entity: "MesCalPlanDO"
|
||||
domain: "CAL"
|
||||
- name: "mes_cal_plan_shift"
|
||||
comment: "排班计划班次表"
|
||||
entity: "MesCalPlanShiftDO"
|
||||
domain: "CAL"
|
||||
- name: "mes_cal_plan_team"
|
||||
comment: "排班计划班组表"
|
||||
entity: "MesCalPlanTeamDO"
|
||||
domain: "CAL"
|
||||
- name: "mes_cal_team"
|
||||
comment: "班组表"
|
||||
entity: "MesCalTeamDO"
|
||||
domain: "CAL"
|
||||
- name: "mes_cal_team_member"
|
||||
comment: "班组成员表"
|
||||
entity: "MesCalTeamMemberDO"
|
||||
domain: "CAL"
|
||||
- name: "mes_cal_team_shift"
|
||||
comment: "班组班次表"
|
||||
entity: "MesCalTeamShiftDO"
|
||||
domain: "CAL"
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
# 主数据域
|
||||
- { from: "mes_md_item", to: "mes_md_item_type", type: "N:1", foreign_key: "item_type_id" }
|
||||
- { from: "mes_md_workstation", to: "mes_md_workshop", type: "N:1", foreign_key: "workshop_id" }
|
||||
- { from: "mes_md_workstation_machine", to: "mes_md_workstation", type: "N:1", foreign_key: "workstation_id" }
|
||||
- { from: "mes_md_workstation_tool", to: "mes_md_workstation", type: "N:1", foreign_key: "workstation_id" }
|
||||
- { from: "mes_md_workstation_worker", to: "mes_md_workstation", type: "N:1", foreign_key: "workstation_id" }
|
||||
- { from: "mes_md_auto_code_part", to: "mes_md_auto_code_rule", type: "N:1", foreign_key: "rule_id" }
|
||||
- { from: "mes_md_auto_code_record", to: "mes_md_auto_code_rule", type: "N:1", foreign_key: "rule_id" }
|
||||
# 生产域
|
||||
- { from: "mes_pro_work_order_bom", to: "mes_pro_work_order", type: "N:1", foreign_key: "work_order_id" }
|
||||
- { from: "mes_pro_task", to: "mes_pro_work_order", type: "N:1", foreign_key: "work_order_id" }
|
||||
- { from: "mes_pro_task", to: "mes_pro_route_process", type: "N:1", foreign_key: "route_process_id" }
|
||||
- { from: "mes_pro_task_issue", to: "mes_pro_task", type: "N:1", foreign_key: "task_id" }
|
||||
- { from: "mes_pro_route_process", to: "mes_pro_route", type: "N:1", foreign_key: "route_id" }
|
||||
- { from: "mes_pro_route_process", to: "mes_pro_process", type: "N:1", foreign_key: "process_id" }
|
||||
- { from: "mes_pro_card_process", to: "mes_pro_card", type: "N:1", foreign_key: "card_id" }
|
||||
- { from: "mes_pro_feedback", to: "mes_pro_work_order", type: "N:1", foreign_key: "work_order_id" }
|
||||
- { from: "mes_pro_andon_record", to: "mes_pro_andon_config", type: "N:1", foreign_key: "config_id" }
|
||||
# 质量域
|
||||
- { from: "mes_qc_template_item", to: "mes_qc_template", type: "N:1", foreign_key: "template_id" }
|
||||
- { from: "mes_qc_template_indicator", to: "mes_qc_template_item", type: "N:1", foreign_key: "template_item_id" }
|
||||
- { from: "mes_qc_iqc_line", to: "mes_qc_iqc", type: "N:1", foreign_key: "iqc_id" }
|
||||
- { from: "mes_qc_ipqc_line", to: "mes_qc_ipqc", type: "N:1", foreign_key: "ipqc_id" }
|
||||
- { from: "mes_qc_oqc_line", to: "mes_qc_oqc", type: "N:1", foreign_key: "oqc_id" }
|
||||
- { from: "mes_qc_rqc_line", to: "mes_qc_rqc", type: "N:1", foreign_key: "rqc_id" }
|
||||
- { from: "mes_qc_indicator_result", to: "mes_qc_indicator", type: "N:1", foreign_key: "indicator_id" }
|
||||
- { from: "mes_qc_indicator_result_detail", to: "mes_qc_indicator_result", type: "N:1", foreign_key: "result_id" }
|
||||
- { from: "mes_qc_defect_record", to: "mes_qc_defect", type: "N:1", foreign_key: "defect_id" }
|
||||
# 设备域
|
||||
- { from: "mes_dv_machinery", to: "mes_dv_machinery_type", type: "N:1", foreign_key: "machinery_type_id" }
|
||||
- { from: "mes_dv_check_plan_machinery", to: "mes_dv_check_plan", type: "N:1", foreign_key: "plan_id" }
|
||||
- { from: "mes_dv_check_plan_subject", to: "mes_dv_check_plan", type: "N:1", foreign_key: "plan_id" }
|
||||
- { from: "mes_dv_check_record", to: "mes_dv_check_plan", type: "N:1", foreign_key: "plan_id" }
|
||||
- { from: "mes_dv_check_record_line", to: "mes_dv_check_record", type: "N:1", foreign_key: "record_id" }
|
||||
- { from: "mes_dv_mainten_record_line", to: "mes_dv_mainten_record", type: "N:1", foreign_key: "record_id" }
|
||||
- { from: "mes_dv_repair_line", to: "mes_dv_repair", type: "N:1", foreign_key: "repair_id" }
|
||||
# 仓库域
|
||||
- { from: "mes_wm_warehouse_area", to: "mes_wm_warehouse", type: "N:1", foreign_key: "warehouse_id" }
|
||||
- { from: "mes_wm_warehouse_location", to: "mes_wm_warehouse_area", type: "N:1", foreign_key: "area_id" }
|
||||
- { from: "mes_wm_material_stock", to: "mes_md_item", type: "N:1", foreign_key: "item_id" }
|
||||
- { from: "mes_wm_arrival_notice_line", to: "mes_wm_arrival_notice", type: "N:1", foreign_key: "notice_id" }
|
||||
- { from: "mes_wm_item_receipt_line", to: "mes_wm_item_receipt", type: "N:1", foreign_key: "receipt_id" }
|
||||
- { from: "mes_wm_item_receipt_detail", to: "mes_wm_item_receipt_line", type: "N:1", foreign_key: "line_id" }
|
||||
- { from: "mes_wm_package_line", to: "mes_wm_package", type: "N:1", foreign_key: "package_id" }
|
||||
- { from: "mes_wm_stock_taking_task", to: "mes_wm_stock_taking_plan", type: "N:1", foreign_key: "plan_id" }
|
||||
- { from: "mes_wm_stock_taking_task_result", to: "mes_wm_stock_taking_task", type: "N:1", foreign_key: "task_id" }
|
||||
# 日历域
|
||||
- { from: "mes_cal_plan_shift", to: "mes_cal_plan", type: "N:1", foreign_key: "plan_id" }
|
||||
- { from: "mes_cal_plan_team", to: "mes_cal_plan", type: "N:1", foreign_key: "plan_id" }
|
||||
- { from: "mes_cal_plan_team", to: "mes_cal_team", type: "N:1", foreign_key: "team_id" }
|
||||
- { from: "mes_cal_team_member", to: "mes_cal_team", type: "N:1", foreign_key: "team_id" }
|
||||
- { from: "mes_cal_team_shift", to: "mes_cal_team", type: "N:1", foreign_key: "team_id" }
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - xxx') - Swagger 文档标签"
|
||||
- "@RestController - REST 控制器"
|
||||
- "@RequestMapping('/admin-api/mes/xxx') - 请求路径前缀"
|
||||
- "@Validated - 参数校验"
|
||||
- "@PreAuthorize('@ss.hasPermission('mes:xxx:action')') - 权限控制"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 物料管理")
|
||||
@RestController
|
||||
@RequestMapping("/admin-api/mes/item")
|
||||
@Validated
|
||||
public class MesItemController {
|
||||
@Resource
|
||||
private MesItemService itemService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建物料")
|
||||
@PreAuthorize("@ss.hasPermission('mes:item:create')")
|
||||
public CommonResult<Long> create(@Valid @RequestBody MesItemSaveReqVO reqVO) {
|
||||
return success(itemService.create(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新物料")
|
||||
@PreAuthorize("@ss.hasPermission('mes:item:update')")
|
||||
public CommonResult<Boolean> update(@Valid @RequestBody MesItemSaveReqVO reqVO) {
|
||||
itemService.update(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除物料")
|
||||
@PreAuthorize("@ss.hasPermission('mes:item:delete')")
|
||||
public CommonResult<Boolean> delete(@RequestParam("id") Long id) {
|
||||
itemService.delete(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取物料详情")
|
||||
@PreAuthorize("@ss.hasPermission('mes:item:query')")
|
||||
public CommonResult<MesItemRespVO> get(@RequestParam("id") Long id) {
|
||||
return success(itemService.get(id));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "物料分页查询")
|
||||
@PreAuthorize("@ss.hasPermission('mes:item:query')")
|
||||
public CommonResult<PageResult<MesItemRespVO>> page(@Valid MesItemPageReqVO reqVO) {
|
||||
return success(itemService.page(reqVO));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "XxxService 接口定义业务方法,XxxServiceImpl 实现类"
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class XxxServiceImpl implements XxxService {
|
||||
@Resource
|
||||
private XxxMapper xxxMapper;
|
||||
|
||||
// 使用 static final 定义常量
|
||||
// 使用 @Transactional 注解控制事务
|
||||
// 使用 validateXxxExists 方法校验存在性
|
||||
// 使用 exception(ErrorCode) 抛出业务异常
|
||||
}
|
||||
state_machine_example: |
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStatus(Long id, Integer status) {
|
||||
MesWorkOrderDO order = validateWorkOrderExists(id);
|
||||
// 校验状态流转
|
||||
if (!MesWorkOrderStatusEnum.canTransit(order.getStatus(), status)) {
|
||||
throw exception(WORK_ORDER_STATUS_TRANSIT_FAIL);
|
||||
}
|
||||
// CAS 更新
|
||||
workOrderMapper.updateStatus(id, order.getStatus(), status);
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
public interface XxxMapper extends BaseMapperX<XxxDO> {
|
||||
default PageResult<XxxDO> selectPage(XxxPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<XxxDO>()
|
||||
.likeIfPresent(XxxDO::getName, reqVO.getName())
|
||||
.eqIfPresent(XxxDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(XxxDO::getId));
|
||||
}
|
||||
}
|
||||
example: |
|
||||
public interface MesItemMapper extends BaseMapperX<MesItemDO> {
|
||||
default PageResult<MesItemDO> selectPage(MesItemPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<MesItemDO>()
|
||||
.likeIfPresent(MesItemDO::getName, reqVO.getName())
|
||||
.likeIfPresent(MesItemDO::getCode, reqVO.getCode())
|
||||
.eqIfPresent(MesItemDO::getItemTypeId, reqVO.getItemTypeId())
|
||||
.eqIfPresent(MesItemDO::getStatus, reqVO.getStatus())
|
||||
.orderByDesc(MesItemDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# VO Assembly 模式
|
||||
vo_assembly: |
|
||||
// 查询时批量获取关联数据,避免 N+1
|
||||
PageResult<MesWorkOrderDO> pageResult = workOrderMapper.selectPage(reqVO);
|
||||
Set<Long> itemIds = pageResult.getList().stream()
|
||||
.map(MesWorkOrderDO::getItemId).collect(Collectors.toSet());
|
||||
Map<Long, MesItemDO> itemMap = itemService.getItemMap(itemIds);
|
||||
|
||||
// 组装 VO
|
||||
return new PageResult<>(pageResult.getList().stream().map(order -> {
|
||||
MesWorkOrderRespVO vo = BeanUtils.toBean(order, MesWorkOrderRespVO.class);
|
||||
MesItemDO item = itemMap.get(order.getItemId());
|
||||
if (item != null) {
|
||||
vo.setItemName(item.getName());
|
||||
}
|
||||
return vo;
|
||||
}).collect(Collectors.toList()), pageResult.getTotal());
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-xxx-xxx-xxx"
|
||||
note: "具体错误码前缀需查阅 ErrorCodeConstants.java 确认"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ErrorCode)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增物料属性
|
||||
new_feature:
|
||||
title: "新增物料属性"
|
||||
steps:
|
||||
- step: "1. 修改数据库表"
|
||||
description: "在 mes_md_item 表中添加新字段"
|
||||
- step: "2. 更新 DO 实体"
|
||||
description: "在 MesItemDO 中添加对应属性"
|
||||
- step: "3. 更新 VO"
|
||||
description: "在 MesItemSaveReqVO 和 MesItemRespVO 中添加字段"
|
||||
- step: "4. 更新 Controller"
|
||||
description: "确保新字段在创建/更新/查询接口中正确处理"
|
||||
|
||||
# 新增出入库类型
|
||||
new_channel:
|
||||
title: "新增出入库类型"
|
||||
steps:
|
||||
- step: "1. 创建出入库表"
|
||||
description: "参考 mes_wm_misc_issue 创建新的出入库主子表"
|
||||
- step: "2. 创建 DO 实体"
|
||||
description: "创建 XxxDO 继承 BaseDO"
|
||||
- step: "3. 创建 Mapper/Service/Controller"
|
||||
description: "遵循标准 CRUD 模式创建各层代码"
|
||||
- step: "4. 集成库存更新"
|
||||
description: "在 Service 中调用 MaterialStockService 更新库存"
|
||||
- step: "5. 添加权限配置"
|
||||
description: "在系统权限中添加 mes:xxx:create/update/delete/query 权限"
|
||||
|
||||
# 新增检验类型
|
||||
new_inspection:
|
||||
title: "新增检验类型"
|
||||
steps:
|
||||
- step: "1. 创建检验单表"
|
||||
description: "参考 mes_qc_iqc 创建新的检验主子表"
|
||||
- step: "2. 创建 DO/VO/Service/Controller"
|
||||
description: "遵循标准 CRUD + 主子表模式"
|
||||
- step: "3. 集成检验模板"
|
||||
description: "关联 QcTemplateDO 的检验项和指标"
|
||||
- step: "4. 集成缺陷管理"
|
||||
description: "检验不合格时关联 QcDefectRecordDO"
|
||||
- step: "5. 联动仓库操作"
|
||||
description: "检验结果触发对应的出入库操作"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "状态机校验"
|
||||
description: "所有状态变更前校验前置状态,使用 CAS 方式更新防止并发覆盖"
|
||||
- practice: "VO Assembly"
|
||||
description: "查询接口批量获取关联实体后组装 VO,避免 N+1 查询"
|
||||
- practice: "库存乐观锁"
|
||||
description: "出库操作使用 quantity >= #{outQuantity} 条件防止库存变为负数"
|
||||
- practice: "自动编码防重"
|
||||
description: "使用 SELECT FOR UPDATE 锁定编码记录,防止并发生成重复编码"
|
||||
- practice: "检验与入库联动"
|
||||
description: "IQC 合格后才能办理采购入库,不合格走供应商退货"
|
||||
- practice: "设备状态联动"
|
||||
description: "维修/保养完成后自动恢复设备状态为正常"
|
||||
- practice: "事务范围控制"
|
||||
description: "只包含写操作,查询移到事务外,避免长事务和锁竞争"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "获取用户信息(工位操作员、报工人等)"
|
||||
- module: "yudao-module-system"
|
||||
api: "RoleApi"
|
||||
purpose: "角色权限校验"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
purpose: "ORM 框架,提供 CRUD 封装,BaseMapperX"
|
||||
- name: "Swagger/OpenAPI"
|
||||
purpose: "API 文档注解"
|
||||
- name: "MapStruct"
|
||||
purpose: "VO/DO 对象映射转换"
|
||||
- name: "Hutool"
|
||||
purpose: "Java 工具类库"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/ItemService.java"
|
||||
purpose: "物料管理服务接口"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/WorkOrderService.java"
|
||||
purpose: "工单管理服务接口,核心状态机"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/TaskService.java"
|
||||
purpose: "生产任务服务,甘特图支持"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/CardService.java"
|
||||
purpose: "流转卡服务,工序执行跟踪"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/FeedbackService.java"
|
||||
purpose: "报工反馈服务,审批流程"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/QcIqcService.java"
|
||||
purpose: "来料检验服务"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/MachineryService.java"
|
||||
purpose: "设备管理服务"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/dv/RepairService.java"
|
||||
purpose: "维修管理服务,状态机"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/MaterialStockService.java"
|
||||
purpose: "库存管理服务,出入库核心"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/TransferService.java"
|
||||
purpose: "调拨服务,双向库存更新"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/AutoCodeService.java"
|
||||
purpose: "自动编码服务,策略模式"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/cal/CalPlanService.java"
|
||||
purpose: "排班计划服务"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java"
|
||||
purpose: "业务类型常量定义"
|
||||
- path: "yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义"
|
||||
@@ -1,638 +0,0 @@
|
||||
# Skill 文档 - 微信公众号模块 (yudao-module-mp)
|
||||
# 提取时间: 2026-03-18
|
||||
|
||||
skill:
|
||||
id: "skill-mp"
|
||||
name: "WeChat MP (Public Account) Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-mp"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:微信公众号全生命周期管理平台
|
||||
# 解决问题:统一管理公众号账号、粉丝、消息、素材、菜单、自动回复、模板消息等核心功能
|
||||
# 系统定位:作为企业与微信用户交互的关键渠道,支撑营销推送、客户服务等业务场景
|
||||
business_position: |
|
||||
微信公众号管理模块,提供完整的公众号运营能力:
|
||||
- 多公众号管理:支持接入多个公众号账号,实现统一管理
|
||||
- 粉丝管理:粉丝信息同步、标签分组、用户画像
|
||||
- 消息管理:消息接收、客服消息、模板消息推送
|
||||
- 素材管理:临时素材、永久素材、图文消息
|
||||
- 菜单管理:自定义菜单创建与响应
|
||||
- 自动回复:关注回复、关键词回复、消息回复
|
||||
- 数据统计:用户分析、消息分析、接口分析
|
||||
|
||||
# 设计原则
|
||||
design_principles:
|
||||
- name: "消息路由模式"
|
||||
description: "采用 WxMpMessageRouter 实现消息分发,通过责任链模式处理不同类型消息和事件"
|
||||
- name: "多账号隔离"
|
||||
description: "通过 MpServiceFactory 工厂模式管理多公众号 WxMpService 实例,实现账号级别的隔离"
|
||||
- name: "事件驱动架构"
|
||||
description: "微信回调事件通过 Handler 处理器链式处理,支持关注、取消关注、菜单点击、扫码等事件"
|
||||
- name: "素材统一管理"
|
||||
description: "临时素材和永久素材统一抽象,自动下载并存储到本地文件服务"
|
||||
- name: "多租户支持"
|
||||
description: "账号层支持租户隔离,通过 TenantBaseDO 实现数据隔离"
|
||||
|
||||
# 领域模型
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "MpAccount"
|
||||
type: "聚合根"
|
||||
description: "公众号账号聚合根,管理公众号配置信息"
|
||||
entities: ["MpMenu", "MpTag", "MpStatistics"]
|
||||
- name: "MpUser"
|
||||
type: "聚合根"
|
||||
description: "公众号粉丝聚合根,关联粉丝标签和消息记录"
|
||||
entities: ["MpTag"]
|
||||
- name: "MpMessage"
|
||||
type: "聚合根"
|
||||
description: "消息聚合根,包含自动回复配置和消息模板"
|
||||
entities: ["MpAutoReply", "MpMessageTemplate"]
|
||||
- name: "MpMaterial"
|
||||
type: "聚合根"
|
||||
description: "素材聚合根,管理临时素材和永久素材"
|
||||
entities: []
|
||||
|
||||
value_objects:
|
||||
- name: "MpMessageDO.Article"
|
||||
description: "图文消息值对象,包含标题、描述、图片链接、跳转链接"
|
||||
- name: "MpAutoReplyTypeEnum"
|
||||
description: "自动回复类型枚举:关注回复、消息回复、关键词回复"
|
||||
- name: "MpAutoReplyMatchEnum"
|
||||
description: "关键词匹配模式枚举:完全匹配、半匹配"
|
||||
- name: "MpMessageSendFromEnum"
|
||||
description: "消息发送方向枚举:粉丝发给公众号、公众号发给粉丝"
|
||||
|
||||
services:
|
||||
- name: "MpAccountService"
|
||||
description: "公众号账号管理服务,提供账号CRUD和缓存能力"
|
||||
- name: "MpUserService"
|
||||
description: "粉丝管理服务,提供粉丝同步、更新、查询能力"
|
||||
- name: "MpMessageService"
|
||||
description: "消息服务,处理消息接收、发送、自动回复"
|
||||
- name: "MpMaterialService"
|
||||
description: "素材服务,处理素材上传、下载、管理"
|
||||
- name: "MpMenuService"
|
||||
description: "菜单服务,管理自定义菜单"
|
||||
- name: "MpAutoReplyService"
|
||||
description: "自动回复服务,处理关注回复、关键词回复、消息回复"
|
||||
- name: "MpMessageTemplateService"
|
||||
description: "模板消息服务,管理模板同步和发送"
|
||||
- name: "MpStatisticsService"
|
||||
description: "统计数据服务,获取用户分析、消息分析数据"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口层,提供管理后台和微信回调接口"
|
||||
components:
|
||||
- "MpAccountController: 公众号账号管理接口"
|
||||
- "MpUserController: 粉丝管理接口"
|
||||
- "MpMessageController: 消息管理接口"
|
||||
- "MpMessageTemplateController: 模板消息接口"
|
||||
- "MpAutoReplyController: 自动回复管理接口"
|
||||
- "MpMaterialController: 素材管理接口"
|
||||
- "MpMenuController: 菜单管理接口"
|
||||
- "MpTagController: 标签管理接口"
|
||||
- "MpOpenController: 微信回调入口(签名校验、消息处理)"
|
||||
- "MpStatisticsController: 数据统计接口"
|
||||
- "MpDraftController: 草稿管理接口"
|
||||
- "MpFreePublishController: 发布能力接口"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "MpAccountService: 账号管理核心服务"
|
||||
- "MpUserService: 粉丝管理服务"
|
||||
- "MpMessageService: 消息处理服务"
|
||||
- "MpMaterialService: 素材管理服务"
|
||||
- "MpMenuService: 菜单管理服务"
|
||||
- "MpAutoReplyService: 自动回复服务"
|
||||
- "MpMessageTemplateService: 模板消息服务"
|
||||
- "MpTagService: 标签管理服务"
|
||||
- "MpStatisticsService: 统计服务"
|
||||
|
||||
- name: "handler"
|
||||
purpose: "微信消息事件处理器"
|
||||
components:
|
||||
- "SubscribeHandler: 关注事件处理器"
|
||||
- "UnsubscribeHandler: 取消关注事件处理器"
|
||||
- "MenuHandler: 菜单点击事件处理器"
|
||||
- "MessageReceiveHandler: 消息接收处理器"
|
||||
- "MessageAutoReplyHandler: 自动回复处理器"
|
||||
- "LocationHandler: 地理位置处理器"
|
||||
- "ScanHandler: 扫码事件处理器"
|
||||
- "KfSessionHandler: 客服会话处理器"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "MpAccountMapper: 账号数据访问"
|
||||
- "MpUserMapper: 粉丝数据访问"
|
||||
- "MpMessageMapper: 消息数据访问"
|
||||
- "MpMaterialMapper: 素材数据访问"
|
||||
- "MpMenuMapper: 菜单数据访问"
|
||||
- "MpAutoReplyMapper: 自动回复数据访问"
|
||||
- "MpMessageTemplateMapper: 模板消息数据访问"
|
||||
- "MpTagMapper: 标签数据访问"
|
||||
|
||||
- name: "framework/mp"
|
||||
purpose: "微信公众号框架核心"
|
||||
components:
|
||||
- "MpServiceFactory: WxMpService 工厂接口"
|
||||
- "DefaultMpServiceFactory: 默认工厂实现,管理多账号 WxMpService"
|
||||
- "MpContextHolder: 公众号上下文持有者"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "Factory Pattern"
|
||||
location: "framework/mp/core/MpServiceFactory.java"
|
||||
purpose: "创建和管理 WxMpService 实例,支持多公众号场景"
|
||||
- pattern: "Chain of Responsibility"
|
||||
location: "DefaultMpServiceFactory.buildMpMessageRouter()"
|
||||
purpose: "WxMpMessageRouter 实现消息处理链,按规则路由到不同 Handler"
|
||||
- pattern: "Strategy Pattern"
|
||||
location: "service/handler/*"
|
||||
purpose: "不同类型消息/事件由不同 Handler 实现 WxMpMessageHandler 接口处理"
|
||||
- pattern: "Convert Pattern"
|
||||
location: "convert/*"
|
||||
purpose: "MapStruct 转换器,VO/DO/微信对象之间转换"
|
||||
- pattern: "Template Method"
|
||||
location: "MpOpenController.handleMessage()"
|
||||
purpose: "统一的微信回调处理流程:签名校验 -> 消息解析 -> 路由处理 -> 响应构建"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: [] # 无对外暴露的 API 模块
|
||||
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
purpose: "依赖用户体系、权限体系"
|
||||
- module: "yudao-module-infra"
|
||||
purpose: "依赖文件服务存储素材"
|
||||
|
||||
mq: [] # 未使用消息队列
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "TenantBaseDO | BaseDO"
|
||||
description: "MpAccountDO 使用 TenantBaseDO 支持多租户,其他实体使用 BaseDO"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
- name: "mp_account"
|
||||
comment: "公众号账号表"
|
||||
entity: "MpAccountDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "name", type: "String", comment: "公众号名称" }
|
||||
- { name: "account", type: "String", comment: "公众号账号" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
- { name: "app_secret", type: "String", comment: "公众号密钥" }
|
||||
- { name: "token", type: "String", comment: "公众号Token" }
|
||||
- { name: "aes_key", type: "String", comment: "消息加解密密钥" }
|
||||
- { name: "qr_code_url", type: "String", comment: "二维码图片URL" }
|
||||
- { name: "remark", type: "String", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_app_id", columns: ["app_id"] }
|
||||
|
||||
- name: "mp_user"
|
||||
comment: "公众号粉丝表"
|
||||
entity: "MpUserDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "openid", type: "String", comment: "粉丝标识OpenId" }
|
||||
- { name: "union_id", type: "String", comment: "微信生态唯一标识" }
|
||||
- { name: "subscribe_status", type: "Integer", comment: "关注状态(1已关注/2取消关注)" }
|
||||
- { name: "subscribe_time", type: "LocalDateTime", comment: "关注时间" }
|
||||
- { name: "unsubscribe_time", type: "LocalDateTime", comment: "取消关注时间" }
|
||||
- { name: "nickname", type: "String", comment: "昵称" }
|
||||
- { name: "head_image_url", type: "String", comment: "头像地址" }
|
||||
- { name: "language", type: "String", comment: "语言" }
|
||||
- { name: "country", type: "String", comment: "国家" }
|
||||
- { name: "province", type: "String", comment: "省份" }
|
||||
- { name: "city", type: "String", comment: "城市" }
|
||||
- { name: "remark", type: "String", comment: "备注" }
|
||||
- { name: "tag_ids", type: "List<Long>", comment: "标签ID数组" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId(冗余)" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
- { name: "uk_appid_openid", columns: ["app_id", "openid"] }
|
||||
|
||||
- name: "mp_message"
|
||||
comment: "公众号消息表"
|
||||
entity: "MpMessageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "msg_id", type: "Long", comment: "微信消息ID" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
- { name: "user_id", type: "Long", comment: "粉丝编号" }
|
||||
- { name: "openid", type: "String", comment: "粉丝OpenId" }
|
||||
- { name: "type", type: "String", comment: "消息类型(text/image/voice/video等)" }
|
||||
- { name: "send_from", type: "Integer", comment: "消息来源(1粉丝发给公众号/2公众号发给粉丝)" }
|
||||
- { name: "content", type: "String", comment: "文本消息内容" }
|
||||
- { name: "media_id", type: "String", comment: "媒体文件ID" }
|
||||
- { name: "media_url", type: "String", comment: "媒体文件URL" }
|
||||
- { name: "recognition", type: "String", comment: "语音识别文本" }
|
||||
- { name: "format", type: "String", comment: "语音格式" }
|
||||
- { name: "title", type: "String", comment: "标题" }
|
||||
- { name: "description", type: "String", comment: "描述" }
|
||||
- { name: "thumb_media_id", type: "String", comment: "缩略图媒体ID" }
|
||||
- { name: "thumb_media_url", type: "String", comment: "缩略图URL" }
|
||||
- { name: "url", type: "String", comment: "链接地址" }
|
||||
- { name: "location_x", type: "Double", comment: "地理位置纬度" }
|
||||
- { name: "location_y", type: "Double", comment: "地理位置经度" }
|
||||
- { name: "scale", type: "Double", comment: "地图缩放级别" }
|
||||
- { name: "label", type: "String", comment: "地理位置信息" }
|
||||
- { name: "articles", type: "List<Article>", comment: "图文消息数组(JSON)" }
|
||||
- { name: "music_url", type: "String", comment: "音乐链接" }
|
||||
- { name: "hq_music_url", type: "String", comment: "高质量音乐链接" }
|
||||
- { name: "event", type: "String", comment: "事件类型" }
|
||||
- { name: "event_key", type: "String", comment: "事件Key值" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
- { name: "idx_user_id", columns: ["user_id"] }
|
||||
|
||||
- name: "mp_material"
|
||||
comment: "公众号素材表"
|
||||
entity: "MpMaterialDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
- { name: "media_id", type: "String", comment: "微信素材ID" }
|
||||
- { name: "type", type: "String", comment: "文件类型(image/voice/video/thumb)" }
|
||||
- { name: "permanent", type: "Boolean", comment: "是否永久素材" }
|
||||
- { name: "url", type: "String", comment: "文件服务器URL" }
|
||||
- { name: "name", type: "String", comment: "文件名称" }
|
||||
- { name: "mp_url", type: "String", comment: "公众号文件URL(永久素材)" }
|
||||
- { name: "title", type: "String", comment: "视频素材标题" }
|
||||
- { name: "introduction", type: "String", comment: "视频素材描述" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
- { name: "idx_media_id", columns: ["media_id"] }
|
||||
|
||||
- name: "mp_menu"
|
||||
comment: "公众号菜单表"
|
||||
entity: "MpMenuDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
- { name: "name", type: "String", comment: "菜单名称" }
|
||||
- { name: "menu_key", type: "String", comment: "菜单标识" }
|
||||
- { name: "parent_id", type: "Long", comment: "父菜单ID" }
|
||||
- { name: "type", type: "String", comment: "按钮类型(click/view/miniprogram等)" }
|
||||
- { name: "url", type: "String", comment: "网页链接" }
|
||||
- { name: "mini_program_app_id", type: "String", comment: "小程序AppId" }
|
||||
- { name: "mini_program_page_path", type: "String", comment: "小程序页面路径" }
|
||||
- { name: "article_id", type: "String", comment: "跳转图文媒体ID" }
|
||||
- { name: "reply_message_type", type: "String", comment: "回复消息类型" }
|
||||
- { name: "reply_content", type: "String", comment: "回复文本内容" }
|
||||
- { name: "reply_media_id", type: "String", comment: "回复媒体ID" }
|
||||
- { name: "reply_media_url", type: "String", comment: "回复媒体URL" }
|
||||
- { name: "reply_articles", type: "List<Article>", comment: "回复图文消息(JSON)" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
|
||||
- name: "mp_auto_reply"
|
||||
comment: "公众号自动回复表"
|
||||
entity: "MpAutoReplyDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
- { name: "type", type: "Integer", comment: "回复类型(1关注回复/2消息回复/3关键词回复)" }
|
||||
- { name: "request_keyword", type: "String", comment: "请求关键词" }
|
||||
- { name: "request_match", type: "Integer", comment: "关键词匹配模式(1完全匹配/2半匹配)" }
|
||||
- { name: "request_message_type", type: "String", comment: "请求消息类型" }
|
||||
- { name: "response_message_type", type: "String", comment: "响应消息类型" }
|
||||
- { name: "response_content", type: "String", comment: "响应文本内容" }
|
||||
- { name: "response_media_id", type: "String", comment: "响应媒体ID" }
|
||||
- { name: "response_media_url", type: "String", comment: "响应媒体URL" }
|
||||
- { name: "response_articles", type: "List<Article>", comment: "响应图文消息(JSON)" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
|
||||
- name: "mp_message_template"
|
||||
comment: "公众号模板消息表"
|
||||
entity: "MpMessageTemplateDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
- { name: "template_id", type: "String", comment: "模板ID" }
|
||||
- { name: "title", type: "String", comment: "模板标题" }
|
||||
- { name: "content", type: "String", comment: "模板内容" }
|
||||
- { name: "example", type: "String", comment: "模板示例" }
|
||||
- { name: "primary_industry", type: "String", comment: "一级行业" }
|
||||
- { name: "deputy_industry", type: "String", comment: "二级行业" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
- { name: "idx_template_id", columns: ["template_id"] }
|
||||
|
||||
- name: "mp_tag"
|
||||
comment: "公众号标签表"
|
||||
entity: "MpTagDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "主键ID" }
|
||||
- { name: "tag_id", type: "Long", comment: "微信标签ID" }
|
||||
- { name: "name", type: "String", comment: "标签名称" }
|
||||
- { name: "count", type: "Integer", comment: "标签下粉丝数" }
|
||||
- { name: "account_id", type: "Long", comment: "公众号账号ID" }
|
||||
- { name: "app_id", type: "String", comment: "公众号AppId" }
|
||||
indexes:
|
||||
- { name: "idx_account_id", columns: ["account_id"] }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "mp_user"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
- from: "mp_message"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
- from: "mp_message"
|
||||
to: "mp_user"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
- from: "mp_material"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
- from: "mp_menu"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
- from: "mp_auto_reply"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
- from: "mp_message_template"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
- from: "mp_tag"
|
||||
to: "mp_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - XXX')"
|
||||
- "@RestController"
|
||||
- "@RequestMapping('/mp/xxx')"
|
||||
- "@Validated"
|
||||
- "@PreAuthorize('@ss.hasPermission(\"mp:xxx:action\")')"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 公众号模版消息")
|
||||
@RestController
|
||||
@RequestMapping("/mp/message-template")
|
||||
@Validated
|
||||
public class MpMessageTemplateController {
|
||||
@Resource
|
||||
private MpMessageTemplateService messageTemplateService;
|
||||
|
||||
@PostMapping("/send")
|
||||
@Operation(summary = "给粉丝发送模版消息")
|
||||
@PreAuthorize("@ss.hasPermission('mp:message-template:send')")
|
||||
public CommonResult<Boolean> sendMessageTemplate(@Valid @RequestBody MpMessageTemplateSendReqVO sendReqVO) {
|
||||
messageTemplateService.sendMessageTempalte(sendReqVO);
|
||||
return success(true);
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "接口定义业务方法,参数使用VO或BO对象"
|
||||
impl_pattern: "实现类注入MpServiceFactory获取WxMpService,处理微信API调用"
|
||||
example: |
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class MpMessageTemplateServiceImpl implements MpMessageTemplateService {
|
||||
|
||||
@Resource
|
||||
@Lazy // 延迟加载,解决循环依赖
|
||||
private MpServiceFactory mpServiceFactory;
|
||||
|
||||
@Override
|
||||
public void sendMessageTempalte(MpMessageTemplateSendReqVO sendReqVO) {
|
||||
// 1. 获得关联信息
|
||||
MpUserDO user = mpUserService.getRequiredUser(sendReqVO.getUserId());
|
||||
MpMessageTemplateDO template = validateMsgTemplateExists(sendReqVO.getId());
|
||||
|
||||
// 2. 构建模板消息并发送
|
||||
WxMpTemplateMessage templateMessage = buildTemplateMessage(template, user, sendReqVO);
|
||||
try {
|
||||
mpServiceFactory.getRequiredMpService(template.getAppId())
|
||||
.getTemplateMsgService().sendTemplateMsg(templateMessage);
|
||||
} catch (WxErrorException e) {
|
||||
throw exception(MESSAGE_TEMPLATE_SEND_FAIL, e.getError().getErrorMsg());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 消息处理器规范
|
||||
handler:
|
||||
pattern: "实现 WxMpMessageHandler 接口,通过 @Component 注册为 Spring Bean"
|
||||
example: |
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SubscribeHandler implements WxMpMessageHandler {
|
||||
|
||||
@Resource
|
||||
private MpUserService mpUserService;
|
||||
@Resource
|
||||
private MpAutoReplyService mpAutoReplyService;
|
||||
|
||||
@Override
|
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> context,
|
||||
WxMpService weixinService, WxSessionManager sessionManager) {
|
||||
// 1. 获取粉丝信息
|
||||
WxMpUser wxMpUser = weixinService.getUserService().userInfo(wxMessage.getFromUser());
|
||||
|
||||
// 2. 保存粉丝信息
|
||||
mpUserService.saveUser(MpContextHolder.getAppId(), wxMpUser);
|
||||
|
||||
// 3. 回复关注欢迎语
|
||||
return mpAutoReplyService.replyForSubscribe(MpContextHolder.getAppId(), wxMessage);
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: "继承 BaseMapperX,使用 LambdaQueryWrapperX 构建查询条件"
|
||||
example: |
|
||||
@Mapper
|
||||
public interface MpMessageMapper extends BaseMapperX<MpMessageDO> {
|
||||
|
||||
default PageResult<MpMessageDO> selectPage(MpMessagePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<MpMessageDO>()
|
||||
.eqIfPresent(MpMessageDO::getAccountId, reqVO.getAccountId())
|
||||
.eqIfPresent(MpMessageDO::getUserId, reqVO.getUserId())
|
||||
.orderByDesc(MpMessageDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-006-XXX-XXX"
|
||||
examples:
|
||||
- code: "1_006_000_000"
|
||||
message: "公众号账号不存在"
|
||||
- code: "1_006_003_000"
|
||||
message: "粉丝不存在"
|
||||
- code: "1_006_005_000"
|
||||
message: "发送消息失败,原因:{}"
|
||||
- code: "1_006_009_000"
|
||||
message: "自动回复不存在"
|
||||
- code: "1_006_010_004"
|
||||
message: "发送模版消息失败,原因:{}"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE, args...)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增消息类型处理
|
||||
new_feature:
|
||||
title: "新增消息类型处理器"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建Handler类"
|
||||
description: "在 service/handler/ 目录下创建新的处理器类,实现 WxMpMessageHandler 接口"
|
||||
- step: 2
|
||||
action: "注册到消息路由"
|
||||
description: "在 DefaultMpServiceFactory.buildMpMessageRouter() 中添加路由规则"
|
||||
code: |
|
||||
// 示例:添加新消息类型处理
|
||||
router.rule().async(false)
|
||||
.msgType(WxConsts.XmlMsgType.XXX)
|
||||
.handler(newHandler).end();
|
||||
- step: 3
|
||||
action: "实现业务逻辑"
|
||||
description: "在 handle() 方法中实现消息处理逻辑,返回 WxMpXmlOutMessage 响应"
|
||||
|
||||
# 新增自动回复类型
|
||||
new_channel:
|
||||
title: "新增自动回复类型"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "扩展枚举"
|
||||
description: "在 MpAutoReplyTypeEnum 中添加新的类型"
|
||||
- step: 2
|
||||
action: "创建自动回复配置"
|
||||
description: "通过 MpAutoReplyController 创建自动回复规则"
|
||||
- step: 3
|
||||
action: "实现回复逻辑"
|
||||
description: "在 MpAutoReplyServiceImpl 中实现对应的回复逻辑"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- title: "多账号管理"
|
||||
description: "使用 MpServiceFactory 获取指定账号的 WxMpService,避免账号混淆"
|
||||
example: "WxMpService mpService = mpServiceFactory.getRequiredMpService(accountId);"
|
||||
- title: "消息上下文"
|
||||
description: "使用 MpContextHolder 在处理链中传递 appId 上下文"
|
||||
example: "String appId = MpContextHolder.getAppId();"
|
||||
- title: "异步处理"
|
||||
description: "消息接收使用异步处理,避免阻塞微信回调响应"
|
||||
example: "router.rule().handler(messageReceiveHandler).next(); // next() 表示继续执行后续规则"
|
||||
- title: "素材下载"
|
||||
description: "收到粉丝发送的媒体消息时,自动下载并存储到本地文件服务"
|
||||
example: "mpMaterialService.downloadMaterialUrl(accountId, mediaId, type);"
|
||||
- title: "错误处理"
|
||||
description: "捕获 WxErrorException 并转换为业务异常"
|
||||
example: |
|
||||
try {
|
||||
wxMpService.someOperation();
|
||||
} catch (WxErrorException e) {
|
||||
throw exception(ERROR_CODE, e.getError().getErrorMsg());
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
purpose: "依赖用户体系、权限验证"
|
||||
- module: "yudao-module-infra"
|
||||
purpose: "依赖文件服务存储素材"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "wx-java-mp-spring-boot-starter"
|
||||
version: "继承自父POM"
|
||||
purpose: "微信公众号Java SDK,提供WxMpService等核心API"
|
||||
- name: "yudao-spring-boot-starter-mybatis"
|
||||
version: "继承自父POM"
|
||||
purpose: "MyBatis-Plus封装,提供BaseMapperX等"
|
||||
- name: "yudao-spring-boot-starter-redis"
|
||||
version: "继承自父POM"
|
||||
purpose: "Redis封装,用于WxMpService配置存储"
|
||||
- name: "yudao-spring-boot-starter-biz-tenant"
|
||||
version: "继承自父POM"
|
||||
purpose: "多租户支持"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/MpServiceFactory.java"
|
||||
purpose: "WxMpService工厂接口,管理多公众号实例"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/framework/mp/core/DefaultMpServiceFactory.java"
|
||||
purpose: "默认工厂实现,构建WxMpService和WxMpMessageRouter"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/controller/admin/open/MpOpenController.java"
|
||||
purpose: "微信回调入口,处理签名校验和消息路由"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/SubscribeHandler.java"
|
||||
purpose: "关注事件处理器,保存粉丝信息并回复欢迎语"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/handler/message/MessageAutoReplyHandler.java"
|
||||
purpose: "自动回复处理器,处理关键词和消息类型回复"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageServiceImpl.java"
|
||||
purpose: "消息服务实现,处理消息接收和发送"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/message/MpMessageTemplateServiceImpl.java"
|
||||
purpose: "模板消息服务实现,同步和发送模板消息"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/account/MpAccountDO.java"
|
||||
purpose: "公众号账号实体,支持多租户"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/dal/dataobject/message/MpMessageDO.java"
|
||||
purpose: "消息实体,包含多种消息类型字段"
|
||||
- path: "yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码定义"
|
||||
@@ -1,654 +0,0 @@
|
||||
# Skill 文档 - 支付模块 (yudao-module-pay)
|
||||
# 支付中台核心模块,支持多渠道支付、退款、转账等功能
|
||||
|
||||
skill:
|
||||
id: "skill-pay"
|
||||
name: "Pay Module Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-pay"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
支付中台模块,为整个系统提供统一的支付能力支持。
|
||||
核心功能包括:
|
||||
1. 多渠道支付:支持微信支付(JSAPI/小程序/App/Native/H5/付款码)、支付宝(PC/WAP/App/扫码/条码)、钱包支付、模拟支付
|
||||
2. 统一退款:支持全额退款和部分退款,自动处理退款状态同步
|
||||
3. 转账功能:支持企业付款到零钱、转账到银行卡
|
||||
4. 钱包系统:用户余额充值、消费、提现
|
||||
5. 异步通知:统一处理各渠道的支付/退款回调
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "策略模式:各支付渠道实现 PayClient 接口,统一调用方式,隔离渠道差异"
|
||||
- "模板方法模式:AbstractPayClient 定义统一流程,子类实现具体细节"
|
||||
- "工厂模式:PayClientFactory 管理客户端创建和缓存,支持动态刷新配置"
|
||||
- "开闭原则:新增支付渠道只需创建新的 PayClient 实现类,无需修改现有代码"
|
||||
- "依赖倒置:Service 层依赖 PayClient 接口而非具体实现"
|
||||
- "单一职责:每个 PayClient 只负责对应渠道的支付逻辑"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "PayOrder"
|
||||
type: "聚合根"
|
||||
description: "支付订单聚合根,管理订单生命周期"
|
||||
entities: ["PayOrderExtension"]
|
||||
invariants:
|
||||
- "订单状态:WAITING -> SUCCESS/REFUND/CLOSED"
|
||||
- "退款金额不能超过支付金额"
|
||||
|
||||
- name: "PayRefund"
|
||||
type: "聚合根"
|
||||
description: "退款单聚合根,管理退款流程"
|
||||
entities: []
|
||||
invariants:
|
||||
- "退款状态:WAITING -> SUCCESS/FAILURE"
|
||||
- "一个支付订单可以有多个退款单"
|
||||
|
||||
- name: "PayChannel"
|
||||
type: "实体"
|
||||
description: "支付渠道配置,关联支付客户端"
|
||||
entities: []
|
||||
|
||||
- name: "PayApp"
|
||||
type: "实体"
|
||||
description: "支付应用,业务系统的支付入口"
|
||||
entities: []
|
||||
|
||||
value_objects:
|
||||
- name: "PayClientConfig"
|
||||
description: "支付客户端配置,不同渠道有不同的配置类型"
|
||||
implementations: ["AlipayPayClientConfig", "WxPayClientConfig"]
|
||||
|
||||
services:
|
||||
- name: "PayOrderService"
|
||||
description: "支付订单领域服务,负责订单创建、提交、回调处理"
|
||||
- name: "PayRefundService"
|
||||
description: "退款领域服务,负责退款发起、回调处理"
|
||||
- name: "PayChannelService"
|
||||
description: "渠道管理服务,负责渠道配置和客户端获取"
|
||||
- name: "PayNotifyService"
|
||||
description: "通知服务,负责异步通知的处理和重试"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口,供其他模块调用"
|
||||
components:
|
||||
- "PayOrderApi: 支付订单API"
|
||||
- "PayRefundApi: 退款API"
|
||||
- "PayTransferApi: 转账API"
|
||||
- "PayWalletApi: 钱包API"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口,暴露给前端调用"
|
||||
components:
|
||||
- "PayOrderController: 支付订单管理"
|
||||
- "PayRefundController: 退款管理"
|
||||
- "PayChannelController: 渠道配置管理"
|
||||
- "PayAppController: 应用管理"
|
||||
- "PayNotifyController: 通知回调"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层,核心领域服务"
|
||||
components:
|
||||
- "PayOrderServiceImpl: 订单创建、提交、回调"
|
||||
- "PayRefundServiceImpl: 退款创建、回调"
|
||||
- "PayChannelServiceImpl: 渠道管理"
|
||||
- "PayNotifyServiceImpl: 通知处理"
|
||||
|
||||
- name: "framework/pay/core/client"
|
||||
purpose: "支付客户端抽象层,对接各支付渠道SDK"
|
||||
components:
|
||||
- "PayClient: 客户端接口"
|
||||
- "AbstractPayClient: 抽象基类"
|
||||
- "PayClientFactory: 客户端工厂"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "PayOrderMapper: 订单数据访问"
|
||||
- "PayRefundMapper: 退款数据访问"
|
||||
- "PayChannelMapper: 渠道数据访问"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "Factory Pattern"
|
||||
location: "yudao-module-pay/framework/pay/core/client/impl/PayClientFactoryImpl.java"
|
||||
purpose: "根据渠道编码创建对应的支付客户端,缓存已创建的客户端,支持配置热刷新"
|
||||
implementation: |
|
||||
// 注册渠道与客户端类的映射
|
||||
clientClass.put(WX_PUB, WxPubPayClient.class);
|
||||
clientClass.put(ALIPAY_WAP, AlipayWapPayClient.class);
|
||||
|
||||
// 反射创建客户端实例
|
||||
ReflectUtil.newInstance(payClientClass, channelId, config);
|
||||
|
||||
- pattern: "Template Method Pattern"
|
||||
location: "yudao-module-pay/framework/pay/core/client/impl/AbstractPayClient.java"
|
||||
purpose: "定义支付/退款/转账的统一流程,包括参数校验、异常处理、日志记录"
|
||||
implementation: |
|
||||
// 模板方法:统一下单
|
||||
public final PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
|
||||
ValidationUtils.validate(reqDTO); // 参数校验
|
||||
try {
|
||||
return doUnifiedOrder(reqDTO); // 子类实现
|
||||
} catch (Throwable ex) {
|
||||
throw buildPayException(ex); // 统一异常处理
|
||||
}
|
||||
}
|
||||
|
||||
- pattern: "Strategy Pattern"
|
||||
location: "yudao-module-pay/framework/pay/core/client/impl/*"
|
||||
purpose: "不同支付渠道实现不同的支付策略,但暴露统一接口"
|
||||
implementations:
|
||||
- "AbstractAlipayPayClient: 支付宝支付策略"
|
||||
- "AbstractWxPayClient: 微信支付策略"
|
||||
- "WalletPayClient: 钱包支付策略"
|
||||
- "MockPayClient: 模拟支付策略"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis:
|
||||
- name: "PayOrderApi"
|
||||
methods: ["createOrder", "getOrder"]
|
||||
consumers: ["订单模块", "会员模块"]
|
||||
- name: "PayRefundApi"
|
||||
methods: ["createRefund", "getRefund"]
|
||||
consumers: ["订单模块"]
|
||||
- name: "PayWalletApi"
|
||||
methods: ["addBalance", "getWallet"]
|
||||
consumers: ["会员模块", "订单模块"]
|
||||
|
||||
consumers: []
|
||||
|
||||
mq:
|
||||
- type: "Job"
|
||||
name: "PayOrderSyncJob"
|
||||
purpose: "定时同步待支付订单状态"
|
||||
- type: "Job"
|
||||
name: "PayOrderExpireJob"
|
||||
purpose: "定时关闭过期订单"
|
||||
- type: "Job"
|
||||
name: "PayNotifyJob"
|
||||
purpose: "异步通知重试"
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO | TenantBaseDO"
|
||||
description: "支付渠道 PayChannelDO 继承 TenantBaseDO 支持多租户,其他实体继承 BaseDO"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
- name: "pay_app"
|
||||
comment: "支付应用表"
|
||||
entity: "PayAppDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "应用编号" }
|
||||
- { name: "name", type: "String", comment: "应用名称" }
|
||||
- { name: "status", type: "Integer", comment: "开启状态" }
|
||||
- { name: "order_notify_url", type: "String", comment: "订单回调地址" }
|
||||
- { name: "refund_notify_url", type: "String", comment: "退款回调地址" }
|
||||
|
||||
- name: "pay_channel"
|
||||
comment: "支付渠道表"
|
||||
entity: "PayChannelDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "渠道编号" }
|
||||
- { name: "code", type: "String", comment: "渠道编码,如 wx_pub, alipay_wap" }
|
||||
- { name: "status", type: "Integer", comment: "开启状态" }
|
||||
- { name: "fee_rate", type: "Double", comment: "渠道费率" }
|
||||
- { name: "app_id", type: "Long", comment: "应用编号" }
|
||||
- { name: "config", type: "PayClientConfig", comment: "支付配置JSON" }
|
||||
|
||||
- name: "pay_order"
|
||||
comment: "支付订单表"
|
||||
entity: "PayOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "订单编号" }
|
||||
- { name: "app_id", type: "Long", comment: "应用编号" }
|
||||
- { name: "channel_id", type: "Long", comment: "渠道编号" }
|
||||
- { name: "channel_code", type: "String", comment: "渠道编码" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "merchant_order_id", type: "String", comment: "商户订单号" }
|
||||
- { name: "subject", type: "String", comment: "商品标题" }
|
||||
- { name: "body", type: "String", comment: "商品描述" }
|
||||
- { name: "price", type: "Integer", comment: "支付金额(分)" }
|
||||
- { name: "status", type: "Integer", comment: "支付状态: 0待支付/10成功/20退款/30关闭" }
|
||||
- { name: "refund_price", type: "Integer", comment: "已退款金额(分)" }
|
||||
- { name: "channel_order_no", type: "String", comment: "渠道订单号" }
|
||||
- { name: "expire_time", type: "LocalDateTime", comment: "过期时间" }
|
||||
- { name: "success_time", type: "LocalDateTime", comment: "支付成功时间" }
|
||||
indexes:
|
||||
- { name: "idx_app_merchant", columns: ["app_id", "merchant_order_id"] }
|
||||
|
||||
- name: "pay_order_extension"
|
||||
comment: "支付订单扩展表"
|
||||
entity: "PayOrderExtensionDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "扩展编号" }
|
||||
- { name: "order_id", type: "Long", comment: "订单编号" }
|
||||
- { name: "no", type: "String", comment: "外部订单号" }
|
||||
- { name: "channel_id", type: "Long", comment: "渠道编号" }
|
||||
- { name: "channel_code", type: "String", comment: "渠道编码" }
|
||||
- { name: "status", type: "Integer", comment: "支付状态" }
|
||||
- { name: "channel_notify_data", type: "String", comment: "渠道回调数据" }
|
||||
|
||||
- name: "pay_refund"
|
||||
comment: "退款订单表"
|
||||
entity: "PayRefundDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "退款编号" }
|
||||
- { name: "no", type: "String", comment: "外部退款号" }
|
||||
- { name: "app_id", type: "Long", comment: "应用编号" }
|
||||
- { name: "channel_id", type: "Long", comment: "渠道编号" }
|
||||
- { name: "order_id", type: "Long", comment: "支付订单编号" }
|
||||
- { name: "order_no", type: "String", comment: "支付订单号" }
|
||||
- { name: "merchant_order_id", type: "String", comment: "商户订单号" }
|
||||
- { name: "merchant_refund_id", type: "String", comment: "商户退款号" }
|
||||
- { name: "pay_price", type: "Integer", comment: "原支付金额(分)" }
|
||||
- { name: "refund_price", type: "Integer", comment: "退款金额(分)" }
|
||||
- { name: "status", type: "Integer", comment: "退款状态: 0待退款/1成功/2失败" }
|
||||
- { name: "reason", type: "String", comment: "退款原因" }
|
||||
- { name: "channel_refund_no", type: "String", comment: "渠道退款单号" }
|
||||
- { name: "success_time", type: "LocalDateTime", comment: "退款成功时间" }
|
||||
|
||||
- name: "pay_wallet"
|
||||
comment: "钱包表"
|
||||
entity: "PayWalletDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "钱包编号" }
|
||||
- { name: "user_id", type: "Long", comment: "用户编号" }
|
||||
- { name: "balance", type: "Integer", comment: "余额(分)" }
|
||||
- { name: "freeze_price", type: "Integer", comment: "冻结金额(分)" }
|
||||
|
||||
- name: "pay_wallet_transaction"
|
||||
comment: "钱包交易表"
|
||||
entity: "PayWalletTransactionDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "交易编号" }
|
||||
- { name: "wallet_id", type: "Long", comment: "钱包编号" }
|
||||
- { name: "biz_type", type: "Integer", comment: "业务类型" }
|
||||
- { name: "biz_id", type: "String", comment: "业务编号" }
|
||||
- { name: "amount", type: "Integer", comment: "交易金额(分)" }
|
||||
- { name: "balance", type: "Integer", comment: "交易后余额(分)" }
|
||||
|
||||
- name: "pay_transfer"
|
||||
comment: "转账表"
|
||||
entity: "PayTransferDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "转账编号" }
|
||||
- { name: "no", type: "String", comment: "外部转账号" }
|
||||
- { name: "app_id", type: "Long", comment: "应用编号" }
|
||||
- { name: "channel_id", type: "Long", comment: "渠道编号" }
|
||||
- { name: "type", type: "Integer", comment: "转账类型" }
|
||||
- { name: "price", type: "Integer", comment: "转账金额(分)" }
|
||||
- { name: "status", type: "Integer", comment: "转账状态" }
|
||||
- { name: "user_name", type: "String", comment: "收款人姓名" }
|
||||
- { name: "user_account", type: "String", comment: "收款人账号" }
|
||||
|
||||
- name: "pay_notify_task"
|
||||
comment: "通知任务表"
|
||||
entity: "PayNotifyTaskDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "Long", comment: "任务编号" }
|
||||
- { name: "app_id", type: "Long", comment: "应用编号" }
|
||||
- { name: "type", type: "Integer", comment: "通知类型: 1订单/2退款" }
|
||||
- { name: "data_id", type: "Long", comment: "数据编号" }
|
||||
- { name: "status", type: "Integer", comment: "通知状态" }
|
||||
- { name: "notify_times", type: "Integer", comment: "通知次数" }
|
||||
- { name: "max_notify_times", type: "Integer", comment: "最大通知次数" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "pay_app"
|
||||
to: "pay_channel"
|
||||
type: "1:N"
|
||||
foreign_key: "app_id"
|
||||
description: "一个应用可以有多个支付渠道"
|
||||
|
||||
- from: "pay_app"
|
||||
to: "pay_order"
|
||||
type: "1:N"
|
||||
foreign_key: "app_id"
|
||||
description: "一个应用可以有多个支付订单"
|
||||
|
||||
- from: "pay_order"
|
||||
to: "pay_order_extension"
|
||||
type: "1:N"
|
||||
foreign_key: "order_id"
|
||||
description: "一个订单可以有多个扩展单(多次支付尝试)"
|
||||
|
||||
- from: "pay_order"
|
||||
to: "pay_refund"
|
||||
type: "1:N"
|
||||
foreign_key: "order_id"
|
||||
description: "一个订单可以有多个退款单"
|
||||
|
||||
- from: "pay_channel"
|
||||
to: "pay_order"
|
||||
type: "1:N"
|
||||
foreign_key: "channel_id"
|
||||
description: "一个渠道可以有多个订单"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@RestController"
|
||||
- "@RequestMapping"
|
||||
- "@PreAuthorize('@ss.hasPermission')")
|
||||
- "@Operation(summary = ...)"
|
||||
example: |
|
||||
@RestController
|
||||
@RequestMapping("/pay/order")
|
||||
@Tag(name = "管理后台 - 支付订单")
|
||||
public class PayOrderController {
|
||||
@PostMapping("/submit")
|
||||
@Operation(summary = "提交支付订单")
|
||||
@PermitAll // 支付提交允许匿名访问
|
||||
public CommonResult<PayOrderSubmitRespVO> submitOrder(
|
||||
@RequestBody @Valid PayOrderSubmitReqVO reqVO,
|
||||
HttpServletRequest request) {
|
||||
return success(orderService.submitOrder(reqVO, getClientIP(request)));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "接口定义业务方法,使用 @Valid 注解校验参数"
|
||||
impl_pattern: "实现类使用 @Service 和 @Validated 注解,事务使用 @Transactional"
|
||||
example: |
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class PayOrderServiceImpl implements PayOrderService {
|
||||
|
||||
@Override
|
||||
public Long createOrder(PayOrderCreateReqDTO reqDTO) {
|
||||
// 1. 校验 App
|
||||
PayAppDO app = appService.validPayApp(reqDTO.getAppKey());
|
||||
|
||||
// 2. 查询是否已存在
|
||||
PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId(
|
||||
app.getId(), reqDTO.getMerchantOrderId());
|
||||
if (order != null) {
|
||||
return order.getId();
|
||||
}
|
||||
|
||||
// 3. 创建订单
|
||||
order = PayOrderConvert.INSTANCE.convert(reqDTO)
|
||||
.setAppId(app.getId())
|
||||
.setStatus(PayOrderStatusEnum.WAITING.getStatus());
|
||||
orderMapper.insert(order);
|
||||
return order.getId();
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: "继承 BaseMapperX,使用 MyBatis-Plus 的 LambdaQueryWrapper"
|
||||
example: |
|
||||
@Mapper
|
||||
public interface PayOrderMapper extends BaseMapperX<PayOrderDO> {
|
||||
|
||||
default PayOrderDO selectByAppIdAndMerchantOrderId(Long appId, String merchantOrderId) {
|
||||
return selectOne(PayOrderDO::getAppId, appId,
|
||||
PayOrderDO::getMerchantOrderId, merchantOrderId);
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(Long id, Integer status, PayOrderDO updateObj) {
|
||||
return update(updateObj, new LambdaUpdateWrapper<PayOrderDO>()
|
||||
.eq(PayOrderDO::getId, id)
|
||||
.eq(PayOrderDO::getStatus, status));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-007-XXX-XXX"
|
||||
examples:
|
||||
- code: "1_007_000_000"
|
||||
message: "App 不存在"
|
||||
- code: "1_007_001_000"
|
||||
message: "支付渠道的配置不存在"
|
||||
- code: "1_007_002_000"
|
||||
message: "支付订单不存在"
|
||||
- code: "1_007_002_003"
|
||||
message: "支付订单已经过期"
|
||||
- code: "1_007_006_000"
|
||||
message: "退款金额超过订单可退款金额"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE, args...)"
|
||||
|
||||
# 支付客户端使用示例
|
||||
pay_client_usage:
|
||||
create_order: |
|
||||
// 1. 获取支付客户端
|
||||
PayClient client = channelService.getPayClient(channelId);
|
||||
|
||||
// 2. 构建统一下单请求
|
||||
PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO()
|
||||
.setOutTradeNo(orderExtension.getNo())
|
||||
.setSubject(order.getSubject())
|
||||
.setPrice(order.getPrice())
|
||||
.setExpireTime(order.getExpireTime())
|
||||
.setNotifyUrl(notifyUrl);
|
||||
|
||||
// 3. 调用支付渠道
|
||||
PayOrderRespDTO respDTO = client.unifiedOrder(reqDTO);
|
||||
|
||||
create_refund: |
|
||||
// 1. 获取支付客户端
|
||||
PayClient client = channelService.getPayClient(channelId);
|
||||
|
||||
// 2. 构建退款请求
|
||||
PayRefundUnifiedReqDTO reqDTO = new PayRefundUnifiedReqDTO()
|
||||
.setOutTradeNo(order.getNo())
|
||||
.setOutRefundNo(refund.getNo())
|
||||
.setRefundPrice(refund.getRefundPrice())
|
||||
.setReason(refund.getReason());
|
||||
|
||||
// 3. 调用退款接口
|
||||
PayRefundRespDTO respDTO = client.unifiedRefund(reqDTO);
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增支付渠道步骤
|
||||
new_channel:
|
||||
title: "新增支付渠道(如添加银联支付)"
|
||||
steps:
|
||||
- step: 1
|
||||
title: "创建支付渠道枚举"
|
||||
file: "PayChannelEnum.java"
|
||||
action: "添加新的渠道编码,如 UNION_PAY(\"union_pay\", \"银联支付\")"
|
||||
|
||||
- step: 2
|
||||
title: "创建配置类"
|
||||
file: "UnionPayClientConfig.java"
|
||||
action: |
|
||||
实现 PayClientConfig 接口,定义银联支付所需的配置字段:
|
||||
@Data
|
||||
public class UnionPayClientConfig implements PayClientConfig {
|
||||
private String merchantId; // 商户号
|
||||
private String apiKey; // API密钥
|
||||
private String certPath; // 证书路径
|
||||
// ...
|
||||
}
|
||||
|
||||
- step: 3
|
||||
title: "创建抽象客户端类(可选)"
|
||||
file: "AbstractUnionPayClient.java"
|
||||
action: |
|
||||
如果有多种支付方式(如扫码、APP),可以创建抽象类:
|
||||
public abstract class AbstractUnionPayClient
|
||||
extends AbstractPayClient<UnionPayClientConfig> {
|
||||
// 实现公共逻辑
|
||||
}
|
||||
|
||||
- step: 4
|
||||
title: "创建具体客户端实现类"
|
||||
file: "UnionPayQrPayClient.java"
|
||||
action: |
|
||||
继承 AbstractPayClient 或抽象类,实现具体支付逻辑:
|
||||
public class UnionPayQrPayClient extends AbstractPayClient<UnionPayClientConfig> {
|
||||
|
||||
public UnionPayQrPayClient(Long channelId, UnionPayClientConfig config) {
|
||||
super(channelId, PayChannelEnum.UNION_PAY.getCode(), config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
// 初始化银联SDK
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
|
||||
// 调用银联下单接口
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayOrderRespDTO doParseOrderNotify(...) {
|
||||
// 解析银联回调
|
||||
}
|
||||
|
||||
// 实现其他抽象方法...
|
||||
}
|
||||
|
||||
- step: 5
|
||||
title: "注册到工厂"
|
||||
file: "PayClientFactoryImpl.java"
|
||||
action: |
|
||||
在构造函数中注册新的客户端:
|
||||
public PayClientFactoryImpl() {
|
||||
// ...
|
||||
clientClass.put(UNION_PAY, UnionPayQrPayClient.class);
|
||||
}
|
||||
|
||||
- step: 6
|
||||
title: "添加单元测试"
|
||||
file: "UnionPayQrPayClientTest.java"
|
||||
action: "编写测试用例验证支付流程"
|
||||
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增业务功能"
|
||||
steps:
|
||||
- step: 1
|
||||
title: "创建DO实体"
|
||||
action: "在 dal/dataobject 下创建实体类"
|
||||
- step: 2
|
||||
title: "创建Mapper"
|
||||
action: "在 dal/mysql 下创建Mapper接口"
|
||||
- step: 3
|
||||
title: "创建Service接口和实现"
|
||||
action: "在 service 下创建服务层"
|
||||
- step: 4
|
||||
title: "创建Controller"
|
||||
action: "在 controller 下创建HTTP接口"
|
||||
- step: 5
|
||||
title: "创建API(如需模块间调用)"
|
||||
action: "在 api 下创建对外API接口"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "支付金额统一使用 Integer 类型,单位为分,避免浮点精度问题"
|
||||
- "订单状态变更使用乐观锁,通过 updateByIdAndStatus 方法保证并发安全"
|
||||
- "支付回调需要验签,使用渠道提供的验签方法"
|
||||
- "异步通知需要支持重试机制,使用 PayNotifyTask 记录通知状态"
|
||||
- "支付订单需要设置过期时间,定时任务关闭过期订单"
|
||||
- "退款金额累计不能超过支付金额,在 Service 层进行校验"
|
||||
- "客户端配置支持热刷新,通过 refresh 方法更新配置"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
api: "TenantApi"
|
||||
purpose: "获取租户信息"
|
||||
- module: "yudao-module-member"
|
||||
api: "MemberUserApi"
|
||||
purpose: "获取用户信息(钱包支付)"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "alipay-sdk-java"
|
||||
version: "4.x"
|
||||
purpose: "支付宝支付SDK"
|
||||
- name: "weixin-java-pay"
|
||||
version: "4.x"
|
||||
purpose: "微信支付SDK(WxJava)"
|
||||
- name: "hutool-all"
|
||||
version: "5.x"
|
||||
purpose: "Java工具库"
|
||||
- name: "mybatis-plus"
|
||||
version: "3.x"
|
||||
purpose: "ORM框架"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/PayClient.java"
|
||||
purpose: "支付客户端接口,定义统一的支付/退款/转账方法"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/AbstractPayClient.java"
|
||||
purpose: "支付客户端抽象基类,实现模板方法模式"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/PayClientFactoryImpl.java"
|
||||
purpose: "支付客户端工厂,管理客户端创建和缓存"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java"
|
||||
purpose: "支付宝支付抽象类,实现支付宝统一的接口"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java"
|
||||
purpose: "微信支付抽象类,实现微信统一的接口"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java"
|
||||
purpose: "支付订单服务实现,核心业务逻辑"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java"
|
||||
purpose: "退款服务实现"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java"
|
||||
purpose: "支付订单实体"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/enums/PayChannelEnum.java"
|
||||
purpose: "支付渠道枚举"
|
||||
|
||||
- path: "yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码定义"
|
||||
@@ -1,345 +0,0 @@
|
||||
# Skill 文档 - yudao-module-report
|
||||
# 数据报表模块知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-report"
|
||||
name: "Report Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-report"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
report 模块主要实现数据可视化报表功能,为系统提供报表设计、大屏设计、数据分析等能力。
|
||||
核心功能包括:
|
||||
1. 积木报表集成:基于 JimuReport 实现打印设计、报表设计、图形设计、大屏设计
|
||||
2. GoView 大屏设计器:提供可视化大屏报表设计能力,支持 SQL 和 HTTP 数据源
|
||||
3. 数据可视化:支持多种图表类型,满足企业数据展示需求
|
||||
模块定位:作为企业级应用的可视化报表解决方案,与业务模块解耦,提供独立的数据展示能力。
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- "单一职责原则:GoViewProject 负责 project 管理,GoViewData 负责数据查询"
|
||||
- "开放封闭原则:通过 JmReportTokenServiceI 接口扩展积木报表认证机制"
|
||||
- "依赖倒置原则:Service 层依赖接口而非实现,便于测试和扩展"
|
||||
- "策略模式:支持多种数据源查询策略(SQL、HTTP),可扩展新数据源"
|
||||
- "适配器模式:JmReportTokenServiceImpl 适配积木报表与 yudao 认证体系"
|
||||
- "数据源抽象:默认使用 JdbcTemplate,支持切换至 ClickHouse 等大数据引擎"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "GoViewProject"
|
||||
type: "聚合根"
|
||||
description: "大屏报表项目,包含项目配置、发布状态、报表内容等"
|
||||
entities: ["GoViewProjectDO"]
|
||||
value_objects: ["GoViewDataRespVO"]
|
||||
|
||||
value_objects:
|
||||
- name: "GoViewDataRespVO"
|
||||
type: "值对象"
|
||||
description: "数据响应结构,包含维度(dimensions)和数据明细(source)"
|
||||
- name: "GoViewProjectCreateReqVO"
|
||||
type: "值对象"
|
||||
description: "项目创建请求参数"
|
||||
- name: "GoViewProjectUpdateReqVO"
|
||||
type: "值对象"
|
||||
description: "项目更新请求参数"
|
||||
|
||||
services:
|
||||
- name: "GoViewProjectService"
|
||||
type: "领域服务"
|
||||
description: "管理大屏项目的创建、更新、删除、查询"
|
||||
- name: "GoViewDataService"
|
||||
type: "领域服务"
|
||||
description: "提供数据查询能力,支持 SQL/HTTP 等数据源"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "controller"
|
||||
purpose: "HTTP 接口层,提供 REST API"
|
||||
components:
|
||||
- "GoViewProjectController: 大屏项目管理接口"
|
||||
- "GoViewDataController: 数据查询接口(SQL/HTTP)"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "GoViewProjectService/GoViewProjectServiceImpl: 项目管理业务逻辑"
|
||||
- "GoViewDataService/GoViewDataServiceImpl: 数据查询业务逻辑"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "GoViewProjectMapper: 项目数据访问"
|
||||
- "GoViewProjectDO: 项目实体"
|
||||
|
||||
- name: "convert"
|
||||
purpose: "对象转换层"
|
||||
components:
|
||||
- "GoViewProjectConvert: DO/VO 对象转换(MapStruct)"
|
||||
|
||||
- name: "framework"
|
||||
purpose: "框架集成层"
|
||||
components:
|
||||
- "JmReportConfiguration: 积木报表配置"
|
||||
- "JmReportTokenServiceImpl: 积木报表 Token 认证适配"
|
||||
- "JmOnlDragExternalServiceImpl: 积木仪表盘扩展服务"
|
||||
- "SecurityConfiguration: 安全配置"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "适配器模式"
|
||||
location: "framework/jmreport/core/service/JmReportTokenServiceImpl.java"
|
||||
purpose: "将 yudao 的 OAuth2 认证体系适配到积木报表的 JmReportTokenServiceI 接口"
|
||||
- pattern: "策略模式"
|
||||
location: "controller/admin/goview/GoViewDataController.java"
|
||||
purpose: "支持多种数据查询策略:getDataBySQL、getDataByHttp"
|
||||
- pattern: "转换器模式"
|
||||
location: "convert/goview/GoViewProjectConvert.java"
|
||||
purpose: "使用 MapStruct 实现 DO 与 VO 之间的转换"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: [] # 对外暴露的API(通过 HTTP 接口暴露)
|
||||
consumers:
|
||||
- module: "system"
|
||||
api: "OAuth2TokenCommonApi"
|
||||
purpose: "验证 Token 获取用户信息"
|
||||
- module: "system"
|
||||
api: "PermissionCommonApi"
|
||||
purpose: "权限校验,判断用户角色"
|
||||
mq: [] # 无消息队列使用
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "report 模块实体继承 BaseDO,包含 id、creator、create_time、updater、update_time、deleted 字段"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
- name: "report_go_view_project"
|
||||
comment: "GoView 大屏项目表"
|
||||
entity: "GoViewProjectDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键ID,自增" }
|
||||
- { name: "name", type: "VARCHAR(255)", comment: "项目名称" }
|
||||
- { name: "pic_url", type: "VARCHAR(1024)", comment: "预览图片URL" }
|
||||
- { name: "content", type: "LONGTEXT", comment: "报表内容,JSON格式配置" }
|
||||
- { name: "status", type: "TINYINT", comment: "发布状态:0-已发布,1-未发布" }
|
||||
- { name: "remark", type: "VARCHAR(255)", comment: "项目备注" }
|
||||
- { name: "creator", type: "VARCHAR(64)", comment: "创建人" }
|
||||
- { name: "create_time", type: "DATETIME", comment: "创建时间" }
|
||||
- { name: "updater", type: "VARCHAR(64)", comment: "更新人" }
|
||||
- { name: "update_time", type: "DATETIME", comment: "更新时间" }
|
||||
- { name: "deleted", type: "BIT", comment: "是否删除" }
|
||||
indexes:
|
||||
- { name: "idx_creator", columns: ["creator"] }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "report_go_view_project"
|
||||
to: "system_users"
|
||||
type: "N:1"
|
||||
foreign_key: "creator"
|
||||
comment: "项目创建人关联用户表"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - XXX')"
|
||||
- "@RestController"
|
||||
- "@RequestMapping('/report/xxx')"
|
||||
- "@Validated"
|
||||
- "@PreAuthorize('@ss.hasPermission(...)')"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - GoView 项目")
|
||||
@RestController
|
||||
@RequestMapping("/report/go-view/project")
|
||||
@Validated
|
||||
public class GoViewProjectController {
|
||||
|
||||
@Resource
|
||||
private GoViewProjectService goViewProjectService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建项目")
|
||||
@PreAuthorize("@ss.hasPermission('report:go-view-project:create')")
|
||||
public CommonResult<Long> createProject(@Valid @RequestBody GoViewProjectCreateReqVO createReqVO) {
|
||||
return success(goViewProjectService.createProject(createReqVO));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "定义接口 + 使用 @Service 注解的实现类"
|
||||
impl_pattern: "使用 @Validated 注解支持参数校验,依赖注入使用 @Resource"
|
||||
example: |
|
||||
public interface GoViewProjectService {
|
||||
Long createProject(@Valid GoViewProjectCreateReqVO createReqVO);
|
||||
void updateProject(@Valid GoViewProjectUpdateReqVO updateReqVO);
|
||||
void deleteProject(Long id);
|
||||
GoViewProjectDO getProject(Long id);
|
||||
PageResult<GoViewProjectDO> getMyProjectPage(PageParam pageReqVO, Long userId);
|
||||
}
|
||||
|
||||
@Service
|
||||
@Validated
|
||||
public class GoViewProjectServiceImpl implements GoViewProjectService {
|
||||
|
||||
@Resource
|
||||
private GoViewProjectMapper goViewProjectMapper;
|
||||
|
||||
@Override
|
||||
public Long createProject(GoViewProjectCreateReqVO createReqVO) {
|
||||
GoViewProjectDO goViewProject = GoViewProjectConvert.INSTANCE.convert(createReqVO)
|
||||
.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
goViewProjectMapper.insert(goViewProject);
|
||||
return goViewProject.getId();
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: "继承 BaseMapperX<DO>,使用 default 方法实现自定义查询"
|
||||
example: |
|
||||
@Mapper
|
||||
public interface GoViewProjectMapper extends BaseMapperX<GoViewProjectDO> {
|
||||
|
||||
default PageResult<GoViewProjectDO> selectPage(PageParam reqVO, Long userId) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<GoViewProjectDO>()
|
||||
.eq(GoViewProjectDO::getCreator, userId)
|
||||
.orderByDesc(GoViewProjectDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1_003_000_000"
|
||||
examples:
|
||||
- code: "1_003_000_000"
|
||||
message: "GoView 项目不存在"
|
||||
constant: "GO_VIEW_PROJECT_NOT_EXISTS"
|
||||
usage: |
|
||||
// 使用方式
|
||||
if (goViewProjectMapper.selectById(id) == null) {
|
||||
throw exception(GO_VIEW_PROJECT_NOT_EXISTS);
|
||||
}
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ERROR_CODE)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增报表功能"
|
||||
steps:
|
||||
- "1. 在 dal/dataobject 下创建 DO 实体类,继承 BaseDO"
|
||||
- "2. 在 dal/mysql 下创建 Mapper 接口,继承 BaseMapperX"
|
||||
- "3. 在 controller/admin/vo 下创建请求/响应 VO"
|
||||
- "4. 在 convert 下创建 Convert 接口(使用 MapStruct)"
|
||||
- "5. 在 service 下创建 Service 接口和实现类"
|
||||
- "6. 在 controller/admin 下创建 Controller"
|
||||
- "7. 在 enums/ErrorCodeConstants 添加错误码"
|
||||
|
||||
# 新增数据源类型
|
||||
new_channel:
|
||||
title: "新增数据源类型"
|
||||
steps:
|
||||
- "1. 在 GoViewDataController 中新增查询方法,如 getDataByElasticsearch"
|
||||
- "2. 在 GoViewDataService 接口中定义新方法"
|
||||
- "3. 在 GoViewDataServiceImpl 中实现查询逻辑"
|
||||
- "4. 创建对应的请求 VO(如 GoViewDataGetByEsReqVO)"
|
||||
- "5. 添加权限注解 @PreAuthorize('@ss.hasPermission(...)')"
|
||||
example: |
|
||||
// 新增 Elasticsearch 数据源查询
|
||||
@RequestMapping("/get-by-es")
|
||||
@Operation(summary = "使用 Elasticsearch 查询数据")
|
||||
@PreAuthorize("@ss.hasPermission('report:go-view-data:get-by-es')")
|
||||
public CommonResult<GoViewDataRespVO> getDataByEs(@Valid @RequestBody GoViewDataGetByEsReqVO reqVO) {
|
||||
return success(goViewDataService.getDataByEs(reqVO));
|
||||
}
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- "数据查询时考虑性能,大数据量场景建议使用 ClickHouse 等列式数据库"
|
||||
- "报表内容使用 JSON 格式存储,前端负责解析渲染"
|
||||
- "项目创建时默认状态为未发布(status=1),需手动发布"
|
||||
- "使用 creator 字段实现数据隔离,用户只能看到自己创建的项目"
|
||||
- "积木报表集成时需注意 Token 传递,通过 customApiHeader 方法处理"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "system"
|
||||
api: "OAuth2TokenCommonApi"
|
||||
purpose: "积木报表 Token 验证"
|
||||
- module: "system"
|
||||
api: "PermissionCommonApi"
|
||||
purpose: "积木报表权限校验"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "jimureport-spring-boot-starter"
|
||||
version: "管理在父 POM"
|
||||
purpose: "积木报表核心依赖,提供报表设计器"
|
||||
- name: "jimubi-spring-boot-starter"
|
||||
version: "管理在父 POM"
|
||||
purpose: "积木仪表盘/大屏设计器"
|
||||
- name: "spring-boot-starter-jdbc"
|
||||
version: "Spring Boot 管理"
|
||||
purpose: "JdbcTemplate 数据源查询"
|
||||
- name: "mapstruct"
|
||||
version: "管理在父 POM"
|
||||
purpose: "对象转换"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/dal/dataobject/goview/GoViewProjectDO.java"
|
||||
purpose: "GoView 项目实体类,对应 report_go_view_project 表"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/service/goview/GoViewProjectService.java"
|
||||
purpose: "项目管理服务接口"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/service/goview/GoViewDataServiceImpl.java"
|
||||
purpose: "数据查询服务实现,核心 SQL 查询逻辑"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/controller/admin/goview/GoViewProjectController.java"
|
||||
purpose: "项目管理 HTTP 接口"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/controller/admin/goview/GoViewDataController.java"
|
||||
purpose: "数据查询 HTTP 接口"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/config/JmReportConfiguration.java"
|
||||
purpose: "积木报表配置类"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImpl.java"
|
||||
purpose: "积木报表 Token 认证适配器"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码定义"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/dal/mysql/goview/GoViewProjectMapper.java"
|
||||
purpose: "项目数据访问 Mapper"
|
||||
- path: "yudao-module-report/src/main/java/cn/iocoder/yudao/module/report/convert/goview/GoViewProjectConvert.java"
|
||||
purpose: "对象转换器(MapStruct)"
|
||||
@@ -1,840 +0,0 @@
|
||||
# Skill 文档 - yudao-module-system 模块
|
||||
# 系统管理模块完整知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-system"
|
||||
name: "System Module Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-system"
|
||||
created_at: "2026-03-18"
|
||||
updated_at: "2026-03-18"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
yudao-module-system 是整个平台的核心基础设施模块,负责系统级的管理功能:
|
||||
- 用户管理:管理后台用户的增删改查、密码管理、状态控制
|
||||
- 角色管理:基于RBAC模型的角色定义与权限分配
|
||||
- 权限管理:菜单权限、按钮权限、数据权限的统一控制
|
||||
- 部门管理:组织架构的树形结构管理
|
||||
- 岗位管理:岗位信息的维护
|
||||
- 租户管理:多租户架构的核心支撑,租户创建、套餐绑定、过期控制
|
||||
- 字典管理:系统字典类型与字典数据的维护
|
||||
- 通知公告:系统公告的发布与管理
|
||||
- 短信/邮件/站内信:消息通知的三驾马车
|
||||
- OAuth2:开放授权协议支持
|
||||
- 社交登录:第三方登录集成(微信、钉钉等)
|
||||
|
||||
该模块是其他所有业务模块的基础,提供统一的用户认证、权限校验、租户隔离能力。
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- principle: "RBAC权限模型"
|
||||
description: "采用基于角色的访问控制模型,用户-角色-菜单三层关联,支持细粒度的按钮级权限控制"
|
||||
- principle: "多租户架构"
|
||||
description: "通过TenantBaseDO基类实现租户数据隔离,所有业务表自动携带tenant_id字段"
|
||||
- principle: "数据权限"
|
||||
description: "支持5种数据权限范围:全部、自定义部门、本部门、本部门及子部门、仅本人"
|
||||
- principle: "分层架构"
|
||||
description: "严格遵循Controller-Service-DAL三层架构,API层提供跨模块调用能力"
|
||||
- principle: "接口隔离"
|
||||
description: "Service接口与实现分离,便于测试和扩展"
|
||||
- principle: "缓存优先"
|
||||
description: "权限数据使用Spring Cache + Redis缓存,减少数据库查询压力"
|
||||
- principle: "统一异常处理"
|
||||
description: "通过ErrorCodeConstants定义统一错误码,格式为1-002-XXX-XXX"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "User(用户聚合)"
|
||||
type: "聚合根"
|
||||
entities: ["AdminUserDO", "UserRoleDO", "UserPostDO"]
|
||||
description: "用户是系统的核心实体,关联角色和岗位,属于租户"
|
||||
- name: "Role(角色聚合)"
|
||||
type: "聚合根"
|
||||
entities: ["RoleDO", "RoleMenuDO"]
|
||||
description: "角色是权限的载体,关联菜单实现功能权限控制"
|
||||
- name: "Menu(菜单聚合)"
|
||||
type: "聚合根"
|
||||
entities: ["MenuDO"]
|
||||
description: "菜单是权限的最小单元,支持目录、菜单、按钮三种类型"
|
||||
- name: "Dept(部门聚合)"
|
||||
type: "聚合根"
|
||||
entities: ["DeptDO"]
|
||||
description: "部门是组织架构的核心,树形结构,支持数据权限"
|
||||
- name: "Tenant(租户聚合)"
|
||||
type: "聚合根"
|
||||
entities: ["TenantDO", "TenantPackageDO"]
|
||||
description: "租户是SaaS架构的核心,绑定套餐控制功能范围"
|
||||
|
||||
value_objects:
|
||||
- name: "Post(岗位)"
|
||||
description: "岗位是用户的职位标识,不涉及复杂业务逻辑"
|
||||
- name: "DictType/DictData(字典)"
|
||||
description: "字典是系统配置的枚举值,全局共享不区分租户"
|
||||
- name: "Notice(通知公告)"
|
||||
description: "通知公告是系统级消息,简单CRUD"
|
||||
|
||||
services:
|
||||
- name: "PermissionService"
|
||||
description: "权限领域服务,处理用户-角色-菜单的关联关系"
|
||||
- name: "AdminAuthService"
|
||||
description: "认证领域服务,处理登录、登出、令牌刷新"
|
||||
- name: "TenantService"
|
||||
description: "租户领域服务,处理租户生命周期管理"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口,供其他模块RPC调用"
|
||||
components:
|
||||
- "PermissionApi: 权限校验接口"
|
||||
- "AdminUserApi: 用户信息查询接口"
|
||||
- "DeptApi: 部门信息查询接口"
|
||||
- "PostApi: 岗位信息查询接口"
|
||||
- "DictDataApi: 字典数据查询接口"
|
||||
- "RoleApi: 角色信息查询接口"
|
||||
- "SmsSendApi: 短信发送接口"
|
||||
- "MailSendApi: 邮件发送接口"
|
||||
- "NotifyMessageSendApi: 站内信发送接口"
|
||||
- "SocialUserApi: 社交用户接口"
|
||||
- "LoginLogApi: 登录日志接口"
|
||||
- "OperateLogApi: 操作日志接口"
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口,供前端调用"
|
||||
components:
|
||||
- "AuthController: 认证相关(登录、登出、刷新令牌)"
|
||||
- "UserController: 用户管理"
|
||||
- "RoleController: 角色管理"
|
||||
- "MenuController: 菜单管理"
|
||||
- "DeptController: 部门管理"
|
||||
- "PostController: 岗位管理"
|
||||
- "DictTypeController/DictDataController: 字典管理"
|
||||
- "TenantController: 租户管理"
|
||||
- "TenantPackageController: 租户套餐管理"
|
||||
- "SmsChannelController/SmsTemplateController: 短信管理"
|
||||
- "MailAccountController/MailTemplateController: 邮件管理"
|
||||
- "NotifyTemplateController/NotifyMessageController: 站内信管理"
|
||||
- "OAuth2ClientController: OAuth2客户端管理"
|
||||
- "SocialClientController: 社交客户端管理"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "auth: AdminAuthService(认证服务)"
|
||||
- "permission: PermissionService, RoleService, MenuService(权限服务)"
|
||||
- "user: AdminUserService(用户服务)"
|
||||
- "dept: DeptService, PostService(组织服务)"
|
||||
- "dict: DictTypeService, DictDataService(字典服务)"
|
||||
- "tenant: TenantService, TenantPackageService(租户服务)"
|
||||
- "sms: SmsChannelService, SmsTemplateService, SmsSendService, SmsCodeService(短信服务)"
|
||||
- "mail: MailAccountService, MailTemplateService, MailSendService(邮件服务)"
|
||||
- "notify: NotifyTemplateService, NotifyMessageService, NotifySendService(站内信服务)"
|
||||
- "oauth2: OAuth2ClientService, OAuth2TokenService, OAuth2CodeService(OAuth2服务)"
|
||||
- "social: SocialClientService, SocialUserService(社交服务)"
|
||||
- "logger: LoginLogService, OperateLogService(日志服务)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject: 32个DO实体类"
|
||||
- "mysql: MyBatis Mapper接口"
|
||||
- "redis: Redis缓存操作"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "策略模式"
|
||||
location: "SmsChannelService, MailSendService"
|
||||
purpose: "不同短信/邮件渠道的发送策略切换"
|
||||
- pattern: "模板方法模式"
|
||||
location: "NotifySendService, SmsSendService"
|
||||
purpose: "消息发送的统一流程,子类实现具体发送逻辑"
|
||||
- pattern: "工厂模式"
|
||||
location: "SmsClientFactory, MailClientFactory"
|
||||
purpose: "根据渠道类型创建对应的客户端实例"
|
||||
- pattern: "代理模式"
|
||||
location: "PermissionServiceImpl.getSelf()"
|
||||
purpose: "解决Spring AOP缓存注解生效问题"
|
||||
- pattern: "DTO模式"
|
||||
location: "api/dto/*"
|
||||
purpose: "跨模块数据传输对象,隔离内部实体"
|
||||
- pattern: "转换器模式"
|
||||
location: "convert/*"
|
||||
purpose: "DO与VO之间的对象转换,使用MapStruct"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: # 对外暴露的API
|
||||
- api: "PermissionApi"
|
||||
methods: ["hasAnyPermissions", "hasAnyRoles", "getUserRoleIdListByRoleIds"]
|
||||
consumers: ["yudao-module-infra", "yudao-module-member"]
|
||||
- api: "AdminUserApi"
|
||||
methods: ["getUser", "getUserList"]
|
||||
consumers: ["yudao-module-infra", "yudao-module-pay"]
|
||||
- api: "DeptApi"
|
||||
methods: ["getDept", "getDeptList"]
|
||||
consumers: ["yudao-module-infra"]
|
||||
- api: "DictDataApi"
|
||||
methods: ["parseDictData", "getDictDataList"]
|
||||
consumers: ["所有模块"]
|
||||
- api: "SmsSendApi"
|
||||
methods: ["sendSingleSms"]
|
||||
consumers: ["yudao-module-member"]
|
||||
- api: "MailSendApi"
|
||||
methods: ["sendSingleMail"]
|
||||
consumers: ["yudao-module-member"]
|
||||
|
||||
consumers: # 消费的其他模块API
|
||||
- api: "PermissionCommonApi (framework-common)"
|
||||
purpose: "基础权限校验能力"
|
||||
- api: "FileApi (yudao-module-infra)"
|
||||
purpose: "文件上传(用户头像等)"
|
||||
|
||||
mq: [] # 暂未使用消息队列
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO | TenantBaseDO"
|
||||
description: |
|
||||
- BaseDO: 包含id, creator, createTime, updater, updateTime, deleted字段
|
||||
- TenantBaseDO: 继承BaseDO,额外包含tenantId字段,实现多租户隔离
|
||||
- 使用@TenantIgnore注解标记不需要租户隔离的表(如system_menu, system_dict_data)
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 用户相关 ==========
|
||||
- name: "system_users"
|
||||
comment: "用户表"
|
||||
entity: "AdminUserDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "用户ID,主键" }
|
||||
- { name: "username", type: "VARCHAR(30)", comment: "用户账号" }
|
||||
- { name: "password", type: "VARCHAR(100)", comment: "加密密码" }
|
||||
- { name: "nickname", type: "VARCHAR(30)", comment: "用户昵称" }
|
||||
- { name: "dept_id", type: "BIGINT", comment: "部门ID" }
|
||||
- { name: "post_ids", type: "VARCHAR(255)", comment: "岗位编号数组,JSON格式" }
|
||||
- { name: "email", type: "VARCHAR(50)", comment: "用户邮箱" }
|
||||
- { name: "mobile", type: "VARCHAR(20)", comment: "手机号码" }
|
||||
- { name: "sex", type: "TINYINT", comment: "用户性别" }
|
||||
- { name: "avatar", type: "VARCHAR(100)", comment: "用户头像" }
|
||||
- { name: "status", type: "TINYINT", comment: "帐号状态(0正常 1停用)" }
|
||||
- { name: "login_ip", type: "VARCHAR(50)", comment: "最后登录IP" }
|
||||
- { name: "login_date", type: "DATETIME", comment: "最后登录时间" }
|
||||
indexes:
|
||||
- { name: "uk_username", columns: ["username", "tenant_id"] }
|
||||
- { name: "uk_mobile", columns: ["mobile", "tenant_id"] }
|
||||
|
||||
- name: "system_user_role"
|
||||
comment: "用户-角色关联表"
|
||||
entity: "UserRoleDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户ID" }
|
||||
- { name: "role_id", type: "BIGINT", comment: "角色ID" }
|
||||
indexes:
|
||||
- { name: "idx_user_id", columns: ["user_id"] }
|
||||
- { name: "idx_role_id", columns: ["role_id"] }
|
||||
|
||||
# ========== 角色相关 ==========
|
||||
- name: "system_role"
|
||||
comment: "角色表"
|
||||
entity: "RoleDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "角色ID" }
|
||||
- { name: "name", type: "VARCHAR(30)", comment: "角色名称" }
|
||||
- { name: "code", type: "VARCHAR(100)", comment: "角色标识" }
|
||||
- { name: "sort", type: "INT", comment: "角色排序" }
|
||||
- { name: "status", type: "TINYINT", comment: "角色状态" }
|
||||
- { name: "type", type: "TINYINT", comment: "角色类型(1系统内置 2自定义)" }
|
||||
- { name: "data_scope", type: "TINYINT", comment: "数据范围" }
|
||||
- { name: "data_scope_dept_ids", type: "VARCHAR(500)", comment: "数据范围部门ID数组" }
|
||||
indexes:
|
||||
- { name: "uk_code", columns: ["code", "tenant_id"] }
|
||||
|
||||
- name: "system_role_menu"
|
||||
comment: "角色-菜单关联表"
|
||||
entity: "RoleMenuDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键" }
|
||||
- { name: "role_id", type: "BIGINT", comment: "角色ID" }
|
||||
- { name: "menu_id", type: "BIGINT", comment: "菜单ID" }
|
||||
indexes:
|
||||
- { name: "idx_role_id", columns: ["role_id"] }
|
||||
- { name: "idx_menu_id", columns: ["menu_id"] }
|
||||
|
||||
# ========== 菜单相关 ==========
|
||||
- name: "system_menu"
|
||||
comment: "菜单表"
|
||||
entity: "MenuDO"
|
||||
extends: "BaseDO"
|
||||
annotation: "@TenantIgnore"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "菜单ID" }
|
||||
- { name: "name", type: "VARCHAR(50)", comment: "菜单名称" }
|
||||
- { name: "permission", type: "VARCHAR(100)", comment: "权限标识" }
|
||||
- { name: "type", type: "TINYINT", comment: "菜单类型(1目录 2菜单 3按钮)" }
|
||||
- { name: "sort", type: "INT", comment: "显示顺序" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "父菜单ID" }
|
||||
- { name: "path", type: "VARCHAR(200)", comment: "路由地址" }
|
||||
- { name: "icon", type: "VARCHAR(100)", comment: "菜单图标" }
|
||||
- { name: "component", type: "VARCHAR(255)", comment: "组件路径" }
|
||||
- { name: "component_name", type: "VARCHAR(100)", comment: "组件名" }
|
||||
- { name: "status", type: "TINYINT", comment: "菜单状态" }
|
||||
- { name: "visible", type: "BIT", comment: "是否可见" }
|
||||
- { name: "keep_alive", type: "BIT", comment: "是否缓存" }
|
||||
- { name: "always_show", type: "BIT", comment: "是否总是显示" }
|
||||
|
||||
# ========== 部门相关 ==========
|
||||
- name: "system_dept"
|
||||
comment: "部门表"
|
||||
entity: "DeptDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "部门ID" }
|
||||
- { name: "name", type: "VARCHAR(30)", comment: "部门名称" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "父部门ID" }
|
||||
- { name: "sort", type: "INT", comment: "显示顺序" }
|
||||
- { name: "leader_user_id", type: "BIGINT", comment: "负责人" }
|
||||
- { name: "phone", type: "VARCHAR(20)", comment: "联系电话" }
|
||||
- { name: "email", type: "VARCHAR(50)", comment: "邮箱" }
|
||||
- { name: "status", type: "TINYINT", comment: "部门状态" }
|
||||
|
||||
- name: "system_post"
|
||||
comment: "岗位表"
|
||||
entity: "PostDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "岗位ID" }
|
||||
- { name: "name", type: "VARCHAR(50)", comment: "岗位名称" }
|
||||
- { name: "code", type: "VARCHAR(64)", comment: "岗位编码" }
|
||||
- { name: "sort", type: "INT", comment: "显示顺序" }
|
||||
- { name: "status", type: "TINYINT", comment: "状态" }
|
||||
|
||||
# ========== 租户相关 ==========
|
||||
- name: "system_tenant"
|
||||
comment: "租户表"
|
||||
entity: "TenantDO"
|
||||
extends: "BaseDO"
|
||||
annotation: "@TenantIgnore"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "租户编号" }
|
||||
- { name: "name", type: "VARCHAR(100)", comment: "租户名" }
|
||||
- { name: "contact_user_id", type: "BIGINT", comment: "联系人的用户编号" }
|
||||
- { name: "contact_name", type: "VARCHAR(100)", comment: "联系人" }
|
||||
- { name: "contact_mobile", type: "VARCHAR(20)", comment: "联系手机" }
|
||||
- { name: "status", type: "TINYINT", comment: "租户状态" }
|
||||
- { name: "websites", type: "VARCHAR(500)", comment: "绑定域名列表" }
|
||||
- { name: "package_id", type: "BIGINT", comment: "租户套餐编号" }
|
||||
- { name: "expire_time", type: "DATETIME", comment: "过期时间" }
|
||||
- { name: "account_count", type: "INT", comment: "账号数量" }
|
||||
|
||||
- name: "system_tenant_package"
|
||||
comment: "租户套餐表"
|
||||
entity: "TenantPackageDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "套餐编号" }
|
||||
- { name: "name", type: "VARCHAR(100)", comment: "套餐名" }
|
||||
- { name: "status", type: "TINYINT", comment: "套餐状态" }
|
||||
- { name: "menu_ids", type: "VARCHAR(2000)", comment: "关联菜单编号数组" }
|
||||
- { name: "remark", type: "VARCHAR(255)", comment: "备注" }
|
||||
|
||||
# ========== 字典相关 ==========
|
||||
- name: "system_dict_type"
|
||||
comment: "字典类型表"
|
||||
entity: "DictTypeDO"
|
||||
extends: "BaseDO"
|
||||
annotation: "@TenantIgnore"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "字典主键" }
|
||||
- { name: "name", type: "VARCHAR(100)", comment: "字典名称" }
|
||||
- { name: "type", type: "VARCHAR(100)", comment: "字典类型" }
|
||||
- { name: "status", type: "TINYINT", comment: "字典状态" }
|
||||
|
||||
- name: "system_dict_data"
|
||||
comment: "字典数据表"
|
||||
entity: "DictDataDO"
|
||||
extends: "BaseDO"
|
||||
annotation: "@TenantIgnore"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "字典主键" }
|
||||
- { name: "sort", type: "INT", comment: "字典排序" }
|
||||
- { name: "label", type: "VARCHAR(100)", comment: "字典标签" }
|
||||
- { name: "value", type: "VARCHAR(100)", comment: "字典值" }
|
||||
- { name: "dict_type", type: "VARCHAR(100)", comment: "字典类型" }
|
||||
- { name: "status", type: "TINYINT", comment: "状态" }
|
||||
- { name: "color_type", type: "VARCHAR(20)", comment: "颜色类型" }
|
||||
|
||||
# ========== 短信相关 ==========
|
||||
- name: "system_sms_channel"
|
||||
comment: "短信渠道表"
|
||||
entity: "SmsChannelDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "渠道编号" }
|
||||
- { name: "signature", type: "VARCHAR(12)", comment: "短信签名" }
|
||||
- { name: "code", type: "VARCHAR(63)", comment: "渠道编码" }
|
||||
- { name: "status", type: "TINYINT", comment: "开启状态" }
|
||||
- { name: "api_key", type: "VARCHAR(128)", comment: "短信API密钥" }
|
||||
- { name: "callback_url", type: "VARCHAR(255)", comment: "短信回调URL" }
|
||||
|
||||
- name: "system_sms_template"
|
||||
comment: "短信模板表"
|
||||
entity: "SmsTemplateDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "模板编号" }
|
||||
- { name: "name", type: "VARCHAR(63)", comment: "模板名称" }
|
||||
- { name: "code", type: "VARCHAR(63)", comment: "模板编码" }
|
||||
- { name: "content", type: "VARCHAR(255)", comment: "模板内容" }
|
||||
- { name: "channel_id", type: "BIGINT", comment: "短信渠道编号" }
|
||||
- { name: "channel_code", type: "VARCHAR(63)", comment: "短信渠道编码" }
|
||||
- { name: "status", type: "TINYINT", comment: "开启状态" }
|
||||
|
||||
- name: "system_sms_log"
|
||||
comment: "短信日志表"
|
||||
entity: "SmsLogDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "日志编号" }
|
||||
- { name: "channel_id", type: "BIGINT", comment: "短信渠道编号" }
|
||||
- { name: "template_id", type: "BIGINT", comment: "模板编号" }
|
||||
- { name: "template_code", type: "VARCHAR(63)", comment: "模板编码" }
|
||||
- { name: "template_content", type: "VARCHAR(255)", comment: "模板内容" }
|
||||
- { name: "template_params", type: "VARCHAR(255)", comment: "模板参数" }
|
||||
- { name: "mobile", type: "VARCHAR(11)", comment: "手机号" }
|
||||
- { name: "send_status", type: "TINYINT", comment: "发送状态" }
|
||||
- { name: "send_time", type: "DATETIME", comment: "发送时间" }
|
||||
- { name: "receive_status", type: "TINYINT", comment: "接收状态" }
|
||||
- { name: "receive_time", type: "DATETIME", comment: "接收时间" }
|
||||
|
||||
# ========== 邮件相关 ==========
|
||||
- name: "system_mail_account"
|
||||
comment: "邮箱账号表"
|
||||
entity: "MailAccountDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键" }
|
||||
- { name: "mail", type: "VARCHAR(100)", comment: "邮箱" }
|
||||
- { name: "username", type: "VARCHAR(100)", comment: "用户名" }
|
||||
- { name: "password", type: "VARCHAR(100)", comment: "密码" }
|
||||
- { name: "host", type: "VARCHAR(100)", comment: "SMTP服务器域名" }
|
||||
- { name: "port", type: "INT", comment: "SMTP服务器端口" }
|
||||
- { name: "ssl_enable", type: "BIT", comment: "是否开启SSL" }
|
||||
|
||||
- name: "system_mail_template"
|
||||
comment: "邮件模板表"
|
||||
entity: "MailTemplateDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "主键" }
|
||||
- { name: "name", type: "VARCHAR(63)", comment: "模板名称" }
|
||||
- { name: "code", type: "VARCHAR(63)", comment: "模板编码" }
|
||||
- { name: "account_id", type: "BIGINT", comment: "发送的邮箱账号编号" }
|
||||
- { name: "nickname", type: "VARCHAR(255)", comment: "发送人名称" }
|
||||
- { name: "title", type: "VARCHAR(255)", comment: "邮件标题" }
|
||||
- { name: "content", type: "VARCHAR(10240)", comment: "邮件内容" }
|
||||
- { name: "status", type: "TINYINT", comment: "开启状态" }
|
||||
|
||||
# ========== OAuth2相关 ==========
|
||||
- name: "system_oauth2_client"
|
||||
comment: "OAuth2客户端表"
|
||||
entity: "OAuth2ClientDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "client_id", type: "VARCHAR(255)", comment: "客户端编号" }
|
||||
- { name: "secret", type: "VARCHAR(255)", comment: "客户端密钥" }
|
||||
- { name: "name", type: "VARCHAR(255)", comment: "应用名" }
|
||||
- { name: "logo", type: "VARCHAR(255)", comment: "应用图标" }
|
||||
- { name: "authorized_grant_types", type: "VARCHAR(255)", comment: "授权类型" }
|
||||
- { name: "scopes", type: "VARCHAR(255)", comment: "授权范围" }
|
||||
- { name: "redirect_uris", type: "VARCHAR(255)", comment: "回调地址" }
|
||||
- { name: "access_token_validity_seconds", type: "INT", comment: "访问令牌有效期" }
|
||||
- { name: "refresh_token_validity_seconds", type: "INT", comment: "刷新令牌有效期" }
|
||||
|
||||
- name: "system_oauth2_access_token"
|
||||
comment: "OAuth2访问令牌表"
|
||||
entity: "OAuth2AccessTokenDO"
|
||||
extends: "TenantBaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "编号" }
|
||||
- { name: "access_token", type: "VARCHAR(255)", comment: "访问令牌" }
|
||||
- { name: "user_id", type: "BIGINT", comment: "用户编号" }
|
||||
- { name: "user_type", type: "TINYINT", comment: "用户类型" }
|
||||
- { name: "client_id", type: "VARCHAR(255)", comment: "客户端编号" }
|
||||
- { name: "expires_time", type: "DATETIME", comment: "过期时间" }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "system_users"
|
||||
to: "system_dept"
|
||||
type: "N:1"
|
||||
foreign_key: "dept_id"
|
||||
description: "用户属于一个部门"
|
||||
- from: "system_users"
|
||||
to: "system_post"
|
||||
type: "N:N"
|
||||
foreign_key: "post_ids (JSON数组)"
|
||||
description: "用户可关联多个岗位"
|
||||
- from: "system_user_role"
|
||||
to: "system_users"
|
||||
type: "N:1"
|
||||
foreign_key: "user_id"
|
||||
- from: "system_user_role"
|
||||
to: "system_role"
|
||||
type: "N:1"
|
||||
foreign_key: "role_id"
|
||||
- from: "system_role_menu"
|
||||
to: "system_role"
|
||||
type: "N:1"
|
||||
foreign_key: "role_id"
|
||||
- from: "system_role_menu"
|
||||
to: "system_menu"
|
||||
type: "N:1"
|
||||
foreign_key: "menu_id"
|
||||
- from: "system_menu"
|
||||
to: "system_menu"
|
||||
type: "N:1"
|
||||
foreign_key: "parent_id"
|
||||
description: "菜单树形结构"
|
||||
- from: "system_dept"
|
||||
to: "system_dept"
|
||||
type: "N:1"
|
||||
foreign_key: "parent_id"
|
||||
description: "部门树形结构"
|
||||
- from: "system_tenant"
|
||||
to: "system_tenant_package"
|
||||
type: "N:1"
|
||||
foreign_key: "package_id"
|
||||
- from: "system_dict_data"
|
||||
to: "system_dict_type"
|
||||
type: "N:1"
|
||||
foreign_key: "dict_type"
|
||||
- from: "system_sms_template"
|
||||
to: "system_sms_channel"
|
||||
type: "N:1"
|
||||
foreign_key: "channel_id"
|
||||
- from: "system_mail_template"
|
||||
to: "system_mail_account"
|
||||
type: "N:1"
|
||||
foreign_key: "account_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - XXX'): OpenAPI 3.0 文档标签"
|
||||
- "@RestController: RESTful控制器"
|
||||
- "@RequestMapping('/system/xxx'): 路由前缀"
|
||||
- "@Validated: 参数校验"
|
||||
- "@Operation(summary = 'XXX'): 接口文档说明"
|
||||
- "@PreAuthorize('@ss.hasPermission('system:xxx:add')'): 权限校验"
|
||||
- "@PermitAll: 允许匿名访问(如登录接口)"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 用户")
|
||||
@RestController
|
||||
@RequestMapping("/system/user")
|
||||
@Validated
|
||||
public class UserController {
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建用户")
|
||||
@PreAuthorize("@ss.hasPermission('system:user:create')")
|
||||
public CommonResult<Long> createUser(@Valid @RequestBody UserSaveReqVO createReqVO) {
|
||||
return success(userService.createUser(createReqVO));
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: |
|
||||
- 接口定义在 service 包下
|
||||
- 方法参数使用 @Valid 注解进行校验
|
||||
- 返回值使用具体类型或 void
|
||||
impl_pattern: |
|
||||
- 实现类放在 service 包下,命名为 XxxServiceImpl
|
||||
- 使用 @Service 注解
|
||||
- 使用 @Resource 注入 Mapper 和其他 Service
|
||||
- 使用 @Transactional(rollbackFor = Exception.class) 标记事务方法
|
||||
- 使用 @Cacheable/@CacheEvict 进行缓存操作
|
||||
example: |
|
||||
@Service
|
||||
@Slf4j
|
||||
public class PermissionServiceImpl implements PermissionService {
|
||||
|
||||
@Resource
|
||||
private RoleMenuMapper roleMenuMapper;
|
||||
@Resource
|
||||
private UserRoleMapper userRoleMapper;
|
||||
|
||||
@Override
|
||||
@DSTransactional // 多数据源事务
|
||||
@Caching(evict = {
|
||||
@CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, allEntries = true),
|
||||
@CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, allEntries = true)
|
||||
})
|
||||
public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
|
||||
// 业务逻辑
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
- Mapper 接口继承 BaseMapperX<DO类>
|
||||
- 使用 @Mapper 注解
|
||||
- 复杂查询使用 MPJLambdaWrapperWrapper 或 XML
|
||||
example: |
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapperX<AdminUserDO> {
|
||||
|
||||
default AdminUserDO selectByUsername(String username) {
|
||||
return selectOne(AdminUserDO::getUsername, username);
|
||||
}
|
||||
|
||||
default List<AdminUserDO> selectListByDeptIds(Collection<Long> deptIds) {
|
||||
return selectList(new LambdaQueryWrapperX<AdminUserDO>()
|
||||
.inIfPresent(AdminUserDO::getDeptId, deptIds));
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "1-002-XXX-XXX"
|
||||
format: "模块号-子模块号-序号"
|
||||
examples:
|
||||
- code: "1_002_000_000"
|
||||
message: "登录失败,账号密码不正确"
|
||||
module: "AUTH"
|
||||
- code: "1_002_001_000"
|
||||
message: "已经存在该名字的菜单"
|
||||
module: "MENU"
|
||||
- code: "1_002_002_000"
|
||||
message: "角色不存在"
|
||||
module: "ROLE"
|
||||
- code: "1_002_003_000"
|
||||
message: "用户账号已经存在"
|
||||
module: "USER"
|
||||
- code: "1_002_004_000"
|
||||
message: "已经存在该名字的部门"
|
||||
module: "DEPT"
|
||||
- code: "1_002_015_000"
|
||||
message: "租户不存在"
|
||||
module: "TENANT"
|
||||
usage: |
|
||||
// 在Service中抛出业务异常
|
||||
if (user == null) {
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
}
|
||||
// 带参数的异常
|
||||
throw exception(ROLE_NAME_DUPLICATE, name);
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "CommonResult.error(errorCode)"
|
||||
usage: |
|
||||
// 成功响应
|
||||
return success(userService.getUser(id));
|
||||
// 失败响应(通常由全局异常处理器处理)
|
||||
throw exception(USER_NOT_EXISTS);
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增业务功能(以新增'操作日志'功能为例)"
|
||||
steps:
|
||||
- step: "1. 创建DO实体类"
|
||||
detail: |
|
||||
在 dal/dataobject/logger/ 下创建 OperateLogDO.java
|
||||
继承 BaseDO 或 TenantBaseDO
|
||||
使用 @TableName 指定表名
|
||||
- step: "2. 创建Mapper接口"
|
||||
detail: |
|
||||
在 dal/mysql/logger/ 下创建 OperateLogMapper.java
|
||||
继承 BaseMapperX<OperateLogDO>
|
||||
- step: "3. 创建Service接口和实现"
|
||||
detail: |
|
||||
在 service/logger/ 下创建 OperateLogService.java 接口
|
||||
在 service/logger/ 下创建 OperateLogServiceImpl.java 实现
|
||||
使用 @Service 和 @Resource 注解
|
||||
- step: "4. 创建Controller"
|
||||
detail: |
|
||||
在 controller/admin/logger/ 下创建 OperateLogController.java
|
||||
使用 @RestController, @RequestMapping, @Tag 等注解
|
||||
使用 @PreAuthorize 进行权限控制
|
||||
- step: "5. 创建VO类"
|
||||
detail: |
|
||||
在 controller/admin/logger/vo/ 下创建请求和响应VO
|
||||
使用 @Data, @Valid 等注解进行参数校验
|
||||
- step: "6. 添加错误码"
|
||||
detail: |
|
||||
在 enums/ErrorCodeConstants.java 中添加错误码
|
||||
格式:ErrorCode XXX_NOT_EXISTS = new ErrorCode(1_002_XXX_000, "XXX不存在");
|
||||
- step: "7. 添加菜单权限"
|
||||
detail: |
|
||||
在数据库 system_menu 表中添加菜单记录
|
||||
配置权限标识如 system:operate-log:query
|
||||
|
||||
# 新增渠道/类型示例
|
||||
new_channel:
|
||||
title: "新增短信渠道(以新增'华为云短信'为例)"
|
||||
steps:
|
||||
- step: "1. 添加渠道枚举"
|
||||
detail: |
|
||||
在 SmsChannelEnum 中添加新渠道类型
|
||||
HUAWEI("huawei", "华为云")
|
||||
- step: "2. 创建客户端实现"
|
||||
detail: |
|
||||
在 framework/sms/core/client/impl/ 下创建 HuaweiSmsClient.java
|
||||
继承 AbstractSmsClient 抽象类
|
||||
实现 doSendSms() 方法
|
||||
- step: "3. 注册到工厂"
|
||||
detail: |
|
||||
在 SmsClientFactory 中注册新渠道
|
||||
clients.put(SmsChannelEnum.HUAWEI, new HuaweiSmsClient());
|
||||
- step: "4. 添加配置类"
|
||||
detail: |
|
||||
创建 HuaweiSmsChannelProperties 配置类
|
||||
继承 SmsChannelProperties 基类
|
||||
|
||||
# 新增字典类型
|
||||
new_dict:
|
||||
title: "新增字典类型"
|
||||
steps:
|
||||
- step: "1. 通过管理后台添加"
|
||||
detail: |
|
||||
登录管理后台 -> 系统管理 -> 字典管理 -> 新增字典类型
|
||||
填写字典名称、字典类型(如 system_user_sex)
|
||||
- step: "2. 添加字典数据"
|
||||
detail: |
|
||||
在字典类型下添加字典数据项
|
||||
设置字典标签、字典值、排序、状态等
|
||||
- step: "3. 代码中使用"
|
||||
detail: |
|
||||
// 获取字典数据列表
|
||||
List<DictDataRespDTO> dictList = dictDataApi.getDictDataList("system_user_sex");
|
||||
// 解析字典值
|
||||
String label = dictDataApi.parseDictData("system_user_sex", "1");
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "权限标识命名规范"
|
||||
detail: "使用 '模块:功能:操作' 格式,如 system:user:create、system:role:update"
|
||||
- practice: "数据权限使用"
|
||||
detail: "在Service方法上使用 @DataPermission 注解控制数据权限,使用 enable=false 关闭"
|
||||
- practice: "缓存使用"
|
||||
detail: "权限相关数据使用 @Cacheable 缓存,修改时使用 @CacheEvict 清除"
|
||||
- practice: "多租户注意"
|
||||
detail: "不需要租户隔离的表使用 @TenantIgnore 注解,如字典表、菜单表"
|
||||
- practice: "事务处理"
|
||||
detail: "跨数据源操作使用 @DSTransactional,单数据源使用 @Transactional"
|
||||
- practice: "日志记录"
|
||||
detail: "使用 @OperateLog 注解记录操作日志,使用 Lombok 的 @Slf4j 记录调试日志"
|
||||
- practice: "参数校验"
|
||||
detail: "VO类使用 @Valid、@NotNull、@NotBlank 等注解进行参数校验"
|
||||
- practice: "API设计"
|
||||
detail: "跨模块调用通过 API 层,不要直接调用 Service 层"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-framework-common"
|
||||
api: "CommonResult, PageResult, ErrorCode"
|
||||
purpose: "通用响应、分页、错误码"
|
||||
- module: "yudao-framework-security"
|
||||
api: "SecurityFrameworkUtils, @PreAuthorize"
|
||||
purpose: "安全框架、权限注解"
|
||||
- module: "yudao-framework-tenant"
|
||||
api: "TenantBaseDO, @TenantIgnore"
|
||||
purpose: "多租户支持"
|
||||
- module: "yudao-framework-mybatis"
|
||||
api: "BaseMapperX, BaseDO"
|
||||
purpose: "MyBatis增强"
|
||||
- module: "yudao-framework-redis"
|
||||
api: "@Cacheable, RedisKeyConstants"
|
||||
purpose: "Redis缓存"
|
||||
- module: "yudao-module-infra"
|
||||
api: "FileApi"
|
||||
purpose: "文件上传"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "spring-boot-starter-web"
|
||||
version: "2.7.x"
|
||||
purpose: "Web框架"
|
||||
- name: "mybatis-plus-boot-starter"
|
||||
version: "3.5.x"
|
||||
purpose: "MyBatis增强"
|
||||
- name: "spring-boot-starter-security"
|
||||
version: "2.7.x"
|
||||
purpose: "安全框架"
|
||||
- name: "spring-boot-starter-validation"
|
||||
version: "2.7.x"
|
||||
purpose: "参数校验"
|
||||
- name: "spring-boot-starter-cache"
|
||||
version: "2.7.x"
|
||||
purpose: "缓存支持"
|
||||
- name: "mapstruct"
|
||||
version: "1.5.x"
|
||||
purpose: "对象转换"
|
||||
- name: "lombok"
|
||||
version: "1.18.x"
|
||||
purpose: "代码简化"
|
||||
- name: "hutool-all"
|
||||
version: "5.8.x"
|
||||
purpose: "工具类库"
|
||||
- name: "knife4j-spring-boot-starter"
|
||||
version: "3.0.x"
|
||||
purpose: "API文档"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义,所有业务异常的统一入口"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java"
|
||||
purpose: "用户实体类,核心聚合根"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java"
|
||||
purpose: "角色实体类,权限载体"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java"
|
||||
purpose: "菜单实体类,权限最小单元"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java"
|
||||
purpose: "权限服务实现,核心业务逻辑"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java"
|
||||
purpose: "认证服务实现,登录登出逻辑"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java"
|
||||
purpose: "认证控制器,登录接口入口"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java"
|
||||
purpose: "权限API接口,跨模块调用入口"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java"
|
||||
purpose: "数据权限枚举,5种数据范围定义"
|
||||
- path: "yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java"
|
||||
purpose: "菜单类型枚举,目录/菜单/按钮"
|
||||
@@ -1,832 +0,0 @@
|
||||
# Skill 文件 - yudao-module-wms 模块
|
||||
# 仓储管理系统知识提取
|
||||
|
||||
skill:
|
||||
id: "skill-wms"
|
||||
name: "Warehouse Management Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-wms"
|
||||
created_at: "2026-06-08"
|
||||
updated_at: "2026-06-08"
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: |
|
||||
wms 模块是仓储管理系统,提供仓储业务全流程管理能力:
|
||||
1. 仓库管理 - 仓库基本信息维护,支持编码、名称、排序
|
||||
2. 物料管理 - 物料主数据管理,包含品牌、分类(树结构)、SKU(多规格)
|
||||
3. 商户管理 - 客户/供应商/既是客户也是供应商的业务伙伴管理
|
||||
4. 库存管理 - 基于 SKU + 仓库维度的库存余额管理,只读设计
|
||||
5. 入库管理 - 入库单创建、审核、完成、取消,完成入库增加库存
|
||||
6. 出库管理 - 出库单创建、审核、完成、取消,完成出库减少库存
|
||||
7. 移库管理 - 库间调拨单创建、审核、完成、取消,完成移库双向变更库存
|
||||
8. 盘点管理 - 库存盘点单创建、审核、完成、取消,完成盘点按实际数量调整库存
|
||||
9. 首页统计 - 订单汇总、订单趋势、库存汇总等仪表盘数据
|
||||
|
||||
定位:面向仓储管理场景,管理仓库、物料、库存及四大单据的完整业务闭环。SKU 作为库存单位,库存以 SKU + 仓库维度跟踪。
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles:
|
||||
- principle: "单据头/明细模式 (Header + Detail Pattern)"
|
||||
description: "所有四大单据(入库、出库、移库、盘点)均采用 Header + Detail 主子表结构,单据头存储汇总信息,明细行存储 SKU 级别数据"
|
||||
- principle: "只读库存控制器"
|
||||
description: "库存和库存历史 Controller 均为只读,所有库存变更必须通过单据完成(complete)操作触发,确保数据一致性"
|
||||
- principle: "并发安全库存更新"
|
||||
description: "使用 SELECT ... FOR UPDATE 行锁 + DuplicateKeyException 捕获实现库存更新的并发安全和懒初始化"
|
||||
- principle: "VO 组装模式"
|
||||
description: "通过批量查询关联实体到 Map 中,再通过 BeanUtils.toBean() 丰富 VO 对象,避免 N+1 查询"
|
||||
- principle: "单据状态机"
|
||||
description: "PREPARE(0) -> FINISHED(4) 或 CANCELED(5),单据只有在 PREPARE 状态下才能完成或取消"
|
||||
- principle: "SKU 作为库存单位"
|
||||
description: "库存以 SKU + 仓库维度跟踪,不跟踪物料级别库存,支持多规格管理"
|
||||
- principle: "订单类型常量分段"
|
||||
description: "WmsOrderTypeConstants 定义订单类型范围:入库 [100,200)、出库 [200,300),用于库存历史来源标识"
|
||||
- principle: "细粒度权限控制"
|
||||
description: "权限标识格式 wms:{entity}:{action},如 wms:receipt-order:create"
|
||||
- principle: "Excel 导出全覆盖"
|
||||
description: "所有主数据和单据均支持 Excel 导出功能"
|
||||
- principle: "字典集成"
|
||||
description: "单据状态、子类型等使用系统字典管理"
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates:
|
||||
- name: "物料聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ItemDO", "ItemSkuDO", "ItemBrandDO", "ItemCategoryDO"]
|
||||
description: "ItemDO 是物料聚合根,ItemSkuDO 是物料的多规格 SKU,ItemBrandDO 和 ItemCategoryDO 是物料的品牌和分类维度"
|
||||
- name: "商户聚合"
|
||||
type: "聚合根"
|
||||
entities: ["MerchantDO"]
|
||||
description: "MerchantDO 是商户聚合根,管理客户和供应商信息"
|
||||
- name: "仓库聚合"
|
||||
type: "聚合根"
|
||||
entities: ["WarehouseDO"]
|
||||
description: "WarehouseDO 是仓库聚合根,管理仓库基本信息"
|
||||
- name: "库存聚合"
|
||||
type: "聚合根"
|
||||
entities: ["InventoryDO", "InventoryHistoryDO"]
|
||||
description: "InventoryDO 以 SKU + 仓库为唯一维度管理库存余额,InventoryHistoryDO 记录每笔库存变动"
|
||||
- name: "入库单聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ReceiptOrderDO", "ReceiptOrderDetailDO"]
|
||||
description: "ReceiptOrderDO 是入库单聚合根,ReceiptOrderDetailDO 是入库明细行,一对多关系"
|
||||
- name: "出库单聚合"
|
||||
type: "聚合根"
|
||||
entities: ["ShipmentOrderDO", "ShipmentOrderDetailDO"]
|
||||
description: "ShipmentOrderDO 是出库单聚合根,ShipmentOrderDetailDO 是出库明细行,一对多关系"
|
||||
- name: "移库单聚合"
|
||||
type: "聚合根"
|
||||
entities: ["MovementOrderDO", "MovementOrderDetailDO"]
|
||||
description: "MovementOrderDO 是移库单聚合根,MovementOrderDetailDO 是移库明细行,一对多关系"
|
||||
- name: "盘点单聚合"
|
||||
type: "聚合根"
|
||||
entities: ["CheckOrderDO", "CheckOrderDetailDO"]
|
||||
description: "CheckOrderDO 是盘点单聚合根,CheckOrderDetailDO 是盘点明细行,一对多关系"
|
||||
|
||||
value_objects:
|
||||
- name: "WmsOrderTypeConstants"
|
||||
description: "订单类型常量,定义各单据类型范围,用于库存历史来源标识"
|
||||
- name: "OrderStatusEnum"
|
||||
description: "单据状态枚举:PREPARE(0), FINISHED(4), CANCELED(5)"
|
||||
|
||||
services:
|
||||
- name: "ItemService"
|
||||
description: "物料管理服务,提供物料及 SKU 的 CRUD 操作"
|
||||
- name: "ItemBrandService"
|
||||
description: "品牌管理服务"
|
||||
- name: "ItemCategoryService"
|
||||
description: "分类管理服务,支持树形结构操作"
|
||||
- name: "MerchantService"
|
||||
description: "商户管理服务"
|
||||
- name: "WarehouseService"
|
||||
description: "仓库管理服务"
|
||||
- name: "InventoryService"
|
||||
description: "库存管理服务,提供库存查询和变更方法"
|
||||
- name: "InventoryHistoryService"
|
||||
description: "库存历史服务,记录每笔库存变动"
|
||||
- name: "ReceiptOrderService"
|
||||
description: "入库单管理服务"
|
||||
- name: "ShipmentOrderService"
|
||||
description: "出库单管理服务"
|
||||
- name: "MovementOrderService"
|
||||
description: "移库单管理服务"
|
||||
- name: "CheckOrderService"
|
||||
description: "盘点单管理服务"
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间 API 接口,供其他模块 RPC 调用"
|
||||
components: []
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP 接口层,提供 RESTful API"
|
||||
components:
|
||||
- "admin/item - 物料管理接口"
|
||||
- "admin/item-sku - SKU 查询接口(只读)"
|
||||
- "admin/item-brand - 品牌管理接口"
|
||||
- "admin/item-category - 分类管理接口"
|
||||
- "admin/merchant - 商户管理接口"
|
||||
- "admin/warehouse - 仓库管理接口"
|
||||
- "admin/inventory - 库存查询接口(只读)"
|
||||
- "admin/inventory-history - 库存历史查询接口(只读)"
|
||||
- "admin/receipt-order - 入库单管理接口"
|
||||
- "admin/receipt-order-detail - 入库明细查询接口"
|
||||
- "admin/shipment-order - 出库单管理接口"
|
||||
- "admin/shipment-order-detail - 出库明细查询接口"
|
||||
- "admin/movement-order - 移库单管理接口"
|
||||
- "admin/movement-order-detail - 移库明细查询接口"
|
||||
- "admin/check-order - 盘点单管理接口"
|
||||
- "admin/check-order-detail - 盘点明细查询接口"
|
||||
- "admin/home-statistics - 首页统计接口"
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑层"
|
||||
components:
|
||||
- "item - 物料服务(ItemService, ItemSkuService)"
|
||||
- "item-brand - 品牌服务(ItemBrandService)"
|
||||
- "item-category - 分类服务(ItemCategoryService)"
|
||||
- "merchant - 商户服务(MerchantService)"
|
||||
- "warehouse - 仓库服务(WarehouseService)"
|
||||
- "inventory - 库存服务(InventoryService, InventoryHistoryService)"
|
||||
- "receipt-order - 入库单服务(ReceiptOrderService, ReceiptOrderDetailService)"
|
||||
- "shipment-order - 出库单服务(ShipmentOrderService, ShipmentOrderDetailService)"
|
||||
- "movement-order - 移库单服务(MovementOrderService, MovementOrderDetailService)"
|
||||
- "check-order - 盘点单服务(CheckOrderService, CheckOrderDetailService)"
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问层"
|
||||
components:
|
||||
- "dataobject - DO 实体类定义"
|
||||
- "mysql - MyBatis Mapper 接口"
|
||||
|
||||
- name: "enums"
|
||||
purpose: "枚举和常量定义"
|
||||
components:
|
||||
- "WmsOrderTypeConstants - 订单类型常量(入库[100,200)、出库[200,300)等)"
|
||||
- "ErrorCodeConstants - 错误码常量"
|
||||
- "OrderStatusEnum - 单据状态枚举"
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns:
|
||||
- pattern: "单据头/明细模式 (Header + Detail Pattern)"
|
||||
location: "service/*-order/"
|
||||
purpose: "所有四大单据采用主子表结构,Header 存储汇总信息,Detail 存储 SKU 级别数据"
|
||||
- pattern: "并发安全模式 (Pessimistic Locking)"
|
||||
location: "service/inventory/"
|
||||
purpose: "SELECT ... FOR UPDATE 行锁 + DuplicateKeyException 捕获实现库存更新并发安全"
|
||||
- pattern: "VO 组装模式 (VO Assembly Pattern)"
|
||||
location: "service/*/xxxServiceImpl.java"
|
||||
purpose: "批量查询关联实体到 Map,通过 BeanUtils.toBean() 丰富 VO,避免 N+1"
|
||||
- pattern: "状态机模式 (State Machine)"
|
||||
location: "service/*-order/"
|
||||
purpose: "单据状态:PREPARE(0) -> FINISHED(4) / CANCELED(5),操作前校验状态"
|
||||
- pattern: "只读控制器模式"
|
||||
location: "controller/admin/inventory/"
|
||||
purpose: "库存和库存历史 Controller 为只读,所有变更通过单据完成触发"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: []
|
||||
consumers:
|
||||
- module: "yudao-module-system"
|
||||
api: "AdminUserApi"
|
||||
purpose: "批量获取用户信息,用于 VO 组装展示用户名"
|
||||
mq: []
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO"
|
||||
description: "所有 DO 继承 BaseDO,包含 creator, createTime, updater, updateTime, deleted 字段。"
|
||||
|
||||
# 核心数据表
|
||||
tables:
|
||||
# ========== 物料相关 ==========
|
||||
- name: "wms_item_brand"
|
||||
comment: "品牌表"
|
||||
entity: "ItemBrandDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "品牌编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "品牌名称" }
|
||||
- { name: "sort", type: "INT", comment: "排序" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
- name: "wms_item_category"
|
||||
comment: "分类表"
|
||||
entity: "ItemCategoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "分类编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "分类名称" }
|
||||
- { name: "parent_id", type: "BIGINT", comment: "父分类编号(0=顶级)" }
|
||||
- { name: "sort", type: "INT", comment: "排序" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_parent_id", columns: ["parent_id"] }
|
||||
|
||||
- name: "wms_item"
|
||||
comment: "物料表"
|
||||
entity: "ItemDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "物料编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "物料名称" }
|
||||
- { name: "code", type: "VARCHAR", comment: "物料编码" }
|
||||
- { name: "unit", type: "VARCHAR", comment: "计量单位" }
|
||||
- { name: "category_id", type: "BIGINT", comment: "分类编号,关联 ItemCategoryDO" }
|
||||
- { name: "brand_id", type: "BIGINT", comment: "品牌编号,关联 ItemBrandDO" }
|
||||
- { name: "status", type: "TINYINT", comment: "物料状态" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_code", columns: ["code"], unique: true }
|
||||
- { name: "idx_category_id", columns: ["category_id"] }
|
||||
- { name: "idx_brand_id", columns: ["brand_id"] }
|
||||
|
||||
- name: "wms_item_sku"
|
||||
comment: "物料 SKU 表"
|
||||
entity: "ItemSkuDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "SKU 编号,主键" }
|
||||
- { name: "item_id", type: "BIGINT", comment: "物料编号,关联 ItemDO" }
|
||||
- { name: "sku_no", type: "VARCHAR", comment: "SKU 编号/条码" }
|
||||
- { name: "specifications", type: "VARCHAR", comment: "规格属性" }
|
||||
- { name: "bar_code", type: "VARCHAR", comment: "条形码" }
|
||||
- { name: "weight", type: "DECIMAL", comment: "重量" }
|
||||
- { name: "volume", type: "DECIMAL", comment: "体积" }
|
||||
- { name: "purchase_price", type: "DECIMAL", comment: "采购价格" }
|
||||
- { name: "selling_price", type: "DECIMAL", comment: "销售价格" }
|
||||
- { name: "status", type: "TINYINT", comment: "SKU 状态" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_item_id", columns: ["item_id"] }
|
||||
- { name: "idx_sku_no", columns: ["sku_no"] }
|
||||
|
||||
# ========== 商户与仓库 ==========
|
||||
- name: "wms_merchant"
|
||||
comment: "商户表"
|
||||
entity: "MerchantDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "商户编号,主键" }
|
||||
- { name: "name", type: "VARCHAR", comment: "商户名称" }
|
||||
- { name: "type", type: "TINYINT", comment: "商户类型(1=客户 2=供应商 3=两者)" }
|
||||
- { name: "contact", type: "VARCHAR", comment: "联系人" }
|
||||
- { name: "phone", type: "VARCHAR", comment: "联系电话" }
|
||||
- { name: "address", type: "VARCHAR", comment: "地址" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
|
||||
- name: "wms_warehouse"
|
||||
comment: "仓库表"
|
||||
entity: "WarehouseDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "仓库编号,主键" }
|
||||
- { name: "code", type: "VARCHAR", comment: "仓库编码" }
|
||||
- { name: "name", type: "VARCHAR", comment: "仓库名称" }
|
||||
- { name: "sort", type: "INT", comment: "排序" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_code", columns: ["code"], unique: true }
|
||||
|
||||
# ========== 库存相关 ==========
|
||||
- name: "wms_inventory"
|
||||
comment: "库存表"
|
||||
entity: "InventoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "库存编号,主键" }
|
||||
- { name: "sku_id", type: "BIGINT", comment: "SKU 编号,关联 ItemSkuDO" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库编号,关联 WarehouseDO" }
|
||||
- { name: "quantity", type: "DECIMAL", comment: "库存数量" }
|
||||
- { name: "price", type: "DECIMAL", comment: "库存均价" }
|
||||
indexes:
|
||||
- { name: "uk_sku_warehouse", columns: ["sku_id", "warehouse_id"], unique: true }
|
||||
|
||||
- name: "wms_inventory_history"
|
||||
comment: "库存历史表"
|
||||
entity: "InventoryHistoryDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "历史编号,主键" }
|
||||
- { name: "sku_id", type: "BIGINT", comment: "SKU 编号,关联 ItemSkuDO" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库编号,关联 WarehouseDO" }
|
||||
- { name: "before_quantity", type: "DECIMAL", comment: "变动前数量" }
|
||||
- { name: "after_quantity", type: "DECIMAL", comment: "变动后数量" }
|
||||
- { name: "quantity", type: "DECIMAL", comment: "变动数量(正=增加,负=减少)" }
|
||||
- { name: "price", type: "DECIMAL", comment: "变动价格" }
|
||||
- { name: "source_order_type", type: "INTEGER", comment: "来源单据类型" }
|
||||
- { name: "source_order_id", type: "BIGINT", comment: "来源单据编号" }
|
||||
- { name: "source_order_no", type: "VARCHAR", comment: "来源单号" }
|
||||
indexes:
|
||||
- { name: "idx_sku_warehouse", columns: ["sku_id", "warehouse_id"] }
|
||||
- { name: "idx_source_order", columns: ["source_order_type", "source_order_id"] }
|
||||
|
||||
# ========== 入库单相关 ==========
|
||||
- name: "wms_receipt_order"
|
||||
comment: "入库单表"
|
||||
entity: "ReceiptOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "入库单编号,主键" }
|
||||
- { name: "no", type: "VARCHAR", comment: "入库单号" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库编号,关联 WarehouseDO" }
|
||||
- { name: "merchant_id", type: "BIGINT", comment: "商户编号,关联 MerchantDO" }
|
||||
- { name: "status", type: "TINYINT", comment: "单据状态(0=PREPARE, 4=FINISHED, 5=CANCELED)" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_no", columns: ["no"], unique: true }
|
||||
- { name: "idx_warehouse_id", columns: ["warehouse_id"] }
|
||||
- { name: "idx_status", columns: ["status"] }
|
||||
|
||||
- name: "wms_receipt_order_detail"
|
||||
comment: "入库明细表"
|
||||
entity: "ReceiptOrderDetailDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "明细编号,主键" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "入库单编号,关联 ReceiptOrderDO" }
|
||||
- { name: "sku_id", type: "BIGINT", comment: "SKU 编号,关联 ItemSkuDO" }
|
||||
- { name: "quantity", type: "DECIMAL", comment: "入库数量" }
|
||||
- { name: "price", type: "DECIMAL", comment: "入库单价" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_order_id", columns: ["order_id"] }
|
||||
|
||||
# ========== 出库单相关 ==========
|
||||
- name: "wms_shipment_order"
|
||||
comment: "出库单表"
|
||||
entity: "ShipmentOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "出库单编号,主键" }
|
||||
- { name: "no", type: "VARCHAR", comment: "出库单号" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库编号,关联 WarehouseDO" }
|
||||
- { name: "merchant_id", type: "BIGINT", comment: "商户编号,关联 MerchantDO" }
|
||||
- { name: "status", type: "TINYINT", comment: "单据状态(0=PREPARE, 4=FINISHED, 5=CANCELED)" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_no", columns: ["no"], unique: true }
|
||||
- { name: "idx_warehouse_id", columns: ["warehouse_id"] }
|
||||
- { name: "idx_status", columns: ["status"] }
|
||||
|
||||
- name: "wms_shipment_order_detail"
|
||||
comment: "出库明细表"
|
||||
entity: "ShipmentOrderDetailDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "明细编号,主键" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "出库单编号,关联 ShipmentOrderDO" }
|
||||
- { name: "sku_id", type: "BIGINT", comment: "SKU 编号,关联 ItemSkuDO" }
|
||||
- { name: "quantity", type: "DECIMAL", comment: "出库数量" }
|
||||
- { name: "price", type: "DECIMAL", comment: "出库单价" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_order_id", columns: ["order_id"] }
|
||||
|
||||
# ========== 移库单相关 ==========
|
||||
- name: "wms_movement_order"
|
||||
comment: "移库单表"
|
||||
entity: "MovementOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "移库单编号,主键" }
|
||||
- { name: "no", type: "VARCHAR", comment: "移库单号" }
|
||||
- { name: "from_warehouse_id", type: "BIGINT", comment: "源仓库编号,关联 WarehouseDO" }
|
||||
- { name: "to_warehouse_id", type: "BIGINT", comment: "目标仓库编号,关联 WarehouseDO" }
|
||||
- { name: "status", type: "TINYINT", comment: "单据状态(0=PREPARE, 4=FINISHED, 5=CANCELED)" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_no", columns: ["no"], unique: true }
|
||||
- { name: "idx_from_warehouse_id", columns: ["from_warehouse_id"] }
|
||||
- { name: "idx_to_warehouse_id", columns: ["to_warehouse_id"] }
|
||||
- { name: "idx_status", columns: ["status"] }
|
||||
|
||||
- name: "wms_movement_order_detail"
|
||||
comment: "移库明细表"
|
||||
entity: "MovementOrderDetailDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "明细编号,主键" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "移库单编号,关联 MovementOrderDO" }
|
||||
- { name: "sku_id", type: "BIGINT", comment: "SKU 编号,关联 ItemSkuDO" }
|
||||
- { name: "quantity", type: "DECIMAL", comment: "移库数量" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_order_id", columns: ["order_id"] }
|
||||
|
||||
# ========== 盘点单相关 ==========
|
||||
- name: "wms_check_order"
|
||||
comment: "盘点单表"
|
||||
entity: "CheckOrderDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "盘点单编号,主键" }
|
||||
- { name: "no", type: "VARCHAR", comment: "盘点单号" }
|
||||
- { name: "warehouse_id", type: "BIGINT", comment: "仓库编号,关联 WarehouseDO" }
|
||||
- { name: "status", type: "TINYINT", comment: "单据状态(0=PREPARE, 4=FINISHED, 5=CANCELED)" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "uk_no", columns: ["no"], unique: true }
|
||||
- { name: "idx_warehouse_id", columns: ["warehouse_id"] }
|
||||
- { name: "idx_status", columns: ["status"] }
|
||||
|
||||
- name: "wms_check_order_detail"
|
||||
comment: "盘点明细表"
|
||||
entity: "CheckOrderDetailDO"
|
||||
extends: "BaseDO"
|
||||
columns:
|
||||
- { name: "id", type: "BIGINT", comment: "明细编号,主键" }
|
||||
- { name: "order_id", type: "BIGINT", comment: "盘点单编号,关联 CheckOrderDO" }
|
||||
- { name: "sku_id", type: "BIGINT", comment: "SKU 编号,关联 ItemSkuDO" }
|
||||
- { name: "system_quantity", type: "DECIMAL", comment: "系统数量" }
|
||||
- { name: "actual_quantity", type: "DECIMAL", comment: "实际数量" }
|
||||
- { name: "remark", type: "VARCHAR", comment: "备注" }
|
||||
indexes:
|
||||
- { name: "idx_order_id", columns: ["order_id"] }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships:
|
||||
- from: "wms_item"
|
||||
to: "wms_item_category"
|
||||
type: "N:1"
|
||||
foreign_key: "category_id"
|
||||
- from: "wms_item"
|
||||
to: "wms_item_brand"
|
||||
type: "N:1"
|
||||
foreign_key: "brand_id"
|
||||
- from: "wms_item_sku"
|
||||
to: "wms_item"
|
||||
type: "N:1"
|
||||
foreign_key: "item_id"
|
||||
- from: "wms_inventory"
|
||||
to: "wms_item_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
- from: "wms_inventory"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
- from: "wms_inventory_history"
|
||||
to: "wms_item_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
- from: "wms_inventory_history"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
- from: "wms_receipt_order"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
- from: "wms_receipt_order"
|
||||
to: "wms_merchant"
|
||||
type: "N:1"
|
||||
foreign_key: "merchant_id"
|
||||
- from: "wms_receipt_order_detail"
|
||||
to: "wms_receipt_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "wms_receipt_order_detail"
|
||||
to: "wms_item_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
- from: "wms_shipment_order"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
- from: "wms_shipment_order"
|
||||
to: "wms_merchant"
|
||||
type: "N:1"
|
||||
foreign_key: "merchant_id"
|
||||
- from: "wms_shipment_order_detail"
|
||||
to: "wms_shipment_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "wms_shipment_order_detail"
|
||||
to: "wms_item_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
- from: "wms_movement_order"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "from_warehouse_id"
|
||||
comment: "源仓库"
|
||||
- from: "wms_movement_order"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "to_warehouse_id"
|
||||
comment: "目标仓库"
|
||||
- from: "wms_movement_order_detail"
|
||||
to: "wms_movement_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "wms_movement_order_detail"
|
||||
to: "wms_item_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
- from: "wms_check_order"
|
||||
to: "wms_warehouse"
|
||||
type: "N:1"
|
||||
foreign_key: "warehouse_id"
|
||||
- from: "wms_check_order_detail"
|
||||
to: "wms_check_order"
|
||||
type: "N:1"
|
||||
foreign_key: "order_id"
|
||||
- from: "wms_check_order_detail"
|
||||
to: "wms_item_sku"
|
||||
type: "N:1"
|
||||
foreign_key: "sku_id"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations:
|
||||
- "@Tag(name = '管理后台 - xxx') - Swagger 文档标签"
|
||||
- "@RestController - REST 控制器"
|
||||
- "@RequestMapping('/wms/xxx') - 请求路径前缀"
|
||||
- "@Validated - 参数校验"
|
||||
- "@PreAuthorize('@ss.hasPermission('wms:xxx:action')') - 权限控制"
|
||||
example: |
|
||||
@Tag(name = "管理后台 - 入库单")
|
||||
@RestController
|
||||
@RequestMapping("/wms/receipt-order")
|
||||
@Validated
|
||||
public class ReceiptOrderController {
|
||||
@Resource
|
||||
private ReceiptOrderService receiptOrderService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建入库单")
|
||||
@PreAuthorize("@ss.hasPermission('wms:receipt-order:create')")
|
||||
public CommonResult<Long> createReceiptOrder(@Valid @RequestBody ReceiptOrderSaveReqVO reqVO) {
|
||||
return success(receiptOrderService.createReceiptOrder(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/complete")
|
||||
@Operation(summary = "完成入库")
|
||||
@PreAuthorize("@ss.hasPermission('wms:receipt-order:update')")
|
||||
public CommonResult<Boolean> completeReceiptOrder(@RequestParam("id") Long id) {
|
||||
receiptOrderService.completeReceiptOrder(id);
|
||||
return success(true);
|
||||
}
|
||||
}
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: "XxxService 接口定义业务方法,XxxServiceImpl 实现类"
|
||||
impl_pattern: |
|
||||
@Service
|
||||
@Validated
|
||||
public class XxxServiceImpl implements XxxService {
|
||||
@Resource
|
||||
private XxxMapper xxxMapper;
|
||||
|
||||
// 使用 static final 定义常量
|
||||
// 使用 @Transactional 注解控制事务
|
||||
// 使用 validateXxxExists 方法校验存在性
|
||||
// 使用 exception(ErrorCode) 抛出业务异常
|
||||
}
|
||||
example: |
|
||||
@Service
|
||||
public class ReceiptOrderServiceImpl implements ReceiptOrderService {
|
||||
@Resource
|
||||
private ReceiptOrderMapper receiptOrderMapper;
|
||||
@Resource
|
||||
private ReceiptOrderDetailMapper receiptOrderDetailMapper;
|
||||
@Resource
|
||||
private InventoryService inventoryService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void completeReceiptOrder(Long id) {
|
||||
// 1. 校验单据存在且状态为 PREPARE
|
||||
ReceiptOrderDO order = receiptOrderMapper.selectById(id);
|
||||
validateReceiptOrderCanComplete(order);
|
||||
|
||||
// 2. 遍历明细行,增加库存
|
||||
List<ReceiptOrderDetailDO> details = receiptOrderDetailMapper.selectListByOrderId(id);
|
||||
for (ReceiptOrderDetailDO detail : details) {
|
||||
inventoryService.addStock(detail.getSkuId(), order.getWarehouseId(),
|
||||
detail.getQuantity(), detail.getPrice(),
|
||||
WmsOrderTypeConstants.RECEIPT_ORDER_TYPE, order.getId(), order.getNo());
|
||||
}
|
||||
|
||||
// 3. 更新单据状态为 FINISHED
|
||||
receiptOrderMapper.updateById(new ReceiptOrderDO().setId(id)
|
||||
.setStatus(OrderStatusEnum.FINISHED.getStatus()));
|
||||
}
|
||||
}
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: |
|
||||
public interface XxxMapper extends BaseMapperX<XxxDO> {
|
||||
// 继承 BaseMapperX 获得通用 CRUD 方法
|
||||
// 自定义查询方法使用 @Select 注解或 XML
|
||||
// 分页查询返回 PageResult<XxxDO>
|
||||
}
|
||||
example: |
|
||||
public interface InventoryMapper extends BaseMapperX<InventoryDO> {
|
||||
default PageResult<InventoryDO> selectPage(InventoryPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<InventoryDO>()
|
||||
.eqIfPresent(InventoryDO::getSkuId, reqVO.getSkuId())
|
||||
.eqIfPresent(InventoryDO::getWarehouseId, reqVO.getWarehouseId())
|
||||
.orderByDesc(InventoryDO::getId));
|
||||
}
|
||||
}
|
||||
|
||||
# 库存并发安全模式
|
||||
inventory_concurrency:
|
||||
description: "库存更新使用 SELECT ... FOR UPDATE 行锁 + DuplicateKeyException 捕获实现并发安全"
|
||||
example: |
|
||||
@Service
|
||||
public class InventoryServiceImpl implements InventoryService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void addStock(Long skuId, Long warehouseId, BigDecimal quantity,
|
||||
BigDecimal price, Integer sourceOrderType,
|
||||
Long sourceOrderId, String sourceOrderNo) {
|
||||
// 1. 查询库存记录(带行锁)
|
||||
InventoryDO inventory = inventoryMapper.selectBySkuIdAndWarehouseIdForUpdate(skuId, warehouseId);
|
||||
|
||||
BigDecimal beforeQuantity;
|
||||
if (inventory == null) {
|
||||
// 2. 首次入库,尝试插入(懒初始化)
|
||||
try {
|
||||
inventory = new InventoryDO().setSkuId(skuId)
|
||||
.setWarehouseId(warehouseId).setQuantity(quantity).setPrice(price);
|
||||
inventoryMapper.insert(inventory);
|
||||
beforeQuantity = BigDecimal.ZERO;
|
||||
} catch (DuplicateKeyException e) {
|
||||
// 3. 并发插入冲突,重新查询
|
||||
inventory = inventoryMapper.selectBySkuIdAndWarehouseIdForUpdate(skuId, warehouseId);
|
||||
beforeQuantity = inventory.getQuantity();
|
||||
inventory.setQuantity(beforeQuantity.add(quantity));
|
||||
inventoryMapper.updateById(inventory);
|
||||
}
|
||||
} else {
|
||||
// 4. 已有记录,直接更新
|
||||
beforeQuantity = inventory.getQuantity();
|
||||
inventory.setQuantity(beforeQuantity.add(quantity));
|
||||
inventoryMapper.updateById(inventory);
|
||||
}
|
||||
|
||||
// 5. 记录库存变动历史
|
||||
inventoryHistoryService.createHistory(skuId, warehouseId,
|
||||
beforeQuantity, inventory.getQuantity(), quantity, price,
|
||||
sourceOrderType, sourceOrderId, sourceOrderNo);
|
||||
}
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: "wms 模块错误码需遵循项目统一的错误码分配规则"
|
||||
examples:
|
||||
- message: "物料不存在"
|
||||
- message: "仓库不存在"
|
||||
- message: "商户不存在"
|
||||
- message: "SKU 不存在"
|
||||
- message: "库存不足"
|
||||
- message: "单据状态不允许操作"
|
||||
- message: "品牌存在关联数据不允许删除"
|
||||
- message: "分类存在子分类不允许删除"
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: "CommonResult.success(data)"
|
||||
error_pattern: "throw exception(ErrorCode)"
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增单据类型
|
||||
new_channel:
|
||||
title: "新增仓储单据类型"
|
||||
steps:
|
||||
- step: "1. 分配订单类型常量"
|
||||
description: "在 WmsOrderTypeConstants 中分配新的类型范围"
|
||||
code: |
|
||||
// 示例:新增调拨单,类型范围 [500, 600)
|
||||
public static final int TRANSFER_ORDER_TYPE_BEGIN = 500;
|
||||
public static final int TRANSFER_ORDER_TYPE_END = 600;
|
||||
- step: "2. 创建单据头和明细表"
|
||||
description: "创建主子表,单据头包含 no、warehouse_id、status 等字段"
|
||||
code: |
|
||||
CREATE TABLE wms_transfer_order (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
no VARCHAR(64) NOT NULL,
|
||||
warehouse_id BIGINT NOT NULL,
|
||||
status TINYINT NOT NULL DEFAULT 0,
|
||||
remark VARCHAR(512),
|
||||
-- BaseDO 字段
|
||||
creator VARCHAR(64), create_time DATETIME, updater VARCHAR(64),
|
||||
update_time DATETIME, deleted BIT DEFAULT 0,
|
||||
UNIQUE KEY uk_no (no)
|
||||
);
|
||||
- step: "3. 创建 DO、Mapper、Service、Controller"
|
||||
description: "按照既有模式创建完整的 CRUD 代码"
|
||||
- step: "4. 实现完成/取消逻辑"
|
||||
description: "在 Service 中实现单据完成时的库存变更逻辑"
|
||||
- step: "5. 添加权限配置"
|
||||
description: "在系统中配置权限标识 wms:transfer-order:{action}"
|
||||
|
||||
# 新增物料属性
|
||||
new_feature:
|
||||
title: "扩展物料属性"
|
||||
steps:
|
||||
- step: "1. 添加字段到 wms_item 或 wms_item_sku"
|
||||
description: "根据属性粒度决定添加到物料表还是 SKU 表"
|
||||
- step: "2. 更新 DO 类"
|
||||
description: "在对应的 DO 类中添加字段"
|
||||
- step: "3. 更新 VO 类"
|
||||
description: "在 SaveReqVO 和 RespVO 中添加字段"
|
||||
- step: "4. 更新 Controller 和 Service"
|
||||
description: "确保新字段在 CRUD 流程中正确处理"
|
||||
- step: "5. 更新 Excel 导出模板"
|
||||
description: "在导出 VO 中添加 @ExcelProperty 注解"
|
||||
|
||||
# 最佳实践
|
||||
best_practices:
|
||||
- practice: "库存更新必须使用行锁"
|
||||
description: "所有库存更新操作必须使用 SELECT ... FOR UPDATE 行锁,避免并发更新丢失"
|
||||
- practice: "单据完成操作使用事务"
|
||||
description: "单据完成涉及状态更新、库存变更、历史记录,必须在同一事务中完成"
|
||||
- practice: "VO 组装使用批量查询"
|
||||
description: "通过 AdminUserApi.getUserMap() 等批量查询避免 N+1 问题"
|
||||
- practice: "单据操作前校验状态"
|
||||
description: "完成、取消、更新、删除操作前必须校验单据状态为 PREPARE"
|
||||
- practice: "订单类型常量分段管理"
|
||||
description: "新单据类型必须在 WmsOrderTypeConstants 中分配范围,避免类型冲突"
|
||||
- practice: "SKU 作为库存单位"
|
||||
description: "库存以 SKU + 仓库维度管理,物料级别不跟踪库存"
|
||||
- practice: "分类树一次性加载"
|
||||
description: "分类查询建议一次性加载所有数据到内存后构建树,避免递归查询数据库"
|
||||
- practice: "Excel 导出控制数据量"
|
||||
description: "大数据量导出使用 EasyExcel 流式写入,避免内存溢出"
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal:
|
||||
- module: "yudao-module-system"
|
||||
purpose: "AdminUserApi - 批量获取用户信息用于 VO 组装"
|
||||
- module: "yudao-framework-common"
|
||||
purpose: "通用工具类、CommonResult、PageResult 等"
|
||||
- module: "yudao-framework-mybatis"
|
||||
purpose: "MyBatis-Plus 封装、BaseMapperX、BaseDO"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external:
|
||||
- name: "MyBatis-Plus"
|
||||
purpose: "ORM 框架,提供 CRUD 封装"
|
||||
- name: "Hutool"
|
||||
purpose: "Java 工具类库"
|
||||
- name: "Swagger/OpenAPI"
|
||||
purpose: "API 文档注解"
|
||||
- name: "Apache POI / EasyExcel"
|
||||
purpose: "Excel 导入导出"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/InventoryServiceImpl.java"
|
||||
purpose: "库存服务实现,核心库存变更逻辑(并发安全)"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/InventoryHistoryServiceImpl.java"
|
||||
purpose: "库存历史服务,记录每笔库存变动"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/receiptorder/ReceiptOrderServiceImpl.java"
|
||||
purpose: "入库单服务实现,完成入库触发库存增加"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/shipmentorder/ShipmentOrderServiceImpl.java"
|
||||
purpose: "出库单服务实现,完成出库触发库存减少"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/movementorder/MovementOrderServiceImpl.java"
|
||||
purpose: "移库单服务实现,完成移库触发双向库存变更"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/checkorder/CheckOrderServiceImpl.java"
|
||||
purpose: "盘点单服务实现,完成盘点触发库存调整"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/item/ItemServiceImpl.java"
|
||||
purpose: "物料服务实现,包含 SKU 嵌入管理"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/InventoryMapper.java"
|
||||
purpose: "库存 Mapper,含 FOR UPDATE 查询方法"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/WmsOrderTypeConstants.java"
|
||||
purpose: "订单类型常量,定义各单据类型范围"
|
||||
- path: "yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java"
|
||||
purpose: "错误码常量定义"
|
||||
@@ -1,91 +0,0 @@
|
||||
# 工厂模式知识库
|
||||
|
||||
pattern:
|
||||
id: "factory-pattern"
|
||||
name: "工厂模式"
|
||||
category: "创建型模式"
|
||||
description: "定义一个创建对象的接口,让子类决定实例化哪一个类,使一个类的实例化延迟到其子类"
|
||||
|
||||
# 模式结构
|
||||
structure:
|
||||
participants:
|
||||
- name: "Factory"
|
||||
role: "抽象工厂接口"
|
||||
description: "声明创建产品对象的抽象方法"
|
||||
- name: "ConcreteFactory"
|
||||
role: "具体工厂"
|
||||
description: "实现抽象工厂接口,创建具体产品"
|
||||
- name: "Product"
|
||||
role: "抽象产品"
|
||||
description: "定义产品的共同接口"
|
||||
- name: "ConcreteProduct"
|
||||
role: "具体产品"
|
||||
description: "实现抽象产品接口的具体类"
|
||||
|
||||
# 项目中的应用
|
||||
applications:
|
||||
- module: "pay"
|
||||
location: "PayClientFactory"
|
||||
purpose: "创建支付客户端实例"
|
||||
code_path: "yudao-module-pay/.../pay/core/client/PayClientFactory.java"
|
||||
implementation: |
|
||||
public interface PayClientFactory {
|
||||
PayClient getPayClient(Long channelId);
|
||||
<Config extends PayClientConfig> PayClient createOrUpdatePayClient(
|
||||
Long channelId, String channelCode, Config config);
|
||||
}
|
||||
|
||||
- module: "infra/sms"
|
||||
location: "SmsClientFactory"
|
||||
purpose: "创建短信客户端实例"
|
||||
code_path: "yudao-module-infra/.../sms/core/client/SmsClientFactory.java"
|
||||
implementation: |
|
||||
public class SmsClientFactoryImpl implements SmsClientFactory {
|
||||
private final ConcurrentMap<Long, AbstractSmsClient> channelIdClients = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public SmsClient createOrUpdateSmsClient(SmsChannelProperties properties) {
|
||||
AbstractSmsClient client = channelIdClients.get(properties.getId());
|
||||
if (client == null) {
|
||||
client = this.createSmsClient(properties);
|
||||
client.init();
|
||||
channelIdClients.put(client.getId(), client);
|
||||
} else {
|
||||
client.refresh(properties);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
||||
|
||||
# 使用场景
|
||||
scenarios:
|
||||
- "需要根据配置动态创建不同类型的客户端"
|
||||
- "需要管理多个同类对象的实例(如缓存)"
|
||||
- "创建逻辑复杂,需要封装"
|
||||
- "需要支持扩展新的产品类型"
|
||||
|
||||
# 优点
|
||||
advantages:
|
||||
- "解耦创建逻辑和使用逻辑"
|
||||
- "便于扩展新产品类型"
|
||||
- "可以缓存和复用对象实例"
|
||||
- "统一管理对象生命周期"
|
||||
|
||||
# 缺点
|
||||
disadvantages:
|
||||
- "增加了类的数量"
|
||||
- "需要维护工厂类的实现"
|
||||
|
||||
# 扩展指南
|
||||
extension_guide:
|
||||
title: "如何新增产品类型"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建具体产品类"
|
||||
description: "实现Product接口"
|
||||
- step: 2
|
||||
action: "修改工厂创建逻辑"
|
||||
description: "在工厂中添加新类型的创建分支"
|
||||
- step: 3
|
||||
action: "配置支持"
|
||||
description: "添加配置枚举或配置项"
|
||||
@@ -1,58 +0,0 @@
|
||||
# 设计模式索引
|
||||
|
||||
patterns:
|
||||
# 创建型模式
|
||||
- id: "factory-pattern"
|
||||
name: "工厂模式"
|
||||
file: "factory-pattern.yaml"
|
||||
modules: ["pay", "infra"]
|
||||
description: "创建对象实例的工厂接口,支持多类型扩展"
|
||||
|
||||
# 行为型模式
|
||||
- id: "template-method-pattern"
|
||||
name: "模板方法模式"
|
||||
file: "template-method-pattern.yaml"
|
||||
modules: ["pay", "infra"]
|
||||
description: "定义算法骨架,子类实现具体步骤"
|
||||
|
||||
- id: "strategy-pattern"
|
||||
name: "策略模式"
|
||||
file: "strategy-pattern.yaml"
|
||||
modules: ["pay", "infra", "ai"]
|
||||
description: "封装可互换的算法族"
|
||||
|
||||
# 模式选择指南
|
||||
selection_guide:
|
||||
- scenario: "需要根据配置创建不同类型的客户端"
|
||||
patterns: ["factory-pattern", "strategy-pattern"]
|
||||
example: "支付渠道、短信渠道"
|
||||
|
||||
- scenario: "需要统一处理流程,但具体实现不同"
|
||||
patterns: ["template-method-pattern"]
|
||||
example: "支付下单流程、短信发送流程"
|
||||
|
||||
- scenario: "需要动态切换算法或行为"
|
||||
patterns: ["strategy-pattern"]
|
||||
example: "AI模型切换、支付方式切换"
|
||||
|
||||
# 模式组合
|
||||
combinations:
|
||||
- name: "工厂 + 策略"
|
||||
description: "工厂创建策略实例"
|
||||
usage: "PayClientFactory创建PayClient实例"
|
||||
|
||||
- name: "工厂 + 模板方法"
|
||||
description: "工厂创建模板方法的具体实现"
|
||||
usage: "SmsClientFactory创建AbstractSmsClient子类"
|
||||
|
||||
- name: "模板方法 + 策略"
|
||||
description: "模板方法定义骨架,策略实现变化部分"
|
||||
usage: "AbstractPayClient定义骨架,具体PayClient实现策略"
|
||||
|
||||
# 扩展指南
|
||||
extension:
|
||||
new_pattern:
|
||||
steps:
|
||||
- "创建模式知识库文件: skills/patterns/{pattern}-pattern.yaml"
|
||||
- "更新此索引文件"
|
||||
- "关联应用到相关模块"
|
||||
@@ -1,136 +0,0 @@
|
||||
# 策略模式知识库
|
||||
|
||||
pattern:
|
||||
id: "strategy-pattern"
|
||||
name: "策略模式"
|
||||
category: "行为型模式"
|
||||
description: "定义一系列算法,把它们封装起来,并使它们可互相替换,让算法独立于使用它的客户而变化"
|
||||
|
||||
# 模式结构
|
||||
structure:
|
||||
participants:
|
||||
- name: "Strategy"
|
||||
role: "策略接口"
|
||||
description: "定义所有支持的算法的公共接口"
|
||||
- name: "ConcreteStrategy"
|
||||
role: "具体策略"
|
||||
description: "实现策略接口的具体算法"
|
||||
- name: "Context"
|
||||
role: "上下文"
|
||||
description: "维护对策略对象的引用"
|
||||
|
||||
# 项目中的应用
|
||||
applications:
|
||||
- module: "pay"
|
||||
location: "PayClient接口及其实现"
|
||||
purpose: "支持多渠道支付"
|
||||
code_path: "yudao-module-pay/.../pay/core/client/PayClient.java"
|
||||
strategies:
|
||||
- name: "AlipayQrPayClient"
|
||||
description: "支付宝扫码支付"
|
||||
- name: "AlipayAppPayClient"
|
||||
description: "支付宝App支付"
|
||||
- name: "WxNativePayClient"
|
||||
description: "微信Native支付"
|
||||
- name: "WxAppPayClient"
|
||||
description: "微信App支付"
|
||||
implementation: |
|
||||
public interface PayClient<Config extends PayClientConfig> {
|
||||
Long getId();
|
||||
PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
|
||||
PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
|
||||
}
|
||||
|
||||
- module: "infra/sms"
|
||||
location: "SmsClient接口及其实现"
|
||||
purpose: "支持多渠道短信发送"
|
||||
strategies:
|
||||
- name: "AliyunSmsClient"
|
||||
description: "阿里云短信"
|
||||
- name: "TencentSmsClient"
|
||||
description: "腾讯云短信"
|
||||
- name: "HuaweiSmsClient"
|
||||
description: "华为云短信"
|
||||
- name: "QiniuSmsClient"
|
||||
description: "七牛云短信"
|
||||
|
||||
- module: "ai"
|
||||
location: "ChatModel接口及其实现"
|
||||
purpose: "支持多AI模型"
|
||||
strategies:
|
||||
- name: "OpenAIChatModel"
|
||||
description: "OpenAI聊天模型"
|
||||
- name: "BaiduChatModel"
|
||||
description: "百度文心一言"
|
||||
- name: "ZhipuChatModel"
|
||||
description: "智谱AI"
|
||||
- name: "TongyiChatModel"
|
||||
description: "阿里通义千问"
|
||||
|
||||
# 使用场景
|
||||
scenarios:
|
||||
- "需要在运行时选择算法"
|
||||
- "有多个类只在行为上有差异"
|
||||
- "需要动态切换算法"
|
||||
- "需要隐藏算法实现细节"
|
||||
|
||||
# 优点
|
||||
advantages:
|
||||
- "算法可自由切换"
|
||||
- "避免使用多重条件判断"
|
||||
- "易于扩展新策略"
|
||||
- "符合开闭原则"
|
||||
|
||||
# 缺点
|
||||
disadvantages:
|
||||
- "客户端需要了解所有策略"
|
||||
- "策略过多时类数量增加"
|
||||
|
||||
# 与工厂模式配合
|
||||
factory_integration:
|
||||
description: "策略模式通常与工厂模式配合使用,由工厂创建具体策略实例"
|
||||
example:
|
||||
factory: "PayClientFactory"
|
||||
purpose: "根据渠道配置创建对应的PayClient实例"
|
||||
code: |
|
||||
// 通过工厂获取策略
|
||||
PayClient client = payClientFactory.getPayClient(channelId);
|
||||
// 使用策略
|
||||
PayOrderRespDTO response = client.unifiedOrder(reqDTO);
|
||||
|
||||
# 扩展指南
|
||||
extension_guide:
|
||||
title: "如何新增策略"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建策略实现类"
|
||||
description: "实现策略接口"
|
||||
- step: 2
|
||||
action: "定义配置类"
|
||||
description: "如果需要配置参数"
|
||||
- step: 3
|
||||
action: "修改工厂"
|
||||
description: "在工厂中添加创建分支"
|
||||
- step: 4
|
||||
action: "添加枚举"
|
||||
description: "如果需要类型枚举"
|
||||
|
||||
example:
|
||||
title: "新增短信渠道"
|
||||
code: |
|
||||
// 1. 创建实现类
|
||||
public class XxxSmsClient extends AbstractSmsClient {
|
||||
@Override
|
||||
protected void doInit() { ... }
|
||||
|
||||
@Override
|
||||
public SmsSendResultDTO sendSms(SmsSendMessageDTO message) { ... }
|
||||
}
|
||||
|
||||
// 2. 添加渠道枚举
|
||||
public enum SmsChannelEnum {
|
||||
XXX(10, "XXX短信");
|
||||
}
|
||||
|
||||
// 3. 修改工厂创建逻辑
|
||||
case XXX: return new XxxSmsClient(properties);
|
||||
@@ -1,119 +0,0 @@
|
||||
# 模板方法模式知识库
|
||||
|
||||
pattern:
|
||||
id: "template-method-pattern"
|
||||
name: "模板方法模式"
|
||||
category: "行为型模式"
|
||||
description: "定义一个操作中的算法骨架,将一些步骤延迟到子类中,使得子类可以不改变算法结构即可重定义算法的某些步骤"
|
||||
|
||||
# 模式结构
|
||||
structure:
|
||||
participants:
|
||||
- name: "AbstractClass"
|
||||
role: "抽象类"
|
||||
description: "定义抽象方法和模板方法,模板方法调用抽象方法"
|
||||
- name: "ConcreteClass"
|
||||
role: "具体类"
|
||||
description: "实现抽象方法,完成具体逻辑"
|
||||
|
||||
# 项目中的应用
|
||||
applications:
|
||||
- module: "pay"
|
||||
location: "AbstractPayClient"
|
||||
purpose: "统一支付流程骨架"
|
||||
code_path: "yudao-module-pay/.../pay/core/client/impl/AbstractPayClient.java"
|
||||
implementation: |
|
||||
public abstract class AbstractPayClient<Config extends PayClientConfig> implements PayClient<Config> {
|
||||
|
||||
// 模板方法:统一下单
|
||||
@Override
|
||||
public final PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
|
||||
ValidationUtils.validate(reqDTO);
|
||||
try {
|
||||
return doUnifiedOrder(reqDTO); // 调用子类实现
|
||||
} catch (Throwable ex) {
|
||||
log.error("[unifiedOrder]异常", ex);
|
||||
throw buildPayException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// 抽象方法:由子类实现具体下单逻辑
|
||||
protected abstract PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Throwable;
|
||||
|
||||
// 抽象方法:初始化
|
||||
protected abstract void doInit();
|
||||
}
|
||||
|
||||
- module: "infra/sms"
|
||||
location: "AbstractSmsClient"
|
||||
purpose: "统一短信发送流程"
|
||||
code_path: "yudao-module-infra/.../sms/core/client/AbstractSmsClient.java"
|
||||
implementation: |
|
||||
public abstract class AbstractSmsClient implements SmsClient {
|
||||
// 模板方法定义发送流程
|
||||
}
|
||||
|
||||
# 模板方法流程
|
||||
template_methods:
|
||||
- name: "unifiedOrder"
|
||||
module: "pay"
|
||||
steps:
|
||||
- "参数校验"
|
||||
- "调用子类实现下单"
|
||||
- "异常处理和转换"
|
||||
hook_methods:
|
||||
- "doUnifiedOrder()"
|
||||
- "doInit()"
|
||||
|
||||
# 使用场景
|
||||
scenarios:
|
||||
- "多个子类有公共行为,但具体实现不同"
|
||||
- "需要控制算法的整体流程"
|
||||
- "需要在不同点调用不同的实现"
|
||||
|
||||
# 优点
|
||||
advantages:
|
||||
- "复用公共代码"
|
||||
- "控制算法流程"
|
||||
- "符合开闭原则"
|
||||
- "便于维护和扩展"
|
||||
|
||||
# 缺点
|
||||
disadvantages:
|
||||
- "每个实现都需要定义子类"
|
||||
- "增加了类的数量"
|
||||
|
||||
# 扩展指南
|
||||
extension_guide:
|
||||
title: "如何新增具体实现"
|
||||
steps:
|
||||
- step: 1
|
||||
action: "创建具体类"
|
||||
description: "继承抽象类"
|
||||
- step: 2
|
||||
action: "实现抽象方法"
|
||||
description: "doUnifiedOrder(), doInit()等"
|
||||
- step: 3
|
||||
action: "注册到工厂"
|
||||
description: "在工厂中添加创建逻辑"
|
||||
|
||||
example:
|
||||
title: "新增支付渠道"
|
||||
code: |
|
||||
public class XxxPayClient extends AbstractPayClient<XxxPayClientConfig> {
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
// 初始化SDK
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Throwable {
|
||||
// 调用第三方支付API
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
// 调用第三方退款API
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
# 模块 Skill 提取提示词模板
|
||||
|
||||
## 角色定义
|
||||
你是一个资深的企业级Java应用架构师,精通DDD领域驱动设计、微服务架构和Spring Boot生态。
|
||||
|
||||
## 任务目标
|
||||
为指定模块提取完整的 Skill 文档,形成可复用的知识资产。
|
||||
|
||||
## 分析框架(五阶段)
|
||||
|
||||
---
|
||||
|
||||
### 第一阶段:设计理念提取
|
||||
|
||||
从以下维度分析模块的设计理念:
|
||||
|
||||
1. **业务定位**
|
||||
- 模块解决什么业务问题?
|
||||
- 模块在整个系统中的定位?
|
||||
- 与其他模块的边界是什么?
|
||||
|
||||
2. **设计原则**
|
||||
- 遵循了哪些设计原则(SOLID、DDD等)?
|
||||
- 有哪些关键的架构决策?
|
||||
- 为什么这样设计?
|
||||
|
||||
3. **领域模型**
|
||||
- 核心领域对象有哪些?
|
||||
- 领域对象之间的关系?
|
||||
- 聚合根是什么?值对象有哪些?
|
||||
|
||||
---
|
||||
|
||||
### 第二阶段:架构设计提取
|
||||
|
||||
分析模块的分层架构:
|
||||
|
||||
1. **目录结构分析**
|
||||
```
|
||||
yudao-module-{xxx}/
|
||||
├── api/ # API接口层(模块间通信)
|
||||
├── controller/ # 控制器层(admin/app双端)
|
||||
├── convert/ # 转换层(对象映射)
|
||||
├── dal/ # 数据访问层(dataobject/mysql/redis)
|
||||
├── service/ # 业务逻辑层
|
||||
├── enums/ # 枚举定义
|
||||
├── framework/ # 模块框架配置
|
||||
├── job/ # 定时任务
|
||||
└── mq/ # 消息队列
|
||||
```
|
||||
|
||||
2. **设计模式应用**
|
||||
- 识别工厂模式、策略模式、模板方法等
|
||||
- 分析模式应用场景和位置
|
||||
- 说明模式选择的理由
|
||||
|
||||
3. **模块间通信**
|
||||
- API接口定义
|
||||
- 消息队列使用
|
||||
- 事件驱动设计
|
||||
|
||||
---
|
||||
|
||||
### 第三阶段:数据表设计提取
|
||||
|
||||
分析数据模型:
|
||||
|
||||
1. **实体继承体系**
|
||||
- BaseDO(基础字段:createTime, updateTime, creator, updater, deleted)
|
||||
- TenantBaseDO(多租户字段:tenantId)
|
||||
|
||||
2. **核心表结构**
|
||||
- 表名、说明、核心字段、关联关系
|
||||
- 主键策略、索引设计
|
||||
|
||||
3. **表关系**
|
||||
- 外键关系
|
||||
- 一对多/多对多关系
|
||||
|
||||
---
|
||||
|
||||
### 第四阶段:代码使用设计提取
|
||||
|
||||
提取代码规范和模式:
|
||||
|
||||
1. **Controller层规范**
|
||||
- @Tag、@Operation 注解
|
||||
- @PreAuthorize 权限控制
|
||||
- CommonResult 统一返回
|
||||
- PageResult 分页封装
|
||||
|
||||
2. **Service层规范**
|
||||
- 接口定义规范
|
||||
- 事务处理规范
|
||||
- 异常处理规范(ErrorCodeConstants)
|
||||
|
||||
3. **数据访问规范**
|
||||
- Mapper接口定义
|
||||
- 分页查询实现
|
||||
- 数据权限控制
|
||||
|
||||
---
|
||||
|
||||
### 第五阶段:扩展指南提取
|
||||
|
||||
总结如何基于此模块扩展:
|
||||
|
||||
1. **新增业务功能步骤**
|
||||
- 步骤化说明
|
||||
- 关键代码示例
|
||||
|
||||
2. **新增渠道/类型示例**
|
||||
- 具体实现步骤
|
||||
- 需要修改的文件
|
||||
|
||||
3. **最佳实践总结**
|
||||
- 开发注意事项
|
||||
- 常见问题解决
|
||||
|
||||
---
|
||||
|
||||
## 输出格式
|
||||
|
||||
生成两个文件:
|
||||
1. `skill-{module}.yaml` - 结构化Skill文档(使用模板格式)
|
||||
2. `skill-{module}.md` - 可读性文档(Markdown格式)
|
||||
|
||||
---
|
||||
|
||||
## 关键参考文件
|
||||
|
||||
| 文件类型 | 路径模式 |
|
||||
|---------|---------|
|
||||
| Controller | `yudao-module-{xxx}/.../controller/` |
|
||||
| Service | `yudao-module-{xxx}/.../service/` |
|
||||
| DO实体 | `yudao-module-{xxx}/.../dal/dataobject/` |
|
||||
| Mapper | `yudao-module-{xxx}/.../dal/mysql/` |
|
||||
| 枚举 | `yudao-module-{xxx}/.../enums/` |
|
||||
| 错误码 | `yudao-module-{xxx}/.../ErrorCodeConstants.java` |
|
||||
@@ -1,161 +0,0 @@
|
||||
# Skill 模板文件
|
||||
# 用于提取模块知识的标准格式
|
||||
|
||||
skill:
|
||||
id: "skill-{module}"
|
||||
name: "{模块名称} Skill"
|
||||
version: "1.0.0"
|
||||
module_path: "yudao-module-{module}"
|
||||
created_at: ""
|
||||
updated_at: ""
|
||||
|
||||
# ============================================
|
||||
# 第一阶段:设计理念
|
||||
# ============================================
|
||||
philosophy:
|
||||
# 业务定位:模块解决什么业务问题?在整个系统中的定位?
|
||||
business_position: ""
|
||||
|
||||
# 设计原则:遵循了哪些设计原则(SOLID、DDD)?有哪些架构决策?
|
||||
design_principles: []
|
||||
|
||||
# 领域模型:核心领域对象有哪些?领域对象之间的关系?聚合根是什么?
|
||||
domain_model:
|
||||
aggregates: []
|
||||
# - name: "聚合根名称"
|
||||
# type: "聚合根"
|
||||
# entities: ["关联实体"]
|
||||
|
||||
value_objects: []
|
||||
services: []
|
||||
|
||||
# ============================================
|
||||
# 第二阶段:架构设计
|
||||
# ============================================
|
||||
architecture:
|
||||
# 分层架构
|
||||
layers:
|
||||
- name: "api"
|
||||
purpose: "模块间API接口"
|
||||
components: []
|
||||
|
||||
- name: "controller"
|
||||
purpose: "HTTP接口"
|
||||
components: []
|
||||
|
||||
- name: "service"
|
||||
purpose: "业务逻辑"
|
||||
components: []
|
||||
|
||||
- name: "dal"
|
||||
purpose: "数据访问"
|
||||
components: []
|
||||
|
||||
# 设计模式应用
|
||||
design_patterns: []
|
||||
# - pattern: "Factory"
|
||||
# location: "具体文件路径"
|
||||
# purpose: "模式用途说明"
|
||||
|
||||
# 模块间通信
|
||||
communication:
|
||||
apis: [] # 对外暴露的API
|
||||
consumers: [] # 消费的其他模块API
|
||||
mq: [] # 消息队列使用
|
||||
|
||||
# ============================================
|
||||
# 第三阶段:数据表设计
|
||||
# ============================================
|
||||
data_model:
|
||||
# 实体继承体系
|
||||
entity_hierarchy:
|
||||
base: "BaseDO | TenantBaseDO"
|
||||
description: ""
|
||||
|
||||
# 核心数据表
|
||||
tables: []
|
||||
# - name: "表名"
|
||||
# comment: "表说明"
|
||||
# entity: "DO类名"
|
||||
# extends: "BaseDO/TenantBaseDO"
|
||||
# columns:
|
||||
# - { name: "字段名", type: "类型", comment: "说明" }
|
||||
# indexes:
|
||||
# - { name: "索引名", columns: ["字段"] }
|
||||
|
||||
# 表关系(ER关系)
|
||||
relationships: []
|
||||
# - from: "表A"
|
||||
# to: "表B"
|
||||
# type: "1:N | N:1 | N:N"
|
||||
# foreign_key: "外键字段"
|
||||
|
||||
# ============================================
|
||||
# 第四阶段:代码使用设计
|
||||
# ============================================
|
||||
code_patterns:
|
||||
# Controller层规范
|
||||
controller:
|
||||
annotations: [] # 常用注解
|
||||
example: ""
|
||||
|
||||
# Service层规范
|
||||
service:
|
||||
interface_pattern: ""
|
||||
impl_pattern: ""
|
||||
example: ""
|
||||
|
||||
# 数据访问规范
|
||||
dal:
|
||||
mapper_pattern: ""
|
||||
example: ""
|
||||
|
||||
# 异常处理
|
||||
error_handling:
|
||||
code_prefix: ""
|
||||
examples: []
|
||||
|
||||
# 统一响应
|
||||
response:
|
||||
success_pattern: ""
|
||||
error_pattern: ""
|
||||
|
||||
# ============================================
|
||||
# 第五阶段:扩展指南
|
||||
# ============================================
|
||||
extension_guide:
|
||||
# 新增功能步骤
|
||||
new_feature:
|
||||
title: "新增业务功能"
|
||||
steps: []
|
||||
|
||||
# 新增渠道/类型示例
|
||||
new_channel:
|
||||
title: "新增渠道/类型"
|
||||
steps: []
|
||||
|
||||
# 最佳实践
|
||||
best_practices: []
|
||||
|
||||
# ============================================
|
||||
# 依赖关系
|
||||
# ============================================
|
||||
dependencies:
|
||||
# 内部依赖(其他模块)
|
||||
internal: []
|
||||
# - module: "模块名"
|
||||
# api: "API接口名"
|
||||
# purpose: "依赖用途"
|
||||
|
||||
# 外部依赖(第三方库)
|
||||
external: []
|
||||
# - name: "库名"
|
||||
# version: "版本"
|
||||
# purpose: "用途"
|
||||
|
||||
# ============================================
|
||||
# 关键文件清单
|
||||
# ============================================
|
||||
key_files:
|
||||
- path: ""
|
||||
purpose: ""
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,363 +0,0 @@
|
||||
# 芋道源码 Skills 使用指南
|
||||
|
||||
> 快速查找适合你开发场景的提示词文档,让 AI 助手更精准地理解你的需求。
|
||||
|
||||
---
|
||||
|
||||
## 自动引用机制说明
|
||||
|
||||
**从 2026-03-31 起,使用样例支持自动引用设计规范**:
|
||||
|
||||
每个使用样例文档头部包含 YAML front matter 配置,声明该场景需要引用的规范文件:
|
||||
|
||||
```yaml
|
||||
---
|
||||
references:
|
||||
design: # 设计规范(全局约束)
|
||||
- skills/design/db-designer.yaml
|
||||
- skills/design/entity-designer.yaml
|
||||
module_guide: # 模块引用提示
|
||||
prompt: "请指定目标模块"
|
||||
mapping:
|
||||
mes: skills/modules/mes/skill-mes.yaml
|
||||
patterns: # 设计模式引用
|
||||
- skills/patterns/factory-pattern.yaml
|
||||
---
|
||||
```
|
||||
|
||||
**使用方式**:
|
||||
1. AI 读取使用样例时自动识别 `references` 配置
|
||||
2. AI 自动加载引用的设计规范文件内容
|
||||
3. 用户在提示词中指定模块名后,AI 加载对应模块 skill
|
||||
4. 生成的代码符合所有规范定义的标准
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [使用指南概述](#使用指南概述)
|
||||
2. [场景分类](#场景分类)
|
||||
3. [使用流程](#使用流程)
|
||||
4. [FAQ](#faq)
|
||||
|
||||
---
|
||||
|
||||
## 使用指南概述
|
||||
|
||||
本目录提供芋道源码项目的 AI 辅助开发使用场景指南。每个场景文档都包含:
|
||||
|
||||
- **适用情况**:什么场景下使用该提示词
|
||||
- **输入要求**:需要提供哪些信息
|
||||
- **预期输出**:AI 将生成什么样的代码或文档
|
||||
- **使用示例**:实际案例演示
|
||||
|
||||
### Skills 资源概览
|
||||
|
||||
| 资源类型 | 目录位置 | 说明 |
|
||||
|---------|---------|------|
|
||||
| 模块 Skills | `skills/modules/` | 各功能模块的完整技术规范 |
|
||||
| 设计模式 | `skills/patterns/` | 常用设计模式的实现模板 |
|
||||
| 文档模板 | `skills/templates/` | Skill 文档编写模板 |
|
||||
|
||||
---
|
||||
|
||||
## 场景分类
|
||||
|
||||
### 一、快速上手
|
||||
|
||||
适用于初次接触项目或快速了解某个模块的开发者。
|
||||
|
||||
| 场景 | 适用情况 | 文档链接 | 一句话描述 |
|
||||
|-----|---------|---------|-----------|
|
||||
| 了解项目结构 | 刚接触项目,需要快速了解整体架构 | `skills/index.yaml` | 查看 12 个核心模块的技术规范索引 |
|
||||
| 系统模块入门 | 需要了解用户、角色、权限等基础功能 | [skill-system.yaml](../modules/system/skill-system.yaml) | 系统管理模块完整开发指南 |
|
||||
| 基础设施入门 | 需要了解文件、配置、任务等基础服务 | [skill-infra.yaml](../modules/infra/skill-infra.yaml) | 基础设施模块完整开发指南 |
|
||||
| **实体类实现** | 需要从 SQL 到实体类创建完整 CRUD 功能 | [entity-implementation.md](entity-implementation.md) | 从建表到完整接口的端到端指南 |
|
||||
|
||||
**推荐使用流程**:
|
||||
|
||||
```
|
||||
阅读 index.yaml --> 选择目标模块 --> 阅读对应 skill-*.yaml --> 开始开发
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 二、扩展模块
|
||||
|
||||
适用于在现有模块基础上添加新功能的场景。
|
||||
|
||||
| 场景 | 适用情况 | 文档链接 | 一句话描述 |
|
||||
|-----|---------|---------|-----------|
|
||||
| 扩展系统模块 | 添加新的系统管理功能 | [skill-system.yaml](../modules/system/skill-system.yaml) | 遵循系统模块规范扩展功能 |
|
||||
| 扩展支付模块 | 添加新的支付渠道或支付方式 | [skill-pay.yaml](../modules/pay/skill-pay.yaml) | 遵循支付模块规范扩展功能 |
|
||||
| 扩展会员模块 | 添加会员等级、积分等功能 | [skill-member.yaml](../modules/member/skill-member.yaml) | 遵循会员模块规范扩展功能 |
|
||||
| 扩展商城模块 | 添加商品、订单相关功能 | [skill-mall.yaml](../modules/mall/skill-mall.yaml) | 遵循商城模块规范扩展功能 |
|
||||
| 扩展 CRM 模块 | 添加客户管理相关功能 | [skill-crm.yaml](../modules/crm/skill-crm.yaml) | 遵循 CRM 模块规范扩展功能 |
|
||||
| 扩展工作流模块 | 添加流程审批相关功能 | [skill-bpm.yaml](../modules/bpm/skill-bpm.yaml) | 遵循工作流模块规范扩展功能 |
|
||||
|
||||
**扩展开发要点**:
|
||||
|
||||
1. 阅读目标模块的 skill 文档
|
||||
2. 理解现有代码结构和命名规范
|
||||
3. 参考文档中的代码示例
|
||||
4. 遵循文档中的开发约束
|
||||
|
||||
---
|
||||
|
||||
### 三、改造模块
|
||||
|
||||
适用于修改现有功能或重构代码的场景。
|
||||
|
||||
| 场景 | 适用情况 | 文档链接 | 一句话描述 |
|
||||
|-----|---------|---------|-----------|
|
||||
| 改造系统功能 | 修改用户、角色、菜单等核心功能 | [skill-system.yaml](../modules/system/skill-system.yaml) | 系统模块改造技术规范 |
|
||||
| 改造支付流程 | 修改支付、退款等核心流程 | [skill-pay.yaml](../modules/pay/skill-pay.yaml) | 支付模块改造技术规范 |
|
||||
| 改造订单流程 | 修改订单创建、支付、发货流程 | [skill-mall.yaml](../modules/mall/skill-mall.yaml) | 商城模块改造技术规范 |
|
||||
| 改造审批流程 | 修改工作流定义和审批逻辑 | [skill-bpm.yaml](../modules/bpm/skill-bpm.yaml) | 工作流模块改造技术规范 |
|
||||
|
||||
**改造开发要点**:
|
||||
|
||||
1. 重点关注文档中的"核心流程"部分
|
||||
2. 了解模块间的依赖关系
|
||||
3. 注意数据库字段和索引的影响
|
||||
4. 遵循向后兼容原则
|
||||
|
||||
---
|
||||
|
||||
### 四、新增模块
|
||||
|
||||
适用于创建全新业务模块的场景。
|
||||
|
||||
| 场景 | 适用情况 | 文档链接 | 一句话描述 |
|
||||
|-----|---------|---------|-----------|
|
||||
| 新增业务模块 | 创建全新的业务功能模块 | [skill-template.yaml](../templates/skill-template.yaml) | Skill 文档编写模板 |
|
||||
| 应用设计模式 | 需要使用设计模式优化代码 | [patterns/](../patterns/) | 设计模式实现模板 |
|
||||
|
||||
**新增模块参考**:
|
||||
|
||||
参考现有模块的 skill 文档结构:
|
||||
|
||||
- 核心模块参考:`skill-system.yaml`、`skill-infra.yaml`
|
||||
- 业务模块参考:`skill-mall.yaml`、`skill-crm.yaml`
|
||||
- 技术模块参考:`skill-bpm.yaml`、`skill-ai.yaml`
|
||||
|
||||
**新增模块步骤**:
|
||||
|
||||
1. 使用 `skill-template.yaml` 创建新模块文档
|
||||
2. 参考 `extraction-prompt.md` 提取技术规范
|
||||
3. 按照模块规范编写代码
|
||||
4. 更新 `skills/index.yaml` 索引
|
||||
|
||||
---
|
||||
|
||||
### 五、设计模式应用
|
||||
|
||||
适用于需要使用设计模式优化代码结构的场景。
|
||||
|
||||
| 模式 | 适用情况 | 文档链接 | 一句话描述 |
|
||||
|-----|---------|---------|-----------|
|
||||
| 工厂模式 | 需要根据条件创建不同类型的对象 | [factory-pattern.yaml](../patterns/factory-pattern.yaml) | 对象创建的统一管理 |
|
||||
| 策略模式 | 需要在多种算法或策略间切换 | [strategy-pattern.yaml](../patterns/strategy-pattern.yaml) | 算法族的统一接口 |
|
||||
| 模板方法模式 | 需要定义算法骨架,子类实现细节 | [template-method-pattern.yaml](../patterns/template-method-pattern.yaml) | 算法骨架的固定结构 |
|
||||
|
||||
**设计模式选择指南**:
|
||||
|
||||
```
|
||||
是否需要创建对象? --> 多种类型 --> 工厂模式
|
||||
|
|
||||
v
|
||||
是否需要切换算法? --> 多种策略 --> 策略模式
|
||||
|
|
||||
v
|
||||
是否需要固定流程? --> 流程相同,细节不同 --> 模板方法模式
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 整体流程图
|
||||
|
||||
```
|
||||
+-------------------+
|
||||
| 明确开发需求 |
|
||||
+--------+----------+
|
||||
|
|
||||
+--------------+--------------+
|
||||
| |
|
||||
v v
|
||||
+-------+-------+ +--------+--------+
|
||||
| 新增功能? | | 改造现有功能? |
|
||||
+-------+-------+ +--------+--------+
|
||||
| |
|
||||
+-------+-------+ |
|
||||
| +-------+-------+
|
||||
v | v
|
||||
+-----+-----+ +-----+-----+ +-----+-----+
|
||||
| 新增模块 | | 扩展现有 | | 改造模块 |
|
||||
+-----+-----+ | 模块 | +-----+-----+
|
||||
| +-----+-----+ |
|
||||
v | v
|
||||
+-----+-----+ v +-----+-----+
|
||||
| 使用模板 | +-----+-----+ | 查看对应 |
|
||||
| 创建文档 | | 查看对应 | | 模块文档 |
|
||||
+-----+-----+ | 模块文档 | +-----+-----+
|
||||
| +-----+-----+ |
|
||||
v | v
|
||||
+-----+-----+ v +-----+-----+
|
||||
| 参考现有 | +-----+-----+ | 关注核心 |
|
||||
| 模块规范 | | 遵循规范 | | 流程部分 |
|
||||
+-----+-----+ | 开发 | +-----+-----+
|
||||
| +-----+-----+ |
|
||||
+-----------+-----------------+---------------+
|
||||
|
|
||||
v
|
||||
+-------+-------+
|
||||
| 开发完成 |
|
||||
+---------------+
|
||||
```
|
||||
|
||||
### 快速查找流程
|
||||
|
||||
1. **确定场景类型**:根据上方的场景分类确定你的开发场景
|
||||
2. **查找对应文档**:在场景表格中找到对应的文档链接
|
||||
3. **阅读技术规范**:打开对应的 skill 文档,了解技术要求
|
||||
4. **开始开发**:按照文档规范进行开发
|
||||
|
||||
### 模块优先级参考
|
||||
|
||||
根据 `skills/index.yaml` 中定义的优先级:
|
||||
|
||||
| 优先级 | 模块 | 说明 |
|
||||
|-------|------|------|
|
||||
| P1 (核心) | system, infra, pay | 系统核心功能,建议优先熟悉 |
|
||||
| P2 (业务) | member, mall, crm, bpm | 主要业务功能,使用频率高 |
|
||||
| P3 (扩展) | erp, ai, iot, mp, report | 扩展业务功能,按需使用 |
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q1: 如何选择正确的 skill 文档?
|
||||
|
||||
**A:** 根据你的开发任务类型选择:
|
||||
|
||||
- 快速上手:从 `skill-system.yaml` 或 `skill-infra.yaml` 开始
|
||||
- 扩展功能:选择对应业务模块的 skill 文档
|
||||
- 改造功能:重点阅读对应模块的"核心流程"部分
|
||||
- 新增模块:使用 `skill-template.yaml` 作为模板
|
||||
|
||||
### Q2: skill 文档包含哪些内容?
|
||||
|
||||
**A:** 标准的 skill 文档包含以下部分:
|
||||
|
||||
1. **模块概述**:模块功能和技术栈说明
|
||||
2. **目录结构**:模块的代码组织结构
|
||||
3. **核心概念**:模块涉及的核心概念和术语
|
||||
4. **技术规范**:命名规范、代码风格、注解使用等
|
||||
5. **核心流程**:关键业务流程的实现方式
|
||||
6. **数据模型**:数据库表结构和实体关系
|
||||
7. **API 规范**:接口设计和参数说明
|
||||
8. **代码示例**:常见开发场景的代码参考
|
||||
9. **注意事项**:开发中需要注意的问题
|
||||
|
||||
### Q3: 如何使用设计模式文档?
|
||||
|
||||
**A:** 设计模式文档提供:
|
||||
|
||||
1. 模式的概念和适用场景
|
||||
2. 项目中的实际应用案例
|
||||
3. 代码实现模板
|
||||
4. 使用注意事项
|
||||
|
||||
当你需要优化代码结构或解决特定设计问题时,参考对应的设计模式文档。
|
||||
|
||||
### Q4: 如何创建新的 skill 文档?
|
||||
|
||||
**A:** 创建新模块的 skill 文档步骤:
|
||||
|
||||
1. 复制 `skills/templates/skill-template.yaml` 作为起点
|
||||
2. 参考 `skills/templates/extraction-prompt.md` 了解提取方法
|
||||
3. 参考现有模块的 skill 文档作为示例
|
||||
4. 按照模板结构填写模块信息
|
||||
5. 更新 `skills/index.yaml` 添加新模块索引
|
||||
|
||||
### Q5: 如何理解模块之间的依赖关系?
|
||||
|
||||
**A:** 模块依赖关系:
|
||||
|
||||
```
|
||||
system (系统管理) <-- 几乎所有模块都依赖
|
||||
|
|
||||
+-- member (会员) <-- mall, crm
|
||||
|
|
||||
+-- infra (基础设施) <-- 所有模块
|
||||
|
|
||||
+-- pay (支付) <-- mall, member
|
||||
```
|
||||
|
||||
开发时注意:
|
||||
|
||||
- system 模块提供用户、权限等基础能力
|
||||
- infra 模块提供文件、配置、任务等基础服务
|
||||
- 业务模块之间可能存在依赖关系
|
||||
|
||||
### Q6: 如何快速找到某个功能的实现位置?
|
||||
|
||||
**A:** 使用 skill 文档的"目录结构"和"核心流程"部分:
|
||||
|
||||
1. 在 skill 文档中查找对应功能的目录位置
|
||||
2. 阅读"核心流程"了解实现逻辑
|
||||
3. 参考"代码示例"找到具体实现
|
||||
|
||||
### Q7: 文档中的代码示例可以直接使用吗?
|
||||
|
||||
**A:** 可以,但需要注意:
|
||||
|
||||
1. 代码示例遵循项目规范,可作为模板
|
||||
2. 使用前需要根据实际业务需求调整
|
||||
3. 注意替换示例中的占位符和示例数据
|
||||
4. 建议先在测试环境验证
|
||||
|
||||
---
|
||||
|
||||
## 附录
|
||||
|
||||
### 模块文档索引
|
||||
|
||||
| 模块 | 文档路径 | 行数 | 状态 |
|
||||
|-----|---------|------|------|
|
||||
| 系统管理 | [skill-system.yaml](../modules/system/skill-system.yaml) | 839 | 已完成 |
|
||||
| 基础设施 | [skill-infra.yaml](../modules/infra/skill-infra.yaml) | 700+ | 已完成 |
|
||||
| 支付模块 | [skill-pay.yaml](../modules/pay/skill-pay.yaml) | 654 | 已完成 |
|
||||
| 会员模块 | [skill-member.yaml](../modules/member/skill-member.yaml) | 608 | 已完成 |
|
||||
| 商城模块 | [skill-mall.yaml](../modules/mall/skill-mall.yaml) | 650+ | 已完成 |
|
||||
| CRM 模块 | [skill-crm.yaml](../modules/crm/skill-crm.yaml) | 850+ | 已完成 |
|
||||
| ERP 模块 | [skill-erp.yaml](../modules/erp/skill-erp.yaml) | 900+ | 已完成 |
|
||||
| 工作流模块 | [skill-bpm.yaml](../modules/bpm/skill-bpm.yaml) | 650+ | 已完成 |
|
||||
| AI 模块 | [skill-ai.yaml](../modules/ai/skill-ai.yaml) | 670+ | 已完成 |
|
||||
| 物联网模块 | [skill-iot.yaml](../modules/iot/skill-iot.yaml) | 721 | 已完成 |
|
||||
| 公众号模块 | [skill-mp.yaml](../modules/mp/skill-mp.yaml) | 660+ | 已完成 |
|
||||
| 报表模块 | [skill-report.yaml](../modules/report/skill-report.yaml) | 320+ | 已完成 |
|
||||
|
||||
### 使用指南文档索引
|
||||
|
||||
| 文档 | 适用场景 | 一句话描述 |
|
||||
|-----|---------|-----------|
|
||||
| [quick-start.md](quick-start.md) | 快速上手 | Skill 文档快速使用指南和常用提示词模板 |
|
||||
| [entity-implementation.md](entity-implementation.md) | 实体类开发 | 从 SQL 建表到完整 CRUD 接口的端到端指南 |
|
||||
| [extend-module.md](extend-module.md) | 扩展模块 | 在现有模块基础上添加新功能 |
|
||||
| [new-module.md](new-module.md) | 新增模块 | 创建全新的业务模块 |
|
||||
| [refactor-module.md](refactor-module.md) | 改造模块 | 修改现有功能或重构代码 |
|
||||
| [pattern-usage.md](pattern-usage.md) | 设计模式 | 常用设计模式的使用场景和实现方法 |
|
||||
|
||||
### 更新记录
|
||||
|
||||
| 日期 | 版本 | 说明 |
|
||||
|-----|------|------|
|
||||
| 2026-03-25 | v1.1 | 新增实体类实现完整流程指南 |
|
||||
| 2026-03-18 | v1.0 | 初始版本,创建使用指南索引 |
|
||||
|
||||
---
|
||||
|
||||
> 提示:本文档会随着项目发展持续更新。如有问题或建议,请提交 Issue 或 Pull Request。
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,594 +0,0 @@
|
||||
# Skill 快速上手指南
|
||||
|
||||
> 本文档提供快速使用 Skill 文档的提示词模板,帮助开发者快速定位代码、理解业务、完成日常开发任务。
|
||||
|
||||
---
|
||||
|
||||
## 一、Skill 快速使用指南
|
||||
|
||||
### 什么是 Skill 文档
|
||||
|
||||
Skill 文档是从 ruoyi-vue-pro 项目代码中提取的结构化知识库,包含:
|
||||
- **设计理念**:业务定位、设计原则、领域模型
|
||||
- **架构设计**:分层架构、设计模式、模块通信
|
||||
- **数据表设计**:实体关系、表结构、字段说明
|
||||
- **代码规范**:Controller/Service/DAL 层代码模式
|
||||
- **扩展指南**:新增功能步骤、最佳实践
|
||||
|
||||
### 如何使用 Skill 文档
|
||||
|
||||
```
|
||||
使用 Skill 文档,帮我 [任务描述]
|
||||
|
||||
参考 Skill 文档:
|
||||
- skills/modules/[模块名]/skill-[模块名].yaml
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
使用 Skill 文档,帮我在 system 模块添加一个"操作日志"功能。
|
||||
参考 skills/modules/system/skill-system.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、一分钟提示词模板
|
||||
|
||||
### 基础模板
|
||||
|
||||
```
|
||||
参考 Skill 文档 skills/modules/[模块]/skill-[模块].yaml,帮我完成以下任务:
|
||||
|
||||
[具体任务描述]
|
||||
|
||||
要求:
|
||||
1. 遵循项目现有的代码规范
|
||||
2. 使用统一响应格式 CommonResult
|
||||
3. 添加必要的权限控制
|
||||
```
|
||||
|
||||
### 快速填充指南
|
||||
|
||||
| 场景 | 模块路径 | 模块名 |
|
||||
|------|---------|--------|
|
||||
| 用户/角色/权限/菜单 | yudao-module-system | system |
|
||||
| 文件/配置/任务/日志 | yudao-module-infra | infra |
|
||||
| 支付/退款/钱包 | yudao-module-pay | pay |
|
||||
| 会员/积分/等级/签到 | yudao-module-member | member |
|
||||
| 商品/订单/促销 | yudao-module-mall | mall |
|
||||
| 客户/线索/合同 | yudao-module-crm | crm |
|
||||
| 采购/销售/库存 | yudao-module-erp | erp |
|
||||
| 工作流/审批 | yudao-module-bpm | bpm |
|
||||
| AI/大模型 | yudao-module-ai | ai |
|
||||
| 物联网设备 | yudao-module-iot | iot |
|
||||
| 微信公众号 | yudao-module-mp | mp |
|
||||
| 报表 | yudao-module-report | report |
|
||||
|
||||
---
|
||||
|
||||
## 三、常见场景快速提示词
|
||||
|
||||
### 3.1 查找代码位置
|
||||
|
||||
```
|
||||
参考 Skill 文档,帮我找到 [功能] 相关的代码位置:
|
||||
- Controller 入口
|
||||
- Service 实现
|
||||
- Mapper 接口
|
||||
- 数据表定义
|
||||
|
||||
模块:[模块名]
|
||||
功能关键词:[关键词]
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
参考 skills/modules/system/skill-system.yaml,帮我找到用户登录相关的代码位置:
|
||||
- Controller 入口
|
||||
- Service 实现
|
||||
- 认证逻辑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 理解业务逻辑
|
||||
|
||||
```
|
||||
参考 Skill 文档 skills/modules/[模块]/skill-[模块].yaml,帮我理解 [业务场景] 的完整流程:
|
||||
|
||||
1. 业务入口在哪里?
|
||||
2. 核心业务逻辑在哪个 Service?
|
||||
3. 涉及哪些数据表?
|
||||
4. 状态流转是怎样的?
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
参考 skills/modules/mall/skill-mall.yaml,帮我理解订单支付的完整流程:
|
||||
1. 支付入口在哪里?
|
||||
2. 核心支付逻辑在哪个 Service?
|
||||
3. 支付回调如何处理?
|
||||
4. 订单状态如何流转?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 修复 Bug
|
||||
|
||||
```
|
||||
参考 Skill 文档 skills/modules/[模块]/skill-[模块].yaml,帮我分析和修复以下问题:
|
||||
|
||||
问题描述:[Bug 描述]
|
||||
错误信息:[错误日志或异常信息]
|
||||
复现步骤:[如何复现]
|
||||
|
||||
请帮我:
|
||||
1. 定位问题代码位置
|
||||
2. 分析问题原因
|
||||
3. 提供修复方案
|
||||
4. 说明需要修改的文件
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
参考 skills/modules/pay/skill-pay.yaml,帮我分析和修复以下问题:
|
||||
|
||||
问题描述:支付回调后订单状态未更新
|
||||
错误信息:无异常,但订单一直是待支付状态
|
||||
|
||||
请帮我:
|
||||
1. 定位回调处理代码
|
||||
2. 分析状态更新逻辑
|
||||
3. 检查事务是否正确
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 添加字段
|
||||
|
||||
```
|
||||
参考 Skill 文档 skills/modules/[模块]/skill-[模块].yaml,帮我在 [实体] 中添加新字段:
|
||||
|
||||
字段名:[字段名]
|
||||
字段类型:[类型]
|
||||
字段用途:[用途说明]
|
||||
|
||||
需要修改:
|
||||
1. DO 实体类
|
||||
2. 数据库表(提供 SQL)
|
||||
3. VO 类(请求/响应)
|
||||
4. Controller(如有需要)
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
参考 skills/modules/member/skill-member.yaml,帮我在会员用户中添加新字段:
|
||||
|
||||
字段名:vipExpireTime
|
||||
字段类型:LocalDateTime
|
||||
字段用途:VIP 会员过期时间
|
||||
|
||||
需要修改:
|
||||
1. MemberUserDO
|
||||
2. member_user 表
|
||||
3. 相关 VO 类
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 添加接口
|
||||
|
||||
```
|
||||
参考 Skill 文档 skills/modules/[模块]/skill-[模块].yaml,帮我添加一个新接口:
|
||||
|
||||
接口名称:[接口名]
|
||||
接口路径:[HTTP 方法和路径]
|
||||
功能描述:[功能说明]
|
||||
请求参数:[参数列表]
|
||||
响应数据:[响应结构]
|
||||
权限标识:[权限码]
|
||||
|
||||
请按照项目规范生成:
|
||||
1. Controller 方法
|
||||
2. Service 接口和实现
|
||||
3. VO 类
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
参考 skills/modules/system/skill-system.yaml,帮我添加一个新接口:
|
||||
|
||||
接口名称:获取当前用户信息
|
||||
接口路径:GET /system/user/current
|
||||
功能描述:获取当前登录用户的详细信息
|
||||
权限标识:system:user:query
|
||||
|
||||
请按照项目规范生成完整代码。
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.6 添加权限
|
||||
|
||||
```
|
||||
参考 Skill 文档 skills/modules/system/skill-system.yaml,帮我添加一个新的权限:
|
||||
|
||||
权限名称:[权限名]
|
||||
权限标识:[权限码,格式:模块:功能:操作]
|
||||
父菜单ID:[父菜单 ID]
|
||||
权限类型:[菜单/按钮]
|
||||
|
||||
请提供:
|
||||
1. 菜单/按钮的 SQL 插入语句
|
||||
2. Controller 中的 @PreAuthorize 注解示例
|
||||
```
|
||||
|
||||
**示例**:
|
||||
```
|
||||
参考 skills/modules/system/skill-system.yaml,帮我添加一个新的权限:
|
||||
|
||||
权限名称:导出用户
|
||||
权限标识:system:user:export
|
||||
父菜单ID:用户管理菜单 ID
|
||||
权限类型:按钮
|
||||
|
||||
请提供:
|
||||
1. 按钮的 SQL 插入语句
|
||||
2. Controller 中的权限注解示例
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、Skill 文档快速导航
|
||||
|
||||
### 4.1 模块索引
|
||||
|
||||
| 模块 | 文档路径 | 核心功能 | 关键实体 |
|
||||
|------|---------|---------|---------|
|
||||
| **system** | skills/modules/system/skill-system.yaml | 用户、角色、权限、菜单、租户、字典 | AdminUserDO, RoleDO, MenuDO |
|
||||
| **infra** | skills/modules/infra/skill-infra.yaml | 文件、配置、任务、日志、代码生成 | FileDO, JobDO, ConfigDO |
|
||||
| **pay** | skills/modules/pay/skill-pay.yaml | 支付、退款、钱包、转账 | PayOrderDO, PayRefundDO, PayWalletDO |
|
||||
| **member** | skills/modules/member/skill-member.yaml | 会员、积分、等级、签到、地址 | MemberUserDO, MemberLevelDO |
|
||||
| **mall** | skills/modules/mall/skill-mall.yaml | 商品、订单、促销、统计 | ProductSpuDO, TradeOrderDO, CouponDO |
|
||||
| **crm** | skills/modules/crm/skill-crm.yaml | 线索、客户、商机、合同、回款 | CrmCustomerDO, CrmContractDO |
|
||||
| **erp** | skills/modules/erp/skill-erp.yaml | 采购、销售、库存、财务 | ErpPurchaseDO, ErpSaleDO |
|
||||
| **bpm** | skills/modules/bpm/skill-bpm.yaml | 流程定义、流程实例、任务 | BpmProcessDefinitionDO |
|
||||
| **ai** | skills/modules/ai/skill-ai.yaml | AI 模型、对话、绘图 | AiChatMessageDO, AiImageDO |
|
||||
| **iot** | skills/modules/iot/skill-iot.yaml | 设备、产品、物模型 | IotDeviceDO, IotProductDO |
|
||||
| **mp** | skills/modules/mp/skill-mp.yaml | 公众号、菜单、消息 | MpAccountDO, MpMessageDO |
|
||||
| **report** | skills/modules/report/skill-report.yaml | 报表、数据源、图表 | ReportDataSourceDO |
|
||||
|
||||
### 4.2 错误码前缀速查
|
||||
|
||||
| 模块 | 错误码前缀 | 示例 |
|
||||
|------|-----------|------|
|
||||
| system | 1_002_XXX_XXX | 1_002_000_000 登录失败 |
|
||||
| infra | 1_001_XXX_XXX | 1_001_000_000 文件不存在 |
|
||||
| pay | 1_007_XXX_XXX | 1_007_002_000 支付订单不存在 |
|
||||
| member | 1_004_XXX_XXX | 1_004_001_000 用户不存在 |
|
||||
| product | 1_008_XXX_XXX | 1_008_005_000 商品不存在 |
|
||||
| trade | 1_011_XXX_XXX | 1_011_000_011 订单不存在 |
|
||||
| promotion | 1_013_XXX_XXX | 1_013_004_000 优惠券模板不存在 |
|
||||
|
||||
### 4.3 权限标识规范
|
||||
|
||||
```
|
||||
格式:模块:功能:操作
|
||||
|
||||
示例:
|
||||
- system:user:query # 查询用户
|
||||
- system:user:create # 创建用户
|
||||
- system:user:update # 更新用户
|
||||
- system:user:delete # 删除用户
|
||||
- system:user:export # 导出用户
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、常用代码片段
|
||||
|
||||
### 5.1 Controller 层
|
||||
|
||||
```java
|
||||
@Tag(name = "管理后台 - [功能名]")
|
||||
@RestController
|
||||
@RequestMapping("/[模块]/[功能]")
|
||||
@Validated
|
||||
public class XxxController {
|
||||
|
||||
@Resource
|
||||
private XxxService xxxService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建[功能]")
|
||||
@PreAuthorize("@ss.hasPermission('[模块]:[功能]:create')")
|
||||
public CommonResult<Long> createXxx(@Valid @RequestBody XxxSaveReqVO createReqVO) {
|
||||
return success(xxxService.createXxx(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新[功能]")
|
||||
@PreAuthorize("@ss.hasPermission('[模块]:[功能]:update')")
|
||||
public CommonResult<Boolean> updateXxx(@Valid @RequestBody XxxSaveReqVO updateReqVO) {
|
||||
xxxService.updateXxx(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除[功能]")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('[模块]:[功能]:delete')")
|
||||
public CommonResult<Boolean> deleteXxx(@RequestParam("id") Long id) {
|
||||
xxxService.deleteXxx(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得[功能]")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('[模块]:[功能]:query')")
|
||||
public CommonResult<XxxRespVO> getXxx(@RequestParam("id") Long id) {
|
||||
XxxDO xxx = xxxService.getXxx(id);
|
||||
return success(BeanUtils.toBean(xxx, XxxRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得[功能]分页")
|
||||
@PreAuthorize("@ss.hasPermission('[模块]:[功能]:query')")
|
||||
public CommonResult<PageResult<XxxRespVO>> getXxxPage(@Valid XxxPageReqVO pageReqVO) {
|
||||
PageResult<XxxDO> pageResult = xxxService.getXxxPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, XxxRespVO.class));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 Service 层
|
||||
|
||||
```java
|
||||
// 接口
|
||||
public interface XxxService {
|
||||
Long createXxx(@Valid XxxSaveReqVO createReqVO);
|
||||
void updateXxx(@Valid XxxSaveReqVO updateReqVO);
|
||||
void deleteXxx(Long id);
|
||||
XxxDO getXxx(Long id);
|
||||
PageResult<XxxDO> getXxxPage(XxxPageReqVO pageReqVO);
|
||||
}
|
||||
|
||||
// 实现
|
||||
@Service
|
||||
@Validated
|
||||
public class XxxServiceImpl implements XxxService {
|
||||
|
||||
@Resource
|
||||
private XxxMapper xxxMapper;
|
||||
|
||||
@Override
|
||||
public Long createXxx(XxxSaveReqVO createReqVO) {
|
||||
XxxDO xxx = BeanUtils.toBean(createReqVO, XxxDO.class);
|
||||
xxxMapper.insert(xxx);
|
||||
return xxx.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateXxx(XxxSaveReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateXxxExists(updateReqVO.getId());
|
||||
// 更新
|
||||
XxxDO updateObj = BeanUtils.toBean(updateReqVO, XxxDO.class);
|
||||
xxxMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteXxx(Long id) {
|
||||
// 校验存在
|
||||
validateXxxExists(id);
|
||||
// 删除
|
||||
xxxMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void validateXxxExists(Long id) {
|
||||
if (xxxMapper.selectById(id) == null) {
|
||||
throw exception(XXX_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XxxDO getXxx(Long id) {
|
||||
return xxxMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<XxxDO> getXxxPage(XxxPageReqVO pageReqVO) {
|
||||
return xxxMapper.selectPage(pageReqVO);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Mapper 层
|
||||
|
||||
```java
|
||||
@Mapper
|
||||
public interface XxxMapper extends BaseMapperX<XxxDO> {
|
||||
|
||||
default PageResult<XxxDO> selectPage(XxxPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<XxxDO>()
|
||||
.likeIfPresent(XxxDO::getName, reqVO.getName())
|
||||
.eqIfPresent(XxxDO::getStatus, reqVO.getStatus())
|
||||
.betweenIfPresent(XxxDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByDesc(XxxDO::getId));
|
||||
}
|
||||
|
||||
default List<XxxDO> selectListByStatus(Integer status) {
|
||||
return selectList(XxxDO::getStatus, status);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 DO 实体类
|
||||
|
||||
```java
|
||||
@TableName("[表名]")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class XxxDO extends TenantBaseDO { // 或 BaseDO
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
### 5.5 VO 类
|
||||
|
||||
```java
|
||||
// 请求 VO(新增/修改共用)
|
||||
@Data
|
||||
public class XxxSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", example = "1")
|
||||
private Long id; // 更新时必填
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试")
|
||||
@NotBlank(message = "名称不能为空")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status;
|
||||
}
|
||||
|
||||
// 分页请求 VO
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class XxxPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "名称", example = "测试")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
|
||||
// 响应 VO
|
||||
@Data
|
||||
public class XxxRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
```
|
||||
|
||||
### 5.6 错误码定义
|
||||
|
||||
```java
|
||||
// 在 ErrorCodeConstants.java 中添加
|
||||
ErrorCode XXX_NOT_EXISTS = new ErrorCode(1_002_XXX_000, "[功能]不存在");
|
||||
ErrorCode XXX_NAME_DUPLICATE = new ErrorCode(1_002_XXX_001, "已存在该名字的[功能]");
|
||||
```
|
||||
|
||||
### 5.7 跨模块 API 调用
|
||||
|
||||
```java
|
||||
// API 接口定义
|
||||
public interface XxxApi {
|
||||
CommonResult<XxxRespDTO> getXxx(Long id);
|
||||
CommonResult<List<XxxRespDTO>> getXxxList(Collection<Long> ids);
|
||||
}
|
||||
|
||||
// API 实现
|
||||
@RestController
|
||||
@FeignClient(name = ApiConstants.NAME)
|
||||
public class XxxApiImpl implements XxxApi {
|
||||
|
||||
@Resource
|
||||
private XxxService xxxService;
|
||||
|
||||
@Override
|
||||
public CommonResult<XxxRespDTO> getXxx(Long id) {
|
||||
XxxDO xxx = xxxService.getXxx(id);
|
||||
return success(BeanUtils.toBean(xxx, XxxRespDTO.class));
|
||||
}
|
||||
}
|
||||
|
||||
// 调用方式
|
||||
@Resource
|
||||
private XxxApi xxxApi;
|
||||
|
||||
public void someMethod() {
|
||||
XxxRespDTO xxx = xxxApi.getXxx(id).getCheckedData();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、快速参考
|
||||
|
||||
### 6.1 分层架构速记
|
||||
|
||||
```
|
||||
Controller (HTTP入口)
|
||||
↓
|
||||
Service (业务逻辑)
|
||||
↓
|
||||
Mapper (数据访问)
|
||||
↓
|
||||
Database
|
||||
```
|
||||
|
||||
### 6.2 常用注解速查
|
||||
|
||||
| 层级 | 常用注解 |
|
||||
|------|---------|
|
||||
| Controller | @RestController, @RequestMapping, @Tag, @Operation, @PreAuthorize |
|
||||
| Service | @Service, @Validated, @Transactional |
|
||||
| Mapper | @Mapper |
|
||||
| DO | @TableName, @TableId, @Data |
|
||||
| VO | @Data, @Schema, @NotBlank, @NotNull |
|
||||
|
||||
### 6.3 命名规范
|
||||
|
||||
| 类型 | 命名规则 | 示例 |
|
||||
|------|---------|------|
|
||||
| Controller | XxxController | UserController |
|
||||
| Service接口 | XxxService | UserService |
|
||||
| Service实现 | XxxServiceImpl | UserServiceImpl |
|
||||
| Mapper | XxxMapper | UserMapper |
|
||||
| DO | XxxDO | UserDO |
|
||||
| 请求VO | XxxSaveReqVO / XxxPageReqVO | UserSaveReqVO |
|
||||
| 响应VO | XxxRespVO | UserRespVO |
|
||||
| API接口 | XxxApi | UserApi |
|
||||
| 错误码 | XXX_NOT_EXISTS | USER_NOT_EXISTS |
|
||||
|
||||
---
|
||||
|
||||
**提示**:使用 Skill 文档时,直接复制对应的提示词模板,替换占位符即可快速获得准确的代码生成和问题解答。
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user