demand: 跟踪预警定时任务demo TMS-收件扫描自动生成,收件网点 应该去 始发网点【

This commit is contained in:
aike 2025-12-17 10:09:45 +08:00
parent b38d5fcfa6
commit accc502ae6
7 changed files with 222 additions and 17 deletions

View File

@ -144,6 +144,28 @@ public class EmisCommonController extends EmisBaseController
@Autowired
private EmisTmsSiteBatchMissMapper emisTmsSiteBatchMissMapper;
/**
* 跟踪预警触发:查询长时间未更新扫描记录的运单并通知相关人员
*/
@GetMapping("/autoTraceWarningTrigger")
public AjaxResult autoTraceWarningTrigger() {
String lockKey = "autoTraceWarningTrigger";
log.info("autoTraceWarningTrigger start");
redissonService.lock(lockKey, 300);
try {
emisWaybillService.autoTraceWarning();
} catch (Exception ex) {
ex.printStackTrace();
log.error("autoTraceWarningTrigger exception:" + ex.getMessage());
} finally {
redissonService.unlock(lockKey);
}
log.info("autoTraceWarningTrigger end");
return AjaxResult.success("发送成功");
}
/**
* 在线支付回调
* @param method

View File

@ -11,8 +11,10 @@
package com.xdadan.erp.emis.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xdadan.erp.emis.domain.EmisSalesmenRel;
import org.apache.ibatis.annotations.Param;
/**
* @ClassName EmisSalesmenRelMapper
* @Description <p> 销售人员关联表 Mapper 接口 </p>
@ -85,4 +87,12 @@ public interface EmisSalesmenRelMapper extends BaseMapper<EmisSalesmenRel>
* @return 结果
*/
public int deleteEmisSalesmenRelByIds(Long[] ids);
/**
* 批量查询有效的销售分享关系
*
* @param salesmenList 销售联系人列表
* @return 关联关系列表
*/
public List<EmisSalesmenRel> selectValidRelBySalesmenList(@Param("salesmenList") List<String> salesmenList);
}

View File

@ -10,6 +10,7 @@
*/
package com.xdadan.erp.emis.mapper;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -398,4 +399,16 @@ public interface EmisWaybillMapper extends BaseMapper<EmisWaybill>
* @return 运单明细列表
*/
List<EmisWaybill> selectMonthlyCustomerShipmentDetail(EmisWaybill emisWaybill);
/**
* 跟踪预警:查询未签收且超过指定时间未有扫描记录的运单(分页)
*
* @param startSendDate 发件开始日期(含)
* @param offset 起始位置
* @param pageSize 每页大小
* @return 运单列表
*/
List<EmisWaybill> selectTraceWarningWaybillList(@Param("startSendDate") Date startSendDate,
@Param("offset") int offset,
@Param("pageSize") int pageSize);
}

View File

@ -322,4 +322,9 @@ public interface IEmisWaybillService extends IDataAuditService
* @return 运单列表
*/
List<EmisWaybill> selectProblemWaybillsForResubmit(EmisWaybill emisWaybill);
/**
* 跟踪预警:查找长时间未更新扫描记录的运单并发送预警通知
*/
void autoTraceWarning();
}

View File

