合并分支

This commit is contained in:
super
2026-05-31 09:28:48 +08:00
parent 9835831415
commit ea37d82d73
35 changed files with 200 additions and 7893 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
import{bN as r}from"./java-modules-B8c-YG5x.js";const n="";function s(e){return r(`${n}/api/brand/expand-folder-recursive`,{folder:e})}export{s as e}; import{bN as r}from"./java-modules-WSWRTEaR.js";const n="";function s(e){return r(`${n}/api/brand/expand-folder-recursive`,{folder:e})}export{s as e};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>格式转换 - 数富AI</title> <title>格式转换 - 数富AI</title>
<script type="module" crossorigin src="/assets/convert.js"></script> <script type="module" crossorigin src="/assets/convert.js"></script>
<link rel="modulepreload" crossorigin href="/assets/java-modules-B8c-YG5x.js"> <link rel="modulepreload" crossorigin href="/assets/java-modules-WSWRTEaR.js">
<link rel="modulepreload" crossorigin href="/assets/brand-COze15GJ.js"> <link rel="modulepreload" crossorigin href="/assets/brand-5GpfAyHD.js">
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css"> <link rel="stylesheet" crossorigin href="/assets/java-modules-B3hGvRck.css">
<link rel="stylesheet" crossorigin href="/assets/convert-6oxZNMye.css"> <link rel="stylesheet" crossorigin href="/assets/convert-6oxZNMye.css">
</head> </head>
<body> <body>

View File

@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数据去重 - 数富AI</title> <title>数据去重 - 数富AI</title>
<script type="module" crossorigin src="/assets/dedupe.js"></script> <script type="module" crossorigin src="/assets/dedupe.js"></script>
<link rel="modulepreload" crossorigin href="/assets/java-modules-B8c-YG5x.js"> <link rel="modulepreload" crossorigin href="/assets/java-modules-WSWRTEaR.js">
<link rel="modulepreload" crossorigin href="/assets/brand-COze15GJ.js"> <link rel="modulepreload" crossorigin href="/assets/brand-5GpfAyHD.js">
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css"> <link rel="stylesheet" crossorigin href="/assets/java-modules-B3hGvRck.css">
<link rel="stylesheet" crossorigin href="/assets/dedupe-DpfPYQDv.css"> <link rel="stylesheet" crossorigin href="/assets/dedupe-DpfPYQDv.css">
</head> </head>
<body> <body>

View File

@@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>删除品牌 - 数富AI</title> <title>删除品牌 - 数富AI</title>
<script type="module" crossorigin src="/assets/delete-brand.js"></script> <script type="module" crossorigin src="/assets/delete-brand.js"></script>
<link rel="modulepreload" crossorigin href="/assets/java-modules-B8c-YG5x.js"> <link rel="modulepreload" crossorigin href="/assets/java-modules-WSWRTEaR.js">
<link rel="modulepreload" crossorigin href="/assets/brand-COze15GJ.js"> <link rel="modulepreload" crossorigin href="/assets/brand-5GpfAyHD.js">
<link rel="modulepreload" crossorigin href="/assets/categorized-timers-JPA-olTr.js"> <link rel="modulepreload" crossorigin href="/assets/categorized-timers-JPA-olTr.js">
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css"> <link rel="stylesheet" crossorigin href="/assets/java-modules-B3hGvRck.css">
<link rel="stylesheet" crossorigin href="/assets/delete-brand-DZ3x5hOX.css"> <link rel="stylesheet" crossorigin href="/assets/delete-brand-DZ3x5hOX.css">
</head> </head>
<body> <body>

View File

@@ -5,9 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>数据拆分 - 数富AI</title> <title>数据拆分 - 数富AI</title>
<script type="module" crossorigin src="/assets/split.js"></script> <script type="module" crossorigin src="/assets/split.js"></script>
<link rel="modulepreload" crossorigin href="/assets/java-modules-B8c-YG5x.js"> <link rel="modulepreload" crossorigin href="/assets/java-modules-WSWRTEaR.js">
<link rel="modulepreload" crossorigin href="/assets/brand-COze15GJ.js"> <link rel="modulepreload" crossorigin href="/assets/brand-5GpfAyHD.js">
<link rel="stylesheet" crossorigin href="/assets/java-modules-l5anrOZ2.css"> <link rel="stylesheet" crossorigin href="/assets/java-modules-B3hGvRck.css">
<link rel="stylesheet" crossorigin href="/assets/split-CR1PtDQE.css"> <link rel="stylesheet" crossorigin href="/assets/split-CR1PtDQE.css">
</head> </head>
<body> <body>

View File

