Files
label_backend/README.md
2026-04-15 15:28:11 +08:00

402 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# label-backend
## 项目简介
`label-backend` 是知识图谱智能标注平台的后端服务,负责资料上传、任务分发、提取标注、问答生成、训练样本导出、系统配置和视频预处理等核心流程。
系统采用 Spring Boot 3 + MyBatis-Plus + PostgreSQL + Redis 的技术组合,面向多租户标注场景设计,支持:
- 公司级数据隔离
- 基于 Redis Token 的认证鉴权
- 提取与问答两阶段标注流程
- 导出训练数据并对接微调任务
- 视频预处理与异步回调
- 审计日志与任务状态追踪
代码结构已按扁平标准目录整理,主包位于 `src/main/java/com/label`
## 功能特性
- 认证鉴权
- 使用 UUID Bearer Token + Redis 会话存储
- 自定义 `@RequireAuth``@RequireRole`
- 角色分级:`ADMIN > REVIEWER > ANNOTATOR > UPLOADER`
- 多租户隔离
- 基于 `CompanyContext` + `TenantLineInnerInterceptor`
- 对租户表自动追加 `company_id` 条件
- 特殊表通过显式 `companyId` 参数校验
- 公司与用户管理
- 公司 CRUD
- 公司内用户创建、状态变更、角色变更
- 角色变更和禁用后即时刷新或失效 Redis Token
- 资料管理
- 支持文本、图片、视频三类原始资料
- 上传到 RustFS数据库保存元数据
- 支持按角色查看、查询详情、删除
- 任务管理
- 任务池、我的任务、待审批队列、管理员全量视图
- Redis 分布式锁 + 数据库原子更新保证任务领取并发安全
- 提取标注
- AI 预标注
- 标注结果整体覆盖更新
- 提交、审批通过、驳回
- 问答生成
- 基于提取审批通过事件生成候选问答对
- 支持编辑、提交、审批、驳回
- 训练数据导出
- 查询已审批样本
- 创建导出批次
- 触发微调任务并查询状态
- 系统配置
- 支持公司专属配置覆盖全局默认配置
- 配置项存储于 `sys_config`
- 视频处理
- 支持触发视频预处理任务
- 支持异步回调、失败重试、管理员重置
## 技术栈
- Java 21
- Spring Boot 3.1.5
- Spring MVC
- MyBatis-Plus 3.5.3.1
- PostgreSQL
- Redis
- Spring AOP
- springdoc-openapi
- Testcontainers
- RustFS / S3 兼容对象存储
## 项目结构
项目根目录结构:
```text
label_backend/
├── assembly/ # 分发包描述与占位目录
├── docs/ # 设计、计划、规范文档
├── scripts/ # 启动脚本
├── src/
│ ├── main/
│ │ ├── java/com/label/ # 主代码
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── logback.xml
│ │ └── sql/ # 初始化 SQL不打入构建产物
│ └── test/
│ ├── java/ # 单元测试与集成测试
│ └── resources/db/init.sql # Testcontainers 测试初始化 SQL
├── docker-compose.yml
├── Dockerfile
├── pom.xml
└── README.md
```
Java 包结构:
```text
com.label
├── annotation # 自定义注解,如 RequireAuth / RequireRole / OperationLog
├── aspect # AOP 审计切面
├── common # 通用能力auth、context、exception、result、storage、ai、statemachine
├── config # Spring 配置、MyBatis-Plus 配置、认证拦截器注册
├── controller # 所有 REST 接口
├── dto # DTO
├── entity # 实体
├── event # 领域事件
├── interceptor # 认证拦截器
├── listener # 事件监听器
├── mapper # MyBatis Mapper
├── service # 业务服务
└── util # 工具类
```
## 数据库表结构
初始化脚本位于 [init.sql](d:/workspace/label/label_backend/src/main/resources/sql/init.sql),包含 11 张核心表:
- `sys_company`
- 租户公司表
- `sys_user`
- 公司用户表,包含角色与状态
- `source_data`
- 原始资料元数据,支持 `TEXT` / `IMAGE` / `VIDEO`
- `annotation_task`
- 标注任务表,支持 `EXTRACTION` / `QA_GENERATION`
- `annotation_result`
- 提取阶段 JSON 结果
- `training_dataset`
- 训练样本数据,存储 GLM 格式 JSON
- `export_batch`
- 导出批次与微调任务状态
- `sys_config`
- 全局与公司级配置
- `sys_operation_log`
- 审计日志,只追加不更新
- `annotation_task_history`
- 任务状态变更历史
- `video_process_job`
- 视频预处理任务与回调状态
当前主要状态机:
- `source_data.status`
- `PENDING` / `PREPROCESSING` / `EXTRACTING` / `QA_REVIEW` / `APPROVED`
- `annotation_task.status`
- `UNCLAIMED` / `IN_PROGRESS` / `SUBMITTED` / `APPROVED` / `REJECTED`
- `training_dataset.status`
- `PENDING_REVIEW` / `APPROVED` / `REJECTED`
- `video_process_job.status`
- `PENDING` / `RETRYING` / `SUCCESS` / `FAILED`
## 配置说明
主配置文件位于 [application.yml](d:/workspace/label/label_backend/src/main/resources/application.yml)。
### 环境变量
| 变量名 | 说明 |
|---|---|
| `SPRING_DATASOURCE_URL` | PostgreSQL JDBC 地址 |
| `SPRING_DATASOURCE_USERNAME` | PostgreSQL 用户名 |
| `SPRING_DATASOURCE_PASSWORD` | PostgreSQL 密码 |
| `SPRING_DATA_REDIS_HOST` | Redis 主机 |
| `SPRING_DATA_REDIS_PORT` | Redis 端口 |
| `SPRING_DATA_REDIS_PASSWORD` | Redis 密码 |
| `RUSTFS_ENDPOINT` | RustFS / S3 兼容服务地址 |
| `RUSTFS_ACCESS_KEY` | RustFS Access Key |
| `RUSTFS_SECRET_KEY` | RustFS Secret Key |
| `AI_SERVICE_BASE_URL` | AI 服务地址 |
| `VIDEO_CALLBACK_SECRET` | 视频处理回调共享密钥 |
### 关键配置项
- `auth.enabled`
- `true` 时启用真实 Token 鉴权
- `false` 时使用 mock 身份,便于本地开发
- `auth.mock-company-id`
- 开发模式下的模拟公司 ID
- `auth.mock-user-id`
- 开发模式下的模拟用户 ID
- `auth.mock-role`
- 开发模式下的模拟角色
- `token.ttl-seconds`
- Token 有效期,默认 7200 秒
- `springdoc.api-docs.path`
- OpenAPI 文档路径,默认 `/v3/api-docs`
- `springdoc.swagger-ui.path`
- Swagger UI 路径,默认 `/swagger-ui.html`
## API接口
以下为当前主要接口分组。
### 1. 认证接口
- `POST /api/auth/login`
- 登录并返回 Bearer Token
- `POST /api/auth/logout`
- 登出并立即失效当前 Token
- `GET /api/auth/me`
- 获取当前登录用户信息
### 2. 公司管理
- `GET /api/companies`
- `POST /api/companies`
- `PUT /api/companies/{id}`
- `PUT /api/companies/{id}/status`
- `DELETE /api/companies/{id}`
### 3. 用户管理
- `GET /api/users`
- `POST /api/users`
- `PUT /api/users/{id}`
- `PUT /api/users/{id}/status`
- `PUT /api/users/{id}/role`
### 4. 资料管理
- `POST /api/source/upload`
- `GET /api/source/list`
- `GET /api/source/{id}`
- `DELETE /api/source/{id}`
### 5. 任务管理
- `GET /api/tasks/pool`
- `GET /api/tasks/mine`
- `GET /api/tasks/pending-review`
- `GET /api/tasks`
- `POST /api/tasks`
- `GET /api/tasks/{id}`
- `POST /api/tasks/{id}/claim`
- `POST /api/tasks/{id}/unclaim`
- `POST /api/tasks/{id}/reclaim`
- `PUT /api/tasks/{id}/reassign`
### 6. 提取标注
- `GET /api/extraction/{taskId}`
- `PUT /api/extraction/{taskId}`
- `POST /api/extraction/{taskId}/submit`
- `POST /api/extraction/{taskId}/approve`
- `POST /api/extraction/{taskId}/reject`
### 7. 问答生成
- `GET /api/qa/{taskId}`
- `PUT /api/qa/{taskId}`
- `POST /api/qa/{taskId}/submit`
- `POST /api/qa/{taskId}/approve`
- `POST /api/qa/{taskId}/reject`
### 8. 导出与微调
- `GET /api/training/samples`
- `POST /api/export/batch`
- `POST /api/export/{batchId}/finetune`
- `GET /api/export/{batchId}/status`
- `GET /api/export/list`
### 9. 系统配置
- `GET /api/config`
- `PUT /api/config/{key}`
### 10. 视频处理
- `POST /api/video/process`
- `GET /api/video/jobs/{jobId}`
- `POST /api/video/jobs/{jobId}/reset`
- `POST /api/video/callback`
## 定时任务
当前项目中**没有启用 Spring `@Scheduled` 定时同步任务**。
现有异步能力主要通过以下方式完成:
- 事务提交后事件监听
- 提取审批通过后触发问答生成
- 外部 AI 服务异步回调
- 视频处理完成后回调 `/api/video/callback`
- Redis 分布式锁
- 用于任务领取并发控制
如果后续需要周期性任务,建议单独引入明确的调度场景,不要复用当前业务链路中的事件机制。
## 部署说明
### 1. 数据库初始化
初始化 SQL 位于:
- 开发/部署初始化脚本
- [src/main/resources/sql/init.sql](d:/workspace/label/label_backend/src/main/resources/sql/init.sql)
说明:
- `src/main/resources/sql/init.sql` 会随源码保存,但**不会被打入 jar、target/classes 或分发包**
- `docker-compose.yml` 通过挂载该文件完成 PostgreSQL 初始化
### 2. 本地构建
```bash
mvn clean package -DskipTests
```
构建产物:
...
- `target/label-backend-1.0.0-SNAPSHOT.zip`
- `target/label-backend-1.0.0-SNAPSHOT.tar.gz`
### 3. 分发包结构
分发包由 [distribution.xml](d:/workspace/label/label_backend/assembly/distribution.xml) 组装,解压后结构如下:
```text
label-backend-<version>/
├── bin/
│ └── start.sh
├── etc/
│ ├── application.yml
│ └── logback.xml
├── libs/
│ ├── label-backend-<version>.jar
│ └── *.jar
└── logs/
```
### 4. 启动脚本
启动脚本位于 [start.sh](d:/workspace/label/label_backend/scripts/start.sh)。
行为说明:
- 在 Docker 容器中检测到 `/.dockerenv` 时,前台 `exec java ...`
- 在宿主机环境中使用 `nohup` 后台启动
- 日志默认写入 `logs/startup.log`
### 5. Docker Compose 启动
```bash
docker compose up -d
```
当前 `docker-compose.yml` 会启动:
- PostgreSQL
- Redis
- RustFS当前使用 MinIO 作为 S3 兼容替代)
- backend
- ai-service 占位服务
- frontend 占位服务
### 6. Docker 镜像构建
```bash
docker build -t label-backend:latest .
```
`Dockerfile` 使用多阶段构建,并从项目根目录的 `scripts/start.sh` 复制启动脚本。
## 注意事项
1. 开发模式下 `auth.enabled=false`
- 此时会使用 mock 用户身份,不适合生产环境
- 生产部署前必须显式启用真实鉴权
2. 多租户隔离仍依赖 `CompanyContext` + `TenantLineInnerInterceptor`
- 租户表查询默认依赖租户拦截器
- 个别特殊场景通过显式 `companyId` 参数校验
3. `sys_config``sys_company``video_process_job` 属于特殊表
- 其中部分表被排除出自动租户注入,需在服务层显式控制
4. SQL 已迁移到 `src/main/resources/sql`
- 仅作为源码级初始化文件保留
- 不会打进构建产物
5. 集成测试依赖 Testcontainers
- 运行完整集成测试需要本机可用 Docker 环境
6. 认证实现已移除 Shiro
- 当前使用自定义拦截器、注解与 Redis Token
7. 用户上下文 ThreadLocal 已移除
- 当前只保留 `CompanyContext`
- 用户主体通过请求属性中的 `TokenPrincipal` 传递
## 开发规范
当前约束摘要:
- 统一扁平目录结构,避免再次引入按业务域分层的旧目录
- DTO 统一放在 `dto/`,不再拆分 `request/response`
- Service 统一放在 `service/`,不拆 `service/impl`
- 业务规则优先放在 ServiceController 只负责 HTTP 协议层
- 新增接口需同步补齐 Swagger 注解与测试
- 所有对外接口参数必须在 Swagger 中明确体现名称、类型和含义
- 固定结构请求体禁止继续使用匿名 `Map<String, Object>``Map<String, String>`,必须定义 DTO 并补齐 `@Schema` 字段说明
- 固定结构响应应优先使用明确 DTO或至少为 Swagger 暴露对象补齐字段级 `@Schema` 注解
- 路径参数、查询参数、请求体、分页包装和通用返回体都必须维护可读的 OpenAPI 文档说明
- 需要保持历史兼容的原始 JSON 字符串请求体可以继续使用 `String`,但必须在 Swagger `@RequestBody` 中说明完整 JSON body 的提交方式和兼容原因
- 修改 Controller 参数、请求 DTO、响应 DTO 或对外实体后,必须运行 `mvn -Dtest=OpenApiAnnotationTest test`,确保 Swagger 参数名称、类型和含义没有回退
- 目录、配置、打包方式变化后README、设计文档和部署说明必须同步更新