优化现有目录结构

This commit is contained in:
wh
2026-04-14 14:59:46 +08:00
parent c524fb08e1
commit ceaac48051
21 changed files with 1181 additions and 39 deletions

172
README.md
View File

@@ -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》中的开发规范包括
- 代码风格规范
- 命名规范
- 异常处理规范
- 日志规范

View File

@@ -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<java.nio.file.Path> paths = java.nio.file.Files.walk(java.nio.file.Path.of("src"))) {
java.util.List<String> 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`

19
pom.xml
View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version> <version>3.1.5</version>
<relativePath/> <relativePath/>
</parent> </parent>
@@ -17,7 +17,11 @@
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<java.version>17</java.version> <java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<postgrescp.version>42.2.24</postgrescp.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<springdoc-openapi.version>2.3.0</springdoc-openapi.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
@@ -71,14 +75,21 @@
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<version>${postgrescp.version}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- MyBatis Plus --> <!-- MyBatis Plus -->
<dependency> <!-- <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.10</version> <version>3.5.10</version>
</dependency> -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency> </dependency>
<!-- MyBatis Plus JSqlParser (required for TenantLineInnerInterceptor in 3.5.7+) --> <!-- MyBatis Plus JSqlParser (required for TenantLineInnerInterceptor in 3.5.7+) -->
@@ -91,7 +102,7 @@
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version> <version>2.3.0</version>
</dependency> </dependency>
<!-- Apache Shiro --> <!-- Apache Shiro -->

View File

@@ -11,9 +11,9 @@ import org.springframework.web.filter.OncePerRequestFilter;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.label.common.context.CompanyContext; 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.common.result.Result;
import com.label.service.RedisService;
import com.label.util.RedisKeyManager;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;

View File

@@ -1,7 +1,8 @@
package com.label.common.shiro; package com.label.common.shiro;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*; import org.apache.shiro.authc.*;

View File

@@ -1,4 +1,4 @@
package com.label.common.shiro; package com.label.config;
import java.util.List; import java.util.List;
@@ -10,7 +10,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import com.fasterxml.jackson.databind.ObjectMapper; 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 安全配置 * Shiro 安全配置

View File

@@ -1,8 +1,6 @@
package com.label.service; package com.label.service;
import com.label.common.exception.BusinessException; 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.shiro.TokenPrincipal;
import com.label.dto.LoginRequest; import com.label.dto.LoginRequest;
import com.label.dto.LoginResponse; import com.label.dto.LoginResponse;
@@ -11,6 +9,8 @@ import com.label.entity.SysCompany;
import com.label.entity.SysUser; import com.label.entity.SysUser;
import com.label.mapper.SysCompanyMapper; import com.label.mapper.SysCompanyMapper;
import com.label.mapper.SysUserMapper; import com.label.mapper.SysUserMapper;
import com.label.util.RedisKeyManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;

View File

@@ -1,4 +1,4 @@
package com.label.common.redis; package com.label.service;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;

View File

@@ -2,8 +2,6 @@ package com.label.service;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.label.common.exception.BusinessException; 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.shiro.TokenPrincipal;
import com.label.common.statemachine.StateValidator; import com.label.common.statemachine.StateValidator;
import com.label.common.statemachine.TaskStatus; import com.label.common.statemachine.TaskStatus;
@@ -11,6 +9,8 @@ import com.label.entity.AnnotationTask;
import com.label.entity.AnnotationTaskHistory; import com.label.entity.AnnotationTaskHistory;
import com.label.mapper.AnnotationTaskMapper; import com.label.mapper.AnnotationTaskMapper;
import com.label.mapper.TaskHistoryMapper; import com.label.mapper.TaskHistoryMapper;
import com.label.util.RedisKeyManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;

View File

@@ -11,12 +11,11 @@ import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.label.common.exception.BusinessException; 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.result.PageResult;
import com.label.common.shiro.TokenPrincipal; import com.label.common.shiro.TokenPrincipal;
import com.label.entity.SysUser; import com.label.entity.SysUser;
import com.label.mapper.SysUserMapper; import com.label.mapper.SysUserMapper;
import com.label.util.RedisKeyManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View File

@@ -1,4 +1,4 @@
package com.label.common.redis; package com.label.util;
/** /**
* Centralized Redis key naming conventions. * Centralized Redis key naming conventions.

View File

@@ -1,8 +1,9 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; import com.label.AbstractIntegrationTest;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;

View File

@@ -1,8 +1,9 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; import com.label.AbstractIntegrationTest;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;

View File

@@ -1,9 +1,10 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; 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.common.result.Result;
import com.label.service.RedisService;
import com.label.util.RedisKeyManager;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;

View File

@@ -1,8 +1,9 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; import com.label.AbstractIntegrationTest;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;

View File

@@ -1,8 +1,9 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; import com.label.AbstractIntegrationTest;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;

View File

@@ -1,8 +1,9 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; import com.label.AbstractIntegrationTest;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;

View File

@@ -1,8 +1,9 @@
package com.label.integration; package com.label.integration;
import com.label.AbstractIntegrationTest; import com.label.AbstractIntegrationTest;
import com.label.common.redis.RedisKeyManager; import com.label.service.RedisService;
import com.label.common.redis.RedisService; import com.label.util.RedisKeyManager;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;

View File

@@ -1,8 +1,9 @@
package com.label.unit; 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.common.shiro.UserRealm;
import com.label.config.ShiroConfig;
import com.label.service.RedisService;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadContext;

View File

@@ -2,12 +2,13 @@ package com.label.unit;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.label.common.context.CompanyContext; 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.TokenFilter;
import com.label.common.shiro.TokenPrincipal; import com.label.common.shiro.TokenPrincipal;
import com.label.common.shiro.UserRealm; 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.SecurityUtils;
import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadContext;

View File

@@ -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
<!-- 核心依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.5</version>
</parent>
<!-- 数据库相关 -->
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<mysql-connector-j.version>8.2.0</mysql-connector-j.version>
<!-- 缓存相关 -->
<spring-session.version>3.1.1</spring-session.version>
<!-- 阿里云相关 -->
<aliyun-sdk-version>2.0.23</aliyun-sdk-version>
<!-- 文档相关 -->
<springdoc-openapi.version>2.3.0</springdoc-openapi.version>
<!-- 微信支付 -->
<wechatpay-java.version>0.2.16</wechatpay-java.version>
<!-- 阿里云凭证 -->
<credentials-java.version>0.3.10</credentials-java.version>
```
## 项目结构规范
### 标准目录结构
```
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<T> {
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 <T> Result<T> success() {
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
}
public static <T> Result<T> success(T data) {
return new Result<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
}
public static <T> Result<T> error(String message) {
return new Result<>(ResultCode.ERROR.getCode(), message, null);
}
public static <T> Result<T> 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<ResponseType> methodName() {
// 业务逻辑
return Result.success(data);
}
}
```
### 5. 服务接口规范
```java
@Service
public interface ModuleService extends IService<EntityName> {
// 自定义业务方法
}
```
### 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
<!-- MongoDB依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
```
### 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<MongoEntity, String> {
// 自定义查询方法
List<MongoEntity> 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
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
```
## 注意事项
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和部署脚本
遵循以上规范可以确保新微服务与现有系统保持一致的技术栈和代码风格。