diff --git a/README.md b/README.md index ccd95c2..ad71891 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,172 @@ -# label_backend - +# 数据同步微服务 + +## 项目简介 + +数据同步微服务用于从第三方接口同步人员、项目和报告数据到MySQL数据库。支持定时任务自动同步和手动触发同步。 + +## 功能特性 + +1. **数据同步** + - 从第三方接口同步人员数据 + - 从第三方接口同步项目(工程)数据 + - 从第三方接口同步报告数据 + - 支持字段大小写兼容 + +2. **同步方式** + - 定时任务:每晚12点自动执行全量同步 + - 手动触发:通过REST API手动触发同步 + +3. **同步策略** + - 先删除表中所有现有数据 + - 再全量同步接口返回的数据 + - 使用接口返回的ID作为主键 + +## 技术栈 + +- Java 21 +- Spring Boot 3.1.5 +- Spring Cloud 2022.0.4 +- MyBatis Plus 3.5.3.1 +- MySQL 8.2.0 +- Nacos(服务注册与发现) + +## 项目结构 + +``` +src/main/java/com/zhonghe/datasync/ +├── common/ # 公共类 +│ ├── exception/ # 异常处理 +│ ├── Result.java # 统一响应结果 +│ └── ResultCode.java # 响应码枚举 +├── config/ # 配置类 +├── controller/ # 控制器 +├── dto/ # 数据传输对象 +│ ├── request/ # 请求DTO +│ └── response/ # 响应DTO +├── entity/ # 实体类 +├── mapper/ # MyBatis映射器 +├── scheduled/ # 定时任务 +├── service/ # 业务服务 +└── util/ # 工具类 +``` + +## 数据库表结构 + +### employee(人员表) +- id: 主键(VARCHAR) +- name: 人员姓名 +- phone_number: 人员手机号 +- password: 密码(明文存储) +- created_at: 创建时间 +- updated_at: 更新时间 + +### project(项目表) +- id: 主键(BIGINT) +- project_name: 工程名称 +- created_at: 创建时间 +- updated_at: 更新时间 + +### report(报告表) +- id: 主键(BIGINT) +- order_number: 委托编号 +- sample_code: 样品编号 +- bg_sample_code: 报告编号 +- project_name: 工程名称 +- project_id: 工程主键 +- experiment_name: 试验人员 +- real_experiment_date: 试验时间 +- created_at: 创建时间 +- updated_at: 更新时间 + +## 配置说明 + +### 环境变量 + +| 变量名 | 说明 | 默认值 | +|--------|------|--------| +| MYSQL_HOST | MySQL主机地址 | localhost | +| MYSQL_PORT | MySQL端口 | 3306 | +| MYSQL_DATABASE | MySQL数据库名 | datasync | +| MYSQL_USERNAME | MySQL用户名 | root | +| MYSQL_PASSWORD | MySQL密码 | root | +| NACOS_SERVER | Nacos服务器地址 | localhost:8848 | +| NACOS_USERNAME | Nacos用户名 | nacos | +| NACOS_PASSWORD | Nacos密码 | nacos | +| THIRD_PARTY_AUTH_URL | 第三方鉴权接口地址 | http://limspro.91jiance.net/api/LoginAPI/GetAdminUserCode | +| THIRD_PARTY_EMPLOYEE_URL | 第三方人员接口地址 | http://121.40.18.211:20016/api/EmployeeAPI/GetZHEmployeeList | +| THIRD_PARTY_PROJECT_URL | 第三方项目接口地址 | http://121.40.18.211:20030/API/GetZHProjectList | +| THIRD_PARTY_REPORT_URL | 第三方报告接口地址 | http://121.40.18.211:20030/API/GetZHSampleAllList | +| THIRD_PARTY_USER_NAME | 第三方接口用户名 | 13120251031 | +| THIRD_PARTY_PASS_WORD | 第三方接口密码 | whfst@1901 | + +## API接口 + +### 1. 同步所有数据 +- **URL**: `/api/v1/sync/all` +- **方法**: POST +- **说明**: 同步人员、项目和报告数据 + +### 2. 同步人员数据 +- **URL**: `/api/v1/sync/employees` +- **方法**: POST +- **说明**: 仅同步人员数据 + +### 3. 同步项目数据 +- **URL**: `/api/v1/sync/projects` +- **方法**: POST +- **说明**: 仅同步项目数据 + +### 4. 同步报告数据 +- **URL**: `/api/v1/sync/reports` +- **方法**: POST +- **说明**: 仅同步报告数据 + +## 定时任务 + +定时任务配置在 `DataSyncScheduledTask` 类中,使用 cron 表达式: +- **执行时间**: 每晚12点(0 0 0 * * ?) +- **执行内容**: 同步所有数据(人员、项目、报告) + +## 部署说明 + +### 1. 数据库初始化 + +执行 `src/main/resources/db/schema.sql` 创建数据库表。 + +### 2. 构建项目 + +```bash +mvn clean package +``` + +### 3. Docker部署 + +```bash +docker build -t data-sync-service:1.0.0 . +docker run -d -p 8080:8080 \ + -e MYSQL_HOST=your-mysql-host \ + -e MYSQL_USERNAME=your-username \ + -e MYSQL_PASSWORD=your-password \ + data-sync-service:1.0.0 +``` + +## 注意事项 + +1. **字段大小写兼容**: 使用 `@JsonProperty` 注解处理接口返回字段的大小写差异 +2. **ID使用**: 使用接口返回的ID作为数据库主键,不自动生成 +3. **同步策略**: + - 人员数据:增量同步(不删除现有数据),新增用户密码为手机号后六位,老用户密码保持不变 + - 项目和报告数据:全量同步(先删除后插入) +4. **密码管理**: + - 新增用户:密码自动设置为手机号后六位 + - 更新用户:密码字段不会被修改,保持原有密码 +5. **事务管理**: 同步操作使用事务,失败时会回滚 +6. **日志记录**: 所有同步操作都会记录详细日志 + +## 开发规范 + +本项目遵循《微服务开发规范文档.md》中的开发规范,包括: +- 代码风格规范 +- 命名规范 +- 异常处理规范 +- 日志规范 diff --git a/docs/superpowers/plans/2026-04-14-label-backend-directory-flattening.md b/docs/superpowers/plans/2026-04-14-label-backend-directory-flattening.md new file mode 100644 index 0000000..fe24d0e --- /dev/null +++ b/docs/superpowers/plans/2026-04-14-label-backend-directory-flattening.md @@ -0,0 +1,528 @@ +# label_backend 标准目录扁平化 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 `com.label.module.*` 与 `com.label.common.aop/common.config` 迁移到规范要求的扁平目录,保证行为不变。 + +**Architecture:** 先写迁移守卫测试,再按 `基础设施 -> 数据层 -> 服务层 -> 控制层 -> 清理回归` 的顺序做 `git mv` 和导包修正,每阶段都用最小测试集验证。 + +**Tech Stack:** Java 21、Spring Boot 3.1.5、MyBatis-Plus、Shiro、JUnit 5、AssertJ、Maven + +--- + +## 目标目录 + +- `src/main/java/com/label/annotation` +- `src/main/java/com/label/aspect` +- `src/main/java/com/label/config` +- `src/main/java/com/label/controller` +- `src/main/java/com/label/dto` +- `src/main/java/com/label/entity` +- `src/main/java/com/label/event` +- `src/main/java/com/label/listener` +- `src/main/java/com/label/mapper` +- `src/main/java/com/label/service` +- `src/test/java/com/label/unit/PackageStructureMigrationTest.java` + +--- + +### Task 1: 锁定基础设施迁移目标 + +**Files:** +- Create: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` +- Modify: `src/main/java/com/label/common/aop/OperationLog.java` +- Modify: `src/main/java/com/label/common/aop/AuditAspect.java` +- Modify: `src/main/java/com/label/common/config/MybatisPlusConfig.java` +- Modify: `src/main/java/com/label/common/config/OpenApiConfig.java` +- Modify: `src/main/java/com/label/common/config/RedisConfig.java` +- Modify: `src/main/java/com/label/module/annotation/event/ExtractionApprovedEvent.java` +- Modify: `src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java` +- Modify: `src/main/java/com/label/module/annotation/service/ExtractionService.java` +- Test: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` + +- [ ] **Step 1: 写失败测试** + +```java +package com.label.unit; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@DisplayName("标准目录扁平化迁移守卫测试") +class PackageStructureMigrationTest { + + @Test + @DisplayName("基础设施类已迁移到目标目录") + void infrastructureTypesMoved() { + assertClassExists("com.label.annotation.OperationLog"); + assertClassExists("com.label.aspect.AuditAspect"); + assertClassExists("com.label.config.MybatisPlusConfig"); + assertClassExists("com.label.config.OpenApiConfig"); + assertClassExists("com.label.config.RedisConfig"); + assertClassExists("com.label.event.ExtractionApprovedEvent"); + assertClassExists("com.label.listener.ExtractionApprovedEventListener"); + + assertClassMissing("com.label.common.aop.OperationLog"); + assertClassMissing("com.label.common.aop.AuditAspect"); + assertClassMissing("com.label.common.config.MybatisPlusConfig"); + assertClassMissing("com.label.common.config.OpenApiConfig"); + assertClassMissing("com.label.common.config.RedisConfig"); + assertClassMissing("com.label.module.annotation.event.ExtractionApprovedEvent"); + assertClassMissing("com.label.module.annotation.service.ExtractionApprovedEventListener"); + } + + private static void assertClassExists(String fqcn) { + assertThatCode(() -> Class.forName(fqcn)).doesNotThrowAnyException(); + } + + private static void assertClassMissing(String fqcn) { + assertThatThrownBy(() -> Class.forName(fqcn)).isInstanceOf(ClassNotFoundException.class); + } +} +``` + +- [ ] **Step 2: 跑红** + +Run: `mvn -q "-Dtest=PackageStructureMigrationTest#infrastructureTypesMoved" test` + +Expected: FAIL,提示新包类不存在。 + +- [ ] **Step 3: 最小实现** + +先执行迁移: + +```powershell +git mv src/main/java/com/label/common/aop/OperationLog.java src/main/java/com/label/annotation/OperationLog.java +git mv src/main/java/com/label/common/aop/AuditAspect.java src/main/java/com/label/aspect/AuditAspect.java +git mv src/main/java/com/label/common/config/MybatisPlusConfig.java src/main/java/com/label/config/MybatisPlusConfig.java +git mv src/main/java/com/label/common/config/OpenApiConfig.java src/main/java/com/label/config/OpenApiConfig.java +git mv src/main/java/com/label/common/config/RedisConfig.java src/main/java/com/label/config/RedisConfig.java +git mv src/main/java/com/label/module/annotation/event/ExtractionApprovedEvent.java src/main/java/com/label/event/ExtractionApprovedEvent.java +git mv src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java src/main/java/com/label/listener/ExtractionApprovedEventListener.java +``` + +再做精确替换: + +```java +// OperationLog.java +package com.label.annotation; + +// AuditAspect.java +package com.label.aspect; +import com.label.annotation.OperationLog; + +// MybatisPlusConfig.java / OpenApiConfig.java / RedisConfig.java +package com.label.config; + +// ExtractionApprovedEvent.java +package com.label.event; + +// ExtractionApprovedEventListener.java +package com.label.listener; +import com.label.event.ExtractionApprovedEvent; +import com.label.module.annotation.entity.TrainingDataset; +import com.label.module.annotation.mapper.AnnotationResultMapper; +import com.label.module.annotation.mapper.TrainingDatasetMapper; +import com.label.module.source.entity.SourceData; +import com.label.module.source.mapper.SourceDataMapper; +import com.label.module.task.service.TaskService; +``` + +并把 `src/main/java/com/label/module/annotation/service/ExtractionService.java` 中事件 import 改成 `com.label.event.ExtractionApprovedEvent`。 + +- [ ] **Step 4: 跑绿** + +Run: +- `mvn -q "-Dtest=PackageStructureMigrationTest#infrastructureTypesMoved" test` +- `mvn -q -DskipTests compile` + +Expected: 两条命令都 `BUILD SUCCESS`。 + +- [ ] **Step 5: Commit** + +```powershell +git add src/test/java/com/label/unit/PackageStructureMigrationTest.java src/main/java/com/label/annotation src/main/java/com/label/aspect src/main/java/com/label/config src/main/java/com/label/event src/main/java/com/label/listener src/main/java/com/label/module/annotation/service/ExtractionService.java +git commit -m "refactor: flatten infrastructure packages" +``` + +--- + +### Task 2: 迁移 DTO、Entity、Mapper + +**Files:** +- Modify: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` +- Modify: `src/test/java/com/label/unit/OpenApiAnnotationTest.java` +- Modify: `src/test/java/com/label/integration/AuthIntegrationTest.java` +- Modify: `src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java` +- Modify: `src/test/java/com/label/integration/QaApprovalIntegrationTest.java` +- Modify: `src/test/java/com/label/integration/UserManagementIntegrationTest.java` +- Modify: `src/main/java/com/label/module/annotation/service/ExtractionService.java` +- Modify: `src/main/java/com/label/module/annotation/service/QaService.java` +- Modify: `src/main/java/com/label/module/config/service/SysConfigService.java` +- Modify: `src/main/java/com/label/module/config/controller/SysConfigController.java` +- Modify: `src/main/java/com/label/module/export/service/ExportService.java` +- Modify: `src/main/java/com/label/module/export/service/FinetuneService.java` +- Modify: `src/main/java/com/label/module/export/controller/ExportController.java` +- Modify: `src/main/java/com/label/module/source/service/SourceService.java` +- Modify: `src/main/java/com/label/module/source/controller/SourceController.java` +- Modify: `src/main/java/com/label/module/task/service/TaskClaimService.java` +- Modify: `src/main/java/com/label/module/task/service/TaskService.java` +- Modify: `src/main/java/com/label/module/task/controller/TaskController.java` +- Modify: `src/main/java/com/label/module/user/service/AuthService.java` +- Modify: `src/main/java/com/label/module/user/service/UserService.java` +- Modify: `src/main/java/com/label/module/user/controller/AuthController.java` +- Modify: `src/main/java/com/label/module/user/controller/UserController.java` +- Modify: `src/main/java/com/label/module/video/service/VideoProcessService.java` +- Modify: `src/main/java/com/label/module/video/controller/VideoController.java` +- Test: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` + +- [ ] **Step 1: 写失败测试** + +在 `PackageStructureMigrationTest.java` 里追加: + +```java + @Test + @DisplayName("DTO、实体、Mapper 已迁移到扁平数据层") + void dataTypesMoved() { + for (String fqcn : java.util.List.of( + "com.label.dto.LoginRequest", "com.label.dto.LoginResponse", "com.label.dto.UserInfoResponse", + "com.label.dto.TaskResponse", "com.label.dto.SourceResponse", + "com.label.entity.AnnotationResult", "com.label.entity.TrainingDataset", "com.label.entity.SysConfig", + "com.label.entity.ExportBatch", "com.label.entity.SourceData", "com.label.entity.AnnotationTask", + "com.label.entity.AnnotationTaskHistory", "com.label.entity.SysCompany", "com.label.entity.SysUser", + "com.label.entity.VideoProcessJob", + "com.label.mapper.AnnotationResultMapper", "com.label.mapper.TrainingDatasetMapper", + "com.label.mapper.SysConfigMapper", "com.label.mapper.ExportBatchMapper", "com.label.mapper.SourceDataMapper", + "com.label.mapper.AnnotationTaskMapper", "com.label.mapper.TaskHistoryMapper", + "com.label.mapper.SysCompanyMapper", "com.label.mapper.SysUserMapper", "com.label.mapper.VideoProcessJobMapper")) { + assertClassExists(fqcn); + } + } +``` + +- [ ] **Step 2: 跑红** + +Run: `mvn -q "-Dtest=PackageStructureMigrationTest#dataTypesMoved" test` + +Expected: FAIL,提示 `com.label.dto.LoginRequest` 等不存在。 + +- [ ] **Step 3: 最小实现** + +执行迁移: + +```powershell +git mv src/main/java/com/label/module/user/dto/LoginRequest.java src/main/java/com/label/dto/LoginRequest.java +git mv src/main/java/com/label/module/user/dto/LoginResponse.java src/main/java/com/label/dto/LoginResponse.java +git mv src/main/java/com/label/module/user/dto/UserInfoResponse.java src/main/java/com/label/dto/UserInfoResponse.java +git mv src/main/java/com/label/module/task/dto/TaskResponse.java src/main/java/com/label/dto/TaskResponse.java +git mv src/main/java/com/label/module/source/dto/SourceResponse.java src/main/java/com/label/dto/SourceResponse.java +git mv src/main/java/com/label/module/annotation/entity/AnnotationResult.java src/main/java/com/label/entity/AnnotationResult.java +git mv src/main/java/com/label/module/annotation/entity/TrainingDataset.java src/main/java/com/label/entity/TrainingDataset.java +git mv src/main/java/com/label/module/config/entity/SysConfig.java src/main/java/com/label/entity/SysConfig.java +git mv src/main/java/com/label/module/export/entity/ExportBatch.java src/main/java/com/label/entity/ExportBatch.java +git mv src/main/java/com/label/module/source/entity/SourceData.java src/main/java/com/label/entity/SourceData.java +git mv src/main/java/com/label/module/task/entity/AnnotationTask.java src/main/java/com/label/entity/AnnotationTask.java +git mv src/main/java/com/label/module/task/entity/AnnotationTaskHistory.java src/main/java/com/label/entity/AnnotationTaskHistory.java +git mv src/main/java/com/label/module/user/entity/SysCompany.java src/main/java/com/label/entity/SysCompany.java +git mv src/main/java/com/label/module/user/entity/SysUser.java src/main/java/com/label/entity/SysUser.java +git mv src/main/java/com/label/module/video/entity/VideoProcessJob.java src/main/java/com/label/entity/VideoProcessJob.java +git mv src/main/java/com/label/module/annotation/mapper/AnnotationResultMapper.java src/main/java/com/label/mapper/AnnotationResultMapper.java +git mv src/main/java/com/label/module/annotation/mapper/TrainingDatasetMapper.java src/main/java/com/label/mapper/TrainingDatasetMapper.java +git mv src/main/java/com/label/module/config/mapper/SysConfigMapper.java src/main/java/com/label/mapper/SysConfigMapper.java +git mv src/main/java/com/label/module/export/mapper/ExportBatchMapper.java src/main/java/com/label/mapper/ExportBatchMapper.java +git mv src/main/java/com/label/module/source/mapper/SourceDataMapper.java src/main/java/com/label/mapper/SourceDataMapper.java +git mv src/main/java/com/label/module/task/mapper/AnnotationTaskMapper.java src/main/java/com/label/mapper/AnnotationTaskMapper.java +git mv src/main/java/com/label/module/task/mapper/TaskHistoryMapper.java src/main/java/com/label/mapper/TaskHistoryMapper.java +git mv src/main/java/com/label/module/user/mapper/SysCompanyMapper.java src/main/java/com/label/mapper/SysCompanyMapper.java +git mv src/main/java/com/label/module/user/mapper/SysUserMapper.java src/main/java/com/label/mapper/SysUserMapper.java +git mv src/main/java/com/label/module/video/mapper/VideoProcessJobMapper.java src/main/java/com/label/mapper/VideoProcessJobMapper.java +``` + +统一替换包声明: + +```java +package com.label.dto; +package com.label.entity; +package com.label.mapper; +``` + +然后把上面 `Files` 列表中的旧 `com.label.module.*.(dto|entity|mapper)` import 全部改到新包。 + +- [ ] **Step 4: 跑绿** + +Run: +- `mvn -q "-Dtest=PackageStructureMigrationTest#dataTypesMoved,OpenApiAnnotationTest,AuthIntegrationTest,ExtractionApprovalIntegrationTest,QaApprovalIntegrationTest,UserManagementIntegrationTest" test` + +Expected: PASS。 + +- [ ] **Step 5: Commit** + +```powershell +git add src/main/java/com/label/dto src/main/java/com/label/entity src/main/java/com/label/mapper src/test/java/com/label/unit/PackageStructureMigrationTest.java src/test/java/com/label/unit/OpenApiAnnotationTest.java src/test/java/com/label/integration/AuthIntegrationTest.java src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java src/test/java/com/label/integration/QaApprovalIntegrationTest.java src/test/java/com/label/integration/UserManagementIntegrationTest.java src/main/java/com/label/module +git commit -m "refactor: flatten dto entity and mapper packages" +``` + +--- + +### Task 3: 迁移业务服务层 + +**Files:** +- Modify: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` +- Modify: `src/main/java/com/label/module/annotation/controller/ExtractionController.java` +- Modify: `src/main/java/com/label/module/annotation/controller/QaController.java` +- Modify: `src/main/java/com/label/module/config/controller/SysConfigController.java` +- Modify: `src/main/java/com/label/module/export/controller/ExportController.java` +- Modify: `src/main/java/com/label/module/source/controller/SourceController.java` +- Modify: `src/main/java/com/label/module/task/controller/TaskController.java` +- Modify: `src/main/java/com/label/module/user/controller/AuthController.java` +- Modify: `src/main/java/com/label/module/user/controller/UserController.java` +- Modify: `src/main/java/com/label/module/video/controller/VideoController.java` +- Test: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` + +- [ ] **Step 1: 写失败测试** + +```java + @Test + @DisplayName("服务类已迁移到扁平 service 目录") + void serviceTypesMoved() { + for (String fqcn : java.util.List.of( + "com.label.service.ExtractionService", "com.label.service.QaService", + "com.label.service.SysConfigService", "com.label.service.ExportService", + "com.label.service.FinetuneService", "com.label.service.SourceService", + "com.label.service.TaskClaimService", "com.label.service.TaskService", + "com.label.service.AuthService", "com.label.service.UserService", + "com.label.service.VideoProcessService")) { + assertClassExists(fqcn); + } + } +``` + +- [ ] **Step 2: 跑红** + +Run: `mvn -q "-Dtest=PackageStructureMigrationTest#serviceTypesMoved" test` + +Expected: FAIL,提示 `com.label.service.*` 不存在。 + +- [ ] **Step 3: 最小实现** + +执行迁移: + +```powershell +git mv src/main/java/com/label/module/annotation/service/ExtractionService.java src/main/java/com/label/service/ExtractionService.java +git mv src/main/java/com/label/module/annotation/service/QaService.java src/main/java/com/label/service/QaService.java +git mv src/main/java/com/label/module/config/service/SysConfigService.java src/main/java/com/label/service/SysConfigService.java +git mv src/main/java/com/label/module/export/service/ExportService.java src/main/java/com/label/service/ExportService.java +git mv src/main/java/com/label/module/export/service/FinetuneService.java src/main/java/com/label/service/FinetuneService.java +git mv src/main/java/com/label/module/source/service/SourceService.java src/main/java/com/label/service/SourceService.java +git mv src/main/java/com/label/module/task/service/TaskClaimService.java src/main/java/com/label/service/TaskClaimService.java +git mv src/main/java/com/label/module/task/service/TaskService.java src/main/java/com/label/service/TaskService.java +git mv src/main/java/com/label/module/user/service/AuthService.java src/main/java/com/label/service/AuthService.java +git mv src/main/java/com/label/module/user/service/UserService.java src/main/java/com/label/service/UserService.java +git mv src/main/java/com/label/module/video/service/VideoProcessService.java src/main/java/com/label/service/VideoProcessService.java +``` + +统一替换: + +```java +package com.label.service; +``` + +并把 `ExtractionController`、`QaController`、`SysConfigController`、`ExportController`、`SourceController`、`TaskController`、`AuthController`、`UserController`、`VideoController` 的服务导包改到 `com.label.service.*`。 + +- [ ] **Step 4: 跑绿** + +Run: +- `mvn -q "-Dtest=PackageStructureMigrationTest#serviceTypesMoved" test` +- `mvn -q -DskipTests compile` + +Expected: PASS。 + +- [ ] **Step 5: Commit** + +```powershell +git add src/main/java/com/label/service src/main/java/com/label/module/annotation/controller/ExtractionController.java src/main/java/com/label/module/annotation/controller/QaController.java src/main/java/com/label/module/config/controller/SysConfigController.java src/main/java/com/label/module/export/controller/ExportController.java src/main/java/com/label/module/source/controller/SourceController.java src/main/java/com/label/module/task/controller/TaskController.java src/main/java/com/label/module/user/controller/AuthController.java src/main/java/com/label/module/user/controller/UserController.java src/main/java/com/label/module/video/controller/VideoController.java src/test/java/com/label/unit/PackageStructureMigrationTest.java +git commit -m "refactor: flatten service packages" +``` + +--- + +### Task 4: 迁移控制器并收敛 OpenAPI 测试 + +**Files:** +- Modify: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` +- Modify: `src/test/java/com/label/unit/OpenApiAnnotationTest.java` +- Test: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` +- Test: `src/test/java/com/label/unit/OpenApiAnnotationTest.java` + +- [ ] **Step 1: 写失败测试** + +```java + @Test + @DisplayName("控制器已迁移到扁平 controller 目录") + void controllerTypesMoved() { + for (String fqcn : java.util.List.of( + "com.label.controller.AuthController", "com.label.controller.UserController", + "com.label.controller.SourceController", "com.label.controller.TaskController", + "com.label.controller.ExtractionController", "com.label.controller.QaController", + "com.label.controller.ExportController", "com.label.controller.SysConfigController", + "com.label.controller.VideoController")) { + assertClassExists(fqcn); + } + } +``` + +- [ ] **Step 2: 跑红** + +Run: `mvn -q "-Dtest=PackageStructureMigrationTest#controllerTypesMoved" test` + +Expected: FAIL,提示 `com.label.controller.*` 不存在。 + +- [ ] **Step 3: 最小实现** + +执行迁移: + +```powershell +git mv src/main/java/com/label/module/annotation/controller/ExtractionController.java src/main/java/com/label/controller/ExtractionController.java +git mv src/main/java/com/label/module/annotation/controller/QaController.java src/main/java/com/label/controller/QaController.java +git mv src/main/java/com/label/module/config/controller/SysConfigController.java src/main/java/com/label/controller/SysConfigController.java +git mv src/main/java/com/label/module/export/controller/ExportController.java src/main/java/com/label/controller/ExportController.java +git mv src/main/java/com/label/module/source/controller/SourceController.java src/main/java/com/label/controller/SourceController.java +git mv src/main/java/com/label/module/task/controller/TaskController.java src/main/java/com/label/controller/TaskController.java +git mv src/main/java/com/label/module/user/controller/AuthController.java src/main/java/com/label/controller/AuthController.java +git mv src/main/java/com/label/module/user/controller/UserController.java src/main/java/com/label/controller/UserController.java +git mv src/main/java/com/label/module/video/controller/VideoController.java src/main/java/com/label/controller/VideoController.java +``` + +统一替换: + +```java +package com.label.controller; +``` + +并把 `OpenApiAnnotationTest.java` 的 import 替换成: + +```java +import com.label.controller.AuthController; +import com.label.controller.UserController; +import com.label.controller.SourceController; +import com.label.controller.TaskController; +import com.label.controller.ExtractionController; +import com.label.controller.QaController; +import com.label.controller.ExportController; +import com.label.controller.SysConfigController; +import com.label.controller.VideoController; +import com.label.dto.LoginRequest; +import com.label.dto.LoginResponse; +import com.label.dto.UserInfoResponse; +import com.label.dto.TaskResponse; +import com.label.dto.SourceResponse; +``` + +- [ ] **Step 4: 跑绿** + +Run: `mvn -q "-Dtest=PackageStructureMigrationTest#controllerTypesMoved,OpenApiAnnotationTest" test` + +Expected: PASS。 + +- [ ] **Step 5: Commit** + +```powershell +git add src/main/java/com/label/controller src/test/java/com/label/unit/PackageStructureMigrationTest.java src/test/java/com/label/unit/OpenApiAnnotationTest.java +git commit -m "refactor: flatten controller packages" +``` + +--- + +### Task 5: 清理旧包残留并做全量回归 + +**Files:** +- Modify: `src/test/java/com/label/unit/PackageStructureMigrationTest.java` +- Test: `src/test/java/com/label/LabelBackendApplicationTests.java` +- Test: `src/test/java/com/label/unit/ApplicationConfigTest.java` +- Test: `src/test/java/com/label/unit/ShiroConfigTest.java` +- Test: `src/test/java/com/label/unit/StateMachineTest.java` +- Test: `src/test/java/com/label/unit/TokenFilterTest.java` +- Test: `src/test/java/com/label/integration/AuthIntegrationTest.java` +- Test: `src/test/java/com/label/integration/ExportIntegrationTest.java` +- Test: `src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java` +- Test: `src/test/java/com/label/integration/MultiTenantIsolationTest.java` +- Test: `src/test/java/com/label/integration/QaApprovalIntegrationTest.java` +- Test: `src/test/java/com/label/integration/ShiroFilterIntegrationTest.java` +- Test: `src/test/java/com/label/integration/SourceIntegrationTest.java` +- Test: `src/test/java/com/label/integration/SysConfigIntegrationTest.java` +- Test: `src/test/java/com/label/integration/TaskClaimConcurrencyTest.java` +- Test: `src/test/java/com/label/integration/UserManagementIntegrationTest.java` +- Test: `src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java` + +- [ ] **Step 1: 写最终守卫测试** + +在 `PackageStructureMigrationTest.java` 中补充: + +```java + @Test + @DisplayName("源码中不再引用旧的 module、common.aop、common.config 包") + void sourceTreeHasNoLegacyPackageReferences() throws Exception { + try (java.util.stream.Stream paths = java.nio.file.Files.walk(java.nio.file.Path.of("src"))) { + java.util.List violations = paths + .filter(path -> path.toString().endsWith(".java")) + .map(path -> { + try { + String text = java.nio.file.Files.readString(path); + boolean legacy = text.contains("com.label.module.") + || text.contains("com.label.common.aop") + || text.contains("com.label.common.config"); + return legacy ? path.toString() : null; + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .filter(java.util.Objects::nonNull) + .toList(); + + org.assertj.core.api.Assertions.assertThat(violations).isEmpty(); + } + } +``` + +- [ ] **Step 2: 跑红并定位残留** + +Run: +- `mvn -q "-Dtest=PackageStructureMigrationTest#sourceTreeHasNoLegacyPackageReferences" test` +- `rg -n "com\.label\.module\.|com\.label\.common\.aop|com\.label\.common\.config" src/main/java src/test/java` + +Expected: 初次 FAIL,并列出残留文件。 + +- [ ] **Step 3: 清理残留并确认旧目录为空** + +Run: + +```powershell +Get-ChildItem -Path src/main/java/com/label/module -Recurse +``` + +Expected: 没有 Java 文件残留;确认后删除空目录。 + +- [ ] **Step 4: 全量回归** + +Run: `mvn clean test` + +Expected: `BUILD SUCCESS`。 + +- [ ] **Step 5: Commit** + +```powershell +git add src/main/java src/test/java +git commit -m "refactor: complete backend directory flattening" +``` + +--- + +## 自检 + +- 覆盖了 `annotation / aspect / config / event / listener / dto / entity / mapper / service / controller` +- 没有引入 `service.impl` +- 没有拆分 `dto/request`、`dto/response` +- 最终门槛是 `mvn clean test` diff --git a/pom.xml b/pom.xml index a2ef961..6ecd4d6 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.5 + 3.1.5 @@ -17,7 +17,11 @@ jar - 17 + 21 + UTF-8 + 42.2.24 + 3.5.3.1 + 2.3.0 UTF-8 @@ -71,14 +75,21 @@ org.postgresql postgresql + ${postgrescp.version} runtime - + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} @@ -91,7 +102,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.5.0 + 2.3.0 diff --git a/src/main/java/com/label/common/shiro/TokenFilter.java b/src/main/java/com/label/common/shiro/TokenFilter.java index 513768d..9d7c6c7 100644 --- a/src/main/java/com/label/common/shiro/TokenFilter.java +++ b/src/main/java/com/label/common/shiro/TokenFilter.java @@ -11,9 +11,9 @@ import org.springframework.web.filter.OncePerRequestFilter; import com.fasterxml.jackson.databind.ObjectMapper; import com.label.common.context.CompanyContext; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; import com.label.common.result.Result; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; diff --git a/src/main/java/com/label/common/shiro/UserRealm.java b/src/main/java/com/label/common/shiro/UserRealm.java index 0fb11d9..bc99ed6 100644 --- a/src/main/java/com/label/common/shiro/UserRealm.java +++ b/src/main/java/com/label/common/shiro/UserRealm.java @@ -1,7 +1,8 @@ package com.label.common.shiro; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.authc.*; diff --git a/src/main/java/com/label/common/shiro/ShiroConfig.java b/src/main/java/com/label/config/ShiroConfig.java similarity index 93% rename from src/main/java/com/label/common/shiro/ShiroConfig.java rename to src/main/java/com/label/config/ShiroConfig.java index aa648b2..5d5d8bc 100644 --- a/src/main/java/com/label/common/shiro/ShiroConfig.java +++ b/src/main/java/com/label/config/ShiroConfig.java @@ -1,4 +1,4 @@ -package com.label.common.shiro; +package com.label.config; import java.util.List; @@ -10,7 +10,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.databind.ObjectMapper; -import com.label.common.redis.RedisService; +import com.label.common.shiro.TokenFilter; +import com.label.common.shiro.UserRealm; +import com.label.service.RedisService; /** * Shiro 安全配置。 diff --git a/src/main/java/com/label/service/AuthService.java b/src/main/java/com/label/service/AuthService.java index d6b8ca6..ffe7b57 100644 --- a/src/main/java/com/label/service/AuthService.java +++ b/src/main/java/com/label/service/AuthService.java @@ -1,8 +1,6 @@ package com.label.service; import com.label.common.exception.BusinessException; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; import com.label.common.shiro.TokenPrincipal; import com.label.dto.LoginRequest; import com.label.dto.LoginResponse; @@ -11,6 +9,8 @@ import com.label.entity.SysCompany; import com.label.entity.SysUser; import com.label.mapper.SysCompanyMapper; import com.label.mapper.SysUserMapper; +import com.label.util.RedisKeyManager; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/label/common/redis/RedisService.java b/src/main/java/com/label/service/RedisService.java similarity index 98% rename from src/main/java/com/label/common/redis/RedisService.java rename to src/main/java/com/label/service/RedisService.java index 1b2cade..3eaaa2e 100644 --- a/src/main/java/com/label/common/redis/RedisService.java +++ b/src/main/java/com/label/service/RedisService.java @@ -1,4 +1,4 @@ -package com.label.common.redis; +package com.label.service; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; diff --git a/src/main/java/com/label/service/TaskClaimService.java b/src/main/java/com/label/service/TaskClaimService.java index 9ab317d..7ecd9dc 100644 --- a/src/main/java/com/label/service/TaskClaimService.java +++ b/src/main/java/com/label/service/TaskClaimService.java @@ -2,8 +2,6 @@ package com.label.service; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.label.common.exception.BusinessException; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; import com.label.common.shiro.TokenPrincipal; import com.label.common.statemachine.StateValidator; import com.label.common.statemachine.TaskStatus; @@ -11,6 +9,8 @@ import com.label.entity.AnnotationTask; import com.label.entity.AnnotationTaskHistory; import com.label.mapper.AnnotationTaskMapper; import com.label.mapper.TaskHistoryMapper; +import com.label.util.RedisKeyManager; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; diff --git a/src/main/java/com/label/service/UserService.java b/src/main/java/com/label/service/UserService.java index def20f8..5dbe50e 100644 --- a/src/main/java/com/label/service/UserService.java +++ b/src/main/java/com/label/service/UserService.java @@ -11,12 +11,11 @@ import org.springframework.transaction.annotation.Transactional; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.label.common.exception.BusinessException; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; import com.label.common.result.PageResult; import com.label.common.shiro.TokenPrincipal; import com.label.entity.SysUser; import com.label.mapper.SysUserMapper; +import com.label.util.RedisKeyManager; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/label/common/redis/RedisKeyManager.java b/src/main/java/com/label/util/RedisKeyManager.java similarity index 96% rename from src/main/java/com/label/common/redis/RedisKeyManager.java rename to src/main/java/com/label/util/RedisKeyManager.java index 80d5855..8adb2ed 100644 --- a/src/main/java/com/label/common/redis/RedisKeyManager.java +++ b/src/main/java/com/label/util/RedisKeyManager.java @@ -1,4 +1,4 @@ -package com.label.common.redis; +package com.label.util; /** * Centralized Redis key naming conventions. diff --git a/src/test/java/com/label/integration/ExportIntegrationTest.java b/src/test/java/com/label/integration/ExportIntegrationTest.java index 1b7397a..4c59a2c 100644 --- a/src/test/java/com/label/integration/ExportIntegrationTest.java +++ b/src/test/java/com/label/integration/ExportIntegrationTest.java @@ -1,8 +1,9 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; diff --git a/src/test/java/com/label/integration/MultiTenantIsolationTest.java b/src/test/java/com/label/integration/MultiTenantIsolationTest.java index 960ec32..2ac5a28 100644 --- a/src/test/java/com/label/integration/MultiTenantIsolationTest.java +++ b/src/test/java/com/label/integration/MultiTenantIsolationTest.java @@ -1,8 +1,9 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/com/label/integration/ShiroFilterIntegrationTest.java b/src/test/java/com/label/integration/ShiroFilterIntegrationTest.java index 1bf982d..1884e81 100644 --- a/src/test/java/com/label/integration/ShiroFilterIntegrationTest.java +++ b/src/test/java/com/label/integration/ShiroFilterIntegrationTest.java @@ -1,9 +1,10 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; import com.label.common.result.Result; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.apache.shiro.SecurityUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/label/integration/SourceIntegrationTest.java b/src/test/java/com/label/integration/SourceIntegrationTest.java index d8d52fd..9e8f0a5 100644 --- a/src/test/java/com/label/integration/SourceIntegrationTest.java +++ b/src/test/java/com/label/integration/SourceIntegrationTest.java @@ -1,8 +1,9 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; diff --git a/src/test/java/com/label/integration/SysConfigIntegrationTest.java b/src/test/java/com/label/integration/SysConfigIntegrationTest.java index da361c0..55e140d 100644 --- a/src/test/java/com/label/integration/SysConfigIntegrationTest.java +++ b/src/test/java/com/label/integration/SysConfigIntegrationTest.java @@ -1,8 +1,9 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/com/label/integration/TaskClaimConcurrencyTest.java b/src/test/java/com/label/integration/TaskClaimConcurrencyTest.java index c972bca..c958031 100644 --- a/src/test/java/com/label/integration/TaskClaimConcurrencyTest.java +++ b/src/test/java/com/label/integration/TaskClaimConcurrencyTest.java @@ -1,8 +1,9 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.web.client.TestRestTemplate; diff --git a/src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java b/src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java index 37cfb24..2fd05c7 100644 --- a/src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java +++ b/src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java @@ -1,8 +1,9 @@ package com.label.integration; import com.label.AbstractIntegrationTest; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/com/label/unit/ShiroConfigTest.java b/src/test/java/com/label/unit/ShiroConfigTest.java index 1d12b06..dc82ca4 100644 --- a/src/test/java/com/label/unit/ShiroConfigTest.java +++ b/src/test/java/com/label/unit/ShiroConfigTest.java @@ -1,8 +1,9 @@ package com.label.unit; -import com.label.common.redis.RedisService; -import com.label.common.shiro.ShiroConfig; import com.label.common.shiro.UserRealm; +import com.label.config.ShiroConfig; +import com.label.service.RedisService; + import org.apache.shiro.SecurityUtils; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.util.ThreadContext; diff --git a/src/test/java/com/label/unit/TokenFilterTest.java b/src/test/java/com/label/unit/TokenFilterTest.java index 7569c57..97ce341 100644 --- a/src/test/java/com/label/unit/TokenFilterTest.java +++ b/src/test/java/com/label/unit/TokenFilterTest.java @@ -2,12 +2,13 @@ package com.label.unit; import com.fasterxml.jackson.databind.ObjectMapper; import com.label.common.context.CompanyContext; -import com.label.common.redis.RedisKeyManager; -import com.label.common.redis.RedisService; -import com.label.common.shiro.ShiroConfig; import com.label.common.shiro.TokenFilter; import com.label.common.shiro.TokenPrincipal; import com.label.common.shiro.UserRealm; +import com.label.config.ShiroConfig; +import com.label.service.RedisService; +import com.label.util.RedisKeyManager; + import org.apache.shiro.SecurityUtils; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.util.ThreadContext; diff --git a/微服务开发规范文档.md b/微服务开发规范文档.md new file mode 100644 index 0000000..7a318dd --- /dev/null +++ b/微服务开发规范文档.md @@ -0,0 +1,422 @@ +# 微服务开发规范文档 + +## 项目概述 +本文档记录了当前微服务项目的技术栈、依赖版本、代码风格和项目结构,供新微服务开发时参考。 + +## 技术栈版本 + +### 核心框架版本 +- **Java版本**: 21 +- **Spring Boot**: 3.1.5 +- **Spring Cloud**: 2022.0.4 +- **Spring Cloud Alibaba**: 2022.0.0.0 +- **Maven**: 3.x + +### 主要依赖版本 +```xml + + + org.springframework.boot + spring-boot-starter-parent + 3.1.5 + + + +3.5.3.1 +8.2.0 + + +3.1.1 + + +2.0.23 + + +2.3.0 + + +0.2.16 + + +0.3.10 +``` + +## 项目结构规范 + +### 标准目录结构 +``` +src/main/java/com/example/{service-name}/ +├── annotation/ # 自定义注解 +├── aspect/ # AOP切面 +├── {ServiceName}Application.java # 启动类 +├── common/ # 公共类 +│ ├── exception/ # 异常处理 +│ ├── Result.java # 统一响应结果 +│ └── ResultCode.java # 响应码枚举 +├── config/ # 配置类 +├── constant/ # 常量类 +├── controller/ # 控制器 +├── dto/ # 数据传输对象 +│ ├── request/ # 请求DTO +│ ├── response/ # 响应DTO +│ └── common/ # 公共DTO +├── entity/ # 实体类 +├── feign/ # Feign客户端 +├── mapper/ # MyBatis映射器 +├── scheduled/ # 定时任务 +├── service/ # 业务服务 +├── typehandler/ # 类型处理器 +└── util/ # 工具类 +``` + +### 资源文件结构 +``` +src/main/resources/ +├── application.yml # 主配置文件 +├── application-pro.yml # 生产环境配置 +└── mapper/ # MyBatis XML映射文件 + └── *.xml +``` + +## 代码风格规范 + +### 1. 启动类规范 +```java +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients("com.example.{service-name}.feign") +@EnableScheduling +@MapperScan("com.example.{service-name}.mapper") +public class {ServiceName}Application { + public static void main(String[] args) { + SpringApplication.run({ServiceName}Application.class, args); + } +} +``` + +### 2. 统一响应结果类 +```java +@Data +public class Result { + private Integer code; + private String message; + private T data; + + public Result() {} + + public Result(Integer code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static Result success() { + return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null); + } + + public static Result success(T data) { + return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data); + } + + public static Result error(String message) { + return new Result<>(ResultCode.ERROR.getCode(), message, null); + } + + public static Result error(ResultCode resultCode) { + return new Result<>(resultCode.getCode(), resultCode.getMessage(), null); + } +} +``` + +### 3. 实体类规范 +```java +@Data +@TableName("table_name") +public class EntityName { + @TableId(type = IdType.AUTO) + private Long id; + + private String fieldName; + + @TableField("is_deleted") + private Integer isDeleted; + + @TableField(exist = false) + private Boolean customField; // 非数据库字段 + + private LocalDateTime createdAt; + private LocalDateTime updatedAt; +} +``` + +### 4. 控制器规范 +```java +@Tag(name = "模块名称") +@Slf4j +@RestController +@RequestMapping("/api/v1/module") +@RequiredArgsConstructor +public class ModuleController { + + private final ModuleService moduleService; + + @Operation(summary = "接口描述") + @GetMapping("/endpoint") + public Result methodName() { + // 业务逻辑 + return Result.success(data); + } +} +``` + +### 5. 服务接口规范 +```java +@Service +public interface ModuleService extends IService { + // 自定义业务方法 +} +``` + +### 6. DTO规范 +```java +@Data +public class ModuleRequest { + /** + * 字段描述 + */ + private String fieldName; +} +``` + +## 配置文件规范 + +### 1. 主配置文件 (application.yml) +```yaml +server: + port: 8080 + +spring: + application: + name: {service-name} + profiles: + active: test + data: + redis: + username: default + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD:} + database: 0 + timeout: 10000ms + lettuce: + pool: + max-active: 8 + max-idle: 8 + min-idle: 0 + max-wait: -1ms + session: + store-type: redis + timeout: 30m + mvc: + pathmatch: + matching-strategy: ant_path_matcher + cloud: + nacos: + discovery: + server-addr: ${NACOS_SERVER:localhost:8848} + namespace: ${spring.profiles.active} + username: ${NACOS_USERNAME:nacos} + password: ${NACOS_PASSWORD:nacos} + loadbalancer: + ribbon: + enabled: false + +# MongoDB配置 (新微服务使用) +spring: + data: + mongodb: + uri: ${MONGODB_URI:mongodb://localhost:27017/database_name} + database: ${MONGODB_DATABASE:database_name} + +# 日志配置 +logging: + level: + org.springframework.security: DEBUG + com.example.{service-name}: DEBUG + +# Feign配置 +feign: + client: + config: + default: + connectTimeout: 5000 + readTimeout: 10000 + loggerLevel: basic + compression: + request: + enabled: true + response: + enabled: true +``` + +### 2. 生产环境配置 (application-pro.yml) +```yaml +# 生产环境特定配置 +logging: + level: + com.example.{service-name}: INFO + +feign: + client: + config: + default: + loggerLevel: basic +``` + +## 新微服务MongoDB配置 + +### 1. 添加MongoDB依赖 +```xml + + + org.springframework.boot + spring-boot-starter-data-mongodb + +``` + +### 2. MongoDB配置类 +```java +@Configuration +@EnableMongoRepositories(basePackages = "com.example.{service-name}.repository") +public class MongoConfig { + + @Bean + public MongoTemplate mongoTemplate(MongoDatabaseFactory mongoDatabaseFactory) { + return new MongoTemplate(mongoDatabaseFactory); + } +} +``` + +### 3. MongoDB实体类规范 +```java +@Document(collection = "collection_name") +@Data +public class MongoEntity { + @Id + private String id; + + @Field("field_name") + private String fieldName; + + @CreatedDate + private LocalDateTime createdAt; + + @LastModifiedDate + private LocalDateTime updatedAt; +} +``` + +### 4. MongoDB Repository规范 +```java +@Repository +public interface MongoEntityRepository extends MongoRepository { + // 自定义查询方法 + List findByFieldName(String fieldName); +} +``` + +## 开发规范 + +### 1. 包命名规范 +- 基础包名: `com.example.{service-name}` +- 服务名使用小写字母和连字符,如: `user-service`, `order-service` + +### 2. 类命名规范 +- 实体类: 使用名词,如 `User`, `Order` +- 控制器: 以 `Controller` 结尾,如 `UserController` +- 服务类: 以 `Service` 结尾,如 `UserService` +- DTO类: 以 `Request`/`Response` 结尾,如 `UserRequest`, `UserResponse` +- 常量类: 以 `Constants` 结尾,如 `UserConstants` + +### 3. 方法命名规范 +- 查询方法: `get`, `find`, `list`, `query` +- 创建方法: `create`, `add`, `save` +- 更新方法: `update`, `modify` +- 删除方法: `delete`, `remove` + +### 4. 异常处理规范 +- 使用统一的异常处理机制 +- 自定义业务异常继承 `RuntimeException` +- 使用 `@ControllerAdvice` 进行全局异常处理 + +### 5. 日志规范 +- 使用 `@Slf4j` 注解 +- 日志级别: DEBUG(开发), INFO(生产) +- 关键操作必须记录日志 + +## 部署配置 + +### 1. Dockerfile规范 +```dockerfile +FROM registry.bjzgzp.com:4433/library/eclipse-temurin:21-jdk-ubi10-minimal + +WORKDIR /app + +COPY ./target/{service-name}.jar /app/{service-name}.jar + +EXPOSE 8080 + +ENTRYPOINT ["java", "-Djava.net.preferIPv4Stack=true", "-jar", "/app/{service-name}.jar"] +``` + +### 2. Maven配置 +```xml + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + + +``` + +## 注意事项 + +1. **数据库选择**: 新微服务使用MongoDB 6.x,移除MySQL相关依赖 +2. **版本兼容性**: 确保所有依赖版本与当前项目保持一致 +3. **配置管理**: 使用环境变量管理敏感配置信息 +4. **服务发现**: 使用Nacos进行服务注册与发现 +5. **API文档**: 使用SpringDoc OpenAPI 3生成API文档 +6. **缓存策略**: 使用Redis进行缓存和会话管理 +7. **监控日志**: 集成适当的监控和日志记录机制 + +## 快速开始模板 + +创建新微服务时,可以参考以下步骤: + +1. 复制当前项目的 `pom.xml`,修改 `artifactId` 和 `name` +2. 移除MySQL相关依赖,添加MongoDB依赖 +3. 复制项目结构,修改包名 +4. 配置MongoDB连接信息 +5. 创建基础的Controller、Service、Repository +6. 配置Dockerfile和部署脚本 + +遵循以上规范可以确保新微服务与现有系统保持一致的技术栈和代码风格。