This commit is contained in:
van
2026-04-07 17:29:32 +08:00
parent 9af1a369f7
commit a22d17de73
2 changed files with 67 additions and 29 deletions

View File

@@ -12,6 +12,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
@@ -1147,24 +1149,6 @@ public class InstructionServiceImpl implements IInstructionService {
stringRedisTemplate.opsForValue().set(orderNumberKey, orderNumberForDedup, 1, TimeUnit.DAYS); stringRedisTemplate.opsForValue().set(orderNumberKey, orderNumberForDedup, 1, TimeUnit.DAYS);
} }
// 第二重判断:地址 24 小时去重校验(白名单放行)
if (stringRedisTemplate != null) {
String addressKey = "address:" + address;
String existed = stringRedisTemplate.opsForValue().get(addressKey);
if (existed != null) {
if (!(existed.contains("李波") || existed.contains("吴胜硕") || existed.contains("小硕硕"))) {
// 如果强制生成,跳过地址重复检查
if (!forceGenerate) {
// 返回特殊错误码,前端会识别并弹出验证码
return "ERROR_CODE:ADDRESS_DUPLICATE\n此地址已经存在请勿重复生成订单";
}
// forceGenerate为true时跳过地址重复检查继续执行
}
}
// 只有在不强制生成或地址不存在时才设置Redis强制生成时也更新Redis记录
stringRedisTemplate.opsForValue().set(addressKey, address, 1, TimeUnit.DAYS);
}
String today = new java.text.SimpleDateFormat("yyyy-MM-dd ").format(new Date()); String today = new java.text.SimpleDateFormat("yyyy-MM-dd ").format(new Date());
String todayNoSpace = today.trim(); String todayNoSpace = today.trim();
String keyWithSpace = "order_count:" + today; // 带空格 String keyWithSpace = "order_count:" + today; // 带空格
@@ -1188,14 +1172,40 @@ public class InstructionServiceImpl implements IInstructionService {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
int total = Math.max(1, num); int total = Math.max(1, num);
// 第二重:同日 + 型号 + 地址 槽位。重复提交视为「更新」——复用当日已分配序号,不递增全局 order_count强制生成则始终新占号并覆盖槽位。
String slotDigest = digestShengSlot(model, address);
String slotKey = "sheng_slot:" + todayNoSpace + ":" + slotDigest;
Integer reusedCounter = null;
if (stringRedisTemplate != null && !forceGenerate && total == 1) {
String slotVal = stringRedisTemplate.opsForValue().get(slotKey);
if (slotVal != null && !slotVal.isEmpty()) {
try {
int c = Integer.parseInt(slotVal.trim());
if (c >= 1) {
reusedCounter = c;
}
} catch (NumberFormatException ignore) {
reusedCounter = null;
}
}
}
int startCount = 1; int startCount = 1;
if (reusedCounter != null) {
startCount = reusedCounter;
if (stringRedisTemplate != null) { if (stringRedisTemplate != null) {
stringRedisTemplate.opsForValue().set(slotKey, String.valueOf(reusedCounter), 1, TimeUnit.DAYS);
}
} else if (stringRedisTemplate != null) {
try { try {
String s = stringRedisTemplate.opsForValue().get(redisKey); String s = stringRedisTemplate.opsForValue().get(redisKey);
int count = s != null ? Integer.parseInt(s) : 0; int count = s != null ? Integer.parseInt(s) : 0;
startCount = count + 1; startCount = count + 1;
int endCount = count + total; int endCount = count + total;
stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(endCount), 30, TimeUnit.DAYS); stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(endCount), 30, TimeUnit.DAYS);
if (total == 1) {
stringRedisTemplate.opsForValue().set(slotKey, String.valueOf(startCount), 1, TimeUnit.DAYS);
}
} catch (Exception ignore) { } catch (Exception ignore) {
} }
} }
@@ -1217,6 +1227,26 @@ public class InstructionServiceImpl implements IInstructionService {
return out.toString(); return out.toString();
} }
/**
* 同日「生」指令槽位键摘要:规范化后的 型号 + 地址,避免仅按地址误伤「同址不同型号」。
*/
private String digestShengSlot(String model, String address) {
String m = normalizeWhitespace(model == null ? "" : model);
String a = normalizeWhitespace(address == null ? "" : address);
String raw = m + "\u0001" + a;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] d = md.digest(raw.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder(32);
for (byte b : d) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
return Integer.toHexString(Objects.hash(m, a));
}
}
/** /**
* 从分销标记中提取订单编号 * 从分销标记中提取订单编号
* 例如:从 "H-TF(10.10 腾锋 JY202510093195)" 中提取 "JY202510093195" * 例如:从 "H-TF(10.10 腾锋 JY202510093195)" 中提取 "JY202510093195"

View File

@@ -1000,6 +1000,22 @@ public class SocialMediaServiceImpl implements ISocialMediaService
String cleanTitle = cleanTitleOrRemark(title.trim()); String cleanTitle = cleanTitleOrRemark(title.trim());
String cleanRemark = StringUtils.isNotEmpty(remark) ? cleanTitleOrRemark(remark.trim()) : ""; String cleanRemark = StringUtils.isNotEmpty(remark) ? cleanTitleOrRemark(remark.trim()) : "";
boolean generateSeed = isTrue(req.get("generateSeedNote"));
if (generateSeed) {
if (StringUtils.isEmpty(asString(req.get("goods_title")))) {
req.put("goods_title", cleanTitle);
}
if (StringUtils.isEmpty(asString(req.get("goods_model")))) {
req.put("goods_model", cleanRemark);
}
String seedError = validateSeedRequiredFields(req);
if (seedError != null) {
result.put("success", false);
result.put("error", seedError);
return result;
}
}
String wenanBase = getPromptTemplateWithDefault("xianyu:wenan_base", DEFAULT_XIANYU_WENAN_BASE); String wenanBase = getPromptTemplateWithDefault("xianyu:wenan_base", DEFAULT_XIANYU_WENAN_BASE);
String jiaonixiadanExtra = getPromptTemplateWithDefault("xianyu:jiaonixiadan_extra", DEFAULT_XIANYU_JIAONIXIADAN_EXTRA); String jiaonixiadanExtra = getPromptTemplateWithDefault("xianyu:jiaonixiadan_extra", DEFAULT_XIANYU_JIAONIXIADAN_EXTRA);
@@ -1028,14 +1044,7 @@ public class SocialMediaServiceImpl implements ISocialMediaService
result.put("daixiadan", daixiadanBuilder.toString()); result.put("daixiadan", daixiadanBuilder.toString());
result.put("jiaonixiadan", jiaonixiadanBuilder.toString()); result.put("jiaonixiadan", jiaonixiadanBuilder.toString());
boolean generateSeed = isTrue(req.get("generateSeedNote"));
if (generateSeed) { if (generateSeed) {
String seedError = validateSeedRequiredFields(req);
if (seedError != null) {
result.put("success", false);
result.put("error", seedError);
return result;
}
try { try {
String seedPrompt = buildSeedPrompt(req); String seedPrompt = buildSeedPrompt(req);
String seedNote = callJarvisLlm(seedPrompt, asString(req.get("profileId"))); String seedNote = callJarvisLlm(seedPrompt, asString(req.get("profileId")));
@@ -1045,9 +1054,8 @@ public class SocialMediaServiceImpl implements ISocialMediaService
result.put("seedNote", seedNote.trim()); result.put("seedNote", seedNote.trim());
} catch (Exception e) { } catch (Exception e) {
log.error("生成闲鱼种草文案失败", e); log.error("生成闲鱼种草文案失败", e);
result.put("success", false); result.put("seedNote", "");
result.put("error", "种草文案生成失败: " + e.getMessage()); result.put("seedNoteError", "种草文案生成失败: " + e.getMessage());
return result;
} }
} }
return result; return result;