Files
smartDriveEE/src/main/java/com/rj/service/impl/LbPurchaseApplyServiceImpl.java
2026-05-14 23:53:37 +08:00

506 lines
21 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.rj.service.impl;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
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;
import java.util.Set;
@Slf4j
@Service
public class LbPurchaseApplyServiceImpl
extends ServiceImpl<LbPurchaseApplyMapper, LbPurchaseApply>
implements ILbPurchaseApplyService {
@Autowired
private LbDailyUserTradeReportMapper lbDailyUserTradeReportMapper;
@Autowired
private ObjectMapper objectMapper;
@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 试用日期(可省略;有 applyDate 时由服务端按 3 个工作日重算);
age 年龄(整数);
workExperience 从业经历;
colleagueName 同事姓名;
colleaguePhone 同事电话;
teamLeader 团队长;
teamBigLeader 双收益团队长;
applyDate 申请日期,格式 yyyy-MM-dd如果年的信息为空请用系统的当前年替换。
不要输出 id、tenantId、createTime、updateTime。
""";
@Override
public Map<String, Object> parseFromOrderInfoByLlm(String orderInfo, String tenantId) {
Map<String, Object> result = new HashMap<>();
try {
if (orderInfo == null || orderInfo.trim().isEmpty()) {
result.put("success", false);
result.put("message", "订货信息不能为空");
return result;
}
if (tenantId == null || tenantId.trim().isEmpty()) {
result.put("success", false);
result.put("message", "tenantId不能为空");
return result;
}
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
result.put("success", false);
result.put("message", "未配置环境变量 DASHSCOPE_API_KEY无法调用大模型");
return result;
}
Generation gen = new Generation();
Message systemMsg = Message.builder()
.role(Role.SYSTEM.getValue())
.content(PARSE_SYSTEM_PROMPT)
.build();
Message userMsg = Message.builder()
.role(Role.USER.getValue())
.content("订货信息如下:\n" + orderInfo.trim())
.build();
GenerationParam param = GenerationParam.builder()
.apiKey(apiKey)
.model(dashScopeChatModel)
.messages(Arrays.asList(systemMsg, userMsg))
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
GenerationResult call = gen.call(param);
String raw = call.getOutput().getChoices().get(0).getMessage().getContent();
if (raw == null || raw.trim().isEmpty()) {
result.put("success", false);
result.put("message", "大模型返回内容为空");
return result;
}
String json = normalizeJson(raw);
LbPurchaseApply entity = objectMapper.readValue(json, LbPurchaseApply.class);
log.debug(
"parseFromOrderInfoByLlmLLM 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);
return result;
} catch (NoApiKeyException e) {
log.error("DashScope API Key 异常", e);
result.put("success", false);
result.put("message", "API密钥异常" + e.getMessage());
return result;
} catch (InputRequiredException e) {
log.error("DashScope 入参异常", e);
result.put("success", false);
result.put("message", "调用大模型入参错误:" + e.getMessage());
return result;
} catch (Exception e) {
log.error("订货信息大模型解析失败", e);
result.put("success", false);
result.put("message", "解析失败:" + e.getMessage());
return result;
}
}
/**
* 试用日期:只要有 {@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(
"trialSpancursor={} 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 "";
}
String trimmed = content.trim();
if (trimmed.startsWith("```")) {
int firstLineBreak = trimmed.indexOf('\n');
int lastFence = trimmed.lastIndexOf("```");
if (firstLineBreak >= 0 && lastFence > firstLineBreak) {
trimmed = trimmed.substring(firstLineBreak + 1, lastFence).trim();
}
}
return trimmed;
}
@Override
public Map<String, Object> add(LbPurchaseApply entity) {
Map<String, Object> result = new HashMap<>();
try {
if (entity.getTenantId() == null || entity.getTenantId().trim().isEmpty()) {
result.put("success", false);
result.put("message", "tenantId不能为空");
return result;
}
if (entity.getApplyUser() == null || entity.getApplyUser().trim().isEmpty()) {
result.put("success", false);
result.put("message", "applyUser不能为空");
return result;
}
if (entity.getApplyPhone() == null || entity.getApplyPhone().trim().isEmpty()) {
result.put("success", false);
result.put("message", "applyPhone不能为空");
return result;
}
if (entity.getApplyDate() == null) {
result.put("success", false);
result.put("message", "applyDate不能为空");
return result;
}
String tenantId = entity.getTenantId().trim();
String phone = entity.getApplyPhone().trim();
entity.setTenantId(tenantId);
entity.setApplyPhone(phone);
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", this.getById(entity.getId()));
}
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "新增异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> update(LbPurchaseApply entity) {
Map<String, Object> result = new HashMap<>();
try {
if (entity.getId() == null) {
result.put("success", false);
result.put("message", "id不能为空");
return result;
}
entity.setUpdateTime(LocalDateTime.now());
boolean ok = this.updateById(entity);
result.put("success", ok);
result.put("message", ok ? "编辑成功" : "编辑失败");
if (ok) {
result.put("data", this.getById(entity.getId()));
}
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "编辑异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> deleteById(Long id) {
Map<String, Object> result = new HashMap<>();
try {
boolean ok = this.removeById(id);
result.put("success", ok);
result.put("message", ok ? "删除成功" : "删除失败");
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "删除异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> pageQuery(Integer current,
Integer size,
String tenantId,
String applyUser,
String applyPhone,
String colleagueName,
String teamLeader,
String teamBigLeader,
LocalDate applyDate) {
Map<String, Object> result = new HashMap<>();
try {
if (current == null || current < 1) {
current = 1;
}
if (size == null || size < 1) {
size = 10;
}
LambdaQueryWrapper<LbPurchaseApply> queryWrapper = new LambdaQueryWrapper<>();
if (tenantId != null && !tenantId.trim().isEmpty()) {
queryWrapper.eq(LbPurchaseApply::getTenantId, tenantId.trim());
}
if (applyUser != null && !applyUser.trim().isEmpty()) {
queryWrapper.like(LbPurchaseApply::getApplyUser, applyUser.trim());
}
if (applyPhone != null && !applyPhone.trim().isEmpty()) {
queryWrapper.like(LbPurchaseApply::getApplyPhone, applyPhone.trim());
}
if (colleagueName != null && !colleagueName.trim().isEmpty()) {
queryWrapper.like(LbPurchaseApply::getColleagueName, colleagueName.trim());
}
if (teamLeader != null && !teamLeader.trim().isEmpty()) {
queryWrapper.like(LbPurchaseApply::getTeamLeader, teamLeader.trim());
}
if (teamBigLeader != null && !teamBigLeader.trim().isEmpty()) {
queryWrapper.like(LbPurchaseApply::getTeamBigLeader, teamBigLeader.trim());
}
if (applyDate != null) {
queryWrapper.eq(LbPurchaseApply::getApplyDate, applyDate);
}
queryWrapper.orderByDesc(LbPurchaseApply::getUpdateTime)
.orderByDesc(LbPurchaseApply::getCreateTime);
Page<LbPurchaseApply> page = this.page(new Page<>(current, size), queryWrapper);
result.put("success", true);
result.put("message", "查询成功");
result.put("data", page.getRecords());
result.put("total", page.getTotal());
result.put("current", page.getCurrent());
result.put("size", page.getSize());
result.put("pages", page.getPages());
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "查询异常:" + e.getMessage());
return result;
}
}
@Override
public Map<String, Object> listNeedPrivilegeRecommenders(LocalDate startDate, LocalDate endDate) {
Map<String, Object> result = new HashMap<>();
try {
if (startDate == null || endDate == null) {
result.put("success", false);
result.put("message", "开始日期和结束日期不能为空");
return result;
}
if (endDate.isBefore(startDate)) {
result.put("success", false);
result.put("message", "结束日期不能早于开始日期");
return result;
}
LambdaQueryWrapper<LbPurchaseApply> purchaseQuery = new LambdaQueryWrapper<>();
purchaseQuery.ge(LbPurchaseApply::getApplyDate, startDate)
.le(LbPurchaseApply::getApplyDate, endDate)
.orderByDesc(LbPurchaseApply::getApplyDate)
.orderByDesc(LbPurchaseApply::getApplyDate) ;
List<LbPurchaseApply> purchaseInfo = this.list(purchaseQuery);
if (purchaseInfo == null || purchaseInfo.isEmpty()) {
result.put("success", true);
result.put("message", "查询成功");
result.put("data", List.of());
result.put("total", 0);
return result;
}
Set<String> applyUsers = new HashSet<>();
for (LbPurchaseApply item : purchaseInfo) {
if (item.getApplyUser() != null && !item.getApplyUser().trim().isEmpty()) {
applyUsers.add(item.getApplyUser().trim());
}
}
if (applyUsers.isEmpty()) {
result.put("success", true);
result.put("message", "查询成功");
result.put("data", List.of());
result.put("total", 0);
return result;
}
LambdaQueryWrapper<LbDailyUserTradeReport> reportQuery = new LambdaQueryWrapper<>();
reportQuery.in(LbDailyUserTradeReport::getNickname, applyUsers)
.select(LbDailyUserTradeReport::getNickname);
List<LbDailyUserTradeReport> matchedReports = lbDailyUserTradeReportMapper.selectList(reportQuery);
Set<String> matchedNicknames = new HashSet<>();
for (LbDailyUserTradeReport report : matchedReports) {
if (report.getNickname() != null && !report.getNickname().trim().isEmpty()) {
matchedNicknames.add(report.getNickname().trim());
}
}
List<LbPurchaseApply> needPrivilegeList = new ArrayList<>();
for (LbPurchaseApply item : purchaseInfo) {
String applyUser = item.getApplyUser() == null ? null : item.getApplyUser().trim();
if (applyUser != null && !applyUser.isEmpty() && matchedNicknames.contains(applyUser)) {
needPrivilegeList.add(item);
}
}
result.put("success", true);
result.put("message", "查询成功");
result.put("data", needPrivilegeList);
result.put("total", needPrivilegeList.size());
result.put("startDate", startDate);
result.put("endDate", endDate);
return result;
} catch (Exception e) {
result.put("success", false);
result.put("message", "查询异常:" + e.getMessage());
return result;
}
}
}