@@ -36,6 +36,10 @@ public class AppearancePatentCozeClient {
private final AtomicLong credentialCursor = new AtomicLong(); private final AtomicLong credentialCursor = new AtomicLong();
public List<AppearancePatentResultRowDto> inspect(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) { public List<AppearancePatentResultRowDto> inspect(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) {
return inspect(rows, prompt, apiKey, null);
}
public List<AppearancePatentResultRowDto> inspect(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) {
if (rows == null || rows.isEmpty()) { if (rows == null || rows.isEmpty()) {
return List.of(); return List.of();
} }
@@ -44,7 +48,7 @@ public class AppearancePatentCozeClient {
return rows.stream().map(this::copy).toList(); return rows.stream().map(this::copy).toList();
} }
try { try {
return inspectWithFallback(rows, prompt, apiKey); return inspectWithFallback(rows, prompt, apiKey, patentToken);
} catch (Exception ex) { } catch (Exception ex) {
String failureMessage = failureMessage(ex); String failureMessage = failureMessage(ex);
log.warn("[appearance-patent] coze batch failed size={} err={}", rows.size(), failureMessage); log.warn("[appearance-patent] coze batch failed size={} err={}", rows.size(), failureMessage);
@@ -53,15 +57,16 @@ public class AppearancePatentCozeClient {
} }
public CozeSubmitResponse submitWorkflow(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) throws Exception { public CozeSubmitResponse submitWorkflow(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) throws Exception {
return submitWorkflow(rows, prompt, apiKey, nextCredential()); return submitWorkflow(rows, prompt, apiKey, null, nextCredential());
} }
public CozeSubmitResponse submitWorkflow(List<AppearancePatentResultRowDto> rows, public CozeSubmitResponse submitWorkflow(List<AppearancePatentResultRowDto> rows,
String prompt, String prompt,
String apiKey, String apiKey,
String patentToken,
CozeCredentialRef credential) throws Exception { CozeCredentialRef credential) throws Exception {
CozeCredentialRef resolvedCredential = resolveCredential(credential); CozeCredentialRef resolvedCredential = resolveCredential(credential);
JsonNode submitRoot = objectMapper.readTree(postWorkflow(rows, prompt, apiKey, resolvedCredential)); JsonNode submitRoot = objectMapper.readTree(postWorkflow(rows, prompt, apiKey, patentToken, resolvedCredential));
ensureSuccess(submitRoot); ensureSuccess(submitRoot);
return new CozeSubmitResponse( return new CozeSubmitResponse(
extractExecuteId(submitRoot), extractExecuteId(submitRoot),
@@ -103,12 +108,12 @@ public class AppearancePatentCozeClient {
return rows.stream().map(this::copy).map(row -> markFailed(row, failureMessage)).toList(); return rows.stream().map(this::copy).map(row -> markFailed(row, failureMessage)).toList();
} }
private List<AppearancePatentResultRowDto> inspectWithFallback(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) { private List<AppearancePatentResultRowDto> inspectWithFallback(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) {
try { try {
if (rows.size() == 1) { if (rows.size() == 1) {
return inspectSingleRowWithRetry(rows, prompt, apiKey); return inspectSingleRowWithRetry(rows, prompt, apiKey, patentToken);
} }
InspectAttempt attempt = inspectOnce(rows, prompt, apiKey); InspectAttempt attempt = inspectOnce(rows, prompt, apiKey, patentToken);
if (attempt.resolvedCount() < rows.size()) { if (attempt.resolvedCount() < rows.size()) {
throw new PartialCozeResultException(attempt.resolvedCount(), rows.size(), attempt.rawResultCount()); throw new PartialCozeResultException(attempt.resolvedCount(), rows.size(), attempt.rawResultCount());
} }
@@ -119,17 +124,17 @@ public class AppearancePatentCozeClient {
log.warn("[appearance-patent] coze batch fallback split size={} left={} right={} err={}", log.warn("[appearance-patent] coze batch fallback split size={} left={} right={} err={}",
rows.size(), middle, rows.size() - middle, failureMessage(ex)); rows.size(), middle, rows.size() - middle, failureMessage(ex));
List<AppearancePatentResultRowDto> merged = new ArrayList<>(rows.size()); List<AppearancePatentResultRowDto> merged = new ArrayList<>(rows.size());
merged.addAll(inspectPartitionWithFailureFallback(rows.subList(0, middle), prompt, apiKey)); merged.addAll(inspectPartitionWithFailureFallback(rows.subList(0, middle), prompt, apiKey, patentToken));
merged.addAll(inspectPartitionWithFailureFallback(rows.subList(middle, rows.size()), prompt, apiKey)); merged.addAll(inspectPartitionWithFailureFallback(rows.subList(middle, rows.size()), prompt, apiKey, patentToken));
return merged; return merged;
} }
throw propagate(ex); throw propagate(ex);
} }
} }
private List<AppearancePatentResultRowDto> inspectPartitionWithFailureFallback(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) { private List<AppearancePatentResultRowDto> inspectPartitionWithFailureFallback(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) {
try { try {
return inspectWithFallback(rows, prompt, apiKey); return inspectWithFallback(rows, prompt, apiKey, patentToken);
} catch (Exception ex) { } catch (Exception ex) {
String failureMessage = failureMessage(ex); String failureMessage = failureMessage(ex);
log.warn("[appearance-patent] coze partition failed size={} err={}", rows.size(), failureMessage); log.warn("[appearance-patent] coze partition failed size={} err={}", rows.size(), failureMessage);
@@ -137,12 +142,12 @@ public class AppearancePatentCozeClient {
} }
} }
private List<AppearancePatentResultRowDto> inspectSingleRowWithRetry(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) throws Exception { private List<AppearancePatentResultRowDto> inspectSingleRowWithRetry(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) throws Exception {
AppearancePatentResultRowDto row = rows.getFirst(); AppearancePatentResultRowDto row = rows.getFirst();
PartialCozeResultException lastFailure = null; PartialCozeResultException lastFailure = null;
for (int attemptIndex = 1; attemptIndex <= 3; attemptIndex++) { for (int attemptIndex = 1; attemptIndex <= 3; attemptIndex++) {
try { try {
InspectAttempt attempt = inspectOnce(rows, prompt, apiKey); InspectAttempt attempt = inspectOnce(rows, prompt, apiKey, patentToken);
if (attempt.resolvedCount() == rows.size()) { if (attempt.resolvedCount() == rows.size()) {
return attempt.mergedRows(); return attempt.mergedRows();
} }
@@ -173,8 +178,8 @@ public class AppearancePatentCozeClient {
throw lastFailure == null ? new PartialCozeResultException(0, rows.size(), 0) : lastFailure; throw lastFailure == null ? new PartialCozeResultException(0, rows.size(), 0) : lastFailure;
} }
private InspectAttempt inspectOnce(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) throws Exception { private InspectAttempt inspectOnce(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) throws Exception {
String raw = runWorkflowAsyncAndWait(rows, prompt, apiKey); String raw = runWorkflowAsyncAndWait(rows, prompt, apiKey, patentToken);
List<CozeResult> results = parseResults(raw); List<CozeResult> results = parseResults(raw);
if (rows.size() > 1 && !results.isEmpty() && results.stream().noneMatch(this::hasIdentity)) { if (rows.size() > 1 && !results.isEmpty() && results.stream().noneMatch(this::hasIdentity)) {
throw new PartialCozeResultException(0, rows.size(), results.size()); throw new PartialCozeResultException(0, rows.size(), results.size());
@@ -183,9 +188,9 @@ public class AppearancePatentCozeClient {
return new InspectAttempt(raw, merged, resolvedCount(merged), results.size()); return new InspectAttempt(raw, merged, resolvedCount(merged), results.size());
} }
private String runWorkflowAsyncAndWait(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) throws Exception { private String runWorkflowAsyncAndWait(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) throws Exception {
CozeCredentialRef credential = nextCredential(); CozeCredentialRef credential = nextCredential();
JsonNode submitRoot = objectMapper.readTree(postWorkflow(rows, prompt, apiKey, credential)); JsonNode submitRoot = objectMapper.readTree(postWorkflow(rows, prompt, apiKey, patentToken, credential));
ensureSuccess(submitRoot); ensureSuccess(submitRoot);
String immediateData = extractResultDataText(submitRoot); String immediateData = extractResultDataText(submitRoot);
@@ -229,8 +234,9 @@ public class AppearancePatentCozeClient {
private String postWorkflow(List<AppearancePatentResultRowDto> rows, private String postWorkflow(List<AppearancePatentResultRowDto> rows,
String prompt, String prompt,
String apiKey, String apiKey,
String patentToken,
CozeCredentialRef credential) { CozeCredentialRef credential) {
Map<String, Object> parameters = buildParameters(rows, prompt, apiKey); Map<String, Object> parameters = buildParameters(rows, prompt, apiKey, patentToken);
Map<String, Object> body = new LinkedHashMap<>(); Map<String, Object> body = new LinkedHashMap<>();
body.put("workflow_id", credential.workflowId()); body.put("workflow_id", credential.workflowId());
body.put("parameters", parameters); body.put("parameters", parameters);
@@ -350,7 +356,7 @@ public class AppearancePatentCozeClient {
return credentials; return credentials;
} }
private Map<String, Object> buildParameters(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey) { private Map<String, Object> buildParameters(List<AppearancePatentResultRowDto> rows, String prompt, String apiKey, String patentToken) {
List<String> groupKeys = rows.stream().map(row -> nonBlank(row.getGroupKey(), rowKey(row))).toList(); List<String> groupKeys = rows.stream().map(row -> nonBlank(row.getGroupKey(), rowKey(row))).toList();
List<String> rowIds = rows.stream().map(row -> nonBlank(row.getId(), "")).toList(); List<String> rowIds = rows.stream().map(row -> nonBlank(row.getId(), "")).toList();
List<String> asins = rows.stream().map(row -> nonBlank(row.getAsin(), "")).toList(); List<String> asins = rows.stream().map(row -> nonBlank(row.getAsin(), "")).toList();
@@ -370,6 +376,9 @@ public class AppearancePatentCozeClient {
if (apiKey != null && !apiKey.isBlank()) { if (apiKey != null && !apiKey.isBlank()) {
parameters.put("api_key", apiKey.trim()); parameters.put("api_key", apiKey.trim());
} }
if (patentToken != null && !patentToken.isBlank()) {
parameters.put("patent_token", patentToken.trim());
}
return parameters; return parameters;
} }
@@ -383,6 +392,10 @@ public class AppearancePatentCozeClient {
if (apiKey instanceof String apiKeyText && !apiKeyText.isBlank()) { if (apiKey instanceof String apiKeyText && !apiKeyText.isBlank()) {
maskedParameters.put("api_key", maskSecret(apiKeyText)); maskedParameters.put("api_key", maskSecret(apiKeyText));
} }
Object patentToken = maskedParameters.get("patent_token");
if (patentToken instanceof String patentTokenText && !patentTokenText.isBlank()) {
maskedParameters.put("patent_token", maskSecret(patentTokenText));
}
masked.put("parameters", maskedParameters); masked.put("parameters", maskedParameters);
} }
return masked; return masked;

View File

@@ -32,4 +32,9 @@ public class AppearancePatentParseRequest {
@Schema(description = "传递给 Coze workflow parameters.api_key 的任务级密钥。") @Schema(description = "传递给 Coze workflow parameters.api_key 的任务级密钥。")
@NotBlank(message = "密钥不能为空") @NotBlank(message = "密钥不能为空")
private String apiKey; private String apiKey;
@JsonProperty("patent_token")
@JsonAlias({"patentToken"})
@Schema(description = "传递给 Coze workflow parameters.patent_token 的专利汇令牌。非必填。")
private String patentToken;
} }

View File

@@ -9,6 +9,7 @@ import java.util.List;
public class AppearancePatentParsedGroupManifestDto { public class AppearancePatentParsedGroupManifestDto {
private String aiPrompt; private String aiPrompt;
private String apiKey; private String apiKey;
private String patentToken;
private Integer pageSize; private Integer pageSize;
private Integer totalGroups; private Integer totalGroups;
private Integer totalRows; private Integer totalRows;

View File

@@ -10,6 +10,7 @@ import java.util.List;
public class AppearancePatentParsedGroupPageDto { public class AppearancePatentParsedGroupPageDto {
private String aiPrompt; private String aiPrompt;
private String apiKey; private String apiKey;
private String patentToken;
private Integer page; private Integer page;
private Integer pageSize; private Integer pageSize;
private Integer totalGroups; private Integer totalGroups;

View File

@@ -17,6 +17,9 @@ public class AppearancePatentParsedPayloadDto {
@Schema(description = "传递给 Coze workflow parameters.api_key 的任务级密钥") @Schema(description = "传递给 Coze workflow parameters.api_key 的任务级密钥")
private String apiKey; private String apiKey;
@Schema(description = "传递给 Coze workflow parameters.patent_token 的专利汇令牌")
private String patentToken;
@Schema(description = "本次解析的源文件列表") @Schema(description = "本次解析的源文件列表")
private List<AppearancePatentSourceFileDto> sourceFiles = new ArrayList<>(); private List<AppearancePatentSourceFileDto> sourceFiles = new ArrayList<>();

View File

@@ -218,11 +218,11 @@ public class AppearancePatentTaskService {
String aggregateScopeKey = buildAggregateScopeKey(sourceFiles); String aggregateScopeKey = buildAggregateScopeKey(sourceFiles);
String sourceScopeHash = DigestUtil.sha256Hex(aggregateScopeKey); String sourceScopeHash = DigestUtil.sha256Hex(aggregateScopeKey);
String parsedPayload = buildParsedPayloadJson(request.getAiPrompt(), request.getApiKey(), sourceFiles, mergedHeaders, allRows); String parsedPayload = buildParsedPayloadJson(request.getAiPrompt(), request.getApiKey(), request.getPatentToken(), sourceFiles, mergedHeaders, allRows);
long payloadBuiltAt = System.nanoTime(); long payloadBuiltAt = System.nanoTime();
String parsedPayloadPointer = storeParsedPayload(task.getId(), sourceScopeHash, parsedPayload); String parsedPayloadPointer = storeParsedPayload(task.getId(), sourceScopeHash, parsedPayload);
long payloadStoredAt = System.nanoTime(); long payloadStoredAt = System.nanoTime();
task.setResultJson(buildTaskResultJson(request.getAiPrompt(), request.getApiKey(), sourceFiles, parsedPayloadPointer)); task.setResultJson(buildTaskResultJson(request.getAiPrompt(), request.getApiKey(), request.getPatentToken(), sourceFiles, parsedPayloadPointer));
task.setUpdatedAt(LocalDateTime.now()); task.setUpdatedAt(LocalDateTime.now());
fileTaskMapper.updateById(task); fileTaskMapper.updateById(task);
@@ -984,10 +984,11 @@ public class AppearancePatentTaskService {
} }
String prompt = readAiPrompt(task); String prompt = readAiPrompt(task);
String apiKey = readApiKey(task); String apiKey = readApiKey(task);
String patentToken = readPatentToken(task);
int batchSize = Math.max(1, properties.getCozeBatchSize()); int batchSize = Math.max(1, properties.getCozeBatchSize());
List<AppearancePatentResultRowDto> result = new ArrayList<>(); List<AppearancePatentResultRowDto> result = new ArrayList<>();
for (int i = 0; i < items.size(); i += batchSize) { for (int i = 0; i < items.size(); i += batchSize) {
result.addAll(cozeClient.inspect(items.subList(i, Math.min(i + batchSize, items.size())), prompt, apiKey)); result.addAll(cozeClient.inspect(items.subList(i, Math.min(i + batchSize, items.size())), prompt, apiKey, patentToken));
if (progressHook != null) { if (progressHook != null) {
progressHook.run(); progressHook.run();
} }
@@ -1287,6 +1288,14 @@ public class AppearancePatentTaskService {
} }
} }
private String readPatentToken(FileTaskEntity task) {
try {
return normalize(readParsedPayload(task).getPatentToken());
} catch (Exception ignored) {
return "";
}
}
private void finalizeTask(FileTaskEntity task, String error, int rowCount, boolean assembleWorkbook) { private void finalizeTask(FileTaskEntity task, String error, int rowCount, boolean assembleWorkbook) {
String finalError = error; String finalError = error;
FileResultEntity result = null; FileResultEntity result = null;
@@ -1592,6 +1601,7 @@ public class AppearancePatentTaskService {
} }
String prompt = readAiPrompt(task); String prompt = readAiPrompt(task);
String apiKey = readApiKey(task); String apiKey = readApiKey(task);
String patentToken = readPatentToken(task);
int batchSize = Math.max(1, properties.getCozeBatchSize()); int batchSize = Math.max(1, properties.getCozeBatchSize());
List<CozeCandidate> candidates = collectPendingCozeCandidates(task, chunks); List<CozeCandidate> candidates = collectPendingCozeCandidates(task, chunks);
boolean flushRemainder = isResultSubmissionComplete(task.getId()); boolean flushRemainder = isResultSubmissionComplete(task.getId());
@@ -1612,7 +1622,7 @@ public class AppearancePatentTaskService {
List<AppearancePatentResultRowDto> batchRows = batchCandidates.stream() List<AppearancePatentResultRowDto> batchRows = batchCandidates.stream()
.map(CozeCandidate::row) .map(CozeCandidate::row)
.toList(); .toList();
pending |= submitCozeBatch(task, result, job, batchRows, batchIndex, batchTotal, prompt, apiKey, allRowsByBaseId); pending |= submitCozeBatch(task, result, job, batchRows, batchIndex, batchTotal, prompt, apiKey, patentToken, allRowsByBaseId);
batchIndex++; batchIndex++;
} }
return pending || countPendingCozeStates(task.getId()) > 0; return pending || countPendingCozeStates(task.getId()) > 0;
@@ -1626,6 +1636,7 @@ public class AppearancePatentTaskService {
int batchTotal, int batchTotal,
String prompt, String prompt,
String apiKey, String apiKey,
String patentToken,
Map<String, List<AppearancePatentParsedRowVo>> allRowsByBaseId) { Map<String, List<AppearancePatentParsedRowVo>> allRowsByBaseId) {
if (batchRows == null || batchRows.isEmpty()) { if (batchRows == null || batchRows.isEmpty()) {
return false; return false;
@@ -1645,7 +1656,7 @@ public class AppearancePatentTaskService {
AppearancePatentCozeClient.CozeCredentialRef credential = cozeClient.nextCredential(); AppearancePatentCozeClient.CozeCredentialRef credential = cozeClient.nextCredential();
try { try {
AppearancePatentCozeClient.CozeSubmitResponse submit = submitCozeWorkflowThrottled( AppearancePatentCozeClient.CozeSubmitResponse submit = submitCozeWorkflowThrottled(
batchRows, prompt, apiKey, credential, true); batchRows, prompt, apiKey, patentToken, credential, true);
if (submit.immediateData() != null && !submit.immediateData().isBlank()) { if (submit.immediateData() != null && !submit.immediateData().isBlank()) {
List<AppearancePatentResultRowDto> cozeRows = mergeUsableCozeRows(batchRows, submit.immediateData()); List<AppearancePatentResultRowDto> cozeRows = mergeUsableCozeRows(batchRows, submit.immediateData());
mergeCozeRowsIntoSubmittedChunks(task, cozeRows, allRowsByBaseId); mergeCozeRowsIntoSubmittedChunks(task, cozeRows, allRowsByBaseId);
@@ -1945,7 +1956,7 @@ public class AppearancePatentTaskService {
} }
try { try {
AppearancePatentCozeClient.CozeSubmitResponse submit = AppearancePatentCozeClient.CozeSubmitResponse submit =
submitCozeWorkflowThrottled(batchRows, readAiPrompt(task), readApiKey(task), submitCozeWorkflowThrottled(batchRows, readAiPrompt(task), readApiKey(task), readPatentToken(task),
cozeClient.credentialByName(context.credentialName()), false); cozeClient.credentialByName(context.credentialName()), false);
Map<String, List<AppearancePatentParsedRowVo>> allRowsByBaseId = loadAllRowsByBaseId(task); Map<String, List<AppearancePatentParsedRowVo>> allRowsByBaseId = loadAllRowsByBaseId(task);
if (submit.immediateData() != null && !submit.immediateData().isBlank()) { if (submit.immediateData() != null && !submit.immediateData().isBlank()) {
@@ -1996,6 +2007,7 @@ public class AppearancePatentTaskService {
List<AppearancePatentResultRowDto> rows, List<AppearancePatentResultRowDto> rows,
String prompt, String prompt,
String apiKey, String apiKey,
String patentToken,
AppearancePatentCozeClient.CozeCredentialRef credential, AppearancePatentCozeClient.CozeCredentialRef credential,
boolean allowCredentialFallback) throws Exception { boolean allowCredentialFallback) throws Exception {
int attempts = allowCredentialFallback ? Math.max(1, cozeClient.configuredCredentialCount()) : 1; int attempts = allowCredentialFallback ? Math.max(1, cozeClient.configuredCredentialCount()) : 1;
@@ -2018,7 +2030,7 @@ public class AppearancePatentTaskService {
continue; continue;
} }
try (lockHandle; borrowedCredential) { try (lockHandle; borrowedCredential) {
return cozeClient.submitWorkflow(rows, prompt, apiKey, currentCredential); return cozeClient.submitWorkflow(rows, prompt, apiKey, patentToken, currentCredential);
} finally { } finally {
sleepQuietly(COZE_SUBMIT_MIN_INTERVAL_MILLIS); sleepQuietly(COZE_SUBMIT_MIN_INTERVAL_MILLIS);
} }
@@ -2088,7 +2100,7 @@ public class AppearancePatentTaskService {
int partIndex = 1; int partIndex = 1;
for (List<AppearancePatentResultRowDto> partRows : partitions) { for (List<AppearancePatentResultRowDto> partRows : partitions) {
AppearancePatentCozeClient.CozeSubmitResponse submit = AppearancePatentCozeClient.CozeSubmitResponse submit =
submitCozeWorkflowThrottled(partRows, readAiPrompt(task), readApiKey(task), submitCozeWorkflowThrottled(partRows, readAiPrompt(task), readApiKey(task), readPatentToken(task),
cozeClient.credentialByName(context.credentialName()), false); cozeClient.credentialByName(context.credentialName()), false);
if (submit.immediateData() != null && !submit.immediateData().isBlank()) { if (submit.immediateData() != null && !submit.immediateData().isBlank()) {
List<AppearancePatentResultRowDto> cozeRows = List<AppearancePatentResultRowDto> cozeRows =
@@ -2615,7 +2627,7 @@ public class AppearancePatentTaskService {
try { try {
taskFileJobService.touchRunning(context.jobId()); taskFileJobService.touchRunning(context.jobId());
AppearancePatentCozeClient.CozeSubmitResponse submit = AppearancePatentCozeClient.CozeSubmitResponse submit =
submitCozeWorkflowThrottled(batchRows, readAiPrompt(task), readApiKey(task), submitCozeWorkflowThrottled(batchRows, readAiPrompt(task), readApiKey(task), readPatentToken(task),
cozeClient.credentialByName(context.credentialName()), false); cozeClient.credentialByName(context.credentialName()), false);
Map<String, List<AppearancePatentParsedRowVo>> allRowsByBaseId = loadAllRowsByBaseId(task); Map<String, List<AppearancePatentParsedRowVo>> allRowsByBaseId = loadAllRowsByBaseId(task);
if (submit.immediateData() != null && !submit.immediateData().isBlank()) { if (submit.immediateData() != null && !submit.immediateData().isBlank()) {
@@ -3814,10 +3826,11 @@ public class AppearancePatentTaskService {
return val == null ? "" : val.replace(String.valueOf((char) 0xFEFF), "").replace((char) 0x3000, ' ').trim().replaceAll("\\s+", " "); return val == null ? "" : val.replace(String.valueOf((char) 0xFEFF), "").replace((char) 0x3000, ' ').trim().replaceAll("\\s+", " ");
} }
private String buildParsedPayloadJson(String aiPrompt, String apiKey, List<AppearancePatentSourceFileDto> sourceFiles, List<String> headers, List<AppearancePatentParsedRowVo> allRows) { private String buildParsedPayloadJson(String aiPrompt, String apiKey, String patentToken, List<AppearancePatentSourceFileDto> sourceFiles, List<String> headers, List<AppearancePatentParsedRowVo> allRows) {
AppearancePatentParsedPayloadDto payload = new AppearancePatentParsedPayloadDto(); AppearancePatentParsedPayloadDto payload = new AppearancePatentParsedPayloadDto();
payload.setAiPrompt(normalize(aiPrompt)); payload.setAiPrompt(normalize(aiPrompt));
payload.setApiKey(normalize(apiKey)); payload.setApiKey(normalize(apiKey));
payload.setPatentToken(normalize(patentToken));
payload.setSourceFiles(sourceFiles == null ? List.of() : sourceFiles); payload.setSourceFiles(sourceFiles == null ? List.of() : sourceFiles);
payload.setHeaders(headers == null ? List.of() : headers); payload.setHeaders(headers == null ? List.of() : headers);
payload.setItems(List.of()); payload.setItems(List.of());
@@ -3826,10 +3839,11 @@ public class AppearancePatentTaskService {
return writeJson(payload, "保存解析结果失败"); return writeJson(payload, "保存解析结果失败");
} }
private String buildTaskResultJson(String aiPrompt, String apiKey, List<AppearancePatentSourceFileDto> sourceFiles, String parsedPayloadPointer) { private String buildTaskResultJson(String aiPrompt, String apiKey, String patentToken, List<AppearancePatentSourceFileDto> sourceFiles, String parsedPayloadPointer) {
Map<String, Object> payload = new LinkedHashMap<>(); Map<String, Object> payload = new LinkedHashMap<>();
payload.put("aiPrompt", normalize(aiPrompt)); payload.put("aiPrompt", normalize(aiPrompt));
payload.put("apiKey", normalize(apiKey)); payload.put("apiKey", normalize(apiKey));
payload.put("patentToken", normalize(patentToken));
payload.put("sourceFileKeys", sourceFiles == null ? List.of() : sourceFiles.stream() payload.put("sourceFileKeys", sourceFiles == null ? List.of() : sourceFiles.stream()
.map(AppearancePatentSourceFileDto::getFileKey) .map(AppearancePatentSourceFileDto::getFileKey)
.filter(Objects::nonNull) .filter(Objects::nonNull)
@@ -3907,6 +3921,7 @@ public class AppearancePatentTaskService {
AppearancePatentParsedPayloadDto queuePayload = new AppearancePatentParsedPayloadDto(); AppearancePatentParsedPayloadDto queuePayload = new AppearancePatentParsedPayloadDto();
queuePayload.setAiPrompt(payload.getAiPrompt()); queuePayload.setAiPrompt(payload.getAiPrompt());
queuePayload.setApiKey(payload.getApiKey()); queuePayload.setApiKey(payload.getApiKey());
queuePayload.setPatentToken(payload.getPatentToken());
queuePayload.setGroups(payload.getGroups() == null ? List.of() : payload.getGroups()); queuePayload.setGroups(payload.getGroups() == null ? List.of() : payload.getGroups());
queuePayload.setItems(List.of()); queuePayload.setItems(List.of());
queuePayload.setAllItems(List.of()); queuePayload.setAllItems(List.of());
@@ -3932,6 +3947,7 @@ public class AppearancePatentTaskService {
AppearancePatentParsedGroupPageDto vo = new AppearancePatentParsedGroupPageDto(); AppearancePatentParsedGroupPageDto vo = new AppearancePatentParsedGroupPageDto();
vo.setAiPrompt(payload.getAiPrompt()); vo.setAiPrompt(payload.getAiPrompt());
vo.setApiKey(payload.getApiKey()); vo.setApiKey(payload.getApiKey());
vo.setPatentToken(payload.getPatentToken());
vo.setPage(safePage); vo.setPage(safePage);
vo.setPageSize(safePageSize); vo.setPageSize(safePageSize);
vo.setTotalGroups(totalGroups); vo.setTotalGroups(totalGroups);

View File

@@ -483,8 +483,11 @@ public class SimilarAsinTaskService {
public SimilarAsinDashboardVo dashboard(Long userId) { public SimilarAsinDashboardVo dashboard(Long userId) {
SimilarAsinDashboardVo vo = new SimilarAsinDashboardVo(); SimilarAsinDashboardVo vo = new SimilarAsinDashboardVo();
vo.setPendingTaskCount(countTask(userId, STATUS_RUNNING)); long completedWithFile = countCompletedTasksWithResultFile(userId);
vo.setSuccessTaskCount(countTask(userId, STATUS_SUCCESS)); long rawRunning = countTask(userId, STATUS_RUNNING);
long rawSuccess = countTask(userId, STATUS_SUCCESS);
vo.setPendingTaskCount(Math.max(0L, rawRunning - completedWithFile));
vo.setSuccessTaskCount(rawSuccess + completedWithFile);
vo.setFailedTaskCount(countTask(userId, STATUS_FAILED)); vo.setFailedTaskCount(countTask(userId, STATUS_FAILED));
vo.setProcessedTaskCount(vo.getSuccessTaskCount() + vo.getFailedTaskCount()); vo.setProcessedTaskCount(vo.getSuccessTaskCount() + vo.getFailedTaskCount());
return vo; return vo;
@@ -4819,6 +4822,22 @@ public class SimilarAsinTaskService {
return count == null ? 0L : count; return count == null ? 0L : count;
} }
private long countCompletedTasksWithResultFile(Long userId) {
if (userId == null || userId <= 0) {
return 0L;
}
Long count = fileTaskMapper.selectCount(new LambdaQueryWrapper<FileTaskEntity>()
.eq(FileTaskEntity::getModuleType, MODULE_TYPE)
.eq(FileTaskEntity::getUserId, userId)
.eq(FileTaskEntity::getStatus, STATUS_RUNNING)
.inSql(FileTaskEntity::getId,
"select distinct task_id from file_result " +
"where module_type = '" + MODULE_TYPE + "' " +
"and user_id = " + userId + " " +
"and result_file_url is not null and result_file_url <> ''"));
return count == null ? 0L : count;
}
private int countChunks(Long taskId, String scopeHash) { private int countChunks(Long taskId, String scopeHash) {
Long count = taskChunkMapper.selectCount(new LambdaQueryWrapper<TaskChunkEntity>() Long count = taskChunkMapper.selectCount(new LambdaQueryWrapper<TaskChunkEntity>()
.eq(TaskChunkEntity::getTaskId, taskId) .eq(TaskChunkEntity::getTaskId, taskId)
@@ -4861,7 +4880,7 @@ public class SimilarAsinTaskService {
SimilarAsinTaskItemVo vo = new SimilarAsinTaskItemVo(); SimilarAsinTaskItemVo vo = new SimilarAsinTaskItemVo();
vo.setId(task.getId()); vo.setId(task.getId());
vo.setTaskNo(task.getTaskNo()); vo.setTaskNo(task.getTaskNo());
vo.setStatus(task.getStatus()); vo.setStatus(resolveDisplayTaskStatus(task));
vo.setErrorMessage(task.getErrorMessage()); vo.setErrorMessage(task.getErrorMessage());
vo.setCreatedAt(fmt(task.getCreatedAt())); vo.setCreatedAt(fmt(task.getCreatedAt()));
vo.setUpdatedAt(fmt(task.getUpdatedAt())); vo.setUpdatedAt(fmt(task.getUpdatedAt()));
@@ -4876,7 +4895,7 @@ public class SimilarAsinTaskService {
vo.setSourceFilename(row.getSourceFilename()); vo.setSourceFilename(row.getSourceFilename());
vo.setResultFilename(row.getResultFilename()); vo.setResultFilename(row.getResultFilename());
vo.setDownloadUrl(buildFreshDownloadUrl(row)); vo.setDownloadUrl(buildFreshDownloadUrl(row));
vo.setTaskStatus(task == null ? null : task.getStatus()); vo.setTaskStatus(resolveDisplayTaskStatus(task, row));
attachFileJobState(vo, row, job, task); attachFileJobState(vo, row, job, task);
vo.setSuccess(row.getSuccess() != null && row.getSuccess() == 1); vo.setSuccess(row.getSuccess() != null && row.getSuccess() == 1);
vo.setError(row.getErrorMessage()); vo.setError(row.getErrorMessage());
@@ -4931,6 +4950,13 @@ public class SimilarAsinTaskService {
int cozePending = countPendingCozeStates(taskId); int cozePending = countPendingCozeStates(taskId);
boolean uploadComplete = isResultSubmissionComplete(taskId); boolean uploadComplete = isResultSubmissionComplete(taskId);
if (!uploadComplete) { if (!uploadComplete) {
if (canTreatTaskAsCompleted(task, row, job)) {
vo.setFileProgressCurrent(1);
vo.setFileProgressTotal(1);
vo.setFileProgressPercent(100);
vo.setFileProgressMessage("结果文件已生成");
return;
}
if (task != null && !STATUS_RUNNING.equals(task.getStatus())) { if (task != null && !STATUS_RUNNING.equals(task.getStatus())) {
return; return;
} }
@@ -5088,6 +5114,42 @@ public class SimilarAsinTaskService {
return "等待 Python 回传,已回传 " + Math.max(0, current) + "/" + Math.max(1, total) + " " + safeUnit; return "等待 Python 回传,已回传 " + Math.max(0, current) + "/" + Math.max(1, total) + " " + safeUnit;
} }
private String resolveDisplayTaskStatus(FileTaskEntity task) {
return resolveDisplayTaskStatus(task, null);
}
private String resolveDisplayTaskStatus(FileTaskEntity task, FileResultEntity row) {
if (task == null) {
return null;
}
if (canTreatTaskAsCompleted(task, row, null)) {
return STATUS_SUCCESS;
}
return task.getStatus();
}
private boolean canTreatTaskAsCompleted(FileTaskEntity task, FileResultEntity row, TaskFileJobEntity job) {
if (task == null) {
return false;
}
if (STATUS_SUCCESS.equals(task.getStatus())) {
return true;
}
if (!STATUS_RUNNING.equals(task.getStatus())) {
return false;
}
if (row == null) {
return false;
}
if (row.getResultFileUrl() == null || row.getResultFileUrl().isBlank()) {
return false;
}
if (job != null && "FAILED".equals(job.getStatus())) {
return false;
}
return true;
}
private int calculateCozeDisplayTotal(TaskProgressSnapshotEntity snapshot, int observedCozeStates) { private int calculateCozeDisplayTotal(TaskProgressSnapshotEntity snapshot, int observedCozeStates) {
int observed = Math.max(0, observedCozeStates); int observed = Math.max(0, observedCozeStates);
// 小任务 totalCount <= 3 时直接使用 observed避免 totalCount - 3 → 0 引起进度条 0/0。 // 小任务 totalCount <= 3 时直接使用 observed避免 totalCount - 3 → 0 引起进度条 0/0。

View File

@@ -121,6 +121,13 @@ public class TaskResultFileJobWorker {
TaskDistributedLockService.LockHandle lockHandle = TaskDistributedLockService.LockHandle lockHandle =
taskDistributedLockService.acquire(job.getModuleType(), job.getTaskId(), TaskDistributedLockService.DEFAULT_WAIT_MILLIS); taskDistributedLockService.acquire(job.getModuleType(), job.getTaskId(), TaskDistributedLockService.DEFAULT_WAIT_MILLIS);
if (lockHandle == null) { if (lockHandle == null) {
TaskFileJobEntity latestOnBusy = taskFileJobService.findById(job.getId());
if (latestOnBusy != null && "RUNNING".equals(latestOnBusy.getStatus())) {
taskFileJobService.touchRunning(job.getId());
log.info("[task-file-job] process skipped because task lock is busy and job is already running jobId={} taskId={} moduleType={} resultId={}",
job.getId(), job.getTaskId(), job.getModuleType(), job.getResultId());
return;
}
taskFileJobService.requeue(job.getId(), "Task is busy, waiting for previous task operation"); taskFileJobService.requeue(job.getId(), "Task is busy, waiting for previous task operation");
log.info("[task-file-job] process requeued because task lock is busy jobId={} taskId={} moduleType={} resultId={}", log.info("[task-file-job] process requeued because task lock is busy jobId={} taskId={} moduleType={} resultId={}",
job.getId(), job.getTaskId(), job.getModuleType(), job.getResultId()); job.getId(), job.getTaskId(), job.getModuleType(), job.getResultId());

View File

@@ -34,7 +34,7 @@
v-model="secretStates[config.key].value" v-model="secretStates[config.key].value"
class="secret-input" class="secret-input"
type="password" type="password"
placeholder="请输入 Coze 接口密钥" :placeholder="config.placeholder"
autocomplete="off" autocomplete="off"
spellcheck="false" spellcheck="false"
/> />
@@ -96,21 +96,30 @@ const retentionOptions: Array<{ value: ApiSecretRetention; label: string }> = [
{ value: 'forever', label: '长期保留' }, { value: 'forever', label: '长期保留' },
] ]
const secretConfigs: Array<{ key: ApiSecretModuleKey; title: string; description: string }> = [ const secretConfigs: Array<{ key: ApiSecretModuleKey; title: string; description: string; placeholder: string }> = [
{ {
key: 'appearance-patent', key: 'appearance-patent',
title: '外观专利密钥', title: '外观专利密钥',
description: '仅用于外观专利检测。', description: '仅用于外观专利检测。',
placeholder: '请输入 Coze 接口密钥',
},
{
key: 'appearance-patent-token',
title: '专利汇令牌',
description: '仅用于外观专利检测,非必填。',
placeholder: '请输入专利汇令牌,可留空',
}, },
{ {
key: 'similar-asin', key: 'similar-asin',
title: '货源查询密钥', title: '货源查询密钥',
description: '仅用于货源查询。', description: '仅用于货源查询。',
placeholder: '请输入 Coze 接口密钥',
}, },
] ]
const secretStates = ref<Record<ApiSecretModuleKey, SecretState>>({ const secretStates = ref<Record<ApiSecretModuleKey, SecretState>>({
'appearance-patent': emptySecretState(), 'appearance-patent': emptySecretState(),
'appearance-patent-token': emptySecretState(),
'similar-asin': emptySecretState(), 'similar-asin': emptySecretState(),
}) })

View File

@@ -261,6 +261,10 @@ function effectiveCozeApiKey() {
return getStoredApiSecret('appearance-patent').trim() return getStoredApiSecret('appearance-patent').trim()
} }
function effectivePatentToken() {
return getStoredApiSecret('appearance-patent-token').trim()
}
function maskSecret(secret: string) { function maskSecret(secret: string) {
if (!secret) return '' if (!secret) return ''
if (secret.length <= 10) return '***' if (secret.length <= 10) return '***'
@@ -274,6 +278,7 @@ function payloadForDisplay<T extends { data?: Record<string, unknown> }>(payload
? { ? {
...payload.data, ...payload.data,
api_key: maskSecret(String(payload.data.api_key || '')), api_key: maskSecret(String(payload.data.api_key || '')),
patent_token: maskSecret(String(payload.data.patent_token || '')),
} }
: payload.data, : payload.data,
} }
@@ -406,7 +411,7 @@ async function parseFiles() {
originalFilename: f.originalFilename, originalFilename: f.originalFilename,
relativePath: f.relativePath, relativePath: f.relativePath,
})) }))
const res = await parseAppearancePatent(files, effectiveAiPrompt(), effectiveCozeApiKey()) const res = await parseAppearancePatent(files, effectiveAiPrompt(), effectiveCozeApiKey(), effectivePatentToken())
parseResult.value = res parseResult.value = res
queuedTaskSummary.value = null queuedTaskSummary.value = null
queuePayloadText.value = '' queuePayloadText.value = ''
@@ -445,6 +450,7 @@ async function pushToPythonQueue() {
taskId, taskId,
prompt: queueAiPrompt(), prompt: queueAiPrompt(),
api_key: effectiveCozeApiKey(), api_key: effectiveCozeApiKey(),
patent_token: effectivePatentToken(),
sourceFileCount: currentParseResult.sourceFileCount || 0, sourceFileCount: currentParseResult.sourceFileCount || 0,
totalRows: currentParseResult.totalRows || 0, totalRows: currentParseResult.totalRows || 0,
acceptedRows: currentParseResult.acceptedRows || 0, acceptedRows: currentParseResult.acceptedRows || 0,

View File

@@ -1529,6 +1529,7 @@ export interface AppearancePatentParseVo {
export interface AppearancePatentParsedPayloadDto { export interface AppearancePatentParsedPayloadDto {
aiPrompt?: string; aiPrompt?: string;
apiKey?: string; apiKey?: string;
patentToken?: string;
sourceFiles?: UploadedFileRef[]; sourceFiles?: UploadedFileRef[];
headers?: string[]; headers?: string[];
items?: AppearancePatentParsedRow[]; items?: AppearancePatentParsedRow[];
@@ -1590,16 +1591,17 @@ export interface AppearancePatentTaskBatchVo {
missingTaskIds?: number[]; missingTaskIds?: number[];
} }
export function parseAppearancePatent(files: UploadedFileRef[], aiPrompt: string, apiKey?: string) { export function parseAppearancePatent(files: UploadedFileRef[], aiPrompt: string, apiKey?: string, patentToken?: string) {
return unwrapJavaResponse( return unwrapJavaResponse(
post< post<
JavaApiResponse<AppearancePatentParseVo>, JavaApiResponse<AppearancePatentParseVo>,
{ user_id: number; files: UploadedFileRef[]; ai_prompt: string; api_key?: string } { user_id: number; files: UploadedFileRef[]; ai_prompt: string; api_key?: string; patent_token?: string }
>(`${JAVA_API_PREFIX}/appearance-patent/parse`, { >(`${JAVA_API_PREFIX}/appearance-patent/parse`, {
user_id: getCurrentUserId(), user_id: getCurrentUserId(),
files, files,
ai_prompt: aiPrompt, ai_prompt: aiPrompt,
api_key: apiKey, api_key: apiKey,
patent_token: patentToken,
}), }),
); );
} }

View File

@@ -1,4 +1,4 @@
export type ApiSecretModuleKey = 'appearance-patent' | 'similar-asin' export type ApiSecretModuleKey = 'appearance-patent' | 'appearance-patent-token' | 'similar-asin'
export type ApiSecretRetention = 'session' | '1d' | '7d' | '30d' | 'forever' export type ApiSecretRetention = 'session' | '1d' | '7d' | '30d' | 'forever'
@@ -19,7 +19,7 @@ export type ApiSecretSnapshot = {
const STORAGE_PREFIX = 'brand:api-secret' const STORAGE_PREFIX = 'brand:api-secret'
const COMMON_SECRET_KEY = 'common' const COMMON_SECRET_KEY = 'common'
const MODULE_SECRET_KEYS: ApiSecretModuleKey[] = ['appearance-patent', 'similar-asin'] const MODULE_SECRET_KEYS: ApiSecretModuleKey[] = ['appearance-patent', 'appearance-patent-token', 'similar-asin']
function currentUserStorageId() { function currentUserStorageId() {
if (typeof window === 'undefined') return '0' if (typeof window === 'undefined') return '0'
@@ -115,7 +115,7 @@ function clearLegacyStoredApiSecrets() {
function migrateCommonRecord(record: ApiSecretRecord) { function migrateCommonRecord(record: ApiSecretRecord) {
if (typeof window === 'undefined') return if (typeof window === 'undefined') return
const storage = record.retention === 'session' ? window.sessionStorage : window.localStorage const storage = record.retention === 'session' ? window.sessionStorage : window.localStorage
for (const moduleKey of MODULE_SECRET_KEYS) { for (const moduleKey of ['appearance-patent', 'similar-asin'] satisfies ApiSecretModuleKey[]) {
if (!getLiveRecordFromKey(moduleKey)) { if (!getLiveRecordFromKey(moduleKey)) {
storage.setItem(buildStorageKey(moduleKey), JSON.stringify(record)) storage.setItem(buildStorageKey(moduleKey), JSON.stringify(record))
} }