task-207(服务层): done=true 终态决策纯语义(SimilarAsinSubmitResultSemantics:done/error→终态回调、chunkIndex/Total 缺省归一)接线回 submit + 8 单测
按 3+2 约定:文件生成/终态落库依赖真实 LLM 流水线,留待真实运行时经 env-gated 回放(CONTRACT_* 已就绪)
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
|||||||
|
package com.nanri.aiimage.modules.similarasin.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* similarasin 结果提交语义的纯计算(task-207 提取,防业务漂移)。
|
||||||
|
*
|
||||||
|
* done=true / error 非空 => 本批为终态回调(触发读元数据/终态处理);
|
||||||
|
* chunkIndex/chunkTotal 缺省归一(0/1)。纯函数,便于离线单测。
|
||||||
|
*/
|
||||||
|
public final class SimilarAsinSubmitResultSemantics {
|
||||||
|
|
||||||
|
private SimilarAsinSubmitResultSemantics() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 是否终态回调:done=true 或 error 非空。 */
|
||||||
|
public static boolean isTerminalRequest(boolean done, String error) {
|
||||||
|
if (done) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return error != null && !error.isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** chunkIndex 缺省(含 <=0 之外的 null)归一为 0(与既有 submit 逻辑一致)。 */
|
||||||
|
public static int chunkIndex(Integer chunkIndex) {
|
||||||
|
return chunkIndex == null ? 0 : chunkIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** chunkTotal 缺省归一为 1。 */
|
||||||
|
public static int chunkTotal(Integer chunkTotal) {
|
||||||
|
return chunkTotal == null ? 1 : chunkTotal;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-3
@@ -1017,12 +1017,15 @@ public class SimilarAsinTaskService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensureTaskOwnedByCurrentInstance(task, "submit result");
|
ensureTaskOwnedByCurrentInstance(task, "submit result");
|
||||||
int chunkIndex = request.getChunkIndex() == null ? 0 : request.getChunkIndex();
|
int chunkIndex = com.nanri.aiimage.modules.similarasin.model.SimilarAsinSubmitResultSemantics
|
||||||
int chunkTotal = request.getChunkTotal() == null ? 1 : request.getChunkTotal();
|
.chunkIndex(request.getChunkIndex());
|
||||||
|
int chunkTotal = com.nanri.aiimage.modules.similarasin.model.SimilarAsinSubmitResultSemantics
|
||||||
|
.chunkTotal(request.getChunkTotal());
|
||||||
boolean done = Boolean.TRUE.equals(request.getDone());
|
boolean done = Boolean.TRUE.equals(request.getDone());
|
||||||
String scopeKey = firstNonBlank(request.getSubmissionId(), "task:" + taskId);
|
String scopeKey = firstNonBlank(request.getSubmissionId(), "task:" + taskId);
|
||||||
String scopeHash = DigestUtil.sha256Hex(scopeKey);
|
String scopeHash = DigestUtil.sha256Hex(scopeKey);
|
||||||
boolean terminalCallback = done || request.getError() != null && !request.getError().isBlank();
|
boolean terminalCallback = com.nanri.aiimage.modules.similarasin.model.SimilarAsinSubmitResultSemantics
|
||||||
|
.isTerminalRequest(done, request.getError());
|
||||||
SubmittedTaskMetadata taskMetadata = terminalCallback
|
SubmittedTaskMetadata taskMetadata = terminalCallback
|
||||||
? readSubmittedTaskMetadata(task)
|
? readSubmittedTaskMetadata(task)
|
||||||
: null;
|
: null;
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package com.nanri.aiimage.modules.similarasin.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* task-207(服务层部分):done=true 最后一批语义纯单测。
|
||||||
|
*
|
||||||
|
* 覆盖 done/error→终态回调决策、chunkIndex/chunkTotal 缺省归一、末片判定;并解析
|
||||||
|
* python-contract/result.request.json(done=true)断言终态触发。文件生成(xlsx/终态落库)
|
||||||
|
* 依赖真实 LLM 流水线,按 3+2 约定在真实运行时经 env-gated 回放验证(本类不依赖 DB)。
|
||||||
|
*/
|
||||||
|
class SimilarAsinSubmitResultSemanticsTest {
|
||||||
|
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void doneTrueIsTerminalEvenWithoutError() {
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.isTerminalRequest(true, null));
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.isTerminalRequest(true, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void errorNonBlankIsTerminal() {
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.isTerminalRequest(false, "source timeout"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void runningChunkWithoutDoneOrErrorIsNotTerminal() {
|
||||||
|
assertFalse(SimilarAsinSubmitResultSemantics.isTerminalRequest(false, null));
|
||||||
|
assertFalse(SimilarAsinSubmitResultSemantics.isTerminalRequest(false, " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void blankOrNullErrorNotTerminalWhenNotDone() {
|
||||||
|
assertFalse(SimilarAsinSubmitResultSemantics.isTerminalRequest(false, ""));
|
||||||
|
assertFalse(SimilarAsinSubmitResultSemantics.isTerminalRequest(false, " "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void chunkIndexNullDefaultsZero() {
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.chunkIndex(null) == 0);
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.chunkIndex(3) == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void chunkTotalNullDefaultsOne() {
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.chunkTotal(null) == 1);
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.chunkTotal(5) == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void explicitValuesPassedThrough() {
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.chunkIndex(7) == 7);
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.chunkTotal(2) == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void fixtureDoneTrueRequestIsTerminal() throws Exception {
|
||||||
|
Path fixture = Path.of(System.getProperty("user.dir"),
|
||||||
|
"src/test/resources/python-contract/result.request.json");
|
||||||
|
JsonNode req = MAPPER.readTree(Files.readAllBytes(fixture));
|
||||||
|
boolean done = req.path("done").asBoolean(false);
|
||||||
|
String error = req.path("error").isNull() ? null : req.path("error").asText();
|
||||||
|
assertTrue(done, "夹具 done 应为 true");
|
||||||
|
assertTrue(SimilarAsinSubmitResultSemantics.isTerminalRequest(done, error),
|
||||||
|
"done=true 的提交应触发终态回调");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user