停止追踪specs,docs等目录文件
This commit is contained in:
@@ -1,517 +0,0 @@
|
||||
# Deploy Optimization 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:** 将 label_backend 从 Spring Boot fat JAR 方式改造为薄 jar + Maven Assembly 分发包,并统一日志级别为 INFO。
|
||||
|
||||
**Architecture:** 移除 `spring-boot-maven-plugin`,改用 `maven-jar-plugin`(薄 jar)+ `maven-dependency-plugin`(复制依赖到 `target/libs/`)+ `maven-assembly-plugin`(组装 zip/tar.gz)。新增 `start.sh`(Docker/VM 双模式启动)和 `logback.xml`(60 MB 滚动)。Dockerfile 改为多阶段构建,从 `target/libs/` 和 `src/main/resources/` 复制构建产物。
|
||||
|
||||
**Tech Stack:** Maven 3.9, JDK 17, Spring Boot 3.2.5, maven-assembly-plugin 3.x, logback 1.4.x (Spring Boot 管理版本)
|
||||
|
||||
---
|
||||
|
||||
## 文件结构
|
||||
|
||||
| 操作 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| 新建 | `src/main/resources/logback.xml` | INFO 级,60 MB 滚动日志配置 |
|
||||
| 新建 | `src/main/scripts/start.sh` | Docker/VM 双模式启动脚本 |
|
||||
| 新建 | `src/main/assembly/distribution.xml` | Assembly 描述符 |
|
||||
| 新建 | `src/main/assembly/empty-logs/.gitkeep` | logs/ 目录占位(Assembly 引用) |
|
||||
| 修改 | `pom.xml` | 替换 spring-boot-maven-plugin |
|
||||
| 修改 | `Dockerfile` | 多阶段构建,复制 etc/ + libs/ |
|
||||
| 批量修改 | 11 个 Service 类 | `log.debug` → `log.info`(21 处) |
|
||||
|
||||
---
|
||||
|
||||
## Task 1: 创建 logback.xml
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/resources/logback.xml`
|
||||
|
||||
- [ ] **Step 1: 创建 logback.xml 文件**
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds">
|
||||
|
||||
<property name="LOG_PATH" value="${LOG_PATH:-logs}"/>
|
||||
<property name="APP_NAME" value="label-backend"/>
|
||||
<property name="LOG_PATTERN"
|
||||
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
|
||||
|
||||
<!-- 控制台输出(Docker 日志采集依赖 stdout) -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 滚动文件:60 MB / 个,按日分组,保留 30 天,总上限 3 GB -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_PATH}/${APP_NAME}.log</file>
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>60MB</maxFileSize>
|
||||
<maxHistory>30</maxHistory>
|
||||
<totalSizeCap>3GB</totalSizeCap>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证文件存在**
|
||||
|
||||
```bash
|
||||
ls src/main/resources/logback.xml
|
||||
```
|
||||
|
||||
预期输出:`src/main/resources/logback.xml`
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/resources/logback.xml
|
||||
git commit -m "feat(deploy): 添加 logback.xml(INFO 级,60 MB 滚动)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: 创建 start.sh 启动脚本
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/scripts/start.sh`
|
||||
|
||||
- [ ] **Step 1: 创建目录并写入 start.sh**
|
||||
|
||||
```bash
|
||||
mkdir -p src/main/scripts
|
||||
```
|
||||
|
||||
文件内容 `src/main/scripts/start.sh`:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# label-backend 启动脚本
|
||||
# - Docker 环境(检测 /.dockerenv):exec 前台运行,保持容器进程存活
|
||||
# - 裸机 / VM:nohup 后台运行,日志追加至 logs/startup.log
|
||||
|
||||
set -e
|
||||
|
||||
BASEDIR=$(cd "$(dirname "$0")/.." && pwd)
|
||||
LIBDIR="$BASEDIR/libs"
|
||||
CONFDIR="$BASEDIR/etc"
|
||||
LOGDIR="$BASEDIR/logs"
|
||||
|
||||
mkdir -p "$LOGDIR"
|
||||
|
||||
JVM_OPTS="${JVM_OPTS:--Xms512m -Xmx1024m}"
|
||||
MAIN_CLASS="com.label.LabelBackendApplication"
|
||||
JAVA_ARGS="$JVM_OPTS \
|
||||
-Dspring.config.location=file:$CONFDIR/application.yml \
|
||||
-Dlogging.config=file:$CONFDIR/logback.xml \
|
||||
-cp $LIBDIR/*"
|
||||
|
||||
if [ -f /.dockerenv ]; then
|
||||
# Docker 容器:exec 替换当前进程,PID=1 接管信号
|
||||
exec java $JAVA_ARGS $MAIN_CLASS
|
||||
else
|
||||
# 裸机 / VM:nohup 后台运行
|
||||
nohup java $JAVA_ARGS $MAIN_CLASS >> "$LOGDIR/startup.log" 2>&1 &
|
||||
echo "label-backend started, PID=$!"
|
||||
fi
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证文件存在**
|
||||
|
||||
```bash
|
||||
ls src/main/scripts/start.sh
|
||||
```
|
||||
|
||||
预期输出:`src/main/scripts/start.sh`
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/scripts/start.sh
|
||||
git commit -m "feat(deploy): 添加 start.sh(Docker exec / VM nohup 双模式)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: 创建 Assembly 描述符和目录占位
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/assembly/distribution.xml`
|
||||
- Create: `src/main/assembly/empty-logs/.gitkeep`
|
||||
|
||||
- [ ] **Step 1: 创建 assembly 目录结构**
|
||||
|
||||
```bash
|
||||
mkdir -p src/main/assembly/empty-logs
|
||||
touch src/main/assembly/empty-logs/.gitkeep
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 创建 distribution.xml**
|
||||
|
||||
文件内容 `src/main/assembly/distribution.xml`:
|
||||
|
||||
```xml
|
||||
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0
|
||||
https://maven.apache.org/xsd/assembly-2.1.0.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
|
||||
<!-- bin/start.sh(0755 可执行) -->
|
||||
<files>
|
||||
<file>
|
||||
<source>src/main/scripts/start.sh</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<fileMode>0755</fileMode>
|
||||
</file>
|
||||
</files>
|
||||
|
||||
<fileSets>
|
||||
<!-- etc/:application.yml + logback.xml -->
|
||||
<fileSet>
|
||||
<directory>src/main/resources</directory>
|
||||
<outputDirectory>etc</outputDirectory>
|
||||
<includes>
|
||||
<include>application.yml</include>
|
||||
<include>logback.xml</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
|
||||
<!-- libs/:薄 jar + 所有运行时依赖 -->
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/libs</directory>
|
||||
<outputDirectory>libs</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
|
||||
<!-- logs/ 空目录占位 -->
|
||||
<fileSet>
|
||||
<directory>src/main/assembly/empty-logs</directory>
|
||||
<outputDirectory>logs</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
</assembly>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 验证文件存在**
|
||||
|
||||
```bash
|
||||
ls src/main/assembly/distribution.xml src/main/assembly/empty-logs/.gitkeep
|
||||
```
|
||||
|
||||
预期输出:两个文件路径均显示
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/assembly/
|
||||
git commit -m "feat(deploy): 添加 Assembly 描述符 distribution.xml"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: 更新 pom.xml(替换 spring-boot-maven-plugin)
|
||||
|
||||
**Files:**
|
||||
- Modify: `pom.xml`
|
||||
|
||||
- [ ] **Step 1: 替换 `<build><plugins>` 段落**
|
||||
|
||||
将 `pom.xml` 的 `<build>` 段(当前仅含 spring-boot-maven-plugin)替换为:
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<!-- 薄 jar:仅打包编译后的 class,输出到 target/libs/ -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.label.LabelBackendApplication</mainClass>
|
||||
<addClasspath>false</addClasspath>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- 将所有运行时依赖复制到 target/libs/ -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals><goal>copy-dependencies</goal></goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/libs</outputDirectory>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- 组装分发包(zip + tar.gz) -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>create-distribution</id>
|
||||
<phase>package</phase>
|
||||
<goals><goal>single</goal></goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/assembly/distribution.xml</descriptor>
|
||||
</descriptors>
|
||||
<finalName>${project.artifactId}-${project.version}</finalName>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
即用上述内容完整替换 `pom.xml` 中现有的 `<build>...</build>` 块(原内容为含 spring-boot-maven-plugin 的单插件配置)。
|
||||
|
||||
- [ ] **Step 2: 验证语法并试构建**
|
||||
|
||||
```bash
|
||||
mvn validate -q
|
||||
```
|
||||
|
||||
预期输出:无错误(只做 pom.xml 解析校验,不实际编译)
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add pom.xml
|
||||
git commit -m "feat(deploy): pom.xml 替换 fat JAR → 薄 jar + maven-dependency + maven-assembly"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: 更新 Dockerfile
|
||||
|
||||
**Files:**
|
||||
- Modify: `Dockerfile`
|
||||
|
||||
- [ ] **Step 1: 替换 Dockerfile 全文**
|
||||
|
||||
```dockerfile
|
||||
# 构建阶段:Maven + JDK 17 编译,生成薄 jar 及依赖
|
||||
FROM maven:3.9-eclipse-temurin-17-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
# 优先复制 pom.xml 利用 Docker 层缓存(依赖不变时跳过 go-offline)
|
||||
COPY pom.xml .
|
||||
RUN mvn dependency:go-offline -q
|
||||
|
||||
COPY src ./src
|
||||
RUN mvn clean package -DskipTests -q
|
||||
|
||||
# 运行阶段:仅含 JRE 的精简镜像
|
||||
FROM eclipse-temurin:17-jre-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# 复制部署结构:bin/ libs/ etc/
|
||||
COPY --from=builder /app/src/main/scripts/start.sh bin/start.sh
|
||||
COPY --from=builder /app/target/libs/ libs/
|
||||
COPY --from=builder /app/src/main/resources/application.yml etc/application.yml
|
||||
COPY --from=builder /app/src/main/resources/logback.xml etc/logback.xml
|
||||
|
||||
RUN mkdir -p logs && chmod +x bin/start.sh
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# start.sh 检测到 /.dockerenv 后以 exec 前台方式运行
|
||||
ENTRYPOINT ["bin/start.sh"]
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证 Dockerfile 语法**
|
||||
|
||||
```bash
|
||||
docker build --no-cache --dry-run . 2>/dev/null || echo "docker not available; syntax check skipped"
|
||||
```
|
||||
|
||||
预期:无语法错误(或 docker 不可用时跳过)
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add Dockerfile
|
||||
git commit -m "feat(deploy): Dockerfile 改为多阶段构建(薄 jar + start.sh)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: 批量替换 log.debug → log.info(11 文件,21 处)
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/java/com/label/module/video/service/VideoProcessService.java` (5 处)
|
||||
- Modify: `src/main/java/com/label/module/source/service/SourceService.java` (2 处)
|
||||
- Modify: `src/main/java/com/label/module/user/service/UserService.java` (3 处)
|
||||
- Modify: `src/main/java/com/label/module/user/service/AuthService.java` (2 处)
|
||||
- Modify: `src/main/java/com/label/module/config/service/SysConfigService.java` (2 处)
|
||||
- Modify: `src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java` (2 处)
|
||||
- Modify: `src/main/java/com/label/module/task/service/TaskService.java` (1 处)
|
||||
- Modify: `src/main/java/com/label/module/annotation/service/QaService.java` (1 处)
|
||||
- Modify: `src/main/java/com/label/module/export/service/FinetuneService.java` (1 处)
|
||||
- Modify: `src/main/java/com/label/module/task/service/TaskClaimService.java` (1 处)
|
||||
- Modify: `src/main/java/com/label/module/export/service/ExportService.java` (1 处)
|
||||
|
||||
- [ ] **Step 1: 批量替换**
|
||||
|
||||
```bash
|
||||
find src/main/java -name "*.java" \
|
||||
-exec sed -i 's/log\.debug(/log.info(/g' {} +
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 验证替换完整,无 log.debug 残留**
|
||||
|
||||
```bash
|
||||
grep -r "log\.debug" src/main/java && echo "FAIL: 仍有 log.debug 残留" || echo "PASS: 无 log.debug"
|
||||
```
|
||||
|
||||
预期输出:`PASS: 无 log.debug`
|
||||
|
||||
- [ ] **Step 3: 验证替换数量正确(应有 21 处 log.info 新增)**
|
||||
|
||||
```bash
|
||||
git diff --stat src/main/java | tail -1
|
||||
```
|
||||
|
||||
预期:显示 11 个文件,21 处改动(`21 insertions(+), 21 deletions(-)`)
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/main/java/
|
||||
git commit -m "refactor(log): log.debug 全量替换为 log.info(11 文件,21 处)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: 全量构建验证
|
||||
|
||||
**Files:** 只读操作,不修改文件
|
||||
|
||||
- [ ] **Step 1: 执行完整构建(跳过测试)**
|
||||
|
||||
```bash
|
||||
mvn clean package -DskipTests
|
||||
```
|
||||
|
||||
预期:`BUILD SUCCESS`,无 ERROR 输出
|
||||
|
||||
- [ ] **Step 2: 验证 target/libs/ 目录存在且包含 jar**
|
||||
|
||||
```bash
|
||||
ls target/libs/*.jar | head -5
|
||||
ls target/libs/label-backend-1.0.0-SNAPSHOT.jar
|
||||
```
|
||||
|
||||
预期:显示薄 jar 及多个依赖 jar
|
||||
|
||||
- [ ] **Step 3: 验证分发包已生成**
|
||||
|
||||
```bash
|
||||
ls target/label-backend-1.0.0-SNAPSHOT.zip
|
||||
ls target/label-backend-1.0.0-SNAPSHOT.tar.gz
|
||||
```
|
||||
|
||||
预期:两个文件均存在
|
||||
|
||||
- [ ] **Step 4: 验证分发包内部结构**
|
||||
|
||||
```bash
|
||||
# 使用 unzip 检查 zip 内容(Windows Git Bash 或 Linux 均可用)
|
||||
unzip -l target/label-backend-1.0.0-SNAPSHOT.zip | grep -E "bin/|etc/|libs/|logs/"
|
||||
```
|
||||
|
||||
预期输出包含:
|
||||
```
|
||||
label-backend-1.0.0-SNAPSHOT/bin/start.sh
|
||||
label-backend-1.0.0-SNAPSHOT/etc/application.yml
|
||||
label-backend-1.0.0-SNAPSHOT/etc/logback.xml
|
||||
label-backend-1.0.0-SNAPSHOT/libs/label-backend-1.0.0-SNAPSHOT.jar
|
||||
label-backend-1.0.0-SNAPSHOT/logs/
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 验证 logback.xml 已包含在 etc/ 内**
|
||||
|
||||
```bash
|
||||
unzip -l target/label-backend-1.0.0-SNAPSHOT.zip | grep logback
|
||||
```
|
||||
|
||||
预期:`label-backend-1.0.0-SNAPSHOT/etc/logback.xml`
|
||||
|
||||
- [ ] **Step 6: Commit 构建验证记录(如有必要则修正问题后提交)**
|
||||
|
||||
若 Step 1-5 全部通过,无需额外提交。若发现问题(如缺少文件、路径错误),修正对应 Task 后重新执行本 Task。
|
||||
|
||||
---
|
||||
|
||||
## 自检
|
||||
|
||||
### Spec 覆盖检查
|
||||
|
||||
| deploy.md 需求 | 对应任务 |
|
||||
|---------------|---------|
|
||||
| 1. 分发包结构 bin/etc/libs/logs | Task 3 (distribution.xml) + Task 7 验证 |
|
||||
| 2. start.sh nohup 后台启动 | Task 2 |
|
||||
| 3. logback.xml INFO + 60 MB 滚动 | Task 1 |
|
||||
| 4. logback.xml + application.yml 在 etc/ | Task 3 (distribution.xml fileSets) |
|
||||
| 5. maven-jar-plugin + maven-dependency-plugin | Task 4 |
|
||||
| 6. Maven Assembly Plugin → zip/tar.gz | Task 3 + Task 4 |
|
||||
| 7. Dockerfile 复制 etc/ + 调用 start.sh | Task 5 |
|
||||
| 8. log.debug → log.info | Task 6 |
|
||||
|
||||
所有 8 条需求均有对应任务。✅
|
||||
|
||||
### 类型一致性
|
||||
|
||||
- `MAIN_CLASS="com.label.LabelBackendApplication"` — 与 `src/main/java/com/label/LabelBackendApplication.java` 一致 ✅
|
||||
- `maven-jar-plugin` 输出路径 `target/libs/` — 与 Dockerfile `COPY --from=builder /app/target/libs/` 一致 ✅
|
||||
- `distribution.xml` `<directory>${project.build.directory}/libs</directory>` — 与 `maven-dependency-plugin` 的 `outputDirectory` 一致 ✅
|
||||
|
||||
## GSTACK REVIEW REPORT
|
||||
|
||||
| Review | Trigger | Why | Runs | Status | Findings |
|
||||
|--------|---------|-----|------|--------|----------|
|
||||
| CEO Review | `/plan-ceo-review` | Scope & strategy | 0 | — | — |
|
||||
| Codex Review | `/codex review` | Independent 2nd opinion | 0 | — | — |
|
||||
| Eng Review | `/plan-eng-review` | Architecture & tests (required) | 0 | — | — |
|
||||
| Design Review | `/plan-design-review` | UI/UX gaps | 0 | — | — |
|
||||
|
||||
**VERDICT:** NO REVIEWS YET — run `/autoplan` for full review pipeline, or individual reviews above.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,66 +0,0 @@
|
||||
# Auth And Company Optimization 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:** Replace the remaining Shiro authorization layer with project-owned Redis token authentication and add company CRUD APIs.
|
||||
|
||||
**Architecture:** Keep the existing UUID token, Redis session storage, and `CompanyContext` tenant injection. Add project-owned `@RequireAuth` and `@RequireRole` annotations plus a Spring MVC `AuthInterceptor`, then remove Shiro config/classes/dependencies. Add `CompanyService` and `CompanyController` for `sys_company` management.
|
||||
|
||||
**Tech Stack:** Java 21, Spring Boot 3.1.5, Spring MVC HandlerInterceptor, RedisTemplate, MyBatis-Plus, JUnit 5, Mockito, AssertJ.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Replace Shiro With Custom Auth Interceptor
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/label/annotation/RequireAuth.java`
|
||||
- Create: `src/main/java/com/label/annotation/RequireRole.java`
|
||||
- Create: `src/main/java/com/label/interceptor/AuthInterceptor.java`
|
||||
- Create: `src/main/java/com/label/common/auth/TokenPrincipal.java`
|
||||
- Create: `src/main/java/com/label/common/context/UserContext.java`
|
||||
- Modify: `src/main/java/com/label/config/ShiroConfig.java`
|
||||
- Modify: `src/main/java/com/label/common/shiro/TokenFilter.java`
|
||||
- Modify: `src/main/java/com/label/common/shiro/BearerToken.java`
|
||||
- Modify: `src/main/java/com/label/common/shiro/UserRealm.java`
|
||||
- Modify: `src/main/java/com/label/controller/*.java`
|
||||
- Modify: `src/main/java/com/label/service/*.java`
|
||||
- Modify: `pom.xml`
|
||||
- Test: `src/test/java/com/label/unit/AuthInterceptorTest.java`
|
||||
|
||||
- [x] Write failing tests for token loading, TTL refresh, role hierarchy, and context cleanup.
|
||||
- [x] Implement annotations, principal, context, and interceptor.
|
||||
- [x] Register the interceptor via Spring MVC config.
|
||||
- [x] Replace controller `@RequiresRoles` usage with `@RequireRole`.
|
||||
- [x] Remove Shiro-only classes, tests, dependencies, and exception handling.
|
||||
- [x] Run `mvn -q "-Dtest=AuthInterceptorTest,OpenApiAnnotationTest" test` and `mvn -q -DskipTests compile`.
|
||||
|
||||
### Task 2: Add Company Management
|
||||
|
||||
**Files:**
|
||||
- Create: `src/main/java/com/label/service/CompanyService.java`
|
||||
- Create: `src/main/java/com/label/controller/CompanyController.java`
|
||||
- Modify: `src/main/java/com/label/mapper/SysUserMapper.java`
|
||||
- Test: `src/test/java/com/label/unit/CompanyServiceTest.java`
|
||||
- Test: `src/test/java/com/label/unit/OpenApiAnnotationTest.java`
|
||||
|
||||
- [x] Write failing tests for create/list/update/status/delete behavior.
|
||||
- [x] Implement service validation and duplicate checks.
|
||||
- [x] Implement admin-only controller endpoints under `/api/companies`.
|
||||
- [x] Run `mvn -q "-Dtest=CompanyServiceTest,OpenApiAnnotationTest" test` and `mvn -q -DskipTests compile`.
|
||||
|
||||
### Task 3: Configuration And Verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/main/resources/application.yml`
|
||||
- Modify: `src/test/java/com/label/unit/ApplicationConfigTest.java`
|
||||
|
||||
- [x] Rename `shiro.auth.*` config to `auth.*`.
|
||||
- [x] Update safe defaults and type-aliases package.
|
||||
- [x] Run targeted unit tests and compile.
|
||||
- [x] Run `mvn clean test` once and record any external environment blockers.
|
||||
|
||||
### Verification Notes
|
||||
|
||||
- `mvn -q "-Dtest=LabelBackendApplicationTests,ApplicationConfigTest,AuthInterceptorTest,CompanyServiceTest,OpenApiAnnotationTest" test` passed.
|
||||
- `mvn -q -DskipTests compile` passed.
|
||||
- `mvn clean test` compiled main/test sources and passed unit tests, then failed only because 10 Testcontainers integration tests could not find a valid Docker environment.
|
||||
@@ -1,528 +0,0 @@
|
||||
# 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`
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,244 +0,0 @@
|
||||
# label_backend 标准目录扁平化设计
|
||||
|
||||
**日期**: 2026-04-14
|
||||
**范围**: `label_backend` 主工程 Java 包结构调整
|
||||
**目标**: 将当前按业务域分层的 `com.label.module.*` 结构重组为符合《微服务开发规范文档》的扁平标准目录结构,同时保持现有业务行为不变。
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与现状
|
||||
|
||||
当前项目主代码位于 `src/main/java/com/label`,整体结构分为两部分:
|
||||
|
||||
- `com.label.common.*`:放置公共能力,如配置、异常、AOP、鉴权、Redis、状态机、存储与 AI 客户端
|
||||
- `com.label.module.*`:按业务域划分的模块目录,如 `user`、`task`、`source`、`annotation`、`export`、`config`、`video`
|
||||
|
||||
现有结构具备一定领域边界,但与《微服务开发规范文档》中规定的标准扁平目录结构不一致。后续如果继续沿用两套组织方式,会增加新代码接入成本,也会让公共能力与业务层级的归属变得不稳定。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
本次调整目标如下:
|
||||
|
||||
- 将主代码目录统一调整为规范文档中的扁平结构
|
||||
- 保持包职责清晰,按“层级职责”而不是“业务模块”组织类
|
||||
- 在不改变业务逻辑和接口行为的前提下完成迁移
|
||||
- 规范包归属与少量命名,不引入新的技术分层
|
||||
- 为后续新增功能提供统一、可预测的目录组织方式
|
||||
|
||||
---
|
||||
|
||||
## 3. 明确不做的事情
|
||||
|
||||
为控制改造范围,本次不包含以下内容:
|
||||
|
||||
- 不新增 `service.impl`
|
||||
- 不将 `dto` 拆分为 `request`、`response`、`common`
|
||||
- 不主动修改接口 URL、接口契约或返回结构
|
||||
- 不进行与目录调整无关的业务重构
|
||||
- 不改造数据库结构、SQL 文件或资源文件布局
|
||||
|
||||
---
|
||||
|
||||
## 4. 目标目录结构
|
||||
|
||||
目标结构定义如下:
|
||||
|
||||
```text
|
||||
src/main/java/com/label/
|
||||
├── annotation/
|
||||
├── aspect/
|
||||
├── common/
|
||||
│ ├── ai/
|
||||
│ ├── context/
|
||||
│ ├── exception/
|
||||
│ ├── redis/
|
||||
│ ├── result/
|
||||
│ ├── shiro/
|
||||
│ ├── statemachine/
|
||||
│ └── storage/
|
||||
├── config/
|
||||
├── constant/
|
||||
├── controller/
|
||||
├── dto/
|
||||
├── entity/
|
||||
├── event/
|
||||
├── feign/
|
||||
├── listener/
|
||||
├── mapper/
|
||||
├── scheduled/
|
||||
├── service/
|
||||
├── typehandler/
|
||||
├── util/
|
||||
└── LabelBackendApplication.java
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `common` 只保留真正跨业务复用的基础能力
|
||||
- `config` 从原 `common.config` 提升到顶层
|
||||
- `annotation` 与 `aspect` 从原 `common.aop` 中拆分
|
||||
- `event` 与 `listener` 分离,避免事件定义与监听逻辑混放
|
||||
- `controller`、`service`、`mapper`、`entity`、`dto` 全部收敛为顶层扁平结构
|
||||
|
||||
---
|
||||
|
||||
## 5. 包迁移规则
|
||||
|
||||
### 5.1 顶层迁移规则
|
||||
|
||||
- `com.label.module.*.controller` -> `com.label.controller`
|
||||
- `com.label.module.*.service` -> `com.label.service`
|
||||
- `com.label.module.*.mapper` -> `com.label.mapper`
|
||||
- `com.label.module.*.entity` -> `com.label.entity`
|
||||
- `com.label.module.*.dto` -> `com.label.dto`
|
||||
- `com.label.module.annotation.event` -> `com.label.event`
|
||||
- 事件监听类 -> `com.label.listener`
|
||||
- `com.label.common.config` -> `com.label.config`
|
||||
- `com.label.common.aop` 中注解类 -> `com.label.annotation`
|
||||
- `com.label.common.aop` 中切面类 -> `com.label.aspect`
|
||||
|
||||
### 5.2 典型映射示例
|
||||
|
||||
- `com.label.module.user.controller.AuthController` -> `com.label.controller.AuthController`
|
||||
- `com.label.module.user.service.UserService` -> `com.label.service.UserService`
|
||||
- `com.label.module.task.mapper.AnnotationTaskMapper` -> `com.label.mapper.AnnotationTaskMapper`
|
||||
- `com.label.module.source.entity.SourceData` -> `com.label.entity.SourceData`
|
||||
- `com.label.module.user.dto.LoginRequest` -> `com.label.dto.LoginRequest`
|
||||
- `com.label.common.aop.OperationLog` -> `com.label.annotation.OperationLog`
|
||||
- `com.label.common.aop.AuditAspect` -> `com.label.aspect.AuditAspect`
|
||||
- `com.label.module.annotation.event.ExtractionApprovedEvent` -> `com.label.event.ExtractionApprovedEvent`
|
||||
- `com.label.module.annotation.service.ExtractionApprovedEventListener` -> `com.label.listener.ExtractionApprovedEventListener`
|
||||
- `com.label.common.config.RedisConfig` -> `com.label.config.RedisConfig`
|
||||
|
||||
### 5.3 命名策略
|
||||
|
||||
- 以“最小必要变更”为原则,优先迁移包路径,不主动重命名类
|
||||
- 仅在类职责与包语义明显不匹配时做必要归位
|
||||
- 现阶段主代码类名不存在重名冲突,因此不需要为扁平化提前引入前缀或后缀
|
||||
|
||||
---
|
||||
|
||||
## 6. 实施顺序
|
||||
|
||||
为降低迁移风险,实际执行采用“目标一次性扁平化,操作分阶段迁移”的方式。
|
||||
|
||||
### 阶段 1:基础公共层归位
|
||||
|
||||
先迁移以下内容,建立新骨架:
|
||||
|
||||
- `annotation`
|
||||
- `aspect`
|
||||
- `config`
|
||||
- `event`
|
||||
- `listener`
|
||||
|
||||
目标是先完成最基础的结构纠偏,并尽早暴露切面、配置和事件扫描问题。
|
||||
|
||||
### 阶段 2:数据承载层迁移
|
||||
|
||||
再迁移下列包:
|
||||
|
||||
- `entity`
|
||||
- `dto`
|
||||
- `mapper`
|
||||
|
||||
这些类依赖通常较窄,适合优先扁平化,也能及早验证 MyBatis 相关扫描与引用是否正常。
|
||||
|
||||
### 阶段 3:业务服务层迁移
|
||||
|
||||
迁移所有业务服务类到 `service`,原则如下:
|
||||
|
||||
- 不新增实现层
|
||||
- 不调整现有业务编排
|
||||
- 只修正导包、注解引用和必要的包路径依赖
|
||||
|
||||
### 阶段 4:控制层迁移
|
||||
|
||||
最后迁移全部控制器到 `controller`。控制层依赖最广,放在后面可以减少中间态反复修改。
|
||||
|
||||
### 阶段 5:启动与扫描校正
|
||||
|
||||
统一检查并修正以下内容:
|
||||
|
||||
- `@MapperScan`
|
||||
- 组件扫描隐式路径
|
||||
- OpenAPI 相关配置
|
||||
- 事件监听与切面装配
|
||||
- 测试代码中的旧包引用
|
||||
|
||||
### 阶段 6:编译与回归验证
|
||||
|
||||
迁移完成后进行全量编译与测试回归,确认结构调整没有引入行为回归。
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与控制策略
|
||||
|
||||
### 风险 1:导包失效
|
||||
|
||||
大量类迁移后,主代码和测试代码中的 import 会同时失效。
|
||||
|
||||
控制策略:
|
||||
|
||||
- 按阶段迁移并同步修复引用
|
||||
- 每个阶段结束后至少执行一次编译检查
|
||||
|
||||
### 风险 2:Spring 扫描路径异常
|
||||
|
||||
若配置类、切面、监听器或 Mapper 的扫描路径与旧包结构耦合,迁移后可能导致 Bean 缺失。
|
||||
|
||||
控制策略:
|
||||
|
||||
- 显式检查启动类与配置类中的扫描配置
|
||||
- 将路径校正作为独立阶段处理
|
||||
|
||||
### 风险 3:事件监听失效
|
||||
|
||||
监听器从 `service` 拆到 `listener` 后,若注解或组件扫描不正确,会导致事件未被消费。
|
||||
|
||||
控制策略:
|
||||
|
||||
- 迁移时同步校验事件类与监听器依赖关系
|
||||
- 通过现有集成测试覆盖事件链路
|
||||
|
||||
### 风险 4:测试回归失败
|
||||
|
||||
测试代码同样引用旧包名,若只改主代码,会造成测试集整体失效。
|
||||
|
||||
控制策略:
|
||||
|
||||
- 测试代码与主代码同步迁移
|
||||
- 将 `mvn test` 作为最终验收门槛
|
||||
|
||||
---
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
本次结构调整完成后,应满足以下标准:
|
||||
|
||||
1. `src/main/java/com/label/module` 目录被完全移除
|
||||
2. 主代码包结构符合标准扁平目录规范
|
||||
3. 项目能够成功编译
|
||||
4. 现有测试能够通过,至少覆盖当前可运行的启动测试、单元测试和集成测试
|
||||
5. 调整范围内不存在残留旧包引用
|
||||
|
||||
---
|
||||
|
||||
## 9. 实现原则
|
||||
|
||||
执行本设计时应遵循以下原则:
|
||||
|
||||
- 先保证结构归位,再追求风格统一
|
||||
- 先保证行为不变,再做命名优化
|
||||
- 所有改动以“目录标准化”为中心,不引入额外架构决策
|
||||
- 每次修改都要让工程更接近规范,而不是制造新的混合结构
|
||||
|
||||
---
|
||||
|
||||
## 10. 结论
|
||||
|
||||
本次改造将 `label_backend` 从“按业务域分层 + 公共包混合”的现状,统一整理为规范文档定义的扁平标准目录结构。该调整不会改变系统能力边界,但会显著提升代码组织一致性、后续开发可预测性以及新成员理解成本。
|
||||
|
||||
实施时采用“目标结构一次确定、操作按阶段推进”的方式,以降低大规模包迁移带来的编译与装配风险。
|
||||
Reference in New Issue
Block a user