diff --git a/docs/superpowers/specs/2026-04-09-label-backend-design.md b/docs/superpowers/specs/2026-04-09-label-backend-design.md index e7e6ca2..82bdfef 100644 --- a/docs/superpowers/specs/2026-04-09-label-backend-design.md +++ b/docs/superpowers/specs/2026-04-09-label-backend-design.md @@ -45,6 +45,13 @@ - [7.4 状态机越界拒绝测试](#74-状态机越界拒绝测试) - [7.5 多租户隔离测试](#75-多租户隔离测试) - [八、宪章合规检查清单](#八宪章合规检查清单) +- [九、部署与发布](#九部署与发布) + - [9.1 Maven 构建配置变更](#91-maven-构建配置变更) + - [9.2 分发包结构](#92-分发包结构) + - [9.3 start.sh 启动脚本](#93-startsh-启动脚本) + - [9.4 logback.xml 配置](#94-logbackxml-配置) + - [9.5 Dockerfile 更新](#95-dockerfile-更新) + - [9.6 日志级别规范(log.debug → log.info)](#96-日志级别规范logdebug--loginfo) --- @@ -1478,12 +1485,14 @@ volumes: **Dockerfile(backend):** +> 完整的多阶段构建 Dockerfile 见 [9.5 Dockerfile 更新](#95-dockerfile-更新)。 +> 以下为旧版占位,已被新版替代(使用薄 jar + start.sh 方式)。 + ```dockerfile -FROM eclipse-temurin:17-jre-alpine -WORKDIR /app -COPY target/label-backend-*.jar app.jar -EXPOSE 8080 -ENTRYPOINT ["java", "-jar", "app.jar"] +# 已废弃——使用 fat JAR 方式,不符合新部署规范 +# FROM eclipse-temurin:17-jre-alpine +# COPY target/label-backend-*.jar app.jar +# ENTRYPOINT ["java", "-jar", "app.jar"] ``` [↑ 返回目录](#目录) @@ -1599,5 +1608,336 @@ PR 合并前评审人**必须**逐条核对以下清单: | 9 | 只追加审计日志、AOP 切面、审计失败不回滚业务 | `AuditAspect`、`@OperationLog` | `sys_operation_log` 无 UPDATE/DELETE;审计异常仅 error 日志 | | 10 | RESTful URL、统一响应格式、分页必须 | `Result`、各 Controller | 无动词路径;无裸 POJO 返回;所有列表接口有分页参数 | | 11 | YAGNI:业务在 Service,Controller 只处理 HTTP | 包结构 | Controller 无业务判断逻辑;无未使用的抽象层 | +| 12 | 部署包结构规范(bin/etc/libs/logs 四级) | `pom.xml`、`distribution.xml` | fat JAR 已移除;assembly 产出 zip + tar.gz;libs/ 含薄 jar + 所有依赖 | +| 13 | start.sh 启动可执行、Docker 兼容 | `src/main/scripts/start.sh` | Docker 内 exec 前台运行;VM 内 nohup 后台运行;日志写入 logs/ | +| 14 | 日志级别统一 INFO、无 log.debug 残留 | 全体 Service 类 | `grep -r "log\.debug"` 返回 0 结果;logback.xml root level=INFO | + +[↑ 返回目录](#目录) + +--- + +## 九、部署与发布 + +### 9.1 Maven 构建配置变更 + +**目标**:用薄 jar(thin jar)替代 Spring Boot fat JAR,由 Assembly Plugin 组装可分发压缩包。 + +**移除** `spring-boot-maven-plugin`(fat JAR),**替换为**以下三个插件: + +```xml + + + + + + org.apache.maven.plugins + maven-jar-plugin + + ${project.build.directory}/libs + + + com.label.LabelBackendApplication + false + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + copy-dependencies + + ${project.build.directory}/libs + test + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + create-distribution + package + single + + + src/main/assembly/distribution.xml + + ${project.artifactId}-${project.version} + false + + + + + + + +``` + +**Assembly 描述符** `src/main/assembly/distribution.xml`: + +```xml + + dist + + zip + tar.gz + + true + + + + + src/main/scripts/start.sh + bin + 0755 + + + + + + + src/main/resources + etc + + application.yml + logback.xml + + + + + src/main/assembly/empty-logs + logs + + + + + + + libs + true + runtime + + + +``` + +> **注意**:需在项目根创建空目录占位文件 `src/main/assembly/empty-logs/.gitkeep`, +> 否则 Assembly Plugin 会因源目录不存在而报错。 + +--- + +### 9.2 分发包结构 + +`mvn clean package` 后在 `target/` 生成: + +``` +target/ +├── libs/ +│ ├── label-backend-1.0.0-SNAPSHOT.jar ← 薄 jar(仅 class) +│ ├── spring-boot-starter-web-3.2.5.jar +│ ├── mybatis-plus-boot-starter-3.5.9.jar +│ └── ... (全部运行时依赖) +├── label-backend-1.0.0-SNAPSHOT.zip +└── label-backend-1.0.0-SNAPSHOT.tar.gz +``` + +压缩包解压后的内部结构: + +``` +label-backend-1.0.0-SNAPSHOT/ +├── bin/ +│ └── start.sh # 启动脚本(0755) +├── etc/ +│ ├── application.yml # Spring 配置(生产环境按需修改) +│ └── logback.xml # 日志配置(INFO + 60 MB 滚动) +├── libs/ +│ ├── label-backend-1.0.0-SNAPSHOT.jar +│ └── ... (所有依赖 jar) +└── logs/ # 空目录,运行时写入日志 +``` + +--- + +### 9.3 start.sh 启动脚本 + +新增文件:`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 +``` + +--- + +### 9.4 logback.xml 配置 + +新增文件:`src/main/resources/logback.xml` + +```xml + + + + + + + + + + + ${LOG_PATTERN} + + + + + + ${LOG_PATH}/${APP_NAME}.log + + ${LOG_PATTERN} + + + ${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log + 60MB + 30 + 3GB + + + + + + + + + +``` + +> **说明**:`LOG_PATH` 可通过环境变量或 JVM 参数覆盖,默认指向相对于工作目录的 `logs/`。 +> Docker Compose 中如需持久化日志,挂载 `/app/logs` 卷并设置 `LOG_PATH=/app/logs`。 + +--- + +### 9.5 Dockerfile 更新 + +替换 `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"] +``` + +**Docker Compose 中配置日志路径持久化(可选):** + +```yaml +backend: + build: ./label_backend + volumes: + - ./logs/backend:/app/logs + environment: + LOG_PATH: /app/logs + # ... 其他环境变量保持不变 +``` + +--- + +### 9.6 日志级别规范(log.debug → log.info) + +代码约定:**所有业务 Service 的正常流程日志使用 `log.info`**,`log.debug` 仅保留在需要 +开发期详细追踪的工具类(当前项目无此场景,全部改为 `log.info`)。 + +**需修改的文件(共 11 个,21 处):** + +| 文件 | `log.debug` 数量 | +|------|-----------------| +| `module/video/service/VideoProcessService.java` | 5 | +| `module/source/service/SourceService.java` | 2 | +| `module/user/service/UserService.java` | 3 | +| `module/user/service/AuthService.java` | 2 | +| `module/config/service/SysConfigService.java` | 2 | +| `module/annotation/service/ExtractionApprovedEventListener.java` | 2 | +| `module/task/service/TaskService.java` | 1 | +| `module/annotation/service/QaService.java` | 1 | +| `module/export/service/FinetuneService.java` | 1 | +| `module/task/service/TaskClaimService.java` | 1 | +| `module/export/service/ExportService.java` | 1 | + +**执行方式**(实现阶段批量替换): + +```bash +# 验证变更范围 +grep -rn "log\.debug" src/main/java + +# 批量替换 +find src/main/java -name "*.java" \ + -exec sed -i 's/log\.debug(/log.info(/g' {} + + +# 确认清零 +grep -r "log\.debug" src/main/java && echo "FAIL" || echo "PASS" +``` [↑ 返回目录](#目录)