@ -27,16 +27,19 @@ package com.xdadan.erp.emis.service.impl;
import com.xdadan.erp.common.utils.BarcodeUtils;
import com.xdadan.erp.common.utils.DateUtils;
import com.xdadan.erp.common.utils.DictUtils;
import com.xdadan.erp.common.utils.SecurityUtils;
import com.xdadan.erp.common.utils.SecurityUtils;
import com.xdadan.erp.common.utils.NoticeUtils;
import com.xdadan.erp.common.utils.file.FileUploadUtils;
import com.xdadan.erp.common.utils.idgen.UUID;
import com.xdadan.erp.emis.domain.*;
import com.xdadan.erp.emis.domain.enumtype.*;
import com.xdadan.erp.emis.domain.exception.EmisBizError;
import com.xdadan.erp.emis.domain.vo.WaybillOrder;
import com.xdadan.erp.emis.domain.vo.scan.ScanInfo;
import com.xdadan.erp.emis.mapper.*;
import com.xdadan.erp.emis.service.*;
import com.xdadan.erp.emis.domain.*;
import com.xdadan.erp.emis.domain.enumtype.*;
import com.xdadan.erp.emis.domain.exception.EmisBizError;
import com.xdadan.erp.emis.domain.vo.WaybillOrder;
import com.xdadan.erp.emis.domain.vo.scan.ScanInfo;
import com.xdadan.erp.emis.mapper.EmisSalesmenRelMapper;
import com.xdadan.erp.emis.domain.EmisSalesmenRel;
import com.xdadan.erp.emis.mapper.*;
import com.xdadan.erp.emis.service.*;
import com.xdadan.erp.emis.service.print.IXddTplPrint;
import com.xdadan.erp.emis.service.print.tanex.TanexPdfPrinter;
import com.xdadan.erp.emis.service.warehouse.DocUtils;
@ -154,6 +157,9 @@ public class EmisWaybillServiceImpl extends EmisBaseService implements IEmisWayb
@Autowired
private EmisWaybillOtherMapper emisWaybillOtherMapper;
@Autowired
private EmisSalesmenRelMapper emisSalesmenRelMapper;
/**
* 保存或更新运单其他信息副表
* @param emisWaybillSave
@ -1908,7 +1914,7 @@ public class EmisWaybillServiceImpl extends EmisBaseService implements IEmisWayb
scanInfo.setScanData(scanDataList);
try {
emisTmsScanRecordService.scan(scanInfo,dispatchOrSendManCode,emisWaybillSave.getSendSiteCode());
emisTmsScanRecordService.scan(scanInfo,dispatchOrSendManCode,emisWaybillSave.getStartSiteCode());
} catch (Exception e) {
e.printStackTrace();
}
@ -2360,13 +2366,6 @@ public class EmisWaybillServiceImpl extends EmisBaseService implements IEmisWayb
*/
private void calcWayBillCommonInfo(EmisWaybill emisWaybillSave,EmisWaybill oldWaybil,boolean isNew) throws EmisBizError {
// 校验货物信息,不能只包含空格或符号
String goodsInfo = emisWaybillSave.getGoodsInfo();
if (StringUtils.isBlank(goodsInfo) || !Pattern.compile("[\\p{L}\\p{N}]").matcher(goodsInfo).find()) {
// 货物信息不能只包含空格、标点或特殊符号
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR1, "货物名称命名不规范,请检查!");
}
String orderSn = WaybillHelper.genOrderSn();
// 订单号
@ -3416,7 +3415,7 @@ public class EmisWaybillServiceImpl extends EmisBaseService implements IEmisWayb
scanDateItem.put("scanWeight", "0.0");
scanDataList.add(scanDateItem);
scanInfo.setScanData(scanDataList);
emisTmsScanRecordService.scan(scanInfo,dispatchOrSendManCode,emisWaybillSave.getSendSiteCode());
emisTmsScanRecordService.scan(scanInfo,dispatchOrSendManCode,emisWaybillSave.getStartSiteCode());
} catch (Exception e) {
e.printStackTrace();
}
@ -6151,6 +6150,123 @@ public class EmisWaybillServiceImpl extends EmisBaseService implements IEmisWayb
return emisWaybillMapper.selectMasterEmisWaybills(emisWaybill);
}
/**
* 跟踪预警:查找长时间未更新扫描记录的运单并发送预警通知
*/
@Override
public void autoTraceWarning() {
try {
// 2025-12-01 00:00:00 之后的运单
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 2025);
cal.set(Calendar.MONTH, Calendar.DECEMBER);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date startSendDate = cal.getTime();
int pageSize = 1000;
int offset = 0;
while (true) {
List<EmisWaybill> waybillList = emisWaybillMapper.selectTraceWarningWaybillList(startSendDate, offset, pageSize);
if (CollectionUtils.isEmpty(waybillList)) {
break;
}
Date now = new Date();
// 当前批次所有销售联系人,批量查询分享关系
Set<String> batchSalesmenSet = new LinkedHashSet<>();
for (EmisWaybill waybill : waybillList) {
if (waybill != null && !StringUtils.isEmpty(waybill.getSalesmen())) {
batchSalesmenSet.add(waybill.getSalesmen());
}
}
Map<String, List<EmisSalesmenRel>> salesRelMap = new HashMap<>();
if (!batchSalesmenSet.isEmpty()) {
List<EmisSalesmenRel> relAllList = emisSalesmenRelMapper
.selectValidRelBySalesmenList(new ArrayList<>(batchSalesmenSet));
if (!CollectionUtils.isEmpty(relAllList)) {
for (EmisSalesmenRel rel : relAllList) {
if (rel == null || StringUtils.isEmpty(rel.getSalesmen())) {
continue;
}
salesRelMap.computeIfAbsent(rel.getSalesmen(), k -> new ArrayList<>()).add(rel);
}
}
}
for (EmisWaybill waybill : waybillList) {
if (waybill == null || StringUtils.isEmpty(waybill.getBillCode())) {
continue;
}
try {
// 收集需要通知的员工编码:销售联系人、销售支持、销售分享人
Set<String> notifyEmpCodes = new LinkedHashSet<>();
// 销售联系人
if (!StringUtils.isEmpty(waybill.getSalesmen())) {
String salesmen = waybill.getSalesmen();
notifyEmpCodes.add(salesmen);
// 销售分享关系:salesmen -> salesAss(使用当前批次预加载的关系)
List<EmisSalesmenRel> relList = salesRelMap.get(salesmen);
if (!CollectionUtils.isEmpty(relList)) {
for (EmisSalesmenRel rel : relList) {
if (rel == null || StringUtils.isEmpty(rel.getSalesAss())) {
continue;
}
notifyEmpCodes.add(rel.getSalesAss());
}
}
}
// 销售支持(可能为逗号分隔)
if (!StringUtils.isEmpty(waybill.getSalesSupport())) {
String[] supports = waybill.getSalesSupport().split("[,,]");
for (String support : supports) {
if (!StringUtils.isEmpty(support)) {
notifyEmpCodes.add(support.trim());
}
}
}
if (notifyEmpCodes.isEmpty()) {
continue;
}
String empCodeStr = String.join(",", notifyEmpCodes);
String billCode = waybill.getBillCode();
String timeStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, now);
String title = "轨迹异常:运单【" + billCode + "】停留超过3天_" + timeStr;
String content = title;
String url = "/d24/waybillManagement/WaybillList?billCode=" + billCode;
try {
NoticeUtils.outSendNoticeToInner(title, content, url, empCodeStr, 3);
} catch (Exception ex) {
log.error("autoTraceWarning: send notice error, billCode={}, empCodes={}", billCode, empCodeStr, ex);
}
} catch (Exception ex) {
log.error("autoTraceWarning: handle waybill error, billCode={}", waybill.getBillCode(), ex);
}
}
if (waybillList.size() < pageSize) {
break;
}
offset += pageSize;
}
} catch (Exception ex) {
log.error("autoTraceWarning: unexpected error", ex);
}
}
@Override
@Async
public void handleHistoryVirtualWaybill() {

View File

@ -143,4 +143,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{id}
</foreach>
</delete>
<!-- 批量查询有效的销售分享关系 -->
<select id="selectValidRelBySalesmenList" parameterType="java.util.List" resultMap="EmisSalesmenRelResult">
select id, salesmen, sales_ass, remark, bl_open, start_date, end_date, del_flag,
create_by, create_time, update_by, update_time, create_site, update_site
from emis_salesmen_rel
where del_flag = '0'
and bl_open = '1'
and now() <![CDATA[ >= ]]> start_date
and now() <![CDATA[ <= ]]> end_date
and salesmen in
<foreach collection="salesmenList" item="salesmen" open="(" separator="," close=")">
#{salesmen}
</foreach>
</select>
</mapper>

View File

@ -592,6 +592,30 @@
</if>
</select>
<!-- 跟踪预警:查询发件日期大于等于指定日期、未签收且最后一条扫描记录距离当前日期>=3天的运单(分页) -->
<select id="selectTraceWarningWaybillList" resultMap="EmisWaybillResult">
<include refid="selectEmisWaybillVo"/>
where a.del_flag = '0'
and (a.bl_sign is null or a.bl_sign != '1')
and a.order_status != '0'
and a.order_status != '2'
and a.send_date <![CDATA[>=]]> #{startSendDate}
and exists (
select 1
from emis_tms_scan_record sr
where sr.del_flag = '0'
and sr.main_bill_code = a.bill_code
and sr.scan_date = (
select max(scan_date)
from emis_tms_scan_record
where del_flag = '0'
and main_bill_code = a.bill_code
)
and DATEDIFF(curdate(), date(sr.scan_date)) <![CDATA[>=]]> 3
)
limit #{offset}, #{pageSize}
</select>
<select id="selectMasterEmisWaybills" parameterType="EmisWaybill" resultMap="EmisWaybillResult">
<include refid="selectEmisWaybillVo"/>
where a.del_flag = '0'