新增删除增加公司名
This commit is contained in:
4
app/.env
4
app/.env
@@ -12,7 +12,7 @@ zn_username=%E8%87%AA%E5%8A%A8%E5%8C%96_Robot
|
||||
client_name=ShuFuAI
|
||||
|
||||
|
||||
java_api_base=http://127.0.0.1:18080
|
||||
# java_api_base=http://8.136.19.173:18080
|
||||
# java_api_base=http://127.0.0.1:18080
|
||||
java_api_base=http://8.136.19.173:18080
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ public class DeleteBrandParsedFileCacheDto {
|
||||
private String fileKey;
|
||||
private String sourceFilename;
|
||||
private String shopName;
|
||||
private String companyName;
|
||||
private String shopId;
|
||||
private String platform;
|
||||
private String openStoreUrl;
|
||||
|
||||
@@ -21,6 +21,9 @@ public class DeleteBrandResultItemVo {
|
||||
@Schema(description = "按 Excel 文件名解析出的店铺名")
|
||||
private String shopName;
|
||||
|
||||
@Schema(description = "紫鸟公司名称")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "是否匹配到紫鸟店铺")
|
||||
private boolean matched;
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ public class DeleteBrandRunService {
|
||||
item.setMatched(matchResult.isMatched());
|
||||
if (matchResult.isMatched()) {
|
||||
item.setShopId(matchResult.getShopId());
|
||||
item.setCompanyName(matchResult.getCompanyName());
|
||||
item.setPlatform(matchResult.getPlatform());
|
||||
item.setOpenStoreUrl(matchResult.getOpenStoreUrl());
|
||||
}
|
||||
@@ -153,6 +154,7 @@ public class DeleteBrandRunService {
|
||||
cacheDto.setSourceFilename(sourceFile.getOriginalFilename());
|
||||
cacheDto.setShopName(item.getShopName());
|
||||
cacheDto.setShopId(item.getShopId());
|
||||
cacheDto.setCompanyName(item.getCompanyName());
|
||||
cacheDto.setPlatform(item.getPlatform());
|
||||
cacheDto.setOpenStoreUrl(item.getOpenStoreUrl());
|
||||
cacheDto.setTotalRows(parsed.totalRows());
|
||||
@@ -204,7 +206,7 @@ public class DeleteBrandRunService {
|
||||
|
||||
task.setSuccessFileCount(parsedSuccessCount);
|
||||
task.setFailedFileCount(parsedFailedCount);
|
||||
task.setResultJson(JSONUtil.toJsonStr(items));
|
||||
task.setResultJson(toCompactTaskResultJsonForDb(items));
|
||||
task.setUpdatedAt(LocalDateTime.now());
|
||||
task.setSourceFileCount(processableFileCount);
|
||||
if (processableFileCount <= 0) {
|
||||
@@ -295,6 +297,7 @@ public class DeleteBrandRunService {
|
||||
}
|
||||
|
||||
item.setShopId(candidate.getShopId());
|
||||
item.setCompanyName(candidate.getCompanyName());
|
||||
item.setPlatform(candidate.getPlatform());
|
||||
item.setOpenStoreUrl(candidate.getOpenStoreUrl());
|
||||
|
||||
@@ -716,6 +719,7 @@ public class DeleteBrandRunService {
|
||||
String oldStatus = item.getMatchStatus();
|
||||
boolean oldMatched = item.isMatched();
|
||||
String oldShopId = item.getShopId();
|
||||
String oldCompanyName = item.getCompanyName();
|
||||
String oldPlatform = item.getPlatform();
|
||||
String oldOpenStoreUrl = item.getOpenStoreUrl();
|
||||
String oldMessage = item.getMatchMessage();
|
||||
@@ -724,12 +728,14 @@ public class DeleteBrandRunService {
|
||||
item.setMatchStatus(refreshed.getMatchStatus());
|
||||
item.setMatchMessage(refreshed.getMatchMessage());
|
||||
item.setShopId(refreshed.getShopId());
|
||||
item.setCompanyName(refreshed.getCompanyName());
|
||||
item.setPlatform(refreshed.getPlatform());
|
||||
item.setOpenStoreUrl(refreshed.getOpenStoreUrl());
|
||||
|
||||
if (!java.util.Objects.equals(oldStatus, item.getMatchStatus())
|
||||
|| oldMatched != item.isMatched()
|
||||
|| !java.util.Objects.equals(oldShopId, item.getShopId())
|
||||
|| !java.util.Objects.equals(oldCompanyName, item.getCompanyName())
|
||||
|| !java.util.Objects.equals(oldPlatform, item.getPlatform())
|
||||
|| !java.util.Objects.equals(oldOpenStoreUrl, item.getOpenStoreUrl())
|
||||
|| !java.util.Objects.equals(oldMessage, item.getMatchMessage())) {
|
||||
@@ -745,7 +751,7 @@ public class DeleteBrandRunService {
|
||||
if (!changed) {
|
||||
return task;
|
||||
}
|
||||
task.setResultJson(JSONUtil.toJsonStr(items));
|
||||
task.setResultJson(toCompactTaskResultJsonForDb(items));
|
||||
task.setSourceFileCount(matchedCount);
|
||||
task.setFailedFileCount(unmatchedCount);
|
||||
task.setUpdatedAt(LocalDateTime.now());
|
||||
@@ -1120,6 +1126,7 @@ public class DeleteBrandRunService {
|
||||
item.setFileKey(parsedFile.getFileKey());
|
||||
item.setSourceFilename(parsedFile.getSourceFilename());
|
||||
item.setShopName(parsedFile.getShopName());
|
||||
item.setCompanyName(parsedFile.getCompanyName());
|
||||
item.setOutputFilename(outputFile.getName());
|
||||
item.setDownloadUrl(ossStorageService.getPublicUrl(objectKey));
|
||||
item.setTotalRows(parsedFile.getTotalRows());
|
||||
@@ -1140,7 +1147,7 @@ public class DeleteBrandRunService {
|
||||
task.setStatus("SUCCESS");
|
||||
task.setSuccessFileCount(successCount);
|
||||
task.setErrorMessage(null);
|
||||
task.setResultJson(JSONUtil.toJsonStr(finalItems));
|
||||
task.setResultJson(toCompactTaskResultJsonForDb(finalItems));
|
||||
task.setUpdatedAt(LocalDateTime.now());
|
||||
task.setFinishedAt(LocalDateTime.now());
|
||||
fileTaskMapper.updateById(task);
|
||||
@@ -1351,6 +1358,33 @@ public class DeleteBrandRunService {
|
||||
return normalized.isBlank() ? defaultValue : normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* 落库用 result_json:去掉国家分组、预览行等大字段,保留匹配摘要与计数。
|
||||
* 索引常命中后单次任务 JSON 体积会陡增,易触发 max_allowed_packet / 更新失败,进而前端 unwrap 报「请求失败」。
|
||||
*/
|
||||
private String toCompactTaskResultJsonForDb(List<DeleteBrandResultItemVo> items) {
|
||||
if (items == null || items.isEmpty()) {
|
||||
return JSONUtil.toJsonStr(List.of());
|
||||
}
|
||||
try {
|
||||
List<DeleteBrandResultItemVo> clone = objectMapper.readValue(
|
||||
objectMapper.writeValueAsString(items),
|
||||
new TypeReference<List<DeleteBrandResultItemVo>>() {
|
||||
});
|
||||
for (DeleteBrandResultItemVo vo : clone) {
|
||||
if (vo == null) {
|
||||
continue;
|
||||
}
|
||||
vo.setCountries(new ArrayList<>());
|
||||
vo.setPreviewRows(new ArrayList<>());
|
||||
}
|
||||
return JSONUtil.toJsonStr(clone);
|
||||
} catch (Exception ex) {
|
||||
log.warn("[delete-brand] compact task result json failed, msg={}", ex.getMessage());
|
||||
return JSONUtil.toJsonStr(items);
|
||||
}
|
||||
}
|
||||
|
||||
private record ParsedDeleteBrandFile(int totalRows,
|
||||
List<DeleteBrandCountryGroupVo> countries,
|
||||
List<DeleteBrandPreviewRowVo> previewRows) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.nanri.aiimage.modules.ziniao.memory.model.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
@@ -15,6 +16,8 @@ public class ZiniaoMemoryStoreEntity {
|
||||
private Long id;
|
||||
private String cacheType;
|
||||
private String cacheKey;
|
||||
@TableField("company_name")
|
||||
private String companyName;
|
||||
private String payloadJson;
|
||||
private LocalDateTime expiresAt;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.nanri.aiimage.common.exception.BusinessException;
|
||||
import com.nanri.aiimage.modules.ziniao.memory.mapper.ZiniaoMemoryStoreMapper;
|
||||
import com.nanri.aiimage.modules.ziniao.memory.model.entity.ZiniaoMemoryStoreEntity;
|
||||
import com.nanri.aiimage.modules.ziniao.model.cache.ZiniaoShopIndexEntryDto;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -25,6 +26,9 @@ public class ZiniaoMemoryStoreService {
|
||||
/** 本表仅用于持久化店铺索引条目(见 ZiniaoShopIndexService)。 */
|
||||
public static final String CACHE_TYPE_SHOP_INDEX_ENTRY = "SHOP_INDEX_ENTRY";
|
||||
|
||||
/** INFO 日志里 company_name 采样最大字符数(避免过长刷屏)。 */
|
||||
private static final int COMPANY_NAME_LOG_SAMPLE_MAX_CHARS = 64;
|
||||
|
||||
private final ZiniaoMemoryStoreMapper ziniaoMemoryStoreMapper;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@@ -115,29 +119,33 @@ public class ZiniaoMemoryStoreService {
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime expiresAt = now.plusSeconds(ttl.getSeconds());
|
||||
String companyName = resolveCompanyName(payload);
|
||||
ZiniaoMemoryStoreEntity entity = findOne(normalizedType, normalizedKey);
|
||||
if (entity == null) {
|
||||
entity = new ZiniaoMemoryStoreEntity();
|
||||
entity.setCacheType(normalizedType);
|
||||
entity.setCacheKey(normalizedKey);
|
||||
entity.setCompanyName(companyName);
|
||||
entity.setPayloadJson(payloadJson);
|
||||
entity.setExpiresAt(expiresAt);
|
||||
entity.setCreatedAt(now);
|
||||
entity.setUpdatedAt(now);
|
||||
ziniaoMemoryStoreMapper.insert(entity);
|
||||
if (CACHE_TYPE_SHOP_INDEX_ENTRY.equals(normalizedType)) {
|
||||
log.info("[ziniao-shop-index-db] insert cacheKey={} expiresAt={} bytes={}",
|
||||
normalizedKey, expiresAt, payloadJson.length());
|
||||
log.info("[ziniao-shop-index-db] insert cacheKey={} companyNameSample={} expiresAt={} bytes={}",
|
||||
normalizedKey, sampleCompanyNameForLog(companyName), expiresAt, payloadJson.length());
|
||||
}
|
||||
return;
|
||||
}
|
||||
companyName = mergeCompanyNameForPersist(entity.getCompanyName(), companyName);
|
||||
entity.setCompanyName(companyName);
|
||||
entity.setPayloadJson(payloadJson);
|
||||
entity.setExpiresAt(expiresAt);
|
||||
entity.setUpdatedAt(now);
|
||||
ziniaoMemoryStoreMapper.updateById(entity);
|
||||
if (CACHE_TYPE_SHOP_INDEX_ENTRY.equals(normalizedType)) {
|
||||
log.info("[ziniao-shop-index-db] update cacheKey={} id={} expiresAt={} bytes={}",
|
||||
normalizedKey, entity.getId(), expiresAt, payloadJson.length());
|
||||
log.info("[ziniao-shop-index-db] update cacheKey={} id={} companyNameSample={} expiresAt={} bytes={}",
|
||||
normalizedKey, entity.getId(), sampleCompanyNameForLog(companyName), expiresAt, payloadJson.length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +157,8 @@ public class ZiniaoMemoryStoreService {
|
||||
if (entity != null) {
|
||||
ziniaoMemoryStoreMapper.deleteById(entity.getId());
|
||||
if (CACHE_TYPE_SHOP_INDEX_ENTRY.equals(normalizedType)) {
|
||||
log.info("[ziniao-shop-index-db] delete cacheKey={} id={}", entity.getCacheKey(), entity.getId());
|
||||
log.info("[ziniao-shop-index-db] delete cacheKey={} id={} companyNameSample={}",
|
||||
entity.getCacheKey(), entity.getId(), sampleCompanyNameForLog(entity.getCompanyName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +219,39 @@ public class ZiniaoMemoryStoreService {
|
||||
return entity.getExpiresAt() == null || !entity.getExpiresAt().isAfter(LocalDateTime.now());
|
||||
}
|
||||
|
||||
private String resolveCompanyName(Object payload) {
|
||||
if (payload instanceof ZiniaoShopIndexEntryDto dto) {
|
||||
return Objects.toString(dto.getCompanyName(), "").trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新行时:若 payload 未带公司名,保留库里已有值,避免历史/仅改 status 的回写把 company_name 清空。
|
||||
*/
|
||||
private static String mergeCompanyNameForPersist(String existingDbValue, String fromPayload) {
|
||||
String fromNew = fromPayload == null ? "" : fromPayload.trim();
|
||||
if (!fromNew.isEmpty()) {
|
||||
return fromNew;
|
||||
}
|
||||
String existing = existingDbValue == null ? "" : existingDbValue.trim();
|
||||
return existing;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志用 company_name 采样:空显示 (empty),过长截断并附带原长度,便于运行时对照 DB 写入意图且不刷屏。
|
||||
*/
|
||||
private static String sampleCompanyNameForLog(String companyName) {
|
||||
String s = companyName == null ? "" : companyName.trim();
|
||||
if (s.isEmpty()) {
|
||||
return "(empty)";
|
||||
}
|
||||
if (s.length() <= COMPANY_NAME_LOG_SAMPLE_MAX_CHARS) {
|
||||
return s;
|
||||
}
|
||||
return s.substring(0, COMPANY_NAME_LOG_SAMPLE_MAX_CHARS) + "…(" + s.length() + " chars)";
|
||||
}
|
||||
|
||||
private String normalizeRequired(String value, String message) {
|
||||
String normalized = Objects.toString(value, "").trim();
|
||||
if (normalized.isEmpty()) {
|
||||
|
||||
@@ -10,6 +10,7 @@ public class ZiniaoShopIndexEntryDto {
|
||||
private String normalizedShopName;
|
||||
private String shopId;
|
||||
private String shopName;
|
||||
private String companyName;
|
||||
private String platform;
|
||||
private Long matchedUserId;
|
||||
private Long companyId;
|
||||
|
||||
@@ -13,6 +13,12 @@ public class ZiniaoShopIndexRefreshCursorDto {
|
||||
private Long lastStartedAt;
|
||||
private Long lastFinishedAt;
|
||||
private Long lastSuccessAt;
|
||||
/** 下一轮刷新从 apiKey 列表的哪个 offset 开始。 */
|
||||
private Integer nextApiKeyOffset;
|
||||
/** 最近一轮可见 apiKey 总量。 */
|
||||
private Integer apiKeyTotal;
|
||||
/** 最近一轮实际处理的 apiKey 数。 */
|
||||
private Integer lastProcessedApiKeyCount;
|
||||
private Integer invalidUserCount;
|
||||
private List<Long> sampleInvalidUserIds = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ public class ZiniaoShopMatchResultVo {
|
||||
@Schema(description = "店铺名称")
|
||||
private String shopName;
|
||||
|
||||
@Schema(description = "紫鸟公司名称")
|
||||
private String companyName;
|
||||
|
||||
@Schema(description = "平台")
|
||||
private String platform;
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ import com.nanri.aiimage.modules.shopkey.model.entity.ShopKeyEntity;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -15,16 +17,33 @@ public class ZiniaoApiKeyProvider {
|
||||
|
||||
private final ShopKeyMapper shopKeyMapper;
|
||||
|
||||
public List<ApiKeyAccount> listApiKeyAccounts() {
|
||||
List<ShopKeyEntity> entities = shopKeyMapper.selectList(new LambdaQueryWrapper<ShopKeyEntity>()
|
||||
.orderByDesc(ShopKeyEntity::getId));
|
||||
Map<String, String> accountByApiKey = new LinkedHashMap<>();
|
||||
for (ShopKeyEntity entity : entities) {
|
||||
String apiKey = normalizeApiKey(entity == null ? null : entity.getZiniaoToken());
|
||||
if (apiKey == null) {
|
||||
continue;
|
||||
}
|
||||
String accountName = entity.getZiniaoAccountName() == null ? null : entity.getZiniaoAccountName().trim();
|
||||
if (!accountByApiKey.containsKey(apiKey)) {
|
||||
accountByApiKey.put(apiKey, accountName);
|
||||
continue;
|
||||
}
|
||||
String existingName = accountByApiKey.get(apiKey);
|
||||
if ((existingName == null || existingName.isBlank()) && accountName != null && !accountName.isBlank()) {
|
||||
accountByApiKey.put(apiKey, accountName);
|
||||
}
|
||||
}
|
||||
return accountByApiKey.entrySet().stream()
|
||||
.map(entry -> new ApiKeyAccount(entry.getKey(), entry.getValue()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<String> listApiKeys() {
|
||||
return shopKeyMapper.selectList(new LambdaQueryWrapper<ShopKeyEntity>()
|
||||
.orderByDesc(ShopKeyEntity::getId))
|
||||
.stream()
|
||||
.map(ShopKeyEntity::getZiniaoToken)
|
||||
.filter(token -> token != null && !token.isBlank())
|
||||
.map(String::trim)
|
||||
.map(token -> token.regionMatches(true, 0, "Bearer ", 0, 7) ? token.substring(7).trim() : token)
|
||||
.filter(token -> !token.isBlank())
|
||||
.distinct()
|
||||
return listApiKeyAccounts().stream()
|
||||
.map(ApiKeyAccount::apiKey)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@@ -40,4 +59,18 @@ public class ZiniaoApiKeyProvider {
|
||||
Long total = shopKeyMapper.selectCount(new LambdaQueryWrapper<ShopKeyEntity>());
|
||||
return total != null && total > 0;
|
||||
}
|
||||
|
||||
private String normalizeApiKey(String token) {
|
||||
if (token == null || token.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
String normalized = token.trim();
|
||||
if (normalized.regionMatches(true, 0, "Bearer ", 0, 7)) {
|
||||
normalized = normalized.substring(7).trim();
|
||||
}
|
||||
return normalized.isBlank() ? null : normalized;
|
||||
}
|
||||
|
||||
public record ApiKeyAccount(String apiKey, String accountName) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public class ZiniaoShopIndexService {
|
||||
cursor.setScopeKey("global");
|
||||
cursor.setStatus("IDLE");
|
||||
cursor.setMessage("索引刷新任务尚未执行");
|
||||
cursor.setNextApiKeyOffset(0);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
@@ -107,25 +108,45 @@ public class ZiniaoShopIndexService {
|
||||
return pendingResult("店铺索引缺少 companyId,请等待后台刷新");
|
||||
}
|
||||
|
||||
String openStoreUrl = ziniaoAuthService.buildOpenStoreUrlByScope(apiKey, companyId, entry.getMatchedUserId(), entry.getShopId());
|
||||
String openStoreUrl = null;
|
||||
boolean openUrlFailed = false;
|
||||
try {
|
||||
openStoreUrl = ziniaoAuthService.buildOpenStoreUrlByScope(apiKey, companyId, entry.getMatchedUserId(), entry.getShopId());
|
||||
} catch (BusinessException ex) {
|
||||
openUrlFailed = true;
|
||||
log.warn("[ziniao-shop-index] buildOpenStoreUrl failed normalizedName={} msg={}", normalizedShopName, ex.getMessage());
|
||||
}
|
||||
ZiniaoShopMatchResultVo vo = new ZiniaoShopMatchResultVo();
|
||||
vo.setMatched(true);
|
||||
vo.setShopId(entry.getShopId());
|
||||
vo.setShopName(entry.getShopName());
|
||||
vo.setCompanyName(entry.getCompanyName());
|
||||
vo.setPlatform(entry.getPlatform());
|
||||
vo.setMatchedUserId(entry.getMatchedUserId());
|
||||
vo.setOpenStoreUrl(openStoreUrl);
|
||||
vo.setMatchStatus(isFresh(entry) ? MATCH_STATUS_MATCHED : MATCH_STATUS_STALE);
|
||||
vo.setMatchMessage(isFresh(entry) ? null : "店铺索引已过保鲜期,请等待后台刷新");
|
||||
if (openUrlFailed) {
|
||||
vo.setMatchStatus(MATCH_STATUS_STALE);
|
||||
vo.setMatchMessage("打开店铺链接暂时无法生成,请稍后重试");
|
||||
} else {
|
||||
vo.setMatchStatus(isFresh(entry) ? MATCH_STATUS_MATCHED : MATCH_STATUS_STALE);
|
||||
vo.setMatchMessage(isFresh(entry) ? null : "店铺索引已过保鲜期,请等待后台刷新");
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
public void refreshShopIndex() {
|
||||
long now = Instant.now().toEpochMilli();
|
||||
ZiniaoShopIndexRefreshCursorDto previousCursor = ziniaoTransientCacheService
|
||||
.get(CACHE_TYPE_SHOP_INDEX_REFRESH_CURSOR, "global", ZiniaoShopIndexRefreshCursorDto.class)
|
||||
.orElse(null);
|
||||
int previousOffset = previousCursor == null || previousCursor.getNextApiKeyOffset() == null
|
||||
? 0
|
||||
: Math.max(previousCursor.getNextApiKeyOffset(), 0);
|
||||
ZiniaoShopIndexRefreshCursorDto cursor = new ZiniaoShopIndexRefreshCursorDto();
|
||||
cursor.setScopeKey("global");
|
||||
cursor.setStatus("RUNNING");
|
||||
cursor.setLastStartedAt(now);
|
||||
cursor.setNextApiKeyOffset(previousOffset);
|
||||
ziniaoTransientCacheService.put(CACHE_TYPE_SHOP_INDEX_REFRESH_CURSOR, "global", cursor, DEFAULT_CURSOR_TTL);
|
||||
log.info("[ziniao-index] refresh started");
|
||||
|
||||
@@ -134,11 +155,16 @@ public class ZiniaoShopIndexService {
|
||||
Set<Long> allInvalidUserIds = new LinkedHashSet<>();
|
||||
int refreshBatchSize = resolveRefreshBatchSize();
|
||||
try {
|
||||
List<String> apiKeys = ziniaoApiKeyProvider.listApiKeys();
|
||||
if (refreshBatchSize > 0 && apiKeys.size() > refreshBatchSize) {
|
||||
apiKeys = apiKeys.subList(0, refreshBatchSize);
|
||||
}
|
||||
for (String apiKey : apiKeys) {
|
||||
List<ZiniaoApiKeyProvider.ApiKeyAccount> allApiKeyAccounts = ziniaoApiKeyProvider.listApiKeyAccounts();
|
||||
List<ZiniaoApiKeyProvider.ApiKeyAccount> apiKeyAccounts = selectApiKeyBatchByOffset(
|
||||
allApiKeyAccounts,
|
||||
previousOffset,
|
||||
refreshBatchSize
|
||||
);
|
||||
int nextOffset = computeNextOffset(allApiKeyAccounts.size(), previousOffset, apiKeyAccounts.size(), refreshBatchSize);
|
||||
for (ZiniaoApiKeyProvider.ApiKeyAccount apiKeyAccount : apiKeyAccounts) {
|
||||
String apiKey = apiKeyAccount.apiKey();
|
||||
String companyName = apiKeyAccount.accountName();
|
||||
Long companyId;
|
||||
try {
|
||||
companyId = ziniaoAuthService.resolveCompanyIdForIndex(apiKey);
|
||||
@@ -198,6 +224,7 @@ public class ZiniaoShopIndexService {
|
||||
entry.setNormalizedShopName(normalizedShopName);
|
||||
entry.setShopId(store.getShopId());
|
||||
entry.setShopName(store.getShopName());
|
||||
entry.setCompanyName(companyName);
|
||||
entry.setPlatform(store.getPlatform());
|
||||
entry.setMatchedUserId(userId);
|
||||
entry.setCompanyId(companyId);
|
||||
@@ -260,12 +287,16 @@ public class ZiniaoShopIndexService {
|
||||
cursor.setMessage(allInvalidUserIds.isEmpty()
|
||||
? null
|
||||
: "本轮刷新已跳过无效 userId 数: " + allInvalidUserIds.size());
|
||||
cursor.setApiKeyTotal(allApiKeyAccounts.size());
|
||||
cursor.setLastProcessedApiKeyCount(apiKeyAccounts.size());
|
||||
cursor.setNextApiKeyOffset(nextOffset);
|
||||
cursor.setInvalidUserCount(allInvalidUserIds.size());
|
||||
cursor.setSampleInvalidUserIds(allInvalidUserIds.stream().limit(20).toList());
|
||||
cursor.setLastFinishedAt(now);
|
||||
cursor.setLastSuccessAt(now);
|
||||
ziniaoTransientCacheService.put(CACHE_TYPE_SHOP_INDEX_REFRESH_CURSOR, "global", cursor, DEFAULT_CURSOR_TTL);
|
||||
log.info("[ziniao-index] refresh success invalidUsersSkipped={}", allInvalidUserIds.size());
|
||||
log.info("[ziniao-index] refresh success invalidUsersSkipped={} apiKeyProcessed={}/{} nextOffset={}",
|
||||
allInvalidUserIds.size(), apiKeyAccounts.size(), allApiKeyAccounts.size(), nextOffset);
|
||||
} catch (Exception ex) {
|
||||
cursor.setStatus("FAILED");
|
||||
cursor.setMessage(ex.getMessage());
|
||||
@@ -313,6 +344,7 @@ public class ZiniaoShopIndexService {
|
||||
entry.setNormalizedShopName(normalizedShopName);
|
||||
entry.setStatus(STATUS_CONFLICT);
|
||||
entry.setShopName(representative == null ? normalizedShopName : representative.getShopName());
|
||||
entry.setCompanyName(representative == null ? null : representative.getCompanyName());
|
||||
entry.setCompanyId(representative == null ? null : representative.getCompanyId());
|
||||
entry.setApiKeyHash(representative == null ? null : representative.getApiKeyHash());
|
||||
entry.setCandidateCount(candidates == null ? 0 : candidates.size());
|
||||
@@ -427,6 +459,17 @@ public class ZiniaoShopIndexService {
|
||||
|| existingEntry.getNormalizedShopName().isBlank()) {
|
||||
continue;
|
||||
}
|
||||
String companyNameInRow = entity.getCompanyName();
|
||||
if ((existingEntry.getCompanyName() == null || existingEntry.getCompanyName().isBlank())
|
||||
&& companyNameInRow != null && !companyNameInRow.isBlank()) {
|
||||
existingEntry.setCompanyName(companyNameInRow.trim());
|
||||
}
|
||||
if (existingEntry.getCompanyName() == null || existingEntry.getCompanyName().isBlank()) {
|
||||
String accountFromKeys = resolveAccountNameByApiKeyHash(existingEntry.getApiKeyHash());
|
||||
if (accountFromKeys != null && !accountFromKeys.isBlank()) {
|
||||
existingEntry.setCompanyName(accountFromKeys.trim());
|
||||
}
|
||||
}
|
||||
if (STATUS_CONFLICT.equals(existingEntry.getStatus())
|
||||
&& (existingEntry.getCandidateCount() == null || existingEntry.getCandidateCount() <= 0)
|
||||
&& (existingEntry.getShopName() == null || existingEntry.getShopName().isBlank())
|
||||
@@ -475,6 +518,36 @@ public class ZiniaoShopIndexService {
|
||||
return batchSize;
|
||||
}
|
||||
|
||||
private List<ZiniaoApiKeyProvider.ApiKeyAccount> selectApiKeyBatchByOffset(
|
||||
List<ZiniaoApiKeyProvider.ApiKeyAccount> allApiKeyAccounts,
|
||||
int offset,
|
||||
int refreshBatchSize) {
|
||||
if (allApiKeyAccounts == null || allApiKeyAccounts.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
int total = allApiKeyAccounts.size();
|
||||
if (refreshBatchSize <= 0 || refreshBatchSize >= total) {
|
||||
return new ArrayList<>(allApiKeyAccounts);
|
||||
}
|
||||
int normalizedOffset = Math.floorMod(offset, total);
|
||||
List<ZiniaoApiKeyProvider.ApiKeyAccount> selected = new ArrayList<>(refreshBatchSize);
|
||||
for (int i = 0; i < refreshBatchSize; i++) {
|
||||
selected.add(allApiKeyAccounts.get((normalizedOffset + i) % total));
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
private int computeNextOffset(int totalApiKeys, int previousOffset, int processedCount, int refreshBatchSize) {
|
||||
if (totalApiKeys <= 0) {
|
||||
return 0;
|
||||
}
|
||||
int step = processedCount > 0 ? processedCount : Math.max(refreshBatchSize, 0);
|
||||
if (step <= 0) {
|
||||
return Math.floorMod(previousOffset, totalApiKeys);
|
||||
}
|
||||
return Math.floorMod(previousOffset + step, totalApiKeys);
|
||||
}
|
||||
|
||||
private List<Long> buildUserIds(List<ZiniaoStaffItemVo> staff) {
|
||||
List<Long> userIds = new ArrayList<>();
|
||||
for (ZiniaoStaffItemVo item : staff) {
|
||||
@@ -535,6 +608,19 @@ public class ZiniaoShopIndexService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String resolveAccountNameByApiKeyHash(String apiKeyHash) {
|
||||
String apiKey = findApiKeyByHash(apiKeyHash);
|
||||
if (apiKey == null) {
|
||||
return null;
|
||||
}
|
||||
for (ZiniaoApiKeyProvider.ApiKeyAccount acc : ziniaoApiKeyProvider.listApiKeyAccounts()) {
|
||||
if (apiKey.equals(acc.apiKey())) {
|
||||
return acc.accountName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String buildApiKeyHash(String apiKey) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
ALTER TABLE biz_ziniao_memory_store
|
||||
ADD COLUMN company_name VARCHAR(255) NULL COMMENT '紫鸟公司名称' AFTER cache_key;
|
||||
|
||||
CREATE INDEX idx_company_name ON biz_ziniao_memory_store (company_name);
|
||||
Reference in New Issue
Block a user