# 开发规范 **本文引用的文件** - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [application.yml](file://crm-app/src/main/resources/application.yml) - [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java) - [application.yml](file://crm-auth/src/main/resources/application.yml) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java) - [BaseDTO.java](file://crm-base/src/main/java/com/crm/base/domain/dto/BaseDTO.java) - [BaseParam.java](file://crm-base/src/main/java/com/crm/base/domain/param/BaseParam.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java) - [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [TraceIdFilter.java](file://crm-base/src/main/java/com/crm/base/filter/TraceIdFilter.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java) - [MetaObjectFillHandler.java](file://crm-base/src/main/java/com/crm/base/config/MetaObjectFillHandler.java) - [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java) - [CustomIdGenerator.java](file://crm-base/src/main/java/com/crm/base/config/CustomIdGenerator.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [SecurityExceptionHandlers.java](file://crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java) - [LoginParam.java](file://crm-auth/src/main/java/com/crm/auth/domain/param/LoginParam.java) - [LoginResultDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java) - [UserInfoDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/UserInfoDTO.java) - [AuthUser.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthUser.java) - [AuthIdentity.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthIdentity.java) - [SysDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysDept.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [SysUserDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserDept.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [IdentityTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/IdentityTypeEnum.java) - [MenuTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuTypeEnum.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileInfo.java](file://crm-file/src/main/java/com/crm/file/domain/entity/FileInfo.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [KkFileViewClient.java](file://crm-file/src/main/java/com/crm/file/service/KkFileViewClient.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) - [DictGroupVO.java](file://crm-dict/src/main/java/com/crm/dict/domain/dto/DictGroupVO.java) - [DictItemVO.java](file://crm-dict/src/main/java/com/crm/dict/domain/dto/DictItemVO.java) - [DictGroup.java](file://crm-dict/src/main/java/com/crm/dict/domain/entity/DictGroup.java) - [DictItem.java](file://crm-dict/src/main/java/com/crm/dict/domain/entity/DictItem.java) - [DictGroupServiceImpl.java](file://crm-dict/src/main/java/com/crm/dict/service/impl/DictGroupServiceImpl.java) - [DictItemServiceImpl.java](file://crm-dict/src/main/java/com/crm/dict/service/impl/DictItemServiceImpl.java) - [UserListDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/UserListDTO.java) - [RoleDetailVO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/RoleDetailVO.java) - [UserStatsVO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/UserStatsVO.java) - [pom.xml](file://pom.xml) ## 更新摘要 **变更内容** - 更新了数据传输对象设计规范章节,增加了DictGroupVO和DictItemVO的实践案例 - 完善了DTO设计模式说明,展示了如何避免直接暴露实体内部结构 - 增强了BaseDTO设计原则的实际应用示例 - 补充了VO与Entity分离的最佳实践指导 ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖分析](#依赖分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本规范面向 CRM 后端多模块工程,统一代码风格、命名约定、包结构、实体与数据传输对象设计、异常与日志规范、注释规范以及 Git 工作流与代码审查流程。目标是通过一致的编码标准提升代码质量、可维护性与团队协作效率。 ## 项目结构 本项目采用多模块 Maven 工程,按领域与职责划分: - crm-app:应用启动入口与全局配置 - crm-base:基础能力(通用实体、DTO/Param/Result、安全上下文、数据权限、异常、工具类、MyBatis Plus 配置) - crm-auth:认证授权与系统管理(用户、角色、菜单、部门、第三方登录等) - crm-file:文件服务(上传、分片、预览、清理任务等) - crm-dict:数据字典服务(分组、项管理、引用计数等) ```mermaid graph TB subgraph "应用层" APP["crm-app
启动与配置"] end subgraph "业务域" AUTH["crm-auth
认证与系统管理"] FILE["crm-file
文件服务"] DICT["crm-dict
数据字典服务"] end subgraph "基础能力" BASE["crm-base
通用实体/DTO/Param/Result
安全上下文/异常/工具/配置"] end APP --> AUTH APP --> FILE APP --> DICT AUTH --> BASE FILE --> BASE DICT --> BASE ``` **图示来源** - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java) - [pom.xml](file://pom.xml) **章节来源** - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [AuthApplication.java](file://crm-auth/src/main/java/com/crm/auth/AuthApplication.java) - [pom.xml](file://pom.xml) ## 核心组件 - 通用实体基类与数据所有权 - BaseEntity:统一审计字段(如创建时间、更新时间等),配合元数据填充处理器自动落库 - OwnedEntity:在 BaseEntity 基础上增加数据归属字段,结合数据可见性上下文实现行级数据权限控制 - 数据传输对象 - BaseDTO:通用 DTO 基类,用于接口返回或跨层传输 - BaseParam:通用请求参数基类,用于入参校验与封装 - Result/PageResult:统一响应包装与分页结果 - 安全与数据权限 - LoginUser/SecurityUtils:当前登录用户上下文获取 - DataOwnership/DataScope/DataScopeLevel/DataScopeHelper/DataVisibilityContext:数据权限注解、级别、辅助方法与上下文 - 异常体系 - BusinessErrorException/MissingParameterException/PermissionErrorException/ResourceNotExistException:业务、参数、权限、资源不存在等异常类型 - GlobalExceptionHandlerAdvice:全局异常处理,统一错误码与消息 - MyBatis Plus 增强 - MetaObjectFillHandler:自动填充审计字段 - SnowflakeIdWorker/CustomIdGenerator:分布式 ID 生成策略 - MybatisPlusConfig:MP 配置注入 **章节来源** - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java) - [BaseDTO.java](file://crm-base/src/main/java/com/crm/base/domain/dto/BaseDTO.java) - [BaseParam.java](file://crm-base/src/main/java/com/crm/base/domain/param/BaseParam.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java) - [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [MetaObjectFillHandler.java](file://crm-base/src/main/java/com/crm/base/config/MetaObjectFillHandler.java) - [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java) - [CustomIdGenerator.java](file://crm-base/src/main/java/com/crm/base/config/CustomIdGenerator.java) - [MybatisPlusConfig.java](file://crm-base/src/main/java/com/crm/base/config/MybatisPlusConfig.java) ## 架构总览 分层与边界: - 表现层(Controller):接收请求、参数校验、调用 Service - 服务层(Service):业务编排、事务边界、调用 Mapper 与外部客户端 - 持久层(Mapper):数据访问,基于 MyBatis Plus - 安全与数据权限:过滤器/拦截器 + 注解 + 上下文,贯穿 Controller -> Service -> Mapper - 基础设施:统一异常、日志追踪、ID 生成、分页、枚举、工具类 ```mermaid sequenceDiagram participant Client as "客户端" participant AuthCtrl as "认证控制器" participant TokenSvc as "令牌服务" participant AuthService as "认证服务" participant DB as "数据库" Client->>AuthCtrl : "POST /auth/login" AuthCtrl->>AuthService : "验证用户名密码" AuthService->>DB : "查询用户信息" DB-->>AuthService : "用户记录" AuthService-->>AuthCtrl : "用户信息" AuthCtrl->>TokenSvc : "签发JWT" TokenSvc-->>AuthCtrl : "令牌" AuthCtrl-->>Client : "登录成功响应" ``` **图示来源** - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [AuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) ## 详细组件分析 ### 实体类设计规范(BaseEntity 与 OwnedEntity) - BaseEntity - 职责:提供统一的审计字段(如创建人、创建时间、更新人、更新时间等) - 使用方式:所有持久化实体继承 BaseEntity;配合元数据填充处理器自动设置审计字段 - 复杂度:O(1) 字段赋值,填充发生在插入/更新前 - OwnedEntity - 职责:在 BaseEntity 基础上增加"数据所有者"字段,用于数据隔离与权限过滤 - 使用方式:需要数据归属控制的实体继承 OwnedEntity;结合 DataOwnership/DataScope 注解与上下文进行行级过滤 - 注意:避免在批量操作中遗漏数据所有者,确保上下文正确设置 ```mermaid classDiagram class BaseEntity { +审计字段集合 +默认填充逻辑 } class OwnedEntity { +数据所有者字段 +数据可见性关联 } BaseEntity <|-- OwnedEntity : "继承" ``` **图示来源** - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java) **章节来源** - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java) - [MetaObjectFillHandler.java](file://crm-base/src/main/java/com/crm/base/config/MetaObjectFillHandler.java) ### 数据传输对象设计(DTO、Param、Result) **更新** 数据传输对象设计规范得到实践验证,通过 DictGroupVO 和 DictItemVO 的重构展示了如何避免直接暴露实体内部结构,遵循了 BaseDTO 的设计原则。 - BaseDTO - 用途:作为通用 DTO 基类,承载接口返回数据或跨层传输数据 - 建议:仅包含必要字段,避免直接暴露实体内部结构 - 特性:支持 Entity 到 DTO 的自动转换,包含序列化支持和状态字段 - BaseParam - 用途:作为通用请求参数基类,承载入参校验与封装 - 建议:与表单/查询参数一一对应,必要时使用分组校验 - 特性:内置分页参数、关键字搜索、排序功能 - Result/PageResult - 用途:统一 API 响应包装,包含状态码、消息、数据体;PageResult 扩展分页信息 - 建议:保持响应结构一致,便于前端解析与错误处理 - 枚举与常量 - ResultCodeEnum:统一错误码定义,避免硬编码 - 业务枚举:集中定义于 domain/enums 包下,保证一致性 #### 实践案例:DictGroupVO 和 DictItemVO DictGroupVO 和 DictItemVO 是数据传输对象设计规范的优秀实践案例: **DictGroupVO** - 字典分组视图对象 - 独立字段设计:不继承实体,只暴露前端需要的业务字段 - 排除敏感字段:不包含审计字段(creatorId/updaterId/updateTime/deleted)和软删内部字段(deleteKey) - 业务增强字段:包含 itemCount(分组下未删除的字典项数量) - 适用场景:分页列表展示 **DictItemVO** - 字典项视图对象 - 独立字段设计:不继承实体,只暴露前端需要的业务字段 - 排除敏感字段:不包含审计字段和软删内部字段 - 业务增强字段:包含 groupName(所属分组名称)、isDefault(是否为分组默认项)、referenced(是否被业务引用)、effectiveSelectable(是否有效可选) - 实时数据:referenced 字段读取实时引用计数,不进入缓存 - 适用场景:分页列表展示 ```mermaid classDiagram class BaseEntity { +id : Long +creatorId : String +createTime : LocalDateTime +updaterId : String +updateTime : LocalDateTime +deleted : Boolean } class DictGroup { +name : String +code : String +sortNo : Integer +status : Integer +description : String +builtin : Boolean +deleteKey : Long } class DictItem { +groupId : Long +name : String +code : String +value : String +sortNo : Integer +status : Integer +description : String +builtin : Boolean +deleteKey : Long } class DictGroupVO { +id : Long +name : String +code : String +sortNo : Integer +status : Integer +description : String +builtin : Boolean +createTime : LocalDateTime +itemCount : Long } class DictItemVO { +id : Long +groupId : Long +name : String +code : String +value : String +sortNo : Integer +status : Integer +description : String +builtin : Boolean +createTime : LocalDateTime +groupName : String +isDefault : Boolean +referenced : Boolean +effectiveSelectable : Boolean } BaseEntity <|-- DictGroup : "继承" BaseEntity <|-- DictItem : "继承" DictGroup --> DictGroupVO : "转换为" DictItem --> DictItemVO : "转换为" ``` **图示来源** - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [DictGroup.java](file://crm-dict/src/main/java/com/crm/dict/domain/entity/DictGroup.java) - [DictItem.java](file://crm-dict/src/main/java/com/crm/dict/domain/entity/DictItem.java) - [DictGroupVO.java](file://crm-dict/src/main/java/com/crm/dict/domain/dto/DictGroupVO.java) - [DictItemVO.java](file://crm-dict/src/main/java/com/crm/dict/domain/dto/DictItemVO.java) #### DTO 转换机制 服务层使用 toDTO 方法进行实体到 DTO 的转换: ```java // DictGroupServiceImpl 中的使用示例 PageResult result = new PageResult<>(page) .convert(g -> g.toDTO(DictGroupVO::new)); // DictItemServiceImpl 中的使用示例 PageResult result = new PageResult<>(page) .convert(i -> i.toDTO(DictItemVO::new)); ``` **章节来源** - [BaseDTO.java](file://crm-base/src/main/java/com/crm/base/domain/dto/BaseDTO.java) - [BaseParam.java](file://crm-base/src/main/java/com/crm/base/domain/param/BaseParam.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [DictGroupVO.java](file://crm-dict/src/main/java/com/crm/dict/domain/dto/DictGroupVO.java) - [DictItemVO.java](file://crm-dict/src/main/java/com/crm/dict/domain/dto/DictItemVO.java) - [DictGroupServiceImpl.java](file://crm-dict/src/main/java/com/crm/dict/service/impl/DictGroupServiceImpl.java) - [DictItemServiceImpl.java](file://crm-dict/src/main/java/com/crm/dict/service/impl/DictItemServiceImpl.java) ### 认证与鉴权流程 - 登录流程 - 控制器接收登录参数,调用认证服务验证用户 - 认证成功后由令牌服务签发 JWT,并返回给客户端 - 鉴权流程 - 请求进入时通过 JWT 过滤器解析令牌,构建登录用户上下文 - 数据权限通过 DataScope 注解与 DataOwnership 注解共同作用,结合 DataVisibilityContext 进行行级过滤 ```mermaid sequenceDiagram participant Client as "客户端" participant Filter as "JWT过滤器" participant Ctrl as "业务控制器" participant Svc as "业务服务" participant MP as "MyBatis Plus" Client->>Filter : "携带JWT的请求" Filter->>Filter : "解析令牌与用户上下文" Filter-->>Ctrl : "放行并注入上下文" Ctrl->>Svc : "执行业务方法" Svc->>MP : "执行SQL带数据权限条件" MP-->>Svc : "返回受控数据" Svc-->>Ctrl : "业务结果" Ctrl-->>Client : "统一响应" ``` **图示来源** - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) **章节来源** - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) ### 文件服务组件 - FileController:提供文件上传、下载、预览等接口 - IFileInfoService/FileInfoServiceImpl:文件信息管理与业务逻辑 - FileApiImpl:对外文件能力抽象实现 - KkFileViewClient:第三方文件预览客户端 - OrphanChunkCleanupTask:定时清理孤立分片任务 ```mermaid classDiagram class FileController { +上传接口 +下载接口 +预览接口 } class IFileInfoService { +文件信息管理 } class FileInfoServiceImpl { +实现文件业务逻辑 } class FileApiImpl { +对外API实现 } class KkFileViewClient { +预览客户端 } class OrphanChunkCleanupTask { +清理任务 } FileController --> IFileInfoService : "调用" IFileInfoService <|.. FileInfoServiceImpl : "实现" FileApiImpl --> IFileInfoService : "依赖" FileController --> KkFileViewClient : "预览" OrphanChunkCleanupTask --> IFileInfoService : "清理" ``` **图示来源** - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [KkFileViewClient.java](file://crm-file/src/main/java/com/crm/file/service/KkFileViewClient.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) **章节来源** - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [FileApiImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileApiImpl.java) - [KkFileViewClient.java](file://crm-file/src/main/java/com/crm/file/service/KkFileViewClient.java) - [OrphanChunkCleanupTask.java](file://crm-file/src/main/java/com/crm/file/task/OrphanChunkCleanupTask.java) ### 复杂逻辑流程图(数据权限过滤) ```mermaid flowchart TD Start(["进入受控方法"]) --> CheckAnno["检查是否标注数据权限注解"] CheckAnno --> |是| BuildScope["构建数据范围条件"] CheckAnno --> |否| Skip["跳过数据权限过滤"] BuildScope --> ApplyScope["应用到查询条件"] ApplyScope --> ExecQuery["执行数据库查询"] Skip --> ExecQuery ExecQuery --> Return["返回受控结果"] ``` **图示来源** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) **章节来源** - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) ## 依赖分析 模块间依赖关系清晰,遵循"上层依赖下层"的原则: - crm-app 依赖 crm-auth、crm-file 与 crm-dict - crm-auth、crm-file 与 crm-dict 均依赖 crm-base - 各模块内按 controller -> service -> mapper 分层依赖 ```mermaid graph LR APP["crm-app"] --> AUTH["crm-auth"] APP --> FILE["crm-file"] APP --> DICT["crm-dict"] AUTH --> BASE["crm-base"] FILE --> BASE DICT --> BASE ``` **图示来源** - [pom.xml](file://pom.xml) **章节来源** - [pom.xml](file://pom.xml) ## 性能考虑 - 分布式 ID:使用雪花算法生成唯一 ID,避免自增主键在高并发下的瓶颈 - 元数据填充:通过 MyBatis Plus 的元数据填充减少手动赋值开销 - 数据权限:尽量将数据权限条件下推到 SQL 层,减少内存过滤 - 缓存与限流:对热点数据(如部门树、菜单)考虑引入缓存;对外部调用(如第三方登录、文件预览)增加超时与重试策略 - 分页查询:合理使用分页,避免一次性加载大量数据 - DTO 优化:通过独立的 VO 对象减少数据传输量,避免暴露不必要的实体字段 ## 故障排查指南 - 全局异常处理 - 统一捕获业务异常、参数异常、权限异常与资源不存在异常 - 返回统一 Result 结构,便于前端处理 - 常见异常定位 - 参数缺失:检查 BaseParam 校验规则与入参映射 - 权限不足:检查 DataScope/DataOwnership 注解与上下文设置 - 资源不存在:检查查询条件与数据归属 - 日志追踪 - TraceIdFilter:为每个请求生成追踪 ID,贯穿日志链路 - 建议在关键路径打印必要上下文,避免泄露敏感信息 **章节来源** - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java) - [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java) - [TraceIdFilter.java](file://crm-base/src/main/java/com/crm/base/filter/TraceIdFilter.java) ## 结论 本规范从代码风格、命名约定、包结构到实体与 DTO 设计、异常与日志、Git 工作流与代码审查,提供了完整的开发标准。通过 DictGroupVO 和 DictItemVO 的实践案例,进一步验证了数据传输对象设计规范的有效性。遵循这些规范有助于提升代码质量、降低协作成本,并为后续扩展与维护奠定基础。 ## 附录 ### 代码风格与命名约定 - 类名:大驼峰,名词或名词短语(如 UserService、FileInfo) - 方法名:小驼峰,动词开头(如 getUserById、uploadFile) - 常量:全大写加下划线(如 MAX_RETRY_COUNT) - 包名:全小写,按功能划分(controller、service、mapper、domain、config、security、utils) - 字段命名:小驼峰,语义明确,避免缩写歧义 ### 包结构规范 - controller:HTTP 接口层,仅做参数校验与调用服务 - service:业务逻辑层,事务边界与方法编排 - mapper:数据访问层,基于 MyBatis Plus - domain: - entity:持久化实体 - dto:数据传输对象 - param:请求参数 - enums:枚举定义 - config:配置类(Spring、MyBatis Plus、Redis、Knife4j 等) - security:安全相关(过滤器、拦截器、上下文、工具) - utils:通用工具类 - advice:全局异常处理 - filter:过滤器(如追踪 ID) ### 实体类设计规范 - 所有实体继承 BaseEntity,获得审计字段 - 需要数据归属控制的实体继承 OwnedEntity - 避免在实体中放置业务逻辑,保持 POJO 特性 - 字段注释完整,便于文档生成与理解 ### DTO、Param、Result 设计模式 **更新** 数据传输对象设计模式的实践验证: - **DTO 设计原则** - 面向接口输出,精简字段,避免泄露内部结构 - 独立字段设计,不直接继承实体,按需组合业务字段 - 排除敏感字段(审计字段、软删字段等) - 支持 Entity 到 DTO 的自动转换 - **实践案例** - DictGroupVO:仅包含前端需要的分组展示字段和业务增强字段 - DictItemVO:包含字典项基本信息和运行时计算的业务字段 - UserListDTO:反范式组装,后端批量填充关联数据 - **Param 设计原则** - 面向输入,严格校验,必要时使用分组校验 - 继承 BaseParam 获得通用分页和搜索功能 - **Result 设计原则** - 统一响应格式,便于前端解析 - PageResult 扩展分页信息 - 枚举:集中定义,避免魔法值 ### 异常处理规范 - 自定义异常分类:业务异常、参数异常、权限异常、资源不存在异常 - 全局异常处理器统一捕获并转换为 Result - 避免吞掉异常,确保错误可追溯 ### 日志记录规范 - 使用统一日志框架,区分 INFO/WARN/ERROR - 关键路径记录必要上下文,避免敏感信息泄露 - 使用 TraceIdFilter 生成的追踪 ID 串联日志 ### 注释编写规范 - 类与方法必须有清晰注释,说明职责、参数、返回值与异常 - 复杂逻辑添加行内注释,解释关键步骤 - 枚举与常量需有含义明确的描述 ### Git 提交信息规范 - 格式:(): - type:feat、fix、docs、style、refactor、test、chore - scope:模块或功能范围(如 auth、file、base、dict) - subject:简洁明了,不超过 50 字符 ### 分支管理策略 - main:稳定版本,仅接受合并请求 - develop:开发主干,日常集成 - feature/*:功能分支,完成后合并至 develop - hotfix/*:热修复分支,紧急修复后合并至 main 与 develop ### 代码审查流程 - 提交前自检:编译通过、单元测试覆盖、符合规范 - 发起 MR/PR:填写变更说明与影响范围 - 审查要点:逻辑正确性、性能、安全性、可维护性 - 通过后合并:确保 CI 通过且无冲突