Revert "refactor: flatten dto entity and mapper packages"

This reverts commit 29766ebd28.
This commit is contained in:
wh
2026-04-14 13:31:50 +08:00
parent 29766ebd28
commit 3e33398dd2
64 changed files with 1780 additions and 1524 deletions

View File

@@ -1,7 +1,7 @@
package com.label.integration;
import com.label.AbstractIntegrationTest;
import com.label.dto.LoginRequest;
import com.label.module.user.dto.LoginRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -14,10 +14,12 @@ import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 鎻愬彇闃舵瀹℃壒闆嗘垚娴嬭瘯锛圲S4锛夈€? *
* 娴嬭瘯鍦烘櫙锛? * 1. 瀹℃壒閫氳繃 鈫?QA_GENERATION 浠诲姟鑷姩鍒涘缓锛宻ource_data 鐘舵€佹洿鏂颁负 QA_REVIEW
* 2. 瀹℃壒浜轰笌鎻愪氦浜虹浉鍚岋紙鑷锛夆啋 403 SELF_REVIEW_FORBIDDEN
* 3. 椹冲洖鍚庢爣娉ㄥ憳鍙噸棰嗕换鍔″苟鍐嶆鎻愪氦
* 提取阶段审批集成测试US4
*
* 测试场景:
* 1. 审批通过 → QA_GENERATION 任务自动创建source_data 状态更新为 QA_REVIEW
* 2. 审批人与提交人相同(自审)→ 403 SELF_REVIEW_FORBIDDEN
* 3. 驳回后标注员可重领任务并再次提交
*/
public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
@@ -31,14 +33,15 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
@BeforeEach
void setup() {
// 鑾峰彇绉嶅瓙鐢ㄦ埛 ID锛坕nit.sql 涓凡鎻掑叆锛? annotatorUserId = jdbcTemplate.queryForObject(
// 获取种子用户 IDinit.sql 中已插入)
annotatorUserId = jdbcTemplate.queryForObject(
"SELECT id FROM sys_user WHERE username = 'annotator01'", Long.class);
reviewerUserId = jdbcTemplate.queryForObject(
"SELECT id FROM sys_user WHERE username = 'reviewer01'", Long.class);
Long companyId = jdbcTemplate.queryForObject(
"SELECT id FROM sys_company WHERE company_code = 'DEMO'", Long.class);
// 鎻掑叆娴嬭瘯 source_data
// 插入测试 source_data
jdbcTemplate.execute(
"INSERT INTO source_data (company_id, uploader_id, data_type, file_path, " +
"file_name, file_size, bucket_name, status) " +
@@ -47,7 +50,7 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
sourceId = jdbcTemplate.queryForObject(
"SELECT id FROM source_data ORDER BY id DESC LIMIT 1", Long.class);
// 鎻掑叆 UNCLAIMED EXTRACTION 浠诲姟
// 插入 UNCLAIMED EXTRACTION 任务
jdbcTemplate.execute(
"INSERT INTO annotation_task (company_id, source_id, task_type, status) " +
"VALUES (" + companyId + ", " + sourceId + ", 'EXTRACTION', 'UNCLAIMED')");
@@ -55,63 +58,66 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
"SELECT id FROM annotation_task ORDER BY id DESC LIMIT 1", Long.class);
}
// ------------------------------------------------------------------ 娴嬭瘯 1: 瀹℃壒閫氳繃 鈫?QA 浠诲姟鑷姩鍒涘缓 --
// ------------------------------------------------------------------ 测试 1: 审批通过 → QA 任务自动创建 --
@Test
@DisplayName("瀹℃壒閫氳繃鍚庯紝QA_GENERATION 浠诲姟鑷姩鍒涘缓锛宻ource_data 鐘舵€佸彉涓?QA_REVIEW")
@DisplayName("审批通过后,QA_GENERATION 任务自动创建source_data 状态变为 QA_REVIEW")
void approveTask_thenQaTaskAndSourceStatusUpdated() {
String annotatorToken = loginAndGetToken("DEMO", "annotator01", "annot123");
String reviewerToken = loginAndGetToken("DEMO", "reviewer01", "review123");
// 1. 鏍囨敞鍛橀鍙栦换鍔? ResponseEntity<Map> claimResp = restTemplate.exchange(
// 1. 标注员领取任务
ResponseEntity<Map> claimResp = restTemplate.exchange(
baseUrl("/api/tasks/" + taskId + "/claim"),
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
assertThat(claimResp.getStatusCode()).isEqualTo(HttpStatus.OK);
// 2. 鏍囨敞鍛樻彁浜ゆ爣娉? ResponseEntity<Map> submitResp = restTemplate.exchange(
// 2. 标注员提交标注
ResponseEntity<Map> submitResp = restTemplate.exchange(
baseUrl("/api/extraction/" + taskId + "/submit"),
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
assertThat(submitResp.getStatusCode()).isEqualTo(HttpStatus.OK);
// 3. 瀹℃牳鍛樺鎵归€氳繃
// 娉細ExtractionApprovedEventListener(@TransactionalEventListener AFTER_COMMIT)
// 鍦ㄥ悓涓€绾跨▼涓悓姝ユ墽琛岋紝HTTP 鍝嶅簲杩斿洖鍓嶅凡瀹屾垚鍚庣画澶勭悊
// 3. 审核员审批通过
// 注:ExtractionApprovedEventListener(@TransactionalEventListener AFTER_COMMIT)
// 在同一线程中同步执行HTTP 响应返回前已完成后续处理
ResponseEntity<Map> approveResp = restTemplate.exchange(
baseUrl("/api/extraction/" + taskId + "/approve"),
HttpMethod.POST, bearerRequest(reviewerToken), Map.class);
assertThat(approveResp.getStatusCode()).isEqualTo(HttpStatus.OK);
// 楠岃瘉锛氬師浠诲姟鐘舵€佸彉涓?APPROVED锛宨s_final=true
// 验证:原任务状态变为 APPROVEDis_final=true
Map<String, Object> taskRow = jdbcTemplate.queryForMap(
"SELECT status, is_final FROM annotation_task WHERE id = ?", taskId);
assertThat(taskRow.get("status")).isEqualTo("APPROVED");
assertThat(taskRow.get("is_final")).isEqualTo(Boolean.TRUE);
// 楠岃瘉锛歈A_GENERATION 浠诲姟宸茶嚜鍔ㄥ垱寤猴紙UNCLAIMED 鐘舵€侊級
// 验证QA_GENERATION 任务已自动创建(UNCLAIMED 状态)
Integer qaTaskCount = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM annotation_task " +
"WHERE source_id = ? AND task_type = 'QA_GENERATION' AND status = 'UNCLAIMED'",
Integer.class, sourceId);
assertThat(qaTaskCount).as("QA_GENERATION 浠诲姟搴斿凡鍒涘缓").isEqualTo(1);
assertThat(qaTaskCount).as("QA_GENERATION 任务应已创建").isEqualTo(1);
// 楠岃瘉锛歴ource_data 鐘舵€佸凡鏇存柊涓?QA_REVIEW
// 验证source_data 状态已更新为 QA_REVIEW
String sourceStatus = jdbcTemplate.queryForObject(
"SELECT status FROM source_data WHERE id = ?", String.class, sourceId);
assertThat(sourceStatus).as("source_data 鐘舵€佸簲涓?QA_REVIEW").isEqualTo("QA_REVIEW");
assertThat(sourceStatus).as("source_data 状态应为 QA_REVIEW").isEqualTo("QA_REVIEW");
// 楠岃瘉锛歵raining_dataset 宸蹭互 PENDING_REVIEW 鐘舵€佸垱寤? Integer datasetCount = jdbcTemplate.queryForObject(
// 验证training_dataset 已以 PENDING_REVIEW 状态创建
Integer datasetCount = jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM training_dataset " +
"WHERE source_id = ? AND status = 'PENDING_REVIEW'",
Integer.class, sourceId);
assertThat(datasetCount).as("training_dataset 搴斿凡鍒涘缓").isEqualTo(1);
assertThat(datasetCount).as("training_dataset 应已创建").isEqualTo(1);
}
// ------------------------------------------------------------------ 娴嬭瘯 2: 鑷杩斿洖 403 --
// ------------------------------------------------------------------ 测试 2: 自审返回 403 --
@Test
@DisplayName("瀹℃壒浜轰笌浠诲姟棰嗗彇浜虹浉鍚岋紙鑷锛夆啋 403 SELF_REVIEW_FORBIDDEN")
@DisplayName("审批人与任务领取人相同(自审)→ 403 SELF_REVIEW_FORBIDDEN")
void approveOwnSubmission_returnsForbidden() {
// 鐩存帴灏嗕换鍔$疆涓?SUBMITTED 骞惰 claimed_by = reviewer01锛堟ā鎷熻嚜瀹″満鏅級
// 直接将任务置为 SUBMITTED 并设 claimed_by = reviewer01(模拟自审场景)
jdbcTemplate.execute(
"UPDATE annotation_task " +
"SET status = 'SUBMITTED', claimed_by = " + reviewerUserId +
@@ -126,63 +132,67 @@ public class ExtractionApprovalIntegrationTest extends AbstractIntegrationTest {
assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
// 楠岃瘉浠诲姟鐘舵€佹湭鍙? String status = jdbcTemplate.queryForObject(
// 验证任务状态未变
String status = jdbcTemplate.queryForObject(
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
assertThat(status).isEqualTo("SUBMITTED");
}
// ------------------------------------------------------------------ 娴嬭瘯 3: 椹冲洖 鈫?閲嶉 鈫?鍐嶆彁浜?--
// ------------------------------------------------------------------ 测试 3: 驳回 → 重领 → 再提交 --
@Test
@DisplayName("椹冲洖鍚庢爣娉ㄥ憳鍙噸棰嗕换鍔″苟鍐嶆鎻愪氦锛屼换鍔$姸鎬佹仮澶嶄负 SUBMITTED")
@DisplayName("驳回后标注员可重领任务并再次提交,任务状态恢复为 SUBMITTED")
void rejectThenReclaimAndResubmit_succeeds() {
String annotatorToken = loginAndGetToken("DEMO", "annotator01", "annot123");
String reviewerToken = loginAndGetToken("DEMO", "reviewer01", "review123");
// 1. 鏍囨敞鍛橀鍙栧苟鎻愪氦
// 1. 标注员领取并提交
restTemplate.exchange(baseUrl("/api/tasks/" + taskId + "/claim"),
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
restTemplate.exchange(baseUrl("/api/extraction/" + taskId + "/submit"),
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
// 2. 瀹℃牳鍛橀┏鍥烇紙椹冲洖鍘熷洜蹇呭~锛? HttpHeaders rejectHeaders = new HttpHeaders();
// 2. 审核员驳回(驳回原因必填)
HttpHeaders rejectHeaders = new HttpHeaders();
rejectHeaders.set("Authorization", "Bearer " + reviewerToken);
rejectHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, String>> rejectReq = new HttpEntity<>(
Map.of("reason", "瀹炰綋璇嗗埆鏈夎锛岃閲嶆柊鏍囨敞"), rejectHeaders);
Map.of("reason", "实体识别有误,请重新标注"), rejectHeaders);
ResponseEntity<Map> rejectResp = restTemplate.exchange(
baseUrl("/api/extraction/" + taskId + "/reject"),
HttpMethod.POST, rejectReq, Map.class);
assertThat(rejectResp.getStatusCode()).isEqualTo(HttpStatus.OK);
// 楠岃瘉锛氫换鍔$姸鎬佸彉涓?REJECTED
// 验证:任务状态变为 REJECTED
String statusAfterReject = jdbcTemplate.queryForObject(
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
assertThat(statusAfterReject).isEqualTo("REJECTED");
// 3. 鏍囨敞鍛橀噸棰嗕换鍔★紙REJECTED 鈫?IN_PROGRESS锛? ResponseEntity<Map> reclaimResp = restTemplate.exchange(
// 3. 标注员重领任务(REJECTED IN_PROGRESS
ResponseEntity<Map> reclaimResp = restTemplate.exchange(
baseUrl("/api/tasks/" + taskId + "/reclaim"),
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
assertThat(reclaimResp.getStatusCode()).isEqualTo(HttpStatus.OK);
// 楠岃瘉锛氫换鍔$姸鎬佹仮澶嶄负 IN_PROGRESS
// 验证:任务状态恢复为 IN_PROGRESS
String statusAfterReclaim = jdbcTemplate.queryForObject(
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
assertThat(statusAfterReclaim).isEqualTo("IN_PROGRESS");
// 4. 鏍囨敞鍛樺啀娆℃彁浜わ紙IN_PROGRESS 鈫?SUBMITTED锛? ResponseEntity<Map> resubmitResp = restTemplate.exchange(
// 4. 标注员再次提交(IN_PROGRESS SUBMITTED
ResponseEntity<Map> resubmitResp = restTemplate.exchange(
baseUrl("/api/extraction/" + taskId + "/submit"),
HttpMethod.POST, bearerRequest(annotatorToken), Map.class);
assertThat(resubmitResp.getStatusCode()).isEqualTo(HttpStatus.OK);
// 楠岃瘉锛氫换鍔$姸鎬佸彉涓?SUBMITTED
// 验证:任务状态变为 SUBMITTED
String finalStatus = jdbcTemplate.queryForObject(
"SELECT status FROM annotation_task WHERE id = ?", String.class, taskId);
assertThat(finalStatus).isEqualTo("SUBMITTED");
}
// ------------------------------------------------------------------ 宸ュ叿鏂规硶 --
// ------------------------------------------------------------------ 工具方法 --
private String loginAndGetToken(String companyCode, String username, String password) {
LoginRequest req = new LoginRequest();