解决特权时间段的Bug
This commit is contained in:
@@ -50,7 +50,7 @@ public class LbPurchaseApplyController {
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "新增")
|
||||
@Operation(summary = "新增或按申请人电话更新", description = "同一租户下若 applyPhone 已存在则更新该条,否则新增")
|
||||
public ResponseEntity<Map<String, Object>> add(
|
||||
@Parameter(description = "实体", required = true) @RequestBody LbPurchaseApply entity) {
|
||||
Map<String, Object> result = lbPurchaseApplyService.add(entity);
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rj.entity.LbDailyUserTradeReport;
|
||||
import com.rj.entity.LbPurchaseApply;
|
||||
import com.rj.util.IsoWorkdayUtils;
|
||||
import com.rj.mapper.LbDailyUserTradeReportMapper;
|
||||
import com.rj.mapper.LbPurchaseApplyMapper;
|
||||
import com.rj.service.ILbPurchaseApplyService;
|
||||
@@ -26,6 +27,7 @@ import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -46,21 +48,27 @@ public class LbPurchaseApplyServiceImpl
|
||||
@Value("${langchain4j.open-ai.chat-model.model-name:qwen-plus}")
|
||||
private String dashScopeChatModel;
|
||||
|
||||
private static final DateTimeFormatter TRY_DATE_MM_DD = DateTimeFormatter.ofPattern("MM.dd");
|
||||
|
||||
/** 试用期包含的工作日天数(周一至周五),周六日不计入。 */
|
||||
private static final int TRIAL_WEEKDAY_COUNT = 3;
|
||||
|
||||
private static final String PARSE_SYSTEM_PROMPT = """
|
||||
你是信息抽取助手。用户会提供一段「进货申请」相关的自然语言订货信息。
|
||||
请只输出一个 JSON 对象,不要 markdown 代码块,不要解释性文字。
|
||||
字段均为可选;无法从原文推断的字段请省略或设为 null。
|
||||
tryDate 试用日期:若同时给出 applyDate,服务端会按 3 个工作日规则重算并覆盖本字段;模型可省略 tryDate。
|
||||
字段名与含义(JSON 键名必须完全一致):
|
||||
applyUser 申请人姓名;
|
||||
applyPhone 申请人电话;
|
||||
tryDate 试用日期(原文怎么说就怎么填字符串,如 yyyy-MM-dd 或中文描述),起始日期是:申请日期applyDate,结束日期:申请日期applyDate 加 2天,跳过周六日 ,比如: 05.14 到 05.16 ;
|
||||
tryDate 试用日期(可省略;有 applyDate 时由服务端按 3 个工作日重算);
|
||||
age 年龄(整数);
|
||||
workExperience 从业经历;
|
||||
colleagueName 同事姓名;
|
||||
colleaguePhone 同事电话;
|
||||
teamLeader 团队长;
|
||||
teamBigLeader 双收益团队长;
|
||||
applyDate 申请日期,格式 yyyy-MM-dd,若原文无明确日期可省略。
|
||||
applyDate 申请日期,格式 yyyy-MM-dd,如果年的信息为空,请用系统的当前年替换。
|
||||
不要输出 id、tenantId、createTime、updateTime。
|
||||
""";
|
||||
|
||||
@@ -113,11 +121,18 @@ public class LbPurchaseApplyServiceImpl
|
||||
|
||||
String json = normalizeJson(raw);
|
||||
LbPurchaseApply entity = objectMapper.readValue(json, LbPurchaseApply.class);
|
||||
log.debug(
|
||||
"parseFromOrderInfoByLlm:LLM JSON 解析后 applyDate={} tryDate={}",
|
||||
entity.getApplyDate(),
|
||||
entity.getTryDate());
|
||||
|
||||
entity.setTenantId(tenantId.trim());
|
||||
entity.setId(null);
|
||||
entity.setCreateTime(null);
|
||||
entity.setUpdateTime(null);
|
||||
|
||||
fillTryDateFromApplyDate(entity);
|
||||
|
||||
result.put("success", true);
|
||||
result.put("message", "解析成功");
|
||||
result.put("data", entity);
|
||||
@@ -140,6 +155,95 @@ public class LbPurchaseApplyServiceImpl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 试用日期:只要有 {@code applyDate},一律按服务端规则计算(覆盖模型可能错填的 tryDate)。
|
||||
* 试用期为连续 {@link #TRIAL_WEEKDAY_COUNT} 个工作日(周一至周五),周六日为无效日不计入。
|
||||
* 展示起始为申请日 applyDate(日历日),结束日为从「申请日及之后首个工作日」起计的第 3 个工作日(含该首日)。
|
||||
* 若申请日年份不是当年,先按 {@link LocalDate#withYear(int)} 校正为当年同月同日(闰年 2 月 29 日在平年变为 2 月 28 日)。
|
||||
*/
|
||||
private void fillTryDateFromApplyDate(LbPurchaseApply entity) {
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
LocalDate applyDate = entity.getApplyDate();
|
||||
if (applyDate == null) {
|
||||
log.info("试用日期:applyDate 为空,不覆盖,保留 LLM tryDate={}", entity.getTryDate());
|
||||
return;
|
||||
}
|
||||
int currentYear = LocalDate.now().getYear();
|
||||
if (applyDate.getYear() != currentYear) {
|
||||
applyDate = applyDate.withYear(currentYear);
|
||||
entity.setApplyDate(applyDate);
|
||||
}
|
||||
String existingTryDate = entity.getTryDate();
|
||||
LocalDate trialFirstWeekday = firstWeekdayOnOrAfter(applyDate);
|
||||
LocalDate endDate = endOfInclusiveWeekdaySpan(trialFirstWeekday, TRIAL_WEEKDAY_COUNT);
|
||||
|
||||
String computed =
|
||||
applyDate.format(TRY_DATE_MM_DD) + " 到 " + endDate.format(TRY_DATE_MM_DD);
|
||||
|
||||
entity.setTryDate(computed);
|
||||
}
|
||||
|
||||
/** 申请日及之后第一个周一至周五(申请日本身为工作日时即为当天)。 */
|
||||
private static LocalDate firstWeekdayOnOrAfter(LocalDate d) {
|
||||
LocalDate x = d;
|
||||
while (IsoWorkdayUtils.isWeekend(x)) {
|
||||
x = x.plusDays(1);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 {@code firstWeekday} 起连续 {@code inclusiveWeekdays} 个工作日(仅周一至周五,含起点当日),返回该段最后一天。
|
||||
* 若起点落在周六日,会先顺延到下一工作日再计数,且返回值保证不为周六日。
|
||||
*/
|
||||
private LocalDate endOfInclusiveWeekdaySpan(LocalDate firstWeekday, int inclusiveWeekdays) {
|
||||
if (inclusiveWeekdays < 1) {
|
||||
throw new IllegalArgumentException("inclusiveWeekdays must be >= 1");
|
||||
}
|
||||
LocalDate d = firstWeekday;
|
||||
while (IsoWorkdayUtils.isWeekend(d)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"trialSpan:起点为周末,顺延 cursor={} dow={} isoDow={}",
|
||||
d,
|
||||
d.getDayOfWeek(),
|
||||
IsoWorkdayUtils.isoDayOfWeek(d));
|
||||
}
|
||||
d = d.plusDays(1);
|
||||
}
|
||||
int weekdaysRemaining = inclusiveWeekdays;
|
||||
int guard = 0;
|
||||
while (true) {
|
||||
boolean weekend = IsoWorkdayUtils.isWeekend(d);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"trialSpan:cursor={} dow={} isoDow={} weekend={} weekdaysRemaining={}",
|
||||
d,
|
||||
d.getDayOfWeek(),
|
||||
IsoWorkdayUtils.isoDayOfWeek(d),
|
||||
weekend,
|
||||
weekdaysRemaining);
|
||||
}
|
||||
if (!weekend) {
|
||||
weekdaysRemaining--;
|
||||
if (weekdaysRemaining == 0) {
|
||||
return d;
|
||||
}
|
||||
}
|
||||
d = d.plusDays(1);
|
||||
guard++;
|
||||
if (guard > 400) {
|
||||
throw new IllegalStateException(
|
||||
"trialSpan:超过 400 步仍未结束,firstWeekday="
|
||||
+ firstWeekday
|
||||
+ " inclusiveWeekdays="
|
||||
+ inclusiveWeekdays);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String normalizeJson(String content) {
|
||||
if (content == null) {
|
||||
return "";
|
||||
@@ -180,17 +284,39 @@ public class LbPurchaseApplyServiceImpl
|
||||
return result;
|
||||
}
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (entity.getCreateTime() == null) {
|
||||
entity.setCreateTime(now);
|
||||
}
|
||||
entity.setUpdateTime(now);
|
||||
String tenantId = entity.getTenantId().trim();
|
||||
String phone = entity.getApplyPhone().trim();
|
||||
entity.setTenantId(tenantId);
|
||||
entity.setApplyPhone(phone);
|
||||
|
||||
boolean ok = this.save(entity);
|
||||
result.put("success", ok);
|
||||
result.put("message", ok ? "新增成功" : "新增失败");
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LambdaQueryWrapper<LbPurchaseApply> phoneQuery = new LambdaQueryWrapper<>();
|
||||
phoneQuery.eq(LbPurchaseApply::getTenantId, tenantId)
|
||||
.eq(LbPurchaseApply::getApplyPhone, phone)
|
||||
.orderByDesc(LbPurchaseApply::getId)
|
||||
.last("LIMIT 1");
|
||||
LbPurchaseApply existing = this.getOne(phoneQuery);
|
||||
|
||||
boolean ok;
|
||||
if (existing != null) {
|
||||
entity.setId(existing.getId());
|
||||
entity.setCreateTime(existing.getCreateTime());
|
||||
entity.setUpdateTime(now);
|
||||
ok = this.updateById(entity);
|
||||
result.put("success", ok);
|
||||
result.put("message", ok ? "按申请人电话已更新" : "更新失败");
|
||||
} else {
|
||||
entity.setId(null);
|
||||
if (entity.getCreateTime() == null) {
|
||||
entity.setCreateTime(now);
|
||||
}
|
||||
entity.setUpdateTime(now);
|
||||
ok = this.save(entity);
|
||||
result.put("success", ok);
|
||||
result.put("message", ok ? "新增成功" : "新增失败");
|
||||
}
|
||||
if (ok) {
|
||||
result.put("data", entity);
|
||||
result.put("data", this.getById(entity.getId()));
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
|
||||
25
src/main/java/com/rj/util/IsoWorkdayUtils.java
Normal file
25
src/main/java/com/rj/util/IsoWorkdayUtils.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package com.rj.util;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.WeekFields;
|
||||
|
||||
/**
|
||||
* ISO-8601 工作日判断(与 {@link LocalDate} 默认历法一致)。
|
||||
* <p>
|
||||
* 周末判定使用 {@link WeekFields#ISO} 的 day-of-week 数值:周一=1 … 周日=7,周六、周日为 6、7。
|
||||
* 与 {@link java.time.LocalDate#getDayOfWeek()} 结论一致;调试时可打印 {@link #isoDayOfWeek(LocalDate)} 便于核对年份。
|
||||
*/
|
||||
public final class IsoWorkdayUtils {
|
||||
|
||||
private IsoWorkdayUtils() {}
|
||||
|
||||
/** ISO 星期字段数值:1=周一 … 7=周日。 */
|
||||
public static int isoDayOfWeek(LocalDate d) {
|
||||
return d.get(WeekFields.ISO.dayOfWeek());
|
||||
}
|
||||
|
||||
/** 周六、周日为 true(isoDow 为 6 或 7)。 */
|
||||
public static boolean isWeekend(LocalDate d) {
|
||||
return isoDayOfWeek(d) >= 6;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user