提交货源采集更新
This commit is contained in:
547002
2026_05_06.log
547002
2026_05_06.log
File diff suppressed because one or more lines are too long
82604
2026_05_07.log
82604
2026_05_07.log
File diff suppressed because one or more lines are too long
4
app/.env
4
app/.env
@@ -13,8 +13,8 @@ client_name=ShuFuAI
|
|||||||
|
|
||||||
|
|
||||||
# java_api_base=http://47.111.163.154:18080
|
# java_api_base=http://47.111.163.154:18080
|
||||||
# java_api_base=http://127.0.0.1:18080
|
java_api_base=http://127.0.0.1:18080
|
||||||
# java_api_base=http://8.136.19.173:18080
|
# java_api_base=http://8.136.19.173:18080
|
||||||
java_api_base=http://121.196.149.225:18080
|
# java_api_base=http://121.196.149.225:18080
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
app/assets/appearance-patent-BbMI4oev.css
Normal file
1
app/assets/appearance-patent-BbMI4oev.css
Normal file
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
@@ -1 +1 @@
|
|||||||
import{bb as r}from"./pywebview-BPTq9Myk.js";const n="";function s(e){return r(`${n}/api/brand/expand-folder-recursive`,{folder:e})}export{s as e};
|
import{be as r}from"./pywebview-DNfEzGOK.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
1
app/assets/similar-asin-0mttxPNV.css
Normal file
1
app/assets/similar-asin-0mttxPNV.css
Normal file
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
@@ -5,8 +5,8 @@
|
|||||||
<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/pywebview-BPTq9Myk.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-DNfEzGOK.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-DqzUfBy1.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-D4E3c93-.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/convert-BKSNvX8i.css">
|
<link rel="stylesheet" crossorigin href="/assets/convert-BKSNvX8i.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<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/pywebview-BPTq9Myk.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-DNfEzGOK.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-DqzUfBy1.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-D4E3c93-.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-DNlVfFj-.css">
|
<link rel="stylesheet" crossorigin href="/assets/dedupe-DNlVfFj-.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<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/pywebview-BPTq9Myk.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-DNfEzGOK.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-DqzUfBy1.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-D4E3c93-.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/pywebview-DNJAt1gR.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/delete-brand-BJod9K65.css">
|
<link rel="stylesheet" crossorigin href="/assets/delete-brand-BJod9K65.css">
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
<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/pywebview-BPTq9Myk.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-DNfEzGOK.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-DqzUfBy1.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-D4E3c93-.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/split-BoBVrLdC.css">
|
<link rel="stylesheet" crossorigin href="/assets/split-BoBVrLdC.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class SimilarAsinProperties {
|
|||||||
private String cozeBaseUrl = "https://api.coze.cn";
|
private String cozeBaseUrl = "https://api.coze.cn";
|
||||||
private String cozeWorkflowPath = "/v1/workflow/run";
|
private String cozeWorkflowPath = "/v1/workflow/run";
|
||||||
private String cozeWorkflowHistoryPath = "/v1/workflows/{workflow_id}/run_histories/{execute_id}";
|
private String cozeWorkflowHistoryPath = "/v1/workflows/{workflow_id}/run_histories/{execute_id}";
|
||||||
private String cozeWorkflowId = "7632683471312355338";
|
private String cozeWorkflowId = "7635328462404583478";
|
||||||
private String cozeToken = "";
|
private String cozeToken = "";
|
||||||
private int cozeBatchSize = 50;
|
private int cozeBatchSize = 50;
|
||||||
private int cozeConnectTimeoutMillis = 10000;
|
private int cozeConnectTimeoutMillis = 10000;
|
||||||
|
|||||||
@@ -556,7 +556,6 @@ public class AppearancePatentTaskService {
|
|||||||
: row.getResultFilename();
|
: row.getResultFilename();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(cron = "${aiimage.appearance-patent.stale-finalize-cron:0 */2 * * * *}")
|
|
||||||
public void finalizeStaleTasks() {
|
public void finalizeStaleTasks() {
|
||||||
if (transactionManager != null) {
|
if (transactionManager != null) {
|
||||||
LocalDateTime threshold = LocalDateTime.now().minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()));
|
LocalDateTime threshold = LocalDateTime.now().minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()));
|
||||||
|
|||||||
@@ -1209,7 +1209,6 @@ public class BrandTaskService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@org.springframework.scheduling.annotation.Scheduled(cron = "${aiimage.brand-progress.stale-check-cron:0 */2 * * * *}")
|
|
||||||
public void failStaleRunningTasks() {
|
public void failStaleRunningTasks() {
|
||||||
DistributedJobLockService.LockHandle lockHandle = distributedJobLockService.tryLock("brand:stale-check", STALE_CHECK_LOCK_TTL);
|
DistributedJobLockService.LockHandle lockHandle = distributedJobLockService.tryLock("brand:stale-check", STALE_CHECK_LOCK_TTL);
|
||||||
if (lockHandle == null) {
|
if (lockHandle == null) {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.nanri.aiimage.common.service.DistributedJobLockService;
|
import com.nanri.aiimage.common.service.DistributedJobLockService;
|
||||||
import com.nanri.aiimage.config.DeleteBrandProgressProperties;
|
import com.nanri.aiimage.config.DeleteBrandProgressProperties;
|
||||||
|
import com.nanri.aiimage.modules.appearancepatent.service.AppearancePatentTaskService;
|
||||||
|
import com.nanri.aiimage.modules.brand.service.BrandTaskService;
|
||||||
import com.nanri.aiimage.modules.productrisk.service.ProductRiskTaskCacheService;
|
import com.nanri.aiimage.modules.productrisk.service.ProductRiskTaskCacheService;
|
||||||
import com.nanri.aiimage.modules.queryasin.service.QueryAsinTaskCacheService;
|
import com.nanri.aiimage.modules.queryasin.service.QueryAsinTaskCacheService;
|
||||||
import com.nanri.aiimage.modules.queryasin.service.QueryAsinTaskService;
|
import com.nanri.aiimage.modules.queryasin.service.QueryAsinTaskService;
|
||||||
@@ -12,6 +14,7 @@ import com.nanri.aiimage.modules.patroldelete.service.PatrolDeleteTaskCacheServi
|
|||||||
import com.nanri.aiimage.modules.patroldelete.service.PatrolDeleteTaskService;
|
import com.nanri.aiimage.modules.patroldelete.service.PatrolDeleteTaskService;
|
||||||
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackTaskCacheService;
|
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackTaskCacheService;
|
||||||
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackTaskService;
|
import com.nanri.aiimage.modules.pricetrack.service.PriceTrackTaskService;
|
||||||
|
import com.nanri.aiimage.modules.similarasin.service.SimilarAsinTaskService;
|
||||||
import com.nanri.aiimage.modules.shopmatch.service.ShopMatchTaskCacheService;
|
import com.nanri.aiimage.modules.shopmatch.service.ShopMatchTaskCacheService;
|
||||||
import com.nanri.aiimage.modules.shopmatch.service.ShopMatchTaskService;
|
import com.nanri.aiimage.modules.shopmatch.service.ShopMatchTaskService;
|
||||||
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
|
import com.nanri.aiimage.modules.task.mapper.FileTaskMapper;
|
||||||
@@ -63,6 +66,9 @@ public class DeleteBrandStaleTaskService {
|
|||||||
private final PatrolDeleteTaskCacheService patrolDeleteTaskCacheService;
|
private final PatrolDeleteTaskCacheService patrolDeleteTaskCacheService;
|
||||||
private final QueryAsinTaskService queryAsinTaskService;
|
private final QueryAsinTaskService queryAsinTaskService;
|
||||||
private final QueryAsinTaskCacheService queryAsinTaskCacheService;
|
private final QueryAsinTaskCacheService queryAsinTaskCacheService;
|
||||||
|
private final BrandTaskService brandTaskService;
|
||||||
|
private final AppearancePatentTaskService appearancePatentTaskService;
|
||||||
|
private final SimilarAsinTaskService similarAsinTaskService;
|
||||||
private final DeleteBrandProgressProperties deleteBrandProgressProperties;
|
private final DeleteBrandProgressProperties deleteBrandProgressProperties;
|
||||||
private final DistributedJobLockService distributedJobLockService;
|
private final DistributedJobLockService distributedJobLockService;
|
||||||
private final TaskDistributedLockService taskDistributedLockService;
|
private final TaskDistributedLockService taskDistributedLockService;
|
||||||
@@ -88,6 +94,9 @@ public class DeleteBrandStaleTaskService {
|
|||||||
ShopMatchStaleCheckStats shopMatchStats = failStaleShopMatchTasks();
|
ShopMatchStaleCheckStats shopMatchStats = failStaleShopMatchTasks();
|
||||||
ShopMatchStaleCheckStats patrolDeleteStats = failStalePatrolDeleteTasks();
|
ShopMatchStaleCheckStats patrolDeleteStats = failStalePatrolDeleteTasks();
|
||||||
ShopMatchStaleCheckStats queryAsinStats = failStaleQueryAsinTasks();
|
ShopMatchStaleCheckStats queryAsinStats = failStaleQueryAsinTasks();
|
||||||
|
runModuleStaleCheck("brand", brandTaskService::failStaleRunningTasks);
|
||||||
|
runModuleStaleCheck("appearance-patent", appearancePatentTaskService::finalizeStaleTasks);
|
||||||
|
runModuleStaleCheck("similar-asin", similarAsinTaskService::finalizeStaleTasks);
|
||||||
log.info("[stale-check] product-risk summary scanned={} finalized={} failed={} skipped={} elapsedMs={} thread={}",
|
log.info("[stale-check] product-risk summary scanned={} finalized={} failed={} skipped={} elapsedMs={} thread={}",
|
||||||
stats.scannedTaskCount,
|
stats.scannedTaskCount,
|
||||||
stats.finalizedTaskCount,
|
stats.finalizedTaskCount,
|
||||||
@@ -126,6 +135,23 @@ public class DeleteBrandStaleTaskService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void runModuleStaleCheck(String moduleName, Runnable action) {
|
||||||
|
long startedAt = System.currentTimeMillis();
|
||||||
|
try {
|
||||||
|
action.run();
|
||||||
|
log.info("[stale-check] {} delegated stale check completed elapsedMs={} thread={}",
|
||||||
|
moduleName,
|
||||||
|
System.currentTimeMillis() - startedAt,
|
||||||
|
Thread.currentThread().getName());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.warn("[stale-check] {} delegated stale check failed elapsedMs={} msg={}",
|
||||||
|
moduleName,
|
||||||
|
System.currentTimeMillis() - startedAt,
|
||||||
|
ex.getMessage(),
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void failStaleDeleteBrandTasks() {
|
private void failStaleDeleteBrandTasks() {
|
||||||
long minutes = Math.max(1L, deleteBrandProgressProperties.getHeartbeatTimeoutMinutes());
|
long minutes = Math.max(1L, deleteBrandProgressProperties.getHeartbeatTimeoutMinutes());
|
||||||
long initialMinutes = Math.max(minutes, deleteBrandProgressProperties.getDeleteBrandInitialTimeoutMinutes());
|
long initialMinutes = Math.max(minutes, deleteBrandProgressProperties.getDeleteBrandInitialTimeoutMinutes());
|
||||||
|
|||||||
@@ -210,6 +210,9 @@ public class SimilarAsinCozeClient {
|
|||||||
Map<String, Object> body = new LinkedHashMap<>();
|
Map<String, Object> body = new LinkedHashMap<>();
|
||||||
body.put("workflow_id", properties.getCozeWorkflowId());
|
body.put("workflow_id", properties.getCozeWorkflowId());
|
||||||
body.put("parameters", parameters);
|
body.put("parameters", parameters);
|
||||||
|
if (apiKey != null && !apiKey.isBlank()) {
|
||||||
|
body.put("api_key", apiKey.trim());
|
||||||
|
}
|
||||||
body.put("is_async", Boolean.TRUE);
|
body.put("is_async", Boolean.TRUE);
|
||||||
log.info("[similar-asin] coze request url={} body={}",
|
log.info("[similar-asin] coze request url={} body={}",
|
||||||
joinUrl(properties.getCozeBaseUrl(), properties.getCozeWorkflowPath()),
|
joinUrl(properties.getCozeBaseUrl(), properties.getCozeWorkflowPath()),
|
||||||
@@ -256,20 +259,12 @@ public class SimilarAsinCozeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildParameters(List<SimilarAsinResultRowDto> rows, String prompt, String apiKey) {
|
private Map<String, Object> buildParameters(List<SimilarAsinResultRowDto> rows, String prompt, String apiKey) {
|
||||||
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> asins = rows.stream().map(row -> nonBlank(row.getAsin(), "")).toList();
|
List<String> asins = rows.stream().map(row -> nonBlank(row.getAsin(), "")).toList();
|
||||||
List<String> countries = rows.stream().map(row -> nonBlank(row.getCountry(), "")).toList();
|
|
||||||
List<String> prices = rows.stream().map(row -> nonBlank(row.getPrice(), "")).toList();
|
|
||||||
List<String> titles = rows.stream().map(row -> nonBlank(row.getTitle(), row.getAsin())).toList();
|
|
||||||
List<String> urls = rows.stream().map(row -> nonBlank(row.getUrl(), "")).toList();
|
List<String> urls = rows.stream().map(row -> nonBlank(row.getUrl(), "")).toList();
|
||||||
List<List<String>> urlLists = rows.stream().map(SimilarAsinResultRowDto::getUrls).toList();
|
List<List<String>> urlLists = rows.stream().map(SimilarAsinResultRowDto::getUrls).toList();
|
||||||
|
|
||||||
Map<String, Object> parameters = new LinkedHashMap<>();
|
Map<String, Object> parameters = new LinkedHashMap<>();
|
||||||
parameters.put("title_list", titles);
|
parameters.put("items", buildItemObjects(rows, asins, urls, urlLists));
|
||||||
parameters.put("url_list", urls);
|
|
||||||
parameters.put("url_lists", urlLists);
|
|
||||||
parameters.put("items", buildItemObjects(rows, groupKeys, rowIds, asins, countries, prices, titles, urls, urlLists));
|
|
||||||
parameters.put("prompt", prompt == null ? "" : prompt);
|
parameters.put("prompt", prompt == null ? "" : prompt);
|
||||||
if (apiKey != null && !apiKey.isBlank()) {
|
if (apiKey != null && !apiKey.isBlank()) {
|
||||||
parameters.put("api_key", apiKey.trim());
|
parameters.put("api_key", apiKey.trim());
|
||||||
@@ -280,6 +275,10 @@ public class SimilarAsinCozeClient {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Map<String, Object> maskCozeRequestBody(Map<String, Object> body) {
|
private Map<String, Object> maskCozeRequestBody(Map<String, Object> body) {
|
||||||
Map<String, Object> masked = new LinkedHashMap<>(body);
|
Map<String, Object> masked = new LinkedHashMap<>(body);
|
||||||
|
Object topLevelApiKey = masked.get("api_key");
|
||||||
|
if (topLevelApiKey instanceof String apiKeyText && !apiKeyText.isBlank()) {
|
||||||
|
masked.put("api_key", maskSecret(apiKeyText));
|
||||||
|
}
|
||||||
Object parametersObj = masked.get("parameters");
|
Object parametersObj = masked.get("parameters");
|
||||||
if (parametersObj instanceof Map<?, ?> parameters) {
|
if (parametersObj instanceof Map<?, ?> parameters) {
|
||||||
Map<String, Object> maskedParameters = new LinkedHashMap<>((Map<String, Object>) parameters);
|
Map<String, Object> maskedParameters = new LinkedHashMap<>((Map<String, Object>) parameters);
|
||||||
@@ -304,25 +303,14 @@ public class SimilarAsinCozeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Map<String, Object>> buildItemObjects(List<SimilarAsinResultRowDto> rows,
|
private List<Map<String, Object>> buildItemObjects(List<SimilarAsinResultRowDto> rows,
|
||||||
List<String> groupKeys,
|
|
||||||
List<String> rowIds,
|
|
||||||
List<String> asins,
|
List<String> asins,
|
||||||
List<String> countries,
|
|
||||||
List<String> prices,
|
|
||||||
List<String> titles,
|
|
||||||
List<String> urls,
|
List<String> urls,
|
||||||
List<List<String>> urlLists) {
|
List<List<String>> urlLists) {
|
||||||
List<Map<String, Object>> items = new ArrayList<>(rows.size());
|
List<Map<String, Object>> items = new ArrayList<>(rows.size());
|
||||||
for (int i = 0; i < rows.size(); i++) {
|
for (int i = 0; i < rows.size(); i++) {
|
||||||
Map<String, Object> item = new LinkedHashMap<>();
|
Map<String, Object> item = new LinkedHashMap<>();
|
||||||
item.put("group_key", groupKeys.get(i));
|
|
||||||
item.put("row_id", rowIds.get(i));
|
|
||||||
item.put("asin", asins.get(i));
|
item.put("asin", asins.get(i));
|
||||||
item.put("country", countries.get(i));
|
|
||||||
item.put("price", prices.get(i));
|
|
||||||
item.put("title", titles.get(i));
|
|
||||||
item.put("url", urls.get(i));
|
item.put("url", urls.get(i));
|
||||||
item.put("urls", urlLists.get(i));
|
|
||||||
item.put("target_urls", urlLists.get(i));
|
item.put("target_urls", urlLists.get(i));
|
||||||
items.add(item);
|
items.add(item);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -616,7 +616,6 @@ public class SimilarAsinTaskService {
|
|||||||
: row.getResultFilename();
|
: row.getResultFilename();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Scheduled(cron = "${aiimage.similar-asin.stale-finalize-cron:0 */2 * * * *}")
|
|
||||||
public void finalizeStaleTasks() {
|
public void finalizeStaleTasks() {
|
||||||
if (transactionManager != null) {
|
if (transactionManager != null) {
|
||||||
LocalDateTime threshold = LocalDateTime.now().minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()));
|
LocalDateTime threshold = LocalDateTime.now().minusMinutes(Math.max(1, properties.getStaleTimeoutMinutes()));
|
||||||
@@ -1429,6 +1428,10 @@ public class SimilarAsinTaskService {
|
|||||||
SimilarAsinCozeClient.CozeSubmitResponse submit = submitCozeWorkflowThrottled(batchRows, prompt, apiKey);
|
SimilarAsinCozeClient.CozeSubmitResponse submit = submitCozeWorkflowThrottled(batchRows, prompt, apiKey);
|
||||||
if (submit.immediateData() != null && !submit.immediateData().isBlank()) {
|
if (submit.immediateData() != null && !submit.immediateData().isBlank()) {
|
||||||
List<SimilarAsinResultRowDto> cozeRows = cozeClient.mergeRowsFromDataText(batchRows, submit.immediateData());
|
List<SimilarAsinResultRowDto> cozeRows = cozeClient.mergeRowsFromDataText(batchRows, submit.immediateData());
|
||||||
|
String emptyResultMessage = emptyCozeResultMessage(cozeRows, batchRows.size());
|
||||||
|
if (!emptyResultMessage.isBlank()) {
|
||||||
|
throw new IllegalStateException(emptyResultMessage);
|
||||||
|
}
|
||||||
mergeCozeRowsIntoChunk(task, chunk.getScopeHash(), chunk.getChunkIndex(), cozeRows, allRowsByBaseId);
|
mergeCozeRowsIntoChunk(task, chunk.getScopeHash(), chunk.getChunkIndex(), cozeRows, allRowsByBaseId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1570,15 +1573,22 @@ public class SimilarAsinTaskService {
|
|||||||
if (batchRows.isEmpty() && failureMessage.isBlank()) {
|
if (batchRows.isEmpty() && failureMessage.isBlank()) {
|
||||||
failureMessage = "Coze batch payload missing";
|
failureMessage = "Coze batch payload missing";
|
||||||
}
|
}
|
||||||
|
List<SimilarAsinResultRowDto> cozeRows;
|
||||||
|
if (failureMessage.isBlank()) {
|
||||||
|
cozeRows = cozeClient.mergeRowsFromDataText(batchRows, poll.resolvedPayloadText());
|
||||||
|
failureMessage = emptyCozeResultMessage(cozeRows, batchRows.size());
|
||||||
|
} else {
|
||||||
|
cozeRows = List.of();
|
||||||
|
}
|
||||||
if (!failureMessage.isBlank() && splitRetryFailedCozeBatchState(state, context, batchRows, failureMessage)) {
|
if (!failureMessage.isBlank() && splitRetryFailedCozeBatchState(state, context, batchRows, failureMessage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!failureMessage.isBlank() && retryFailedCozeBatchState(state, context, batchRows, failureMessage)) {
|
if (!failureMessage.isBlank() && retryFailedCozeBatchState(state, context, batchRows, failureMessage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<SimilarAsinResultRowDto> cozeRows = failureMessage.isBlank()
|
if (!failureMessage.isBlank()) {
|
||||||
? cozeClient.mergeRowsFromDataText(batchRows, poll.resolvedPayloadText())
|
cozeRows = cozeClient.markRowsFailed(batchRows, failureMessage);
|
||||||
: cozeClient.markRowsFailed(batchRows, failureMessage);
|
}
|
||||||
FileTaskEntity task = fileTaskMapper.selectById(state.getTaskId());
|
FileTaskEntity task = fileTaskMapper.selectById(state.getTaskId());
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
Map<String, List<SimilarAsinParsedRowVo>> allRowsByBaseId = loadAllRowsByBaseId(task);
|
Map<String, List<SimilarAsinParsedRowVo>> allRowsByBaseId = loadAllRowsByBaseId(task);
|
||||||
@@ -1819,6 +1829,7 @@ public class SimilarAsinTaskService {
|
|||||||
String normalized = normalize(failureMessage).toLowerCase(Locale.ROOT);
|
String normalized = normalize(failureMessage).toLowerCase(Locale.ROOT);
|
||||||
return normalized.contains("timeout")
|
return normalized.contains("timeout")
|
||||||
|| normalized.contains("timed out")
|
|| normalized.contains("timed out")
|
||||||
|
|| normalized.contains("empty result rows")
|
||||||
|| normalized.contains("out of limit")
|
|| normalized.contains("out of limit")
|
||||||
|| normalized.contains("execution limit")
|
|| normalized.contains("execution limit")
|
||||||
|| normalized.contains("720712008")
|
|| normalized.contains("720712008")
|
||||||
@@ -1834,6 +1845,7 @@ public class SimilarAsinTaskService {
|
|||||||
|| normalized.contains("retry later")
|
|| normalized.contains("retry later")
|
||||||
|| normalized.contains("timeout")
|
|| normalized.contains("timeout")
|
||||||
|| normalized.contains("timed out")
|
|| normalized.contains("timed out")
|
||||||
|
|| normalized.contains("empty result rows")
|
||||||
|| normalized.contains("out of limit")
|
|| normalized.contains("out of limit")
|
||||||
|| normalized.contains("execution limit")
|
|| normalized.contains("execution limit")
|
||||||
|| normalized.contains("702093018")
|
|| normalized.contains("702093018")
|
||||||
@@ -1846,6 +1858,21 @@ public class SimilarAsinTaskService {
|
|||||||
|| normalized.contains("\u8c03\u7528\u8d85\u65f6");
|
|| normalized.contains("\u8c03\u7528\u8d85\u65f6");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String emptyCozeResultMessage(List<SimilarAsinResultRowDto> cozeRows, int expectedRows) {
|
||||||
|
if (cozeRows == null || cozeRows.isEmpty()) {
|
||||||
|
return expectedRows > 0 ? "Coze async workflow returned empty result rows" : "";
|
||||||
|
}
|
||||||
|
long unresolved = cozeRows.stream()
|
||||||
|
.filter(row -> row != null
|
||||||
|
&& !hasResolvedCozeFields(row)
|
||||||
|
&& !isTechnicalCozeFailure(row.getError()))
|
||||||
|
.count();
|
||||||
|
if (unresolved <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return "Coze async workflow returned empty result rows: " + unresolved + "/" + Math.max(expectedRows, cozeRows.size());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateCozeStateRunning(TaskScopeStateEntity state, String error) {
|
private void updateCozeStateRunning(TaskScopeStateEntity state, String error) {
|
||||||
int updated = taskScopeStateMapper.update(null, new LambdaUpdateWrapper<TaskScopeStateEntity>()
|
int updated = taskScopeStateMapper.update(null, new LambdaUpdateWrapper<TaskScopeStateEntity>()
|
||||||
.eq(TaskScopeStateEntity::getId, state.getId())
|
.eq(TaskScopeStateEntity::getId, state.getId())
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ AIIMAGE_APPEARANCE_PATENT_STALE_TIMEOUT_MINUTES=20
|
|||||||
|
|
||||||
AIIMAGE_SIMILAR_ASIN_COZE_BASE_URL=https://api.coze.cn
|
AIIMAGE_SIMILAR_ASIN_COZE_BASE_URL=https://api.coze.cn
|
||||||
AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_PATH=/v1/workflow/run
|
AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_PATH=/v1/workflow/run
|
||||||
AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_ID=7632683471312355338
|
AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_ID=7635328462404583478
|
||||||
AIIMAGE_SIMILAR_ASIN_COZE_TOKEN=
|
AIIMAGE_SIMILAR_ASIN_COZE_TOKEN=
|
||||||
AIIMAGE_SIMILAR_ASIN_COZE_BATCH_SIZE=50
|
AIIMAGE_SIMILAR_ASIN_COZE_BATCH_SIZE=50
|
||||||
AIIMAGE_SIMILAR_ASIN_COZE_READ_TIMEOUT_MILLIS=60000
|
AIIMAGE_SIMILAR_ASIN_COZE_READ_TIMEOUT_MILLIS=60000
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ aiimage:
|
|||||||
similar-asin:
|
similar-asin:
|
||||||
coze-base-url: ${AIIMAGE_SIMILAR_ASIN_COZE_BASE_URL:https://api.coze.cn}
|
coze-base-url: ${AIIMAGE_SIMILAR_ASIN_COZE_BASE_URL:https://api.coze.cn}
|
||||||
coze-workflow-path: ${AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_PATH:/v1/workflow/run}
|
coze-workflow-path: ${AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_PATH:/v1/workflow/run}
|
||||||
coze-workflow-id: ${AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_ID:7632683471312355338}
|
coze-workflow-id: ${AIIMAGE_SIMILAR_ASIN_COZE_WORKFLOW_ID:7635328462404583478}
|
||||||
coze-token: ${AIIMAGE_SIMILAR_ASIN_COZE_TOKEN:Bearer sat_gsInckdsN0qqhnZSK7K4Zevw6neJCHmYHyIbm85oGgJIGEmSdkH5OFctSKLsAHvT}
|
coze-token: ${AIIMAGE_SIMILAR_ASIN_COZE_TOKEN:Bearer sat_gsInckdsN0qqhnZSK7K4Zevw6neJCHmYHyIbm85oGgJIGEmSdkH5OFctSKLsAHvT}
|
||||||
coze-batch-size: ${AIIMAGE_SIMILAR_ASIN_COZE_BATCH_SIZE:50}
|
coze-batch-size: ${AIIMAGE_SIMILAR_ASIN_COZE_BATCH_SIZE:50}
|
||||||
coze-connect-timeout-millis: ${AIIMAGE_SIMILAR_ASIN_COZE_CONNECT_TIMEOUT_MILLIS:10000}
|
coze-connect-timeout-millis: ${AIIMAGE_SIMILAR_ASIN_COZE_CONNECT_TIMEOUT_MILLIS:10000}
|
||||||
|
|||||||
@@ -44,6 +44,29 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="category-card">
|
||||||
|
<div class="category-card-head">
|
||||||
|
<div class="section-title">商品类目</div>
|
||||||
|
<div class="category-actions">
|
||||||
|
<button type="button" class="opt-btn" @click="openCategoryDialog">选择类目</button>
|
||||||
|
<button
|
||||||
|
v-if="selectedProductCategoryIds.length"
|
||||||
|
type="button"
|
||||||
|
class="link-danger"
|
||||||
|
@click="clearSelectedProductCategories"
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="!selectedProductCategories.length" class="empty-conditions">暂未选择商品类目</div>
|
||||||
|
<div v-else class="selected-category-list">
|
||||||
|
<span v-for="category in selectedProductCategories" :key="category.id" class="category-chip">
|
||||||
|
{{ category.path || category.name }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="run-row">
|
<div class="run-row">
|
||||||
<button type="button" class="btn-run" :disabled="parsing || !uploadedFiles.length" @click="parseFiles">
|
<button type="button" class="btn-run" :disabled="parsing || !uploadedFiles.length" @click="parseFiles">
|
||||||
{{ parsing ? '解析中...' : '解析并创建任务' }}
|
{{ parsing ? '解析中...' : '解析并创建任务' }}
|
||||||
@@ -159,6 +182,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="categoryDialogVisible"
|
||||||
|
title="选择商品类目"
|
||||||
|
width="560px"
|
||||||
|
class="category-dialog"
|
||||||
|
>
|
||||||
|
<div class="category-dialog-toolbar">
|
||||||
|
<span class="muted">可多选,确认后会追加到筛选条件 prompt 后面。</span>
|
||||||
|
<button type="button" class="link-danger" @click="clearCategoryTreeSelection">清空选择</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="loadingProductCategories" class="empty-tasks">正在加载商品类目...</div>
|
||||||
|
<div v-else-if="!productCategoryTree.length" class="empty-tasks">暂无商品类目</div>
|
||||||
|
<el-tree
|
||||||
|
v-else
|
||||||
|
ref="categoryTreeRef"
|
||||||
|
class="category-tree"
|
||||||
|
:data="productCategoryTree"
|
||||||
|
node-key="id"
|
||||||
|
show-checkbox
|
||||||
|
default-expand-all
|
||||||
|
:props="{ label: 'name', children: 'children' }"
|
||||||
|
>
|
||||||
|
<template #default="{ data }">
|
||||||
|
<span class="category-tree-node">
|
||||||
|
<span>{{ data.name }}</span>
|
||||||
|
<span v-if="data.description" class="category-description">{{ data.description }}</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
<template #footer>
|
||||||
|
<button type="button" class="opt-btn" @click="categoryDialogVisible = false">取消</button>
|
||||||
|
<button type="button" class="btn-run category-confirm" @click="confirmCategorySelection">确定</button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -176,8 +234,10 @@ import {
|
|||||||
getSimilarAsinHistory,
|
getSimilarAsinHistory,
|
||||||
getSimilarAsinResultDownloadUrl,
|
getSimilarAsinResultDownloadUrl,
|
||||||
getSimilarAsinTaskProgressBatch,
|
getSimilarAsinTaskProgressBatch,
|
||||||
|
listProductCategories,
|
||||||
listSimilarAsinFilterConditions,
|
listSimilarAsinFilterConditions,
|
||||||
parseSimilarAsin,
|
parseSimilarAsin,
|
||||||
|
type ProductCategoryItemVo,
|
||||||
type SimilarAsinFilterConditionVo,
|
type SimilarAsinFilterConditionVo,
|
||||||
type SimilarAsinParsedGroup,
|
type SimilarAsinParsedGroup,
|
||||||
type SimilarAsinDashboardVo,
|
type SimilarAsinDashboardVo,
|
||||||
@@ -203,6 +263,12 @@ const queuedTaskSummary = ref<TaskSummary | null>(null)
|
|||||||
const filterConditionInput = ref('')
|
const filterConditionInput = ref('')
|
||||||
const filterConditions = ref<SimilarAsinFilterConditionVo[]>([])
|
const filterConditions = ref<SimilarAsinFilterConditionVo[]>([])
|
||||||
const selectedFilterConditionId = ref<number | null>(null)
|
const selectedFilterConditionId = ref<number | null>(null)
|
||||||
|
const productCategoryTree = ref<ProductCategoryItemVo[]>([])
|
||||||
|
const productCategoryItems = ref<ProductCategoryItemVo[]>([])
|
||||||
|
const selectedProductCategoryIds = ref<number[]>([])
|
||||||
|
const categoryDialogVisible = ref(false)
|
||||||
|
const loadingProductCategories = ref(false)
|
||||||
|
const categoryTreeRef = ref<any>(null)
|
||||||
const parsing = ref(false)
|
const parsing = ref(false)
|
||||||
const pushing = ref(false)
|
const pushing = ref(false)
|
||||||
const savingCondition = ref(false)
|
const savingCondition = ref(false)
|
||||||
@@ -243,13 +309,80 @@ function selectedFilterConditionText() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function effectiveFilterCondition() {
|
function effectiveFilterCondition() {
|
||||||
return filterConditionInput.value.trim() || selectedFilterConditionText()
|
return appendProductCategoriesToPrompt(filterConditionInput.value.trim() || selectedFilterConditionText())
|
||||||
}
|
}
|
||||||
|
|
||||||
function effectiveCozeApiKey() {
|
function effectiveCozeApiKey() {
|
||||||
return getStoredApiSecret('similar-asin').trim()
|
return getStoredApiSecret('similar-asin').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedProductCategories = computed(() => {
|
||||||
|
const selected = new Set(selectedProductCategoryIds.value)
|
||||||
|
return productCategoryItems.value.filter((item) => selected.has(item.id))
|
||||||
|
})
|
||||||
|
|
||||||
|
function appendProductCategoriesToPrompt(prompt: string) {
|
||||||
|
const lines = selectedProductCategories.value
|
||||||
|
.map((item) => {
|
||||||
|
const path = item.path || item.name
|
||||||
|
const description = item.description?.trim()
|
||||||
|
return description ? `${path}(${description})` : path
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
if (!lines.length) return prompt
|
||||||
|
const categoryBlock = [
|
||||||
|
'商品类目要求:',
|
||||||
|
...lines.map((line) => `- ${line}`),
|
||||||
|
].join('\n')
|
||||||
|
return prompt ? `${prompt}\n\n${categoryBlock}` : categoryBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadProductCategories() {
|
||||||
|
if (loadingProductCategories.value) return
|
||||||
|
loadingProductCategories.value = true
|
||||||
|
try {
|
||||||
|
const res = await listProductCategories()
|
||||||
|
productCategoryTree.value = res.tree || []
|
||||||
|
productCategoryItems.value = res.items || []
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error instanceof Error ? error.message : '加载商品类目失败')
|
||||||
|
} finally {
|
||||||
|
loadingProductCategories.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openCategoryDialog() {
|
||||||
|
categoryDialogVisible.value = true
|
||||||
|
if (!productCategoryTree.value.length) {
|
||||||
|
await loadProductCategories()
|
||||||
|
}
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
categoryTreeRef.value?.setCheckedKeys?.(selectedProductCategoryIds.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasCheckedDescendant(item: ProductCategoryItemVo, checkedIds: Set<number>): boolean {
|
||||||
|
return Boolean(item.children?.some((child) => checkedIds.has(child.id) || hasCheckedDescendant(child, checkedIds)))
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmCategorySelection() {
|
||||||
|
const checkedNodes = (categoryTreeRef.value?.getCheckedNodes?.(false, false) || []) as ProductCategoryItemVo[]
|
||||||
|
const checkedIds = new Set(checkedNodes.map((item) => item.id))
|
||||||
|
selectedProductCategoryIds.value = checkedNodes
|
||||||
|
.filter((item) => !hasCheckedDescendant(item, checkedIds))
|
||||||
|
.map((item) => item.id)
|
||||||
|
categoryDialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCategoryTreeSelection() {
|
||||||
|
categoryTreeRef.value?.setCheckedKeys?.([])
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSelectedProductCategories() {
|
||||||
|
selectedProductCategoryIds.value = []
|
||||||
|
categoryTreeRef.value?.setCheckedKeys?.([])
|
||||||
|
}
|
||||||
|
|
||||||
function maskSecret(secret: string) {
|
function maskSecret(secret: string) {
|
||||||
if (!secret) return ''
|
if (!secret) return ''
|
||||||
if (secret.length <= 10) return '***'
|
if (secret.length <= 10) return '***'
|
||||||
@@ -781,6 +914,7 @@ onMounted(async () => {
|
|||||||
loadPollingIds()
|
loadPollingIds()
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
loadFilterConditions().catch(() => undefined),
|
loadFilterConditions().catch(() => undefined),
|
||||||
|
loadProductCategories().catch(() => undefined),
|
||||||
loadDashboard().catch(() => undefined),
|
loadDashboard().catch(() => undefined),
|
||||||
loadHistory().catch(() => undefined),
|
loadHistory().catch(() => undefined),
|
||||||
])
|
])
|
||||||
@@ -827,6 +961,18 @@ onUnmounted(() => {
|
|||||||
.condition-check input { width: 16px; height: 16px; margin: 0; flex: 0 0 auto; }
|
.condition-check input { width: 16px; height: 16px; margin: 0; flex: 0 0 auto; }
|
||||||
.condition-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.condition-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.link-danger { border: none; background: transparent; color: #ff8f8f; cursor: pointer; font-size: 12px; padding: 2px 0; white-space: nowrap; }
|
.link-danger { border: none; background: transparent; color: #ff8f8f; cursor: pointer; font-size: 12px; padding: 2px 0; white-space: nowrap; }
|
||||||
|
.category-card { margin: 0 0 14px; padding: 12px; border: 1px solid #2a2a2a; border-radius: 8px; background: #202020; }
|
||||||
|
.category-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
||||||
|
.category-card-head .section-title { margin-bottom: 0; }
|
||||||
|
.category-actions { display: flex; align-items: center; gap: 10px; }
|
||||||
|
.category-actions .opt-btn { padding: 6px 12px; }
|
||||||
|
.selected-category-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; max-height: 96px; overflow: auto; }
|
||||||
|
.category-chip { max-width: 100%; padding: 4px 8px; border-radius: 6px; background: rgba(52, 152, 219, .16); color: #cfe7ff; font-size: 12px; line-height: 1.4; }
|
||||||
|
.category-dialog-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
|
||||||
|
.category-tree { max-height: 420px; overflow: auto; padding: 8px; border: 1px solid #2f2f2f; border-radius: 8px; background: #202020; --el-tree-bg-color: #202020; --el-tree-text-color: #d8d8d8; --el-tree-node-hover-bg-color: #2a2a2a; }
|
||||||
|
.category-tree-node { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
|
||||||
|
.category-description { color: #888; font-size: 12px; }
|
||||||
|
.category-confirm { padding: 8px 16px; }
|
||||||
.parse-card, .queue-payload { margin-top: 14px; padding: 12px; border: 1px solid #2a2a2a; border-radius: 8px; background: #202020; color: #b8c1cc; font-size: 12px; }
|
.parse-card, .queue-payload { margin-top: 14px; padding: 12px; border: 1px solid #2a2a2a; border-radius: 8px; background: #202020; color: #b8c1cc; font-size: 12px; }
|
||||||
.queue-payload { max-height: 220px; overflow: auto; color: #8fd3ff; white-space: pre-wrap; }
|
.queue-payload { max-height: 220px; overflow: auto; color: #8fd3ff; white-space: pre-wrap; }
|
||||||
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; font-size: 15px; font-weight: 600; color: #ddd; }
|
.panel-header { padding: 16px 20px; border-bottom: 1px solid #2a2a2a; font-size: 15px; font-weight: 600; color: #ddd; }
|
||||||
|
|||||||
@@ -1680,6 +1680,31 @@ export function getAppearancePatentResultDownloadUrl(resultId: number) {
|
|||||||
|
|
||||||
// ========== 货源查询 ==========
|
// ========== 货源查询 ==========
|
||||||
|
|
||||||
|
export interface ProductCategoryItemVo {
|
||||||
|
id: number;
|
||||||
|
parentId?: number | null;
|
||||||
|
name: string;
|
||||||
|
categoryKey?: string;
|
||||||
|
sortOrder?: number;
|
||||||
|
description?: string;
|
||||||
|
isBuiltin?: boolean;
|
||||||
|
childCount?: number;
|
||||||
|
level?: number;
|
||||||
|
path?: string;
|
||||||
|
children?: ProductCategoryItemVo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProductCategoryListVo {
|
||||||
|
tree: ProductCategoryItemVo[];
|
||||||
|
items: ProductCategoryItemVo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listProductCategories() {
|
||||||
|
return unwrapJavaResponse(
|
||||||
|
get<JavaApiResponse<ProductCategoryListVo>>(`${JAVA_API_PREFIX}/admin/product-categories`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export interface SimilarAsinFilterConditionVo {
|
export interface SimilarAsinFilterConditionVo {
|
||||||
id: number;
|
id: number;
|
||||||
conditionText: string;
|
conditionText: string;
|
||||||
|
|||||||
1
new_web_source/assets/brand-D4E3c93-.js
Normal file
1
new_web_source/assets/brand-D4E3c93-.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import{be as r}from"./pywebview-DNfEzGOK.js";const n="";function s(e){return r(`${n}/api/brand/expand-folder-recursive`,{folder:e})}export{s as e};
|
||||||
1
new_web_source/assets/el-table-column-CZbviO2X.css
Normal file
1
new_web_source/assets/el-table-column-CZbviO2X.css
Normal file
File diff suppressed because one or more lines are too long
55
new_web_source/assets/pywebview-DNfEzGOK.js
Normal file
55
new_web_source/assets/pywebview-DNfEzGOK.js
Normal file
File diff suppressed because one or more lines are too long
1
new_web_source/assets/similar-asin-0mttxPNV.css
Normal file
1
new_web_source/assets/similar-asin-0mttxPNV.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,12 +5,12 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>货源查询</title>
|
<title>货源查询</title>
|
||||||
<script type="module" crossorigin src="/assets/similar-asin.js"></script>
|
<script type="module" crossorigin src="/assets/similar-asin.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-CcblbV5J.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-DNfEzGOK.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/brand-CEi7uRFN.js">
|
<link rel="modulepreload" crossorigin href="/assets/brand-D4E3c93-.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/pywebview-DNJAt1gR.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-DNJAt1gR.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/similar-asin-CVTb4S1E.css">
|
<link rel="stylesheet" crossorigin href="/assets/similar-asin-0mttxPNV.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/el-table-column-BoPoyBFZ.css">
|
<link rel="stylesheet" crossorigin href="/assets/el-table-column-CZbviO2X.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
BIN
tmp/repair-7686/7686-current.xlsx
Normal file
BIN
tmp/repair-7686/7686-current.xlsx
Normal file
Binary file not shown.
BIN
tmp/repair-7686/7686-redownload-full.xlsx
Normal file
BIN
tmp/repair-7686/7686-redownload-full.xlsx
Normal file
Binary file not shown.
BIN
tmp/repair-7686/7686-redownload.xlsx
Normal file
BIN
tmp/repair-7686/7686-redownload.xlsx
Normal file
Binary file not shown.
BIN
tmp/repair-7686/魏振峰查询专利-7686-repaired-full.xlsx
Normal file
BIN
tmp/repair-7686/魏振峰查询专利-7686-repaired-full.xlsx
Normal file
Binary file not shown.
BIN
tmp/repair-7686/魏振峰查询专利-7686-repaired.xlsx
Normal file
BIN
tmp/repair-7686/魏振峰查询专利-7686-repaired.xlsx
Normal file
Binary file not shown.
BIN
tmp/repair-7686/魏振峰查询专利-7686-supply-repaired.xlsx
Normal file
BIN
tmp/repair-7686/魏振峰查询专利-7686-supply-repaired.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user