# BladeX 项目开发指南 > 本指南提供在 BladeX 框架下高效开发的最佳实践 ## 目录 1. [快速开始](#一快速开始) 2. [开发流程](#二标准开发流程) 3. [代码规范](#三代码规范) 4. [常见场景](#四常见开发场景) 5. [最佳实践](#五最佳实践) 6. [调试技巧](#六调试技巧) 7. [常见问题](#七常见问题) --- ## 一、快速开始 ### 1.1 环境准备 ```bash # 确保服务运行 docker ps | grep -E "(dev-mysql|dev-redis)" # 在 VS Code 中按 F5 启动调试 # 或在终端运行: mvn spring-boot:run ``` ### 1.2 访问应用 ``` API 文档:http://localhost:8123/doc.html Druid 监控:http://localhost:8123/druid 健康检查:http://localhost:8123/actuator/health ``` ### 1.3 核心开发目录 **99% 的开发工作在这里:** ``` src/main/java/org/springblade/modules/martial/ ├── controller/ # API 接口 ├── service/ # 业务逻辑 ├── mapper/ # 数据访问 └── pojo/ ├── entity/ # 实体类 ├── dto/ # 数据传输对象 └── vo/ # 视图对象 ``` --- ## 二、标准开发流程 ### 2.1 新增功能完整流程 以"添加裁判等级管理"为例: #### 步骤 1:创建数据库表 ```sql -- doc/sql/mysql/martial-competition-tables.sql CREATE TABLE mt_judge_level ( id BIGINT PRIMARY KEY, level_name VARCHAR(50) NOT NULL COMMENT '等级名称', level_code VARCHAR(20) NOT NULL COMMENT '等级代码', description VARCHAR(500) COMMENT '描述', -- BladeX 基础字段(必须) create_user BIGINT COMMENT '创建人', create_dept BIGINT COMMENT '创建部门', create_time DATETIME COMMENT '创建时间', update_user BIGINT COMMENT '更新人', update_time DATETIME COMMENT '更新时间', status INT DEFAULT 1 COMMENT '状态', is_deleted INT DEFAULT 0 COMMENT '是否删除', -- 多租户字段(如果需要) tenant_id VARCHAR(12) COMMENT '租户ID' ) COMMENT '裁判等级表'; ``` #### 步骤 2:创建实体类 Entity ```java // modules/martial/pojo/entity/JudgeLevel.java package org.springblade.modules.martial.pojo.entity; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import org.springblade.core.tenant.mp.TenantEntity; /** * 裁判等级实体类 */ @Data @TableName("mt_judge_level") @EqualsAndHashCode(callSuper = true) @Schema(description = "裁判等级") public class JudgeLevel extends TenantEntity { private static final long serialVersionUID = 1L; @Schema(description = "等级名称") private String levelName; @Schema(description = "等级代码") private String levelCode; @Schema(description = "描述") private String description; } ``` **关键点**: - ✅ 继承 `TenantEntity`(包含 id、createTime 等基础字段) - ✅ 使用 `@TableName` 指定表名 - ✅ 使用 `@Schema` 添加 Swagger 文档 - ✅ 使用 Lombok `@Data` 减少代码 #### 步骤 3:创建 Mapper 接口 ```java // modules/martial/mapper/JudgeLevelMapper.java package org.springblade.modules.martial.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springblade.modules.martial.pojo.entity.JudgeLevel; /** * 裁判等级 Mapper */ public interface JudgeLevelMapper extends BaseMapper { // 基础 CRUD 已由 BaseMapper 提供 // 只需添加复杂查询 } ``` **关键点**: - ✅ 继承 `BaseMapper` - ✅ 自动获得增删改查方法 - ✅ 复杂 SQL 可以在 XML 中编写 #### 步骤 4:创建 Mapper XML(可选) 如果有复杂查询,创建 XML 文件: ```xml ``` #### 步骤 5:创建 Service 接口 ```java // modules/martial/service/IJudgeLevelService.java package org.springblade.modules.martial.service; import com.baomidou.mybatisplus.extension.service.IService; import org.springblade.modules.martial.pojo.entity.JudgeLevel; /** * 裁判等级服务接口 */ public interface IJudgeLevelService extends IService { // 基础 CRUD 已由 IService 提供 /** * 根据等级代码查询 */ JudgeLevel getByLevelCode(String levelCode); } ``` #### 步骤 6:创建 Service 实现类 ```java // modules/martial/service/impl/JudgeLevelServiceImpl.java package org.springblade.modules.martial.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.AllArgsConstructor; import org.springblade.modules.martial.mapper.JudgeLevelMapper; import org.springblade.modules.martial.pojo.entity.JudgeLevel; import org.springblade.modules.martial.service.IJudgeLevelService; import org.springframework.stereotype.Service; /** * 裁判等级服务实现 */ @Service @AllArgsConstructor public class JudgeLevelServiceImpl extends ServiceImpl implements IJudgeLevelService { @Override public JudgeLevel getByLevelCode(String levelCode) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(JudgeLevel::getLevelCode, levelCode); return this.getOne(wrapper); } } ``` **关键点**: - ✅ 继承 `ServiceImpl` - ✅ 实现自己的 Service 接口 - ✅ 使用 `@Service` 注解 - ✅ 使用 `@AllArgsConstructor` 自动注入 #### 步骤 7:创建 Controller ```java // modules/martial/controller/JudgeLevelController.java package org.springblade.modules.martial.controller; import com.baomidou.mybatisplus.core.metadata.IPage; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; import org.springblade.core.boot.ctrl.BladeController; import org.springblade.core.mp.support.Condition; import org.springblade.core.mp.support.Query; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.Func; import org.springblade.modules.martial.pojo.entity.JudgeLevel; import org.springblade.modules.martial.service.IJudgeLevelService; import org.springframework.web.bind.annotation.*; /** * 裁判等级控制器 */ @RestController @AllArgsConstructor @RequestMapping("/api/martial/judge-level") @Tag(name = "裁判等级管理", description = "裁判等级相关接口") public class JudgeLevelController extends BladeController { private final IJudgeLevelService judgeLevelService; /** * 详情 */ @GetMapping("/detail") @ApiOperationSupport(order = 1) @Operation(summary = "详情", description = "传入id") public R detail(@RequestParam Long id) { JudgeLevel detail = judgeLevelService.getById(id); return R.data(detail); } /** * 分页列表 */ @GetMapping("/list") @ApiOperationSupport(order = 2) @Operation(summary = "分页", description = "传入查询条件") public R> list(JudgeLevel judgeLevel, Query query) { IPage pages = judgeLevelService.page( Condition.getPage(query), Condition.getQueryWrapper(judgeLevel) ); return R.data(pages); } /** * 新增或修改 */ @PostMapping("/submit") @ApiOperationSupport(order = 3) @Operation(summary = "新增或修改", description = "传入实体") public R submit(@RequestBody JudgeLevel judgeLevel) { return R.status(judgeLevelService.saveOrUpdate(judgeLevel)); } /** * 删除 */ @PostMapping("/remove") @ApiOperationSupport(order = 4) @Operation(summary = "逻辑删除", description = "传入ids") public R remove(@RequestParam String ids) { return R.status(judgeLevelService.deleteLogic(Func.toLongList(ids))); } } ``` **关键点**: - ✅ 继承 `BladeController` - ✅ 使用 `@RestController` 和 `@RequestMapping` - ✅ 统一返回 `R` 包装类 - ✅ 详细的 Swagger 注解 #### 步骤 8:测试 1. 启动应用(F5) 2. 访问 http://localhost:8123/doc.html 3. 找到"裁判等级管理"模块 4. 测试增删改查接口 --- ## 三、代码规范 ### 3.1 命名规范 #### 类命名 ```java // Entity(实体类) JudgeLevel.java // 名词,大驼峰 // DTO(数据传输对象) JudgeLevelDTO.java // 实体名 + DTO // VO(视图对象) JudgeLevelVO.java // 实体名 + VO // Mapper JudgeLevelMapper.java // 实体名 + Mapper // Service IJudgeLevelService.java // I + 实体名 + Service JudgeLevelServiceImpl.java // 实体名 + ServiceImpl // Controller JudgeLevelController.java // 实体名 + Controller ``` #### 方法命名 ```java // Service 层 JudgeLevel getById(Long id) // get + By + 条件 List listByStatus(Integer status) // list + By + 条件 boolean saveOrUpdate(JudgeLevel entity) // save/update/delete boolean deleteLogic(List ids) // 逻辑删除 // Controller 层 R detail(@RequestParam Long id) // detail(详情) R> list(...) // list(列表) R submit(@RequestBody JudgeLevel entity) // submit(提交) R remove(@RequestParam String ids) // remove(删除) ``` #### 变量命名 ```java // 小驼峰 private String levelName; // ✅ private String level_name; // ❌ private String LevelName; // ❌ // 集合 List judgeLevelList; // ✅ 复数或带List后缀 List levels; // ✅ List judgeLevel; // ❌ 单数 ``` ### 3.2 注解规范 #### Entity 注解 ```java @Data // Lombok,生成getter/setter @TableName("mt_judge_level") // MyBatis-Plus,指定表名 @EqualsAndHashCode(callSuper = true) // Lombok,继承父类equals/hashCode @Schema(description = "裁判等级") // Swagger,API文档描述 public class JudgeLevel extends TenantEntity { @Schema(description = "等级名称") // 字段描述 private String levelName; @TableField(exist = false) // 非数据库字段 private Integer judgeCount; } ``` #### Controller 注解 ```java @RestController // REST控制器 @AllArgsConstructor // Lombok,构造器注入 @RequestMapping("/api/martial/judge-level") // 请求路径 @Tag(name = "裁判等级管理", description = "裁判等级相关接口") // Swagger分组 public class JudgeLevelController extends BladeController { @GetMapping("/detail") // GET请求 @ApiOperationSupport(order = 1) // Swagger排序 @Operation(summary = "详情", description = "传入id") // 接口描述 public R detail(@RequestParam Long id) { ... } } ``` ### 3.3 包结构规范 ``` modules/martial/ ├── controller/ │ └── XxxController.java # 一个实体一个Controller │ ├── service/ │ ├── IXxxService.java # 接口 │ └── impl/ │ └── XxxServiceImpl.java # 实现 │ ├── mapper/ │ └── XxxMapper.java # Mapper接口 │ └── pojo/ ├── entity/ │ └── Xxx.java # 实体类 ├── dto/ │ └── XxxDTO.java # DTO(可选) └── vo/ └── XxxVO.java # VO(可选) ``` --- ## 四、常见开发场景 ### 4.1 简单查询 ```java // Service public JudgeLevel getByLevelCode(String levelCode) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(JudgeLevel::getLevelCode, levelCode); return this.getOne(wrapper); } // 或使用 Condition(更简洁) public JudgeLevel getByLevelCode(String levelCode) { JudgeLevel query = new JudgeLevel(); query.setLevelCode(levelCode); return this.getOne(Condition.getQueryWrapper(query)); } ``` ### 4.2 复杂条件查询 ```java public List search(String keyword, Integer status) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); // 模糊查询 wrapper.like(Func.isNotBlank(keyword), JudgeLevel::getLevelName, keyword); // 等值查询 wrapper.eq(status != null, JudgeLevel::getStatus, status); // 排序 wrapper.orderByDesc(JudgeLevel::getCreateTime); return this.list(wrapper); } ``` ### 4.3 分页查询 ```java // Controller @GetMapping("/list") public R> list(JudgeLevel judgeLevel, Query query) { IPage pages = judgeLevelService.page( Condition.getPage(query), // 分页参数:current, size Condition.getQueryWrapper(judgeLevel) // 查询条件 ); return R.data(pages); } // 请求示例: // GET /api/martial/judge-level/list?current=1&size=10&levelName=国家级 ``` ### 4.4 关联查询 ```java // 方式1:Mapper XML // JudgeLevelMapper.xml // JudgeLevelMapper.java List selectWithJudgeCount(); // 方式2:代码中关联 public JudgeLevelVO getDetailWithCount(Long id) { JudgeLevel level = this.getById(id); JudgeLevelVO vo = new JudgeLevelVO(); BeanUtil.copy(level, vo); // 统计关联的裁判数量 long count = judgeService.count( Wrappers.lambdaQuery() .eq(Judge::getLevelId, id) ); vo.setJudgeCount(count); return vo; } ``` ### 4.5 批量操作 ```java // 批量新增 public boolean batchSave(List list) { return this.saveBatch(list); } // 批量删除(逻辑删除) public boolean batchDelete(List ids) { return this.deleteLogic(ids); } // 批量更新 public boolean batchUpdate(List list) { return this.updateBatchById(list); } ``` ### 4.6 事务处理 ```java @Service public class JudgeLevelServiceImpl extends ServiceImpl implements IJudgeLevelService { @Autowired private IJudgeService judgeService; /** * 删除等级,同时更新关联的裁判 */ @Transactional(rollbackFor = Exception.class) public boolean deleteWithUpdate(Long levelId, Long newLevelId) { // 1. 更新关联的裁判到新等级 judgeService.update( Wrappers.lambdaUpdate() .eq(Judge::getLevelId, levelId) .set(Judge::getLevelId, newLevelId) ); // 2. 删除等级 return this.deleteLogic(Collections.singletonList(levelId)); } } ``` ### 4.7 DTO 和 VO 的使用 ```java // DTO:接收前端数据 public class JudgeLevelDTO { private Long id; private String levelName; private String levelCode; // 可能包含额外的业务字段 private List judgeIds; // 批量关联裁判 } // VO:返回给前端 public class JudgeLevelVO extends JudgeLevel { private Integer judgeCount; // 关联的裁判数量 private String createUserName; // 创建人姓名 } // Controller @PostMapping("/submit") public R submit(@RequestBody JudgeLevelDTO dto) { JudgeLevel entity = new JudgeLevel(); BeanUtil.copy(dto, entity); // 处理额外逻辑 if (Func.isNotEmpty(dto.getJudgeIds())) { // 批量关联裁判 } return R.status(judgeLevelService.saveOrUpdate(entity)); } @GetMapping("/detail-vo") public R detailWithVO(@RequestParam Long id) { JudgeLevelVO vo = judgeLevelService.getDetailVO(id); return R.data(vo); } ``` --- ## 五、最佳实践 ### 5.1 Service 层最佳实践 ```java @Service @AllArgsConstructor public class JudgeLevelServiceImpl extends ServiceImpl implements IJudgeLevelService { // ✅ 使用 @AllArgsConstructor 注入依赖 // 避免 @Autowired /** * ✅ 方法命名清晰 * ✅ 参数验证 * ✅ 异常处理 */ @Override public JudgeLevel getByLevelCode(String levelCode) { // 参数验证 if (Func.isBlank(levelCode)) { throw new ServiceException("等级代码不能为空"); } LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(JudgeLevel::getLevelCode, levelCode); JudgeLevel level = this.getOne(wrapper); if (level == null) { throw new ServiceException("等级不存在"); } return level; } /** * ✅ 复杂业务逻辑在 Service 层 * ✅ Controller 只负责接收和返回 */ @Transactional(rollbackFor = Exception.class) public boolean saveWithValidation(JudgeLevel judgeLevel) { // 1. 验证等级代码唯一性 long count = this.count( Wrappers.lambdaQuery() .eq(JudgeLevel::getLevelCode, judgeLevel.getLevelCode()) .ne(judgeLevel.getId() != null, JudgeLevel::getId, judgeLevel.getId()) ); if (count > 0) { throw new ServiceException("等级代码已存在"); } // 2. 保存 return this.saveOrUpdate(judgeLevel); } } ``` ### 5.2 Controller 层最佳实践 ```java @RestController @AllArgsConstructor @RequestMapping("/api/martial/judge-level") @Tag(name = "裁判等级管理", description = "裁判等级相关接口") public class JudgeLevelController extends BladeController { private final IJudgeLevelService judgeLevelService; /** * ✅ 统一返回 R * ✅ 清晰的 Swagger 文档 * ✅ 参数校验 */ @GetMapping("/detail") @ApiOperationSupport(order = 1) @Operation(summary = "详情", description = "传入id") public R detail(@RequestParam Long id) { // Controller 只负责调用 Service,不包含业务逻辑 JudgeLevel detail = judgeLevelService.getById(id); return R.data(detail); } /** * ✅ 使用 @Valid 参数校验(如果有DTO) */ @PostMapping("/submit") @ApiOperationSupport(order = 3) @Operation(summary = "新增或修改", description = "传入实体") public R submit(@Valid @RequestBody JudgeLevel judgeLevel) { // 复杂逻辑在 Service 层 return R.status(judgeLevelService.saveWithValidation(judgeLevel)); } /** * ✅ RESTful 风格 * ❌ 避免:/add, /update, /delete * ✅ 使用:/submit, /remove 或 HTTP 方法区分 */ } ``` ### 5.3 异常处理 ```java // 使用框架提供的 ServiceException import org.springblade.core.tool.api.ServiceException; // Service 层 public JudgeLevel getById(Long id) { JudgeLevel level = super.getById(id); if (level == null) { throw new ServiceException("裁判等级不存在"); } return level; } // Controller 会自动捕获并返回错误信息: // { // "code": 400, // "success": false, // "msg": "裁判等级不存在" // } ``` ### 5.4 缓存使用 ```java // 使用 BladeX 提供的缓存工具 import org.springblade.common.cache.CacheNames; import org.springblade.core.cache.utils.CacheUtil; @Service public class JudgeLevelServiceImpl extends ServiceImpl implements IJudgeLevelService { // 定义缓存名 private static final String CACHE_KEY = "judge:level:"; public JudgeLevel getByIdWithCache(Long id) { // 先查缓存 String cacheKey = CACHE_KEY + id; JudgeLevel cached = CacheUtil.get(CacheNames.DICT_CACHE, cacheKey, JudgeLevel.class); if (cached != null) { return cached; } // 查数据库 JudgeLevel level = this.getById(id); // 存缓存 if (level != null) { CacheUtil.put(CacheNames.DICT_CACHE, cacheKey, level); } return level; } @Override public boolean saveOrUpdate(JudgeLevel entity) { boolean result = super.saveOrUpdate(entity); // 清除缓存 if (result && entity.getId() != null) { CacheUtil.evict(CacheNames.DICT_CACHE, CACHE_KEY + entity.getId()); } return result; } } ``` --- ## 六、调试技巧 ### 6.1 VS Code 调试 ```java // 1. 在代码中打断点 @GetMapping("/detail") public R detail(@RequestParam Long id) { JudgeLevel detail = judgeLevelService.getById(id); // ← 点击行号打断点 return R.data(detail); } // 2. 按 F5 启动调试模式 // 3. 访问 http://localhost:8123/api/martial/judge-level/detail?id=1 // 4. 程序会在断点处暂停 // 5. 使用调试工具栏: // F10 - 单步跳过 // F11 - 单步进入 // Shift+F11 - 单步跳出 // F5 - 继续执行 ``` ### 6.2 日志调试 ```java import lombok.extern.slf4j.Slf4j; @Slf4j // Lombok 自动生成 log 对象 @Service public class JudgeLevelServiceImpl extends ServiceImpl implements IJudgeLevelService { public JudgeLevel getById(Long id) { log.info("查询裁判等级,id: {}", id); JudgeLevel level = super.getById(id); log.debug("查询结果: {}", level); // debug 级别 if (level == null) { log.warn("裁判等级不存在,id: {}", id); // warn 级别 } return level; } } // 日志级别配置(application.yml) logging: level: org.springblade.modules.martial: DEBUG # 设置 martial 模块为 DEBUG ``` ### 6.3 SQL 调试 ```yaml # application-dev.yml mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL # 控制台会输出: # ==> Preparing: SELECT * FROM mt_judge_level WHERE id = ? # ==> Parameters: 1(Long) # <== Total: 1 ``` ### 6.4 使用 Knife4j 调试 1. 访问:http://localhost:8123/doc.html 2. 找到对应的接口 3. 点击"调试" 4. 填写参数 5. 点击"发送"查看响应 --- ## 七、常见问题 ### 7.1 实体类继承选择 ```java // ❓ 我应该继承哪个基类? // 选择1:TenantEntity(推荐,支持多租户) public class JudgeLevel extends TenantEntity { // 包含字段: // - id, createUser, createDept, createTime // - updateUser, updateTime, status, isDeleted // - tenantId(租户ID) } // 选择2:BaseEntity(不需要多租户) public class JudgeLevel extends BaseEntity { // 包含字段: // - id, createUser, createDept, createTime // - updateUser, updateTime, status, isDeleted // - 没有 tenantId } // 选择3:不继承(不推荐) public class JudgeLevel { private Long id; // 需要手动定义所有字段,失去框架支持 } // ✅ 建议:统一继承 TenantEntity ``` ### 7.2 Mapper 没有生效 ```java // ❌ 问题:注入 Mapper 报错 @Autowired private JudgeLevelMapper judgeLevelMapper; // 找不到 Bean // ✅ 解决方案1:确保主类有 @MapperScan @SpringBootApplication @MapperScan("org.springblade.**.mapper.**") // 扫描所有 mapper public class Application { ... } // ✅ 解决方案2:在 Mapper 上添加 @Mapper @Mapper public interface JudgeLevelMapper extends BaseMapper { ... } ``` ### 7.3 查询返回空 ```java // ❌ 问题:查询总是返回 null JudgeLevel level = judgeLevelMapper.selectById(1L); // level = null // ✅ 检查项: // 1. 表名是否正确 @TableName("mt_judge_level") // 确保表名对应 // 2. 是否被逻辑删除 // BladeX 默认启用逻辑删除,is_deleted = 1 的数据查不到 SELECT * FROM mt_judge_level WHERE id = 1; // 检查 is_deleted 字段 // 3. 多租户隔离 // 如果启用多租户,只能查到当前租户的数据 // 检查 tenant_id 字段 ``` ### 7.4 删除不生效 ```java // ❌ 问题:删除后数据还在 judgeLevelService.removeById(1L); // 数据库中数据还在 // ✅ 原因:BladeX 使用逻辑删除 // 实际执行的是:UPDATE mt_judge_level SET is_deleted = 1 WHERE id = 1 // 如果需要物理删除: @TableLogic(value = "0", delval = "1") // 禁用逻辑删除 private Integer isDeleted; // 或在 application.yml 配置 mybatis-plus: global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 ``` ### 7.5 时间字段自动填充 ```java // ✅ BladeX 已自动配置时间字段填充 // 继承 BaseEntity 或 TenantEntity 即可 public class JudgeLevel extends TenantEntity { // createTime 和 updateTime 会自动填充 } // 如果需要自定义字段自动填充: public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); } } ``` ### 7.6 分页查询无效 ```java // ❌ 问题:分页不生效,返回全部数据 @GetMapping("/list") public R> list(Query query) { IPage pages = judgeLevelService.page( Condition.getPage(query) ); return R.data(pages); } // ✅ 解决:确保前端传了分页参数 // GET /api/martial/judge-level/list?current=1&size=10 // ✅ 确保配置了分页插件(BladeX 已配置) @Configuration public class MybatisPlusConfiguration { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } } ``` --- ## 八、开发清单 ### 8.1 新增功能检查清单 - [ ] 创建数据库表(包含 BladeX 基础字段) - [ ] 创建 Entity(继承 TenantEntity) - [ ] 创建 Mapper 接口(继承 BaseMapper) - [ ] 创建 Mapper XML(如有复杂SQL) - [ ] 创建 Service 接口(继承 IService) - [ ] 创建 Service 实现(继承 ServiceImpl) - [ ] 创建 Controller(继承 BladeController) - [ ] 添加 Swagger 注解(完善 API 文档) - [ ] 测试增删改查功能 - [ ] 编写单元测试(可选) ### 8.2 代码审查清单 - [ ] 类命名符合规范 - [ ] 方法命名清晰 - [ ] 使用了 Lombok 注解 - [ ] 添加了 Swagger 文档 - [ ] Service 层包含业务逻辑 - [ ] Controller 层只负责接收和返回 - [ ] 使用了统一的返回格式 R - [ ] 添加了必要的日志 - [ ] 处理了异常情况 - [ ] 代码格式化 --- ## 九、快速参考 ### 9.1 常用 MyBatis-Plus 方法 ```java // 查询 T getById(Serializable id) T getOne(Wrapper queryWrapper) List list() List list(Wrapper queryWrapper) IPage page(IPage page, Wrapper queryWrapper) long count(Wrapper queryWrapper) // 新增 boolean save(T entity) boolean saveBatch(Collection entityList) // 更新 boolean updateById(T entity) boolean update(Wrapper updateWrapper) boolean updateBatchById(Collection entityList) // 删除 boolean removeById(Serializable id) boolean removeByIds(Collection idList) boolean deleteLogic(Collection idList) // 逻辑删除 ``` ### 9.2 常用工具类 ```java // 字符串工具 Func.isBlank(str) // 判断空字符串 Func.isNotBlank(str) // 判断非空 Func.toLong(obj) // 转 Long Func.toLongList(str) // 字符串转 Long 列表(如:"1,2,3") // Bean 拷贝 BeanUtil.copy(source, target) // 缓存工具 CacheUtil.get(cacheName, key, type) CacheUtil.put(cacheName, key, value) CacheUtil.evict(cacheName, key) // 当前用户 AuthUtil.getUserId() // 当前用户ID AuthUtil.getUserName() // 当前用户名 AuthUtil.getTenantId() // 当前租户ID ``` ### 9.3 常用注解速查 ```java // Lombok @Data // getter/setter/toString/equals/hashCode @AllArgsConstructor // 全参构造器 @NoArgsConstructor // 无参构造器 @Slf4j // 日志对象 log // MyBatis-Plus @TableName("表名") // 指定表名 @TableField(exist = false) // 非数据库字段 @TableLogic // 逻辑删除字段 // Spring @Service // Service 层 @RestController // Controller 层 @RequestMapping("/path") // 请求路径 @GetMapping // GET 请求 @PostMapping // POST 请求 @RequestParam // 请求参数 @RequestBody // 请求体 @Transactional // 事务 // Swagger @Tag(name = "xxx") // API 分组 @Operation(summary = "xxx") // 接口说明 @Schema(description = "xxx") // 字段说明 @ApiOperationSupport(order = 1) // 排序 ``` --- ## 十、总结 ### 核心要点 1. **专注业务开发** - 主要工作在 `modules/martial/` - 不需要关心框架其他模块 2. **遵循现有规范** - 模仿 `modules/system/` 的写法 - 保持代码风格一致 3. **利用框架能力** - 继承 BaseMapper、IService 获得基础CRUD - 使用 BladeX 提供的工具类 - 遵循统一的返回格式 4. **理解不完美** - 架构有缺陷,但可以正常工作 - 接受现状,不要试图大规模重构 ### 学习路径 1. **第一周**:熟悉现有代码,理解框架规范 2. **第二周**:完成一个简单的 CRUD 功能 3. **第三周**:尝试复杂业务逻辑和关联查询 4. **第四周**:掌握调试技巧和常见问题解决 --- **相关文档**: - [架构说明.md](./架构说明.md) - 理解 BladeX 架构设计 - [CLAUDE.md](../CLAUDE.md) - 项目整体说明 - [VS Code 调试指南](../.vscode/DEBUG_GUIDE.md) - 调试配置