Merge pull request 'develop' (#246) from develop into master

Reviewed-on: http://git.xdadan.loc/tanex/emis-service/pulls/246
This commit is contained in:
heyu 2025-11-28 10:48:46 +08:00
commit f377b55361
18 changed files with 1717 additions and 32 deletions

View File

@ -28,6 +28,7 @@ import com.xdadan.erp.emis.domain.exception.EmisBizError;
import com.xdadan.erp.emis.domain.stat.BillPayStatInfo;
import com.xdadan.erp.emis.domain.stat.OpenBillStatInfo;
import com.xdadan.erp.emis.domain.vo.AddressParseResult;
import com.xdadan.erp.emis.mapper.EmisTmsSiteBatchMissMapper;
import com.xdadan.erp.emis.service.*;
import com.xdadan.erp.emis.service.kefu.RL7moorService;
import com.xdadan.erp.emis.service.print.IXddTplPrint;
@ -140,6 +141,9 @@ public class EmisCommonController extends EmisBaseController
@Autowired
private IEmisTransLineService emisTransLineService;
@Autowired
private EmisTmsSiteBatchMissMapper emisTmsSiteBatchMissMapper;
/**
* 在线支付回调
* @param method
@ -903,6 +907,42 @@ public class EmisCommonController extends EmisBaseController
return AjaxResult.success("发送成功");
}
/**
* 定时扫描运单货物组批次是否正确录入-V2
*
* @return
*/
@GetMapping("/autoScanSiteBatchV2")
public AjaxResult autoScanSiteBatchV2(String lineCode) {
String statusKey = "autoScanSiteBatchV2_status";
log.info("autoScanSiteBatchV2 start");
// 检查任务是否正在执行
String currentStatus = redissonService.getStr(statusKey);
if ("RUNNING".equals(currentStatus)) {
log.warn("autoScanSiteBatchV2 is already running, request rejected");
return AjaxResult.error("任务正在执行中,请稍后再试");
}
try {
// 设置任务状态为运行中,设置较长的过期时间(30分钟)
// 这样即使异步任务没有正确清除状态,也会在30分钟后自动过期
redissonService.setStr(statusKey, "RUNNING", 1800);
// 异步执行任务
emisTmsSiteBatchMissService.autoScanSiteBatchV2(lineCode);
log.info("autoScanSiteBatchV2 async task started");
return AjaxResult.success("异步任务已启动,请稍后查看执行结果");
} catch (Exception ex) {
ex.printStackTrace();
log.error("autoScanSiteBatchV2 exception:" + ex.getMessage());
// 如果启动失败,清除状态
redissonService.setStr(statusKey, "", 0);
return AjaxResult.error("任务启动失败:" + ex.getMessage());
}
}
/**
* 定时扫描运单货物组批次是否正确录入
@ -929,6 +969,7 @@ public class EmisCommonController extends EmisBaseController
// 如果lineCode为空,查询所有线路的lineCode,去重后循环调用
if (StringUtils.isEmpty(lineCode)) {
log.info("lineCode is empty, querying all line codes");
emisTmsSiteBatchMissMapper.deleteReally(lineCode);
EmisTransLine queryLine = new EmisTransLine();
// queryLine.setStatus(1); // 只查询启用的线路
List<EmisTransLine> lineList = emisTransLineService.selectEmisTransLineList(queryLine);
@ -959,6 +1000,7 @@ public class EmisCommonController extends EmisBaseController
log.info("autoScanSiteBatch async task started for all line codes");
return AjaxResult.success("异步任务已启动,共处理 " + lineCodeSet.size() + " 个线路,请稍后查看执行结果");
} else {
emisTmsSiteBatchMissMapper.deleteReally(lineCode);
// 异步执行任务
emisTmsSiteBatchMissService.autoScanSiteBatch(lineCode);
log.info("autoScanSiteBatch async task started for lineCode: {}", lineCode);

View File

@ -116,6 +116,19 @@ public class EmisTmsSiteBatchController extends EmisBaseController
}
/**
* 根据批次号扫描组批次--V2
*
* @return
*/
@GetMapping("/scanSiteBatchV2")
// @PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatch:scanSiteBatch')")
public AjaxResult scanSiteBatchV2(String batchNo) {
emisTmsSiteBatchService.scanSiteBatchV2(batchNo);
return AjaxResult.success("发送成功");
}
/**
* 批次明细
* @param emisTmsSiteBatch

View File

@ -74,6 +74,24 @@ public class EmisTmsSiteBatchMissController extends EmisBaseController {
}
}
/**
* 查询TMS漏组批次信息列表
*/
@PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatchMiss:listV2')")
@GetMapping("/list/v2")
public TableDataInfo listV2(EmisTmsSiteBatchMiss emisTmsSiteBatchMiss) {
startPage();
setPrivParams(emisTmsSiteBatchMiss);
List<EmisTmsSiteBatchMiss> list = emisTmsSiteBatchMissService.selectEmisTmsSiteBatchMissListV2(emisTmsSiteBatchMiss);
if(emisTmsSiteBatchMiss.getParams().containsKey("blZExport")){
List exportList=getCompressExportList(list);
return getDataTable(exportList);
}else {
return getDataTable(list);
}
}
/**
* 导出TMS漏组批次信息列表
*/
@ -218,4 +236,50 @@ public class EmisTmsSiteBatchMissController extends EmisBaseController {
emisTmsSiteBatchMissService.cancelConfirmBatch(billCodes);
return AjaxResult.success("取消确认成功");
}
/**
* 批量取消确认组批次--v2
*/
@PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatchMiss:cancelConfirmV2')")
@Log(title = "TMS漏组批次信息", businessType = BusinessType.UPDATE)
@PostMapping("/cancelConfirmBatchV2")
public AjaxResult cancelConfirmBatchV2(@RequestBody EmisTmsSiteBatchMiss req) throws EmisBizError {
Object listObj = req.getParams() != null ? req.getParams().get("billCodeList") : null;
if (listObj == null) {
return AjaxResult.error("参数缺失:billCodeList");
}
List<String> billCodes;
if (listObj instanceof List) {
List<?> tmp = (List<?>) listObj;
billCodes = new java.util.ArrayList<>();
for (Object o : tmp) {
if (o != null) {
String s = String.valueOf(o).trim();
if (!s.isEmpty()) {
billCodes.add(s);
}
}
}
} else if (listObj instanceof String) {
String str = ((String) listObj).trim();
if (str.isEmpty()) {
billCodes = java.util.Collections.emptyList();
} else {
String[] arr = str.split(",");
billCodes = new java.util.ArrayList<>(arr.length);
for (String a : arr) {
if (a != null) {
String s = a.trim();
if (!s.isEmpty()) {
billCodes.add(s);
}
}
}
}
} else {
return AjaxResult.error("参数格式错误:billCodeList");
}
emisTmsSiteBatchMissService.cancelConfirmBatchV2(billCodes);
return AjaxResult.success("取消确认成功");
}
}

View File

@ -51,6 +51,26 @@ public class EmisTmsSiteBatchMissStatisticsController extends BaseController {
}
}
/**
* 查询漏组批次统计列表--v2
*/
@PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatchMissStatistics:listV2')")
@GetMapping("/list/v2")
public TableDataInfo listV2(EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics) {
try {
startPage();
List<EmisTmsSiteBatchMissStatistics> list = statisticsService
.selectStatisticsListV2(emisTmsSiteBatchMissStatistics);
return getDataTable(list);
} catch (Exception e) {
log.error("查询漏组批次统计列表失败", e);
TableDataInfo tableDataInfo = getDataTable(new ArrayList<>());
tableDataInfo.setCode(500);
tableDataInfo.setMsg("查询失败:" + e.getMessage());
return tableDataInfo;
}
}
/**
* 导出漏组详细数据(按月份分sheet)
* 优化版本:支持异步处理、流式导出、缓存优化
@ -85,6 +105,40 @@ public class EmisTmsSiteBatchMissStatisticsController extends BaseController {
}
}
/**
* 导出漏组详细数据(按月份分sheet)--V2
* 优化版本:支持异步处理、流式导出、缓存优化
*/
@PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatchMissStatistics:exportDetailV2')")
@Log(title = "漏组详细数据导出", businessType = BusinessType.EXPORT)
@PostMapping("/exportDetailV2")
public void exportDetailV2(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics) {
try {
// 设置响应头,支持流式传输
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
// 异步流式导出,避免内存溢出
statisticsService.exportDetailDataByMonthStreamV2(response, emisTmsSiteBatchMissStatistics);
} catch (Exception e) {
log.error("导出漏组详细数据失败", e);
try {
// 检查响应是否已经开始
if (!response.isCommitted()) {
response.reset();
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"success\":false,\"message\":\"导出失败:" + e.getMessage() + "\"}");
}
} catch (Exception ex) {
log.error("写入错误信息失败", ex);
}
}
}
/**
* 导出漏组批次统计查询接口数据
*/
@ -104,4 +158,24 @@ public class EmisTmsSiteBatchMissStatisticsController extends BaseController {
}
}
}
/**
* 导出漏组批次统计查询接口数据
*/
@PreAuthorize("@ss.hasPermi('emis:emisTmsSiteBatchMissStatistics:exportStatisticsV2')")
@Log(title = "漏组批次统计导出", businessType = BusinessType.EXPORT)
@PostMapping("/exportStatisticsV2")
public void exportStatisticsV2(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics) {
try {
statisticsService.exportStatisticsDataV2(response, emisTmsSiteBatchMissStatistics);
} catch (Exception e) {
log.error("导出漏组批次统计数据失败", e);
try {
response.getWriter().write("导出失败:" + e.getMessage());
} catch (Exception ex) {
log.error("写入错误信息失败", ex);
}
}
}
}

View File

@ -5,7 +5,7 @@ import lombok.Data;
/**
* TMS漏组批次扫描记录对象
*
*
* @author heyu
*/
@Data
@ -36,4 +36,7 @@ public class EmisTmsSiteBatchMissRecord extends BaseEntity {
/** 失败运单号列表 */
private String failedBillCodes;
}
/** 扫描版本 */
private String scanVersion;
}

View File

@ -7,13 +7,13 @@ import org.apache.ibatis.annotations.Param;
/**
* TMS漏组批次信息Mapper接口
*
*
* @author heyu
*/
public interface EmisTmsSiteBatchMissMapper {
/**
* 查询TMS漏组批次信息
*
*
* @param id TMS漏组批次信息主键
* @return TMS漏组批次信息
*/
@ -21,15 +21,23 @@ public interface EmisTmsSiteBatchMissMapper {
/**
* 查询TMS漏组批次信息列表
*
*
* @param emisTmsSiteBatchMiss TMS漏组批次信息
* @return TMS漏组批次信息集合
*/
public List<EmisTmsSiteBatchMiss> selectEmisTmsSiteBatchMissList(EmisTmsSiteBatchMiss emisTmsSiteBatchMiss);
/**
* 查询TMS漏组批次信息列表_v2
*
* @param emisTmsSiteBatchMiss TMS漏组批次信息
* @return TMS漏组批次信息集合
*/
public List<EmisTmsSiteBatchMiss> selectEmisTmsSiteBatchMissListV2(EmisTmsSiteBatchMiss emisTmsSiteBatchMiss);
/**
* 新增TMS漏组批次信息
*
*
* @param emisTmsSiteBatchMiss TMS漏组批次信息
* @return 结果
*/
@ -37,7 +45,7 @@ public interface EmisTmsSiteBatchMissMapper {
/**
* 修改TMS漏组批次信息
*
*
* @param emisTmsSiteBatchMiss TMS漏组批次信息
* @return 结果
*/
@ -45,7 +53,7 @@ public interface EmisTmsSiteBatchMissMapper {
/**
* 删除TMS漏组批次信息
*
*
* @param id TMS漏组批次信息主键
* @return 结果
*/
@ -53,7 +61,7 @@ public interface EmisTmsSiteBatchMissMapper {
/**
* 批量删除TMS漏组批次信息
*
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
@ -61,12 +69,20 @@ public interface EmisTmsSiteBatchMissMapper {
/**
* 批量插入TMS漏组批次信息
*
*
* @param list TMS漏组批次信息列表
* @return 结果
*/
public int batchInsertEmisTmsSiteBatchMiss(List<EmisTmsSiteBatchMiss> list);
/**
* 批量插入TMS漏组批次信息--V2
*
* @param list TMS漏组批次信息列表
* @return 结果
*/
public int batchInsertEmisTmsSiteBatchMissV2(List<EmisTmsSiteBatchMiss> list);
/**
*
* @param lineCode
@ -87,10 +103,19 @@ public interface EmisTmsSiteBatchMissMapper {
*/
void deleteByBillCodes(@Param("billCodes") List<String> billCodes);
/**
*
* @param billCodes
*/
void deleteByBillCodesV2(@Param("billCodes") List<String> billCodes);
void deleteReally(String lineCode);
void deleteReallyV2(String lineCode);
/**
* 根据运单号批量查询TMS漏组批次信息
*/
List<EmisWaybill> selectByBillCodes(@Param("billCodes") List<String> billCodes);
}
}

View File

@ -23,6 +23,15 @@ public interface EmisTmsSiteBatchMissStatisticsMapper {
List<EmisTmsSiteBatchMissStatistics> selectStatisticsList(
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 查询漏组批次统计列表--V2
*
* @param emisTmsSiteBatchMissStatistics 查询条件对象
* @return 统计结果列表
*/
List<EmisTmsSiteBatchMissStatistics> selectStatisticsListV2(
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 查询漏组详细数据列表(按月份分组)
*
@ -31,6 +40,14 @@ public interface EmisTmsSiteBatchMissStatisticsMapper {
*/
List<EmisTmsSiteBatchMiss> selectDetailDataByMonth(EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 查询漏组详细数据列表(按月份分组)
*
* @param emisTmsSiteBatchMissStatistics 查询条件
* @return 详细数据列表
*/
List<EmisTmsSiteBatchMiss> selectDetailDataByMonthV2(EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 分页查询漏组详细数据列表(按月份分组)- 性能优化版本
*
@ -51,4 +68,12 @@ public interface EmisTmsSiteBatchMissStatisticsMapper {
* @return 总记录数
*/
long countDetailDataByMonth(EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 统计漏组详细数据总数
*
* @param emisTmsSiteBatchMissStatistics 查询条件
* @return 总记录数
*/
long countDetailDataByMonthV2(EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
}

View File

@ -4669,7 +4669,7 @@ public class EmisBaseService {
// 保存扫描记录
saveRecord(totalProcessed, successCount, failCount, processTime, failedBillCodes, totalBatchStatusRecords,
totalMissRecords);
totalMissRecords,"1");
log.info(
"Auto scan completed. Total processed: {}, Success: {}, Failed: {}, Time taken: {}ms, Batch status records: {}, Miss records: {}",
@ -4744,7 +4744,7 @@ public class EmisBaseService {
}
public void saveRecord(int totalProcessed, int successCount, int failCount, long processTime,
List<String> failedBillCodes, int totalBatchStatusRecords, int totalMissRecords) {
List<String> failedBillCodes, int totalBatchStatusRecords, int totalMissRecords,String scanVersion) {
EmisTmsSiteBatchMissRecord record = new EmisTmsSiteBatchMissRecord();
record.setTotalProcessed(totalProcessed);
record.setSuccessCount(successCount);
@ -4757,6 +4757,7 @@ public class EmisBaseService {
record.setCreateBy("system");
record.setCreateTime(new Date());
record.setCreateSite("88888");
record.setScanVersion(scanVersion);
emisTmsSiteBatchMissRecordMapper.insertEmisTmsSiteBatchMissRecord(record);
}

View File

@ -2,6 +2,7 @@ package com.xdadan.erp.emis.service;
import java.util.List;
import com.xdadan.erp.emis.domain.EmisTmsSiteBatchMiss;
import com.xdadan.erp.emis.domain.EmisWaybill;
import com.xdadan.erp.emis.domain.exception.EmisBizError;
/**
@ -26,6 +27,14 @@ public interface IEmisTmsSiteBatchMissService {
*/
public List<EmisTmsSiteBatchMiss> selectEmisTmsSiteBatchMissList(EmisTmsSiteBatchMiss emisTmsSiteBatchMiss);
/**
* 查询TMS漏组批次信息列表_v2
*
* @param emisTmsSiteBatchMiss TMS漏组批次信息
* @return TMS漏组批次信息集合
*/
public List<EmisTmsSiteBatchMiss> selectEmisTmsSiteBatchMissListV2(EmisTmsSiteBatchMiss emisTmsSiteBatchMiss);
/**
* 新增TMS漏组批次信息
*
@ -90,4 +99,21 @@ public interface IEmisTmsSiteBatchMissService {
* @param billCodes 运单号列表
*/
void cancelConfirmBatch(List<String> billCodes) throws EmisBizError;
/**
* 批量取消确认组批次--V2:
* 1) 根据运单号列表删除运单组批次状态数据
* 2) 重新扫描运单组批次状态
*
* @param billCodes 运单号列表
*/
void cancelConfirmBatchV2(List<String> billCodes) throws EmisBizError;
/**
* 定时扫描运单货物组批次是否正确录入-V2
*
*/
void autoScanSiteBatchV2(String lineCode);
void scanSiteBatchByWaybillsV2(List<EmisWaybill> emisWaybills);
}

View File

@ -24,6 +24,15 @@ public interface IEmisTmsSiteBatchMissStatisticsService {
List<EmisTmsSiteBatchMissStatistics> selectStatisticsList(
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 查询漏组批次统计列表--V2
*
* @param emisTmsSiteBatchMissStatistics 查询条件
* @return 统计结果列表
*/
List<EmisTmsSiteBatchMissStatistics> selectStatisticsListV2(
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 流式导出漏组详细数据(按月份分sheet)- 性能优化版本
* 支持大数据量导出,避免内存溢出
@ -34,6 +43,16 @@ public interface IEmisTmsSiteBatchMissStatisticsService {
void exportDetailDataByMonthStream(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 流式导出漏组详细数据(按月份分sheet)- 性能优化版本
* 支持大数据量导出,避免内存溢出
*
* @param response HTTP响应
* @param emisTmsSiteBatchMissStatistics 查询条件
*/
void exportDetailDataByMonthStreamV2(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 导出漏组批次统计查询接口数据
*
@ -42,4 +61,13 @@ public interface IEmisTmsSiteBatchMissStatisticsService {
*/
void exportStatisticsData(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
/**
* 导出漏组批次统计查询接口数据
*
* @param response HTTP响应
* @param emisTmsSiteBatchMissStatistics 查询条件
*/
void exportStatisticsDataV2(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics);
}

View File

@ -114,6 +114,13 @@ public interface IEmisTmsSiteBatchService extends IDataAuditService
*/
void scanSiteBatch(String batchNo);
/**
* 根据批次号扫描组批次--V2
*
* @param batchNo
*/
void scanSiteBatchV2(String batchNo);
/**
*
* @param emisTmsSiteBatch

View File

@ -20,6 +20,7 @@ import com.xdadan.erp.emis.utils.WaybillHelper;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@ -137,6 +138,31 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
return list;
}
/**
* 查询TMS漏组批次信息列表_v2
*
* @param emisTmsSiteBatchMiss TMS漏组批次信息
* @return TMS漏组批次信息
*/
@Override
public List<EmisTmsSiteBatchMiss> selectEmisTmsSiteBatchMissListV2(EmisTmsSiteBatchMiss emisTmsSiteBatchMiss) {
log.info("Selecting TMS site batch miss list with params: {}", emisTmsSiteBatchMiss);
List<EmisTmsSiteBatchMiss> list = null;
// 如果单号不为空,则去除其他参数
if (!StringUtils.isEmpty(emisTmsSiteBatchMiss.getBillCode())) {
emisTmsSiteBatchMiss.setBillCode(WaybillHelper.formatQueryValue(emisTmsSiteBatchMiss.getBillCode()));
String[] billCodeSortList = emisTmsSiteBatchMiss.getBillCode().split(",");
if (billCodeSortList.length > 1) {
emisTmsSiteBatchMiss.getParams().put("billCodeSortList", Arrays.asList(billCodeSortList));
}
list = emisTmsSiteBatchMissMapper.selectEmisTmsSiteBatchMissListV2(emisTmsSiteBatchMiss);
} else {
list = emisTmsSiteBatchMissMapper.selectEmisTmsSiteBatchMissListV2(emisTmsSiteBatchMiss);
}
return list;
}
/**
* 新增TMS漏组批次信息
*
@ -208,6 +234,7 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
// 1) 逻辑删除漏组批次数据
emisTmsSiteBatchMissMapper.deleteByBillCodes(billCodes);
emisTmsSiteBatchMissMapper.deleteByBillCodesV2(billCodes);
// 2) 新增运单组批次状态 site_batch_status=1(幂等:insert ignore)
Date now = new Date();
@ -249,6 +276,29 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
// 3) 调用scanSiteBatchByWaybills重新扫描
if (!waybills.isEmpty()) {
this.scanSiteBatchByWaybills(waybills);
this.scanSiteBatchByWaybillsV2(waybills);
}
}
/**
* 批量取消确认组批次--V2
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelConfirmBatchV2(List<String> billCodes) throws EmisBizError {
if (billCodes == null || billCodes.isEmpty()) {
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR, "运单号不能为空");
}
// 1) 彻底删除运单组批次状态数据
emisWaybillBatchStatusMapper.deleteByBillCodes(billCodes);
// 2) 批量查询运单信息
List<EmisWaybill> waybills = emisWaybillMapper.selectEmisWaybillListByBillCodes(billCodes);
// 3) 调用scanSiteBatchByWaybills重新扫描
if (!waybills.isEmpty()) {
this.scanSiteBatchByWaybillsV2(waybills);
}
}
@ -257,7 +307,6 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
public void autoScanSiteBatch(String lineCode) {
ExecutorService executorService = null;
try {
emisTmsSiteBatchMissMapper.deleteReally(lineCode);
long startTime = System.currentTimeMillis();
int pageSize = 1000;
int offset = 0;
@ -272,6 +321,20 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
List<EmisTransPlanRule> treeRules = this.selectEmisTransPlanRuleTree(lineCode);
if (treeRules.isEmpty()) {
log.info("No trans plan rules found, skip auto scan");
List<EmisWaybill> waybills = emisWaybillMapper.selectWaybillListBySiteBatchStatus(0, 100000,
lineCode);
if (waybills.isEmpty()) {
return;
}
List<EmisTmsSiteBatchMiss> missRecords = waybills.stream().map(waybill -> {
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason("当前线路没有创建对应的线路规划");
miss.setMissType("1");
miss.setLineCode(waybill.getTransLineType());
miss.setProductType(waybill.getProductType());
return miss;
}).collect(Collectors.toList());
emisTmsSiteBatchMissMapper.batchInsertEmisTmsSiteBatchMiss(missRecords);
return;
}
@ -282,7 +345,7 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
// 创建线程池
int processors = Runtime.getRuntime().availableProcessors() ;
executorService = new ThreadPoolExecutor(
processors/2, // 核心线程数
1, // 核心线程数
processors, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
@ -335,7 +398,7 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
batches.size(), batchSize, allWaybills.size());
// 复用现有线程池
List<Future<ProcessResult>> processFutures = new ArrayList<>();
List<Future<EmisBaseService.ProcessResult>> processFutures = new ArrayList<>();
for (List<EmisWaybill> batch : batches) {
processFutures.add(executorService.submit(() -> processWaybillBatch(batch, transPlanRulesByLineCode)));
@ -346,9 +409,9 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
List<EmisTmsSiteBatchMiss> allMissRecords = new ArrayList<>();
int timeoutMinutes = 10; // 10分钟超时
for (Future<ProcessResult> future : processFutures) {
for (Future<EmisBaseService.ProcessResult> future : processFutures) {
try {
ProcessResult result = future.get(timeoutMinutes, TimeUnit.MINUTES);
EmisBaseService.ProcessResult result = future.get(timeoutMinutes, TimeUnit.MINUTES);
successCount += result.getSuccessCount();
failCount += result.getFailCount();
failedBillCodes.addAll(result.getFailedBillCodes());
@ -406,7 +469,7 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
// 保存扫描记录
saveRecord(totalProcessed, successCount, failCount, processTime, failedBillCodes, totalBatchStatusRecords,
totalMissRecords);
totalMissRecords,"1");
log.info(
"Auto scan completed. Total processed: {}, Success: {}, Failed: {}, Time taken: {}ms, Batch status records: {}, Miss records: {}",
@ -451,4 +514,718 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
return tree;
}
public List<EmisTransPlanRule> selectEmisTransPlanRuleTreeV2(String lineCode) {
EmisTransPlanRule emisTransPlanRule = new EmisTransPlanRule();
emisTransPlanRule.setLineCode(lineCode);
List<EmisTransPlanRule> allRules = emisTransPlanRuleMapper.selectRulesForAllScan(emisTransPlanRule);
// List<EmisTransPlanRule> tree = buildTree(allRules, new EmisTransPlanRule());
return allRules;
}
@Async
@Override
public void autoScanSiteBatchV2(String lineCode) {
ExecutorService executorService = null;
try {
emisTmsSiteBatchMissMapper.deleteReallyV2(lineCode);
long startTime = System.currentTimeMillis();
int pageSize = 1000;
int offset = 0;
int totalProcessed = 0;
int successCount = 0;
int failCount = 0;
int totalBatchStatusRecords = 0;
int totalMissRecords = 0;
List<String> failedBillCodes = new ArrayList<>();
// 获取所有线路规划
List<EmisTransPlanRule> treeRules = this.selectEmisTransPlanRuleTreeV2(lineCode);
if (treeRules.isEmpty()) {
log.info("No trans plan rules found, skip auto scan v2");
return;
}
// 按线路编码分组
Map<String, List<EmisTransPlanRule>> transPlanRulesByLineCode = treeRules.stream()
.collect(Collectors.groupingBy(EmisTransPlanRule::getLineCode));
// 创建线程池
int processors = Runtime.getRuntime().availableProcessors() ;
executorService = new ThreadPoolExecutor(
1, // 核心线程数
processors, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(), // 工作队列
r -> {
Thread t = new Thread(r);
t.setName("emis-scan-" + t.getId());
t.setDaemon(false);
return t;
} // 线程工厂
);
List<Future<List<EmisWaybill>>> futures = new ArrayList<>();
// 先查询所有数据
while (true) {
List<EmisWaybill> waybills = emisWaybillMapper.selectWaybillListBySiteBatchStatus(offset, pageSize,
lineCode);
if (waybills.isEmpty()) {
break;
}
futures.add(executorService.submit(() -> waybills));
offset += pageSize;
}
// 等待所有查询完成并收集结果
List<EmisWaybill> allWaybills = new ArrayList<>();
for (Future<List<EmisWaybill>> future : futures) {
try {
List<EmisWaybill> waybills = future.get();
allWaybills.addAll(waybills);
totalProcessed += waybills.size();
} catch (Exception e) {
log.error("Error getting waybill results", e);
}
}
int batchSize = 1000;
List<List<EmisWaybill>> batches = new ArrayList<>();
for (int i = 0; i < allWaybills.size(); i += batchSize) {
int end = Math.min(i + batchSize, allWaybills.size());
batches.add(allWaybills.subList(i, end));
}
if (CollectionUtil.isEmpty(batches)) {
log.info("No waybills to process, skip batch processing");
return;
}
log.info("Processing {} batches with batch size: {}, total waybills: {}",
batches.size(), batchSize, allWaybills.size());
// 复用现有线程池
List<Future<ProcessResult>> processFutures = new ArrayList<>();
for (List<EmisWaybill> batch : batches) {
processFutures.add(executorService.submit(() -> processWaybillBatchV2(batch, transPlanRulesByLineCode)));
}
// 添加超时机制,避免长时间阻塞
List<EmisWaybillBatchStatus> allBatchStatus = new ArrayList<>();
List<EmisTmsSiteBatchMiss> allMissRecords = new ArrayList<>();
int timeoutMinutes = 10; // 10分钟超时
for (Future<ProcessResult> future : processFutures) {
try {
ProcessResult result = future.get(timeoutMinutes, TimeUnit.MINUTES);
successCount += result.getSuccessCount();
failCount += result.getFailCount();
failedBillCodes.addAll(result.getFailedBillCodes());
allBatchStatus.addAll(result.getBatchStatusList());
allMissRecords.addAll(result.getMissRecords());
} catch (TimeoutException e) {
log.error("Batch processing timeout after {} minutes", timeoutMinutes);
future.cancel(true); // 取消超时任务
} catch (Exception e) {
log.error("Error processing batch", e);
}
}
// 全局去重
Map<String, EmisWaybillBatchStatus> batchStatusMap = allBatchStatus.stream()
.collect(Collectors.toMap(
EmisWaybillBatchStatus::getBillCode,
v -> v,
(existing, replacement) -> existing));
List<EmisWaybillBatchStatus> uniqueBatchStatus = new ArrayList<>(batchStatusMap.values());
Map<String, EmisTmsSiteBatchMiss> missMap = allMissRecords.stream()
.collect(Collectors.toMap(
r -> r.getBillCode() + "_" + r.getMissReason(),
v -> v,
(existing, replacement) -> existing));
List<EmisTmsSiteBatchMiss> uniqueMissRecords = new ArrayList<>(missMap.values());
if (!uniqueBatchStatus.isEmpty()) {
filterBatchStatusList(uniqueBatchStatus, uniqueMissRecords);
}
// 批量插入 - 每次插入5000条
if (!uniqueBatchStatus.isEmpty()) {
int insertBatchSize = 5000;
for (int i = 0; i < uniqueBatchStatus.size(); i += insertBatchSize) {
int end = Math.min(i + insertBatchSize, uniqueBatchStatus.size());
List<EmisWaybillBatchStatus> batch = uniqueBatchStatus.subList(i, end);
emisWaybillBatchStatusMapper.batchInsertEmisWaybillBatchStatus(batch);
totalBatchStatusRecords += batch.size();
}
}
if (!uniqueMissRecords.isEmpty()) {
int insertBatchSize = 5000;
for (int i = 0; i < uniqueMissRecords.size(); i += insertBatchSize) {
int end = Math.min(i + insertBatchSize, uniqueMissRecords.size());
List<EmisTmsSiteBatchMiss> batch = uniqueMissRecords.subList(i, end);
emisTmsSiteBatchMissMapper.batchInsertEmisTmsSiteBatchMissV2(batch);
totalMissRecords += batch.size();
}
}
long endTime = System.currentTimeMillis();
long processTime = endTime - startTime;
// 保存扫描记录
saveRecord(totalProcessed, successCount, failCount, processTime, failedBillCodes, totalBatchStatusRecords,
totalMissRecords,"2");
log.info(
"Auto scan v2 completed. Total processed: {}, Success: {}, Failed: {}, Time taken: {}ms, Batch status records: {}, Miss records: {}",
totalProcessed, successCount, failCount, processTime,
totalBatchStatusRecords, totalMissRecords);
if (CollectionUtil.isNotEmpty(failedBillCodes)) {
log.warn("Failed bill codes: {}", failedBillCodes);
}
} catch (Exception e) {
log.error("Error in autoScanSiteBatch v2", e);
} finally {
// 确保线程池资源释放
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdown();
try {
if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
log.warn("Thread pool did not terminate gracefully, forcing shutdown");
executorService.shutdownNow();
}
} catch (InterruptedException e) {
log.error("Thread pool shutdown interrupted", e);
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
// 清除任务状态
try {
String statusKey = "autoScanSiteBatchV2_status";
redissonService.setStr(statusKey, "", 0);
log.info("autoScanSiteBatch v2 task status cleared");
} catch (Exception e) {
log.error("Failed to clear autoScanSiteBatch status", e);
}
}
}
public void scanSiteBatchByWaybillsV2(List<EmisWaybill> emisWaybills) {
try {
if (CollectionUtil.isEmpty(emisWaybills)) {
return;
}
List<String> billCodes = emisWaybills.stream().map(EmisWaybill::getBillCode).collect(Collectors.toList());
if (CollectionUtil.isEmpty(billCodes)) {
return;
}
List<String> lineCodes = emisWaybills.stream().map(EmisWaybill::getTransLineType).distinct()
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(lineCodes)) {
return;
}
emisTmsSiteBatchMissMapper.deleteByBillCodesV2(billCodes);
long startTime = System.currentTimeMillis();
int totalProcessed = emisWaybills.size();
int successCount = 0;
int failCount = 0;
int totalBatchStatusRecords = 0;
int totalMissRecords = 0;
List<String> failedBillCodes = new ArrayList<>();
// 获取所有线路规划
List<EmisTransPlanRule> treeRules = this.selectEmisTransPlanRuleTree(lineCodes);
if (treeRules.isEmpty()) {
log.info("No trans plan rules found, skip auto scan v2");
return;
}
// 按线路编码分组
Map<String, List<EmisTransPlanRule>> transPlanRulesByLineCode = treeRules.stream()
.collect(Collectors.groupingBy(EmisTransPlanRule::getLineCode));
// 直接处理运单批次
ProcessResult result = processWaybillBatchV2(emisWaybills, transPlanRulesByLineCode);
successCount += result.getSuccessCount();
failCount += result.getFailCount();
failedBillCodes.addAll(result.getFailedBillCodes());
// 批量插入运单组批次状态记录
List<EmisWaybillBatchStatus> batchStatusList = result.getBatchStatusList();
List<EmisTmsSiteBatchMiss> missRecords = result.getMissRecords();
if (!batchStatusList.isEmpty()) {
filterBatchStatusList(batchStatusList, missRecords);
}
// 批量插入运单组批次状态记录
if (!batchStatusList.isEmpty()) {
emisWaybillBatchStatusMapper.batchInsertEmisWaybillBatchStatus(result.getBatchStatusList());
totalBatchStatusRecords += result.getBatchStatusList().size();
}
// 批量保存漏组记录
if (!missRecords.isEmpty()) {
List<EmisTmsSiteBatchMiss> filteredRecords = missRecords.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
record -> record.getBillCode() + "_" + record.getMissReason(),
record -> record,
(existing, replacement) -> existing),
map -> new ArrayList<>(map.values())));
emisTmsSiteBatchMissMapper.batchInsertEmisTmsSiteBatchMissV2(filteredRecords);
totalMissRecords += filteredRecords.size();
}
long endTime = System.currentTimeMillis();
long processTime = endTime - startTime;
// 保存扫描记录
saveRecord(totalProcessed, successCount, failCount, processTime, failedBillCodes, totalBatchStatusRecords,
totalMissRecords,"2");
log.info(
"Auto scan V2 completed. Total processed: {}, Success: {}, Failed: {}, Time taken: {}ms, Batch status records: {}, Miss records: {}",
totalProcessed, successCount, failCount, processTime,
totalBatchStatusRecords, totalMissRecords);
if (CollectionUtil.isNotEmpty(failedBillCodes)) {
log.warn("Failed bill codes: {}", failedBillCodes);
}
} catch (Exception e) {
log.error("Error in autoScanSiteBatch V2", e);
}
}
public static class ProcessResult {
private final int successCount;
private final int failCount;
private final List<String> failedBillCodes;
private final List<EmisWaybillBatchStatus> batchStatusList;
private final List<EmisTmsSiteBatchMiss> missRecords;
public ProcessResult(int successCount, int failCount, List<String> failedBillCodes,
List<EmisWaybillBatchStatus> batchStatusList, List<EmisTmsSiteBatchMiss> missRecords) {
this.successCount = successCount;
this.failCount = failCount;
this.failedBillCodes = failedBillCodes;
this.batchStatusList = batchStatusList;
this.missRecords = missRecords;
}
public int getSuccessCount() {
return successCount;
}
public int getFailCount() {
return failCount;
}
public List<String> getFailedBillCodes() {
return failedBillCodes;
}
public List<EmisWaybillBatchStatus> getBatchStatusList() {
return batchStatusList;
}
public List<EmisTmsSiteBatchMiss> getMissRecords() {
return missRecords;
}
}
public ProcessResult processWaybillBatchV2(List<EmisWaybill> waybills,
Map<String, List<EmisTransPlanRule>> transPlanRulesByLineCode) {
int successCount = 0;
int failCount = 0;
List<String> failedBillCodes = new ArrayList<>();
List<EmisTmsSiteBatchMiss> missRecords = new ArrayList<>();
List<EmisWaybillBatchStatus> batchStatusList = new ArrayList<>();
// 获取所有运单号
List<String> billCodes = waybills.stream()
.map(EmisWaybill::getBillCode)
.collect(Collectors.toList());
// 一次性查询所有运单对应的组批次信息
List<EmisTmsSiteBatch> siteBatches = emisTmsSiteBatchMapper
.selectEmisTmsSiteBatchListByBillCodes(billCodes);
// 按运单号分组
Map<String, List<EmisTmsSiteBatch>> siteBatchesByBillCode = new HashMap<>();
for (EmisTmsSiteBatch batch : siteBatches) {
if (batch.getBatchDtlList() != null) {
for (EmisTmsSiteBatchDtl dtl : batch.getBatchDtlList()) {
siteBatchesByBillCode.computeIfAbsent(dtl.getBillCode(), k -> new ArrayList<>()).add(batch);
}
}
}
for (EmisWaybill waybill : waybills) {
try {
// 获取运单对应的线路规划
List<EmisTransPlanRule> ruleListByTransLineType = transPlanRulesByLineCode
.get(waybill.getTransLineType());
if (CollectionUtil.isEmpty(ruleListByTransLineType)) {
// 创建漏组记录
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason("当前线路没有创建对应的线路规划");
miss.setMissType("1");
miss.setLineCode(waybill.getTransLineType());
miss.setProductType(waybill.getProductType());
missRecords.add(miss);
successCount++;
continue;
}
List<EmisTransPlanRule> ruleListByProductType = ruleListByTransLineType.stream()
.filter(i -> StringUtils.isNotBlank(i.getProductType())
&& i.getProductType().contains(waybill.getProductType()))
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(ruleListByProductType)) {
// 创建漏组记录
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason("当前线路没有创建对应的产品类型");
miss.setMissType("1");
miss.setLineCode(waybill.getTransLineType());
miss.setProductType(waybill.getProductType());
missRecords.add(miss);
successCount++;
continue;
}
// 获取运单对应的组批次信息
List<EmisTmsSiteBatch> waybillBatches = siteBatchesByBillCode.get(waybill.getBillCode());
if (CollectionUtil.isEmpty(waybillBatches)) {
// 创建漏组记录
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason(waybill.getStartSiteName() +" 尚未完成组批次,请尽快完成,避免影响下一程同事组批次进度" );
miss.setLineCode(waybill.getTransLineType());
miss.setProductType(waybill.getProductType());
miss.setStartSiteCode(waybill.getStartSiteCode());
miss.setNextSiteCode("");
miss.setMissType("0");
missRecords.add(miss);
successCount++;
continue;
}
// 合并相同起始网点和下一网点的批次
Map<String, EmisTmsSiteBatch> mergedBatches = new HashMap<>();
int status2Index = 0;
for (EmisTmsSiteBatch batch : waybillBatches) {
if (StringUtils.isAnyBlank(batch.getStartSiteCode(), batch.getNextSiteCode(),
batch.getSupplierCode())) {
continue;
}
// 检查明细列表中是否有waybillBatchStatus=2的记录,如果有则不合并
boolean hasWaybillBatchStatus2 = false;
if (batch.getBatchDtlList() != null && !batch.getBatchDtlList().isEmpty()) {
for (EmisTmsSiteBatchDtl dtl : batch.getBatchDtlList()) {
if ("2".equals(dtl.getWaybillBatchStatus())||"1".equals(dtl.getWaybillBatchStatus())) {
hasWaybillBatchStatus2 = true;
break;
}
}
}
// 如果有waybillBatchStatus=2的记录,不合并supplierCode,但添加到mergedBatches
if (hasWaybillBatchStatus2) {
String specialKey = batch.getStartSiteCode() + "_" + batch.getNextSiteCode() + "_S2_"
+ (status2Index++);
EmisTmsSiteBatch newBatch = new EmisTmsSiteBatch();
BeanUtils.copyProperties(batch, newBatch);
Set<String> supplierSet = new TreeSet<>();
if (StringUtils.isNotBlank(batch.getSupplierCode())) {
supplierSet.addAll(Arrays.asList(batch.getSupplierCode().split(",")));
}
newBatch.setSupplierCode(String.join(",", supplierSet));
mergedBatches.put(specialKey, newBatch);
continue;
}
String key = batch.getStartSiteCode() + "_" + batch.getNextSiteCode();
if (mergedBatches.containsKey(key)) {
EmisTmsSiteBatch existingBatch = mergedBatches.get(key);
// 合并supplierCode,去重、排序
Set<String> supplierSet = new TreeSet<>();
if (StringUtils.isNotBlank(existingBatch.getSupplierCode())) {
supplierSet.addAll(Arrays.asList(existingBatch.getSupplierCode().split(",")));
}
if (StringUtils.isNotBlank(batch.getSupplierCode())) {
supplierSet.addAll(Arrays.asList(batch.getSupplierCode().split(",")));
}
// 新建一个新的Batch对象,复制属性,保证线程安全
EmisTmsSiteBatch newBatch = new EmisTmsSiteBatch();
BeanUtils.copyProperties(existingBatch, newBatch);
newBatch.setSupplierCode(String.join(",", supplierSet));
mergedBatches.put(key, newBatch);
} else {
// 新建对象,避免外部引用被修改
EmisTmsSiteBatch newBatch = new EmisTmsSiteBatch();
BeanUtils.copyProperties(batch, newBatch);
Set<String> supplierSet = new TreeSet<>();
if (StringUtils.isNotBlank(batch.getSupplierCode())) {
supplierSet.addAll(Arrays.asList(batch.getSupplierCode().split(",")));
}
newBatch.setSupplierCode(String.join(",", supplierSet));
mergedBatches.put(key, newBatch);
}
}
waybillBatches = new ArrayList<>(mergedBatches.values());
// 检查运单是否匹配线路规划树
String startSiteCode = StringUtil.isBlank(waybill.getStartSiteCode()) ? waybill.getSendSiteCode() : waybill.getStartSiteCode();
boolean isAnyBranchMatched = checkBranchMatchV2(ruleListByProductType, waybillBatches, waybill,
missRecords,startSiteCode);
if (isAnyBranchMatched) {
// 创建批次状态记录
EmisWaybillBatchStatus batchStatus = new EmisWaybillBatchStatus();
batchStatus.setBillCode(waybill.getBillCode());
batchStatus.setSiteBatchStatus("1"); // 1表示匹配成功
// batchStatus.setBlManual("1");
Date now = new Date();
batchStatus.setCreateBy("system");
batchStatus.setCreateTime(now);
batchStatus.setCreateSite("88888");
batchStatusList.add(batchStatus);
}
successCount++;
} catch (Exception e) {
log.error("Failed to process waybill: {}", waybill.getBillCode(), e);
failCount++;
failedBillCodes.add(waybill.getBillCode());
}
}
return new ProcessResult(successCount, failCount, failedBillCodes, batchStatusList,
missRecords);
}
private EmisTmsSiteBatchMiss createMissRecord(EmisWaybill waybill) {
EmisTmsSiteBatchMiss miss = new EmisTmsSiteBatchMiss();
miss.setBillCode(waybill.getBillCode());
Date now = new Date();
miss.setCreateBy("system");
miss.setCreateTime(now);
miss.setCreateSite("88888");
miss.setStartSiteCode(waybill.getStartSiteCode());
miss.setNextSiteCode(waybill.getDestinationCode());
return miss;
}
/**
* 检查一个分支是否完全匹配(按起始网点递归向下遍历)
*
* @param ruleList 线路规划规则列表(同一线路+产品类型)
* @param waybillBatches 运单批次列表(已按起始/下一网点合并)
* @param waybill 运单
* @param missRecords 漏组记录集合
* @param startSiteCode 当前起始网点
* @return 是否存在任意一条完全匹配的路径
*/
private boolean checkBranchMatchV2(List<EmisTransPlanRule> ruleList, List<EmisTmsSiteBatch> waybillBatches,
EmisWaybill waybill, List<EmisTmsSiteBatchMiss> missRecords, String startSiteCode) {
if (StringUtils.isBlank(startSiteCode)) {
return false;
}
// 当前起始网点下的规则
List<EmisTransPlanRule> currentRules = ruleList.stream()
.filter(rule -> StringUtils.equals(rule.getStartSiteCode(), startSiteCode))
.collect(Collectors.toList());
if (CollectionUtil.isEmpty(currentRules)) {
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason(waybill.getStartSiteName() +" 尚未完成组批次,请尽快完成,避免影响下一程同事组批次进度" );
miss.setLineCode(waybill.getTransLineType());
miss.setProductType(waybill.getProductType());
miss.setStartSiteCode(startSiteCode);
miss.setNextSiteCode("");
miss.setMissType("0");
missRecords.add(miss);
return false;
}
// 使用包装方法,内部带 visitedSite 防止死循环
return checkBranchMatchV2(ruleList, waybillBatches, waybill, missRecords, startSiteCode, new HashSet<>());
}
/**
* 真正的递归实现,增加 visitedSiteCode 防止起始网点出现环路导致死递归。
*/
private boolean checkBranchMatchV2(List<EmisTransPlanRule> ruleList,
List<EmisTmsSiteBatch> waybillBatches,
EmisWaybill waybill,
List<EmisTmsSiteBatchMiss> missRecords,
String startSiteCode,
Set<String> visitedSiteCodes) {
if (StringUtils.isBlank(startSiteCode)) {
return false;
}
// 防止环路递归
if (!visitedSiteCodes.add(startSiteCode)) {
return false;
}
// 当前起始网点下的规则
List<EmisTransPlanRule> currentRules = ruleList.stream()
.filter(rule -> StringUtils.equals(rule.getStartSiteCode(), startSiteCode))
.collect(Collectors.toList());
// 当前起始网点下没有配置规则,认为本分支到此结束,不再校验
if (CollectionUtil.isEmpty(currentRules)) {
return true;
}
boolean hasNextSite = false;
for (EmisTransPlanRule rule : currentRules) {
// 当前下一网点下的规则
List<EmisTransPlanRule> nextRules = ruleList.stream()
.filter(r -> StringUtils.equals(r.getStartSiteCode(), rule.getNextSiteCode()))
.collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(nextRules)) {
hasNextSite = true;
break;
}
}
// 检查是否为特殊运输线路类型或清关类型
boolean isSpecialTransLine = "004".equals(waybill.getTransLineType()) &&
("24401".equals(waybill.getProductType()) || "24402".equals(waybill.getProductType()));
boolean isSpecialCustomsClear = "2".equals(waybill.getCustomsClear()) && "2".equals(waybill.getTransType());
if ((isSpecialTransLine || isSpecialCustomsClear) && !hasNextSite && currentRules.get(0).getStartSiteName().contains("机场")) {
return true;
}
// 当前起点对应的所有批次
List<EmisTmsSiteBatch> startBatches = waybillBatches.stream()
.filter(batch -> startSiteCode.equals(batch.getStartSiteCode()))
.collect(Collectors.toMap(
// 以起始网点、下一网点、供应商的组合作为key进行去重
batch -> StringUtils.defaultString(batch.getStartSiteCode()) + "_" +
StringUtils.defaultString(batch.getNextSiteCode()) + "_" +
StringUtils.defaultString(batch.getSupplierCode()),
batch -> batch,
// 如果key重复,保留第一个
(existing, replacement) -> existing
))
.values()
.stream()
.collect(Collectors.toList());
// 没有任何批次从该网点发出,则这些规则都视为漏组
if (CollectionUtil.isEmpty(startBatches)) {
setMissRecordsV2(waybill, missRecords, currentRules);
return false;
}
if(startBatches.size()>1){
setMissRecordsV2(waybill, missRecords, currentRules);
return false;
}
EmisTmsSiteBatch startBatch = startBatches.get(0);
boolean anyPathMatched = false;
// 找到与当前批次(nextSiteCode + supplier)完全匹配的规则
List<EmisTransPlanRule> matchedRules = currentRules.stream()
.filter(rule -> StringUtils.equals(rule.getNextSiteCode(), startBatch.getNextSiteCode())
&& compareSupplierCodes(rule.getSupplierCode(), startBatch.getSupplierCode()) && startBatch.getEtd().before(rule.getEndDate())
&& !startBatch.getEtd().before(rule.getStartDate()))
.collect(Collectors.toList());
// 如果该批次在当前起点下没有任何匹配规则,则当前起点下的规则整体视为漏组
if (CollectionUtil.isEmpty(matchedRules)) {
setMissRecordsV2(waybill, missRecords, currentRules);
return anyPathMatched;
}
// 存在匹配规则,则沿着下一网点继续递归校验
boolean childMatched = checkBranchMatchV2(ruleList, waybillBatches, waybill,
missRecords, startBatch.getNextSiteCode(), visitedSiteCodes);
if (childMatched) {
anyPathMatched = true;
// 已经有一条路径完全匹配,可以直接返回
}
return anyPathMatched;
}
/**
* 按 V2 规则生成漏组记录:
* 循环 ruleList,为每一个 EmisTransPlanRule 生成一条 miss 记录。
* 参考 EmisBaseService.setMissRecords 中设置 miss 的逻辑。
*/
private void setMissRecordsV2(EmisWaybill waybill,
List<EmisTmsSiteBatchMiss> missRecords,
List<EmisTransPlanRule> ruleList) {
if (CollectionUtil.isEmpty(ruleList)) {
return;
}
// 按规则 ID 去重,避免重复生成同一规则的漏组
List<EmisTransPlanRule> distinctRules = ruleList.stream()
.filter(Objects::nonNull)
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(EmisTransPlanRule::getId))),
ArrayList::new));
// for (EmisTransPlanRule rule : distinctRules) {
// if (rule == null) {
// continue;
// }
// EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
//
// String supplierNames = rule.getSupplierName();
// String missReasonDetail = String.format("%s-%s-%s",
// StringUtils.defaultString(rule.getStartSiteName(), rule.getStartSiteCode()),
// StringUtils.defaultString(rule.getNextSiteName(), rule.getNextSiteCode()),
// StringUtils.defaultString(supplierNames, rule.getSupplierCode()));
// miss.setMissReason("组批次有遗漏/错误,请根据推荐路线排查: " + missReasonDetail);
//
// // 参考 EmisBaseService.setRule 填充规则相关字段
// miss.setTransPlanRuleId(rule.getId());
// miss.setLineCode(rule.getLineCode());
// miss.setProductType(waybill.getProductType());
// miss.setStartSiteCode(rule.getStartSiteCode());
// miss.setNextSiteCode(rule.getNextSiteCode());
// miss.setManager(rule.getManager());
// miss.setSupplierCode(rule.getSupplierCode());
// miss.setStartDate(rule.getStartDate());
// miss.setEndDate(rule.getEndDate());
// miss.setMissType("0");
//
// missRecords.add(miss);
// }
EmisTransPlanRule rule = distinctRules.get(0);
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason(rule.getStartSiteName() +" 尚未完成组批次,请尽快完成,避免影响下一程同事组批次进度" );
// 参考 EmisBaseService.setRule 填充规则相关字段
miss.setTransPlanRuleId(rule.getId());
miss.setLineCode(rule.getLineCode());
miss.setProductType(waybill.getProductType());
miss.setStartSiteCode(rule.getStartSiteCode());
miss.setNextSiteCode("");
miss.setManager(rule.getManager());
// miss.setSupplierCode(rule.getSupplierCode());
miss.setStartDate(rule.getStartDate());
miss.setEndDate(rule.getEndDate());
miss.setMissType("0");
missRecords.add(miss);
}
}

View File

@ -55,6 +55,21 @@ public class EmisTmsSiteBatchMissStatisticsServiceImpl implements IEmisTmsSiteBa
return result;
}
/**
* 查询漏组批次统计列表
*/
@Override
public List<EmisTmsSiteBatchMissStatistics> selectStatisticsListV2(
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics) {
log.info("查询漏组批次统计列表,startSiteCode: {}, billMonth: {}",
emisTmsSiteBatchMissStatistics.getStartSiteCode(), emisTmsSiteBatchMissStatistics.getBillMonth());
List<EmisTmsSiteBatchMissStatistics> result = statisticsMapper
.selectStatisticsListV2(emisTmsSiteBatchMissStatistics);
return result;
}
/**
* 导出漏组批次统计查询接口数据
*/
@ -89,6 +104,40 @@ public class EmisTmsSiteBatchMissStatisticsServiceImpl implements IEmisTmsSiteBa
}
}
/**
* 导出漏组批次统计查询接口数据
*/
@Override
public void exportStatisticsDataV2(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics) {
try {
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("漏组批次统计汇总_" + System.currentTimeMillis(), "UTF-8").replaceAll("\\+",
"%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 查询统计数据
List<EmisTmsSiteBatchMissStatistics> statisticsList = selectStatisticsListV2(emisTmsSiteBatchMissStatistics);
// 转换为导出格式
List<EmisTmsSiteBatchMissStatisticsExport> exportList = convertToExportFormat(statisticsList);
// 添加合计行
addTotalRow(exportList);
// 导出Excel
EasyExcel.write(response.getOutputStream(), EmisTmsSiteBatchMissStatisticsExport.class)
.sheet("漏组批次统计汇总")
.doWrite(exportList);
} catch (IOException e) {
log.error("导出漏组批次统计数据失败", e);
throw new RuntimeException("导出失败:" + e.getMessage());
}
}
/**
* 转换为导出格式
*/
@ -363,6 +412,136 @@ public class EmisTmsSiteBatchMissStatisticsServiceImpl implements IEmisTmsSiteBa
}
}
/**
* 流式导出漏组详细数据(按月份分sheet)- 性能优化版本
* 支持大数据量导出,避免内存溢出
*/
@Override
public void exportDetailDataByMonthStreamV2(HttpServletResponse response,
EmisTmsSiteBatchMissStatistics emisTmsSiteBatchMissStatistics) {
long startTime = System.currentTimeMillis();
try {
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("漏组详细数据_" + System.currentTimeMillis(), "UTF-8").replaceAll("\\+",
"%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 先获取总记录数,用于进度跟踪
long totalCount = statisticsMapper.countDetailDataByMonthV2(emisTmsSiteBatchMissStatistics);
log.info("开始流式导出漏组详细数据,总记录数: {}, 查询条件: {}", totalCount, emisTmsSiteBatchMissStatistics);
if (totalCount == 0) {
// 创建空Excel
EasyExcel.write(response.getOutputStream(), EmisTmsSiteBatchMiss.class)
.sheet("无数据")
.doWrite(new ArrayList<>());
return;
}
// 简化版本:直接查询所有数据并导出,避免复杂的流式处理
// 如果数据量很大,可以考虑分批处理
List<EmisTmsSiteBatchMiss> allData = statisticsMapper
.selectDetailDataByMonthV2(emisTmsSiteBatchMissStatistics);
log.info("查询到数据条数: {}", allData != null ? allData.size() : 0);
if (allData == null || allData.isEmpty()) {
EasyExcel.write(response.getOutputStream(), EmisTmsSiteBatchMissExport.class)
.sheet("无数据")
.doWrite(new ArrayList<>());
return;
}
// 转换为专门的导出DTO,避免Map类型问题
List<EmisTmsSiteBatchMissExport> exportData = allData.stream()
.filter(Objects::nonNull)
.map(this::convertToExportDto)
.collect(Collectors.toList());
log.info("转换后数据条数: {}", exportData.size());
// 按月份分组数据
Map<String, List<EmisTmsSiteBatchMissExport>> dataByMonth = exportData.stream()
.collect(Collectors.groupingBy(item -> {
if (item.getSendDate() != null) {
synchronized (MONTH_FORMAT) {
return MONTH_FORMAT.format(item.getSendDate());
}
}
return "未知月份";
}));
// 创建Excel写入器
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
// 按月份从小到大排序
List<String> sortedMonths = dataByMonth.keySet().stream()
.filter(month -> !"未知月份".equals(month))
.sorted()
.collect(Collectors.toList());
// 如果有未知月份,放在最后
if (dataByMonth.containsKey("未知月份")) {
sortedMonths.add("未知月份");
}
int sheetIndex = 0;
for (String month : sortedMonths) {
List<EmisTmsSiteBatchMissExport> monthData = dataByMonth.get(month);
if (monthData != null && !monthData.isEmpty()) {
// 创建sheet,使用月份作为sheet名称
String sheetName = month + "月";
WriteSheet writeSheet = EasyExcel.writerSheet(sheetIndex, sheetName)
.head(EmisTmsSiteBatchMissExport.class)
.build();
// 写入数据
excelWriter.write(monthData, writeSheet);
sheetIndex++;
log.info("写入{}月数据,条数: {}", month, monthData.size());
}
}
// 关闭写入器
excelWriter.finish();
log.info("Excel写入成功,共创建{}个sheet", sheetIndex);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
log.info("流式导出完成,总处理记录数: {}, 耗时: {}ms, 平均处理速度: {}/s",
allData.size(), duration, allData.size() > 0 ? (allData.size() * 1000 / duration) : 0);
} catch (IOException e) {
long endTime = System.currentTimeMillis();
log.error("流式导出漏组详细数据失败,耗时: {}ms", endTime - startTime, e);
// 不抛出异常,避免重复处理响应
try {
if (!response.isCommitted()) {
response.reset();
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"success\":false,\"message\":\"导出失败:" + e.getMessage() + "\"}");
}
} catch (Exception ex) {
log.error("写入错误响应失败", ex);
}
} catch (Exception e) {
long endTime = System.currentTimeMillis();
log.error("流式导出漏组详细数据发生未知错误,耗时: {}ms", endTime - startTime, e);
try {
if (!response.isCommitted()) {
response.reset();
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"success\":false,\"message\":\"导出失败:" + e.getMessage() + "\"}");
}
} catch (Exception ex) {
log.error("写入错误响应失败", ex);
}
}
}
/**
* 将EmisTmsSiteBatchMiss转换为EmisTmsSiteBatchMissExport用于导出
* 根据实际Excel截图字段进行映射

View File

@ -27,6 +27,7 @@ import com.xdadan.erp.emis.mapper.EmisTmsSiteBatchLogMapper;
import com.xdadan.erp.emis.mapper.EmisWaybillMapper;
import com.xdadan.erp.emis.mapper.EmisTmsCostFeeMapper;
import com.xdadan.erp.emis.service.EmisBaseService;
import com.xdadan.erp.emis.service.IEmisTmsSiteBatchMissService;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@ -71,6 +72,9 @@ public class EmisTmsSiteBatchServiceImpl extends EmisBaseService implements IEmi
@Autowired
private EmisTmsCostFeeMapper emisTmsCostFeeMapper;
@Autowired
private IEmisTmsSiteBatchMissService emisTmsSiteBatchMissService;
/**
* 获取稽核数据
*
@ -512,6 +516,20 @@ public class EmisTmsSiteBatchServiceImpl extends EmisBaseService implements IEmi
scanSiteBatchByWaybills(waybills);
}
@Override
@Async
public void scanSiteBatchV2(String batchNo) {
log.info("start scan site batch V2 ,batchNo:{}", batchNo);
if (StringUtil.isBlank(batchNo)) {
return;
}
List<EmisWaybill> waybills = emisTmsSiteBatchDtlMapper.selectEmisWayBillByBatchNo(batchNo);
if (CollectionUtil.isEmpty(waybills)) {
return;
}
emisTmsSiteBatchMissService.scanSiteBatchByWaybillsV2(waybills);
}
@Override
@Transactional
public int addAll(EmisTmsSiteBatch emisTmsSiteBatch) throws EmisBizError {

View File

@ -91,6 +91,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join sys_user u on w.register_man_code=u.emp_code and u.del_flag='0'
</sql>
<sql id="selectEmisTmsSiteBatchMissVoV2">
select
m.id, m.bill_code, m.miss_reason, m.create_time, m.update_time, m.remark, m.del_flag,
m.line_code, m.product_type,
COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(m.next_site_code, w.destination_code) as next_site_code,
m.supplier_code, m.start_date, m.end_date,m.miss_type,
w.send_site_code, s1.site_name as send_site_name,s2.site_name as start_site_name,s3.site_name as next_site_name, w.send_country, c1.name as send_country_name, w.destination_code, s4.site_name as dispatch_underling_site_name,w.bl_is_question,
l.line_name as line_name,
w.receive_country, c2.name as receive_country_name, w.send_date, w.bl_sign, w.sign_date,
w.product_type,w.problem_type,w.virtual_remark,w.dispatch_underling_site_code,
w.payment_type, w.parcel_qty, w.bill_weight, w.total_volume, w.volume_weight,
w.settlement_weight, w.freight, w.remark as waybill_remark, w.fee_remark, m.manager, k.trans_manager,
w.payee, w.salesmen, u.emp_name as registerManName,
(SELECT GROUP_CONCAT(s.name)
FROM emis_supplier s
WHERE FIND_IN_SET(s.code, m.supplier_code)
AND s.del_flag = '0') as supplier_name,
(SELECT GROUP_CONCAT(p.prod_name)
FROM emis_trans_product p
WHERE FIND_IN_SET(p.prod_code, m.product_type)
AND p.del_flag = '0') as product_type_name
from emis_tms_site_batch_miss_v2 m
left join emis_waybill w on m.bill_code = w.bill_code and w.del_flag='0'
left join emis_site s1 on w.send_site_code=s1.site_code and s1.del_flag=0
left join emis_site s2 on COALESCE(m.start_site_code, w.start_site_code, w.send_site_code)=s2.site_code and s2.del_flag=0
left join emis_site s3 on COALESCE(m.next_site_code, w.destination_code)=s3.site_code and s3.del_flag=0
left join emis_country c1 on w.send_country=c1.code and c1.del_flag='0'
left join emis_country c2 on w.receive_country=c2.code and c2.del_flag='0'
left join emis_site s4 on w.dispatch_underling_site_code=s4.site_code and s4.del_flag=0
left join emis_trans_line k on w.trans_line_type=k.line_code and k.del_flag='0'
left join emis_trans_plan_rule r on m.trans_plan_rule_id=r.id and r.del_flag='0'
left join emis_trans_line l on m.line_code=l.line_code and l.del_flag='0'
left join sys_user u on w.register_man_code=u.emp_code and u.del_flag='0'
</sql>
<!-- <select id="selectEmisTmsSiteBatchMissList" parameterType="EmisTmsSiteBatchMiss" resultMap="EmisTmsSiteBatchMissResult">-->
<!-- <include refid="selectEmisTmsSiteBatchMissVo"/>-->
<!-- <where>-->
@ -148,8 +184,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
b.bill_code, null as id, '已组批次成功' as miss_reason,
null as create_by, null as create_time, null as update_by, null as update_time,
null as remark, '0' as del_flag,
w.trans_line_type as line_code, w.product_type,
COALESCE(null, w.start_site_code, w.send_site_code) as start_site_code,
w.trans_line_type as line_code, w.product_type,
COALESCE(null, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(null, w.destination_code) as next_site_code,
null as supplier_code, null as start_date, null as end_date, '0' as miss_type,
w.send_site_code, s1.site_name as send_site_name,
@ -350,6 +386,219 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</choose>
</select>
<select id="selectEmisTmsSiteBatchMissListV2" parameterType="EmisTmsSiteBatchMiss" resultMap="EmisTmsSiteBatchMissResult">
<choose>
<when test="missCount == -4 or missCount == -7">
<!-- 当missCount=-4时,查询emis_waybill_batch_status表 -->
select
b.bill_code, null as id, '已组批次成功' as miss_reason,
null as create_by, null as create_time, null as update_by, null as update_time,
null as remark, '0' as del_flag,
w.trans_line_type as line_code, w.product_type,
COALESCE(null, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(null, w.destination_code) as next_site_code,
null as supplier_code, null as start_date, null as end_date, '0' as miss_type,
w.send_site_code, s1.site_name as send_site_name,
w.send_country, c1.name as send_country_name, w.destination_code, s2.site_name as dispatch_underling_site_name, w.bl_is_question,
l.line_name as line_name,
w.receive_country, c2.name as receive_country_name, w.send_date, w.bl_sign, w.sign_date,
w.payment_type, w.parcel_qty, w.bill_weight, w.total_volume, w.volume_weight,
w.settlement_weight, w.freight, w.remark as waybill_remark, w.fee_remark,
null as manager, null as trans_manager,
w.payee, w.salesmen, u.emp_name as registerManName,
(SELECT GROUP_CONCAT(s.site_name ORDER BY FIND_IN_SET(s.site_code, w.start_site_code))
FROM emis_site s
WHERE FIND_IN_SET(s.site_code, w.start_site_code) AND s.del_flag = 0) as start_site_name,
(SELECT GROUP_CONCAT(s.site_name ORDER BY FIND_IN_SET(s.site_code, w.next_site_code))
FROM emis_site s
WHERE FIND_IN_SET(s.site_code, w.next_site_code) AND s.del_flag = 0) as next_site_name,
null as supplier_name,
(SELECT GROUP_CONCAT(p.prod_name)
FROM emis_trans_product p
WHERE FIND_IN_SET(p.prod_code, w.product_type)
AND p.del_flag = '0') as product_type_name,
b.remark as manual_remark,b.bl_manual
from emis_waybill_batch_status b
inner join emis_waybill w on b.bill_code = w.bill_code and w.del_flag='0'
left join emis_site s1 on w.send_site_code=s1.site_code and s1.del_flag=0
left join emis_country c1 on w.send_country=c1.code and c1.del_flag=0
left join emis_site s2 on w.dispatch_underling_site_code=s2.site_code and s2.del_flag=0
left join emis_trans_line l on w.trans_line_type=l.line_code and l.del_flag=0
left join emis_country c2 on w.receive_country=c2.code and c2.del_flag=0
left join sys_user u on w.register_man_code=u.emp_code and u.del_flag='0'
<where>
b.site_batch_status = '1'
<if test="billCode != null and billCode != '' and ( params.billCodeSortList == null or (params.billCodeSortList != null and params.billCodeSortList.size() == 1 ) )">
and ( b.`bill_code`=#{billCode} or b.bill_code like concat('%',#{billCode}) )
</if>
<if test="missCount == -7">
AND (b.bl_manual = '2' or b.bl_manual = '3')
</if>
<if test="params.billCodeSortList != null and params.billCodeSortList.size() > 1 ">
and b.bill_code in
<foreach collection="params.billCodeSortList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="lineCode != null and lineCode != ''">
AND w.trans_line_type = #{lineCode}
</if>
<if test="productType != null and productType != ''">
AND FIND_IN_SET(#{productType}, w.product_type)
</if>
<if test="startSiteCode != null and startSiteCode != ''">
AND (
w.start_site_code REGEXP REPLACE(#{startSiteCode}, ',', '|')
)
</if>
<if test="nextSiteCode != null and nextSiteCode != ''">
AND (
w.next_site_code REGEXP REPLACE(#{nextSiteCode}, ',', '|')
)
</if>
<if test="blIsQuestion != null and blIsQuestion != ''">
AND w.bl_is_question = #{blIsQuestion}
</if>
<if test="sendSiteCode != null and sendSiteCode != ''">
AND w.send_site_code = #{sendSiteCode}
</if>
<if test="params.beginSendDate != null and params.beginSendDate != ''">
and w.send_date <![CDATA[ >= ]]> #{params.beginSendDate}
</if>
<if test="params.endSendDate != null and params.endSendDate != ''">
and w.send_date <![CDATA[ <= ]]> #{params.endSendDate}
</if>
<if test="params.privSiteCode != null and params.privSiteCode != '88888'">
and (w.start_site_code=#{params.privSiteCode} or w.send_site_code=#{params.privSiteCode})
</if>
<if test="payee != null and payee != ''">
AND w.payee like concat('%', #{payee}, '%')
</if>
<if test="salesmen != null and salesmen != ''">
AND w.salesmen like concat('%', #{salesmen}, '%')
</if>
<if test="registerManName != null and registerManName != ''">
AND u.emp_name like concat('%', #{registerManName}, '%')
</if>
</where>
</when>
<otherwise>
<include refid="selectEmisTmsSiteBatchMissVoV2"/>
<where>
m.del_flag = '0'
<if test="missCount != null">
<choose>
<when test="missCount == -1">
AND m.miss_type = 2
</when>
<when test="missCount == -2">
AND m.miss_type = 3
</when>
<when test="missCount == -3">
AND m.miss_type = 1
</when>
<when test="missCount == -6">
AND m.miss_type = 4
</when>
<when test="missCount == 1">
AND m.miss_type = 0
</when>
<!-- <when test="missCount == -5">-->
<!-- AND m.bill_code IN (-->
<!-- SELECT bill_code-->
<!-- FROM emis_tms_site_batch_miss-->
<!-- WHERE del_flag = '0'-->
<!-- GROUP BY bill_code-->
<!-- HAVING COUNT(*) > 9-->
<!-- )-->
<!-- </when>-->
<!-- <when test="missCount > 0">-->
<!-- AND m.bill_code IN (-->
<!-- SELECT bill_code-->
<!-- FROM emis_tms_site_batch_miss-->
<!-- WHERE del_flag = '0'-->
<!-- GROUP BY bill_code-->
<!-- HAVING COUNT(*) = #{missCount}-->
<!-- )-->
<!-- AND m.miss_type = 0-->
<!-- </when>-->
</choose>
</if>
<if test="billCode != null and billCode != '' and ( params.billCodeSortList == null or (params.billCodeSortList != null and params.billCodeSortList.size() == 1 ) )">
and ( m.`bill_code`=#{billCode} or m.bill_code like concat('%',#{billCode}) )
</if>
<if test="params.billCodeSortList != null and params.billCodeSortList.size() > 1 ">
and m.bill_code in
<foreach collection="params.billCodeSortList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="missReason != null and missReason != ''">
AND m.miss_reason like concat('%', #{missReason}, '%')
</if>
<if test="manager != null and manager != ''">
AND (
m.manager REGEXP REPLACE(#{manager}, ',', '|')
)
</if>
<if test="transManager != null and transManager != ''">
AND k.trans_manager like concat('%', #{transManager}, '%')
</if>
<if test="params.privSiteCode != null and params.privSiteCode != '88888'">
and (w.start_site_code=#{params.privSiteCode} or w.send_site_code=#{params.privSiteCode} or m.start_site_code=#{params.privSiteCode} or m.next_site_code=#{params.privSiteCode})
</if>
<if test="lineCode != null and lineCode != ''">
AND m.line_code = #{lineCode}
</if>
<if test="productType != null and productType != ''">
AND FIND_IN_SET(#{productType}, m.product_type)
</if>
<if test="startSiteCode != null and startSiteCode != ''">
AND (
m.start_site_code REGEXP REPLACE(#{startSiteCode}, ',', '|')
)
</if>
<if test="nextSiteCode != null and nextSiteCode != ''">
AND (
m.next_site_code REGEXP REPLACE(#{nextSiteCode}, ',', '|')
)
</if>
<if test="supplierCode != null and supplierCode != ''">
AND FIND_IN_SET(#{supplierCode}, m.supplier_code)
</if>
<if test="startDate != null">
AND m.start_date = #{startDate}
</if>
<if test="endDate != null">
AND m.end_date = #{endDate}
</if>
<if test="blIsQuestion != null and blIsQuestion != ''">
AND w.bl_is_question = #{blIsQuestion}
</if>
<if test="sendSiteCode != null and sendSiteCode != ''">
AND w.send_site_code = #{sendSiteCode}
</if>
<if test="params.beginSendDate != null and params.beginSendDate != ''">
and w.send_date <![CDATA[ >= ]]> #{params.beginSendDate}
</if>
<if test="params.endSendDate != null and params.endSendDate != ''">
and w.send_date <![CDATA[ <= ]]> #{params.endSendDate}
</if>
<if test="payee != null and payee != ''">
AND w.payee like concat('%', #{payee}, '%')
</if>
<if test="salesmen != null and salesmen != ''">
AND w.salesmen like concat('%', #{salesmen}, '%')
</if>
<if test="registerManName != null and registerManName != ''">
AND u.emp_name like concat('%', #{registerManName}, '%')
</if>
</where>
order by m.bill_code desc
</otherwise>
</choose>
</select>
<select id="selectEmisTmsSiteBatchMissById" parameterType="Long" resultMap="EmisTmsSiteBatchMissResult">
<include refid="selectEmisTmsSiteBatchMissVo"/>
where m.id = #{id} and m.del_flag = '0'
@ -414,6 +663,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
</delete>
<delete id="deleteReallyV2">
delete from emis_tms_site_batch_miss_v2
<if test="lineCode != null and lineCode != ''">
where line_code = #{lineCode}
</if>
</delete>
<update id="deleteByBillCodes">
update emis_tms_site_batch_miss set del_flag = '1'
where bill_code in
@ -422,6 +678,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</update>
<update id="deleteByBillCodesV2">
update emis_tms_site_batch_miss_v2 set del_flag = '1'
where bill_code in
<foreach collection="billCodes" item="billCode" open="(" separator="," close=")">
#{billCode}
</foreach>
</update>
<insert id="batchInsertEmisTmsSiteBatchMiss" parameterType="java.util.List">
insert into emis_tms_site_batch_miss
(trans_plan_rule_id, bill_code, miss_reason, line_code,manager, product_type,
@ -454,6 +718,38 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</foreach>
</insert>
<insert id="batchInsertEmisTmsSiteBatchMissV2" parameterType="java.util.List">
insert into emis_tms_site_batch_miss_v2
(trans_plan_rule_id, bill_code, miss_reason, line_code,manager, product_type,
start_site_code, next_site_code, supplier_code, start_date, end_date,
create_by, create_time, update_by, update_time, remark,
create_site, update_site, miss_type)
values
<foreach collection="list" item="item" separator=",">
(
#{item.transPlanRuleId,jdbcType=BIGINT},
#{item.billCode,jdbcType=VARCHAR},
#{item.missReason,jdbcType=VARCHAR},
#{item.lineCode,jdbcType=VARCHAR},
#{item.manager,jdbcType=VARCHAR},
#{item.productType,jdbcType=VARCHAR},
#{item.startSiteCode,jdbcType=VARCHAR},
#{item.nextSiteCode,jdbcType=VARCHAR},
#{item.supplierCode,jdbcType=VARCHAR},
#{item.startDate,jdbcType=TIMESTAMP},
#{item.endDate,jdbcType=TIMESTAMP},
#{item.createBy,jdbcType=VARCHAR},
#{item.createTime,jdbcType=TIMESTAMP},
#{item.updateBy,jdbcType=VARCHAR},
#{item.updateTime,jdbcType=TIMESTAMP},
#{item.remark,jdbcType=VARCHAR},
#{item.createSite,jdbcType=VARCHAR},
#{item.updateSite,jdbcType=VARCHAR},
#{item.missType,jdbcType=VARCHAR}
)
</foreach>
</insert>
<select id="selectByBillCodeCount" parameterType="Integer" resultMap="EmisTmsSiteBatchMissResult">
SELECT t.*
FROM emis_tms_site_batch_miss t

View File

@ -3,7 +3,7 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xdadan.erp.emis.mapper.EmisTmsSiteBatchMissRecordMapper">
<resultMap type="EmisTmsSiteBatchMissRecord" id="EmisTmsSiteBatchMissRecordResult">
<result property="id" column="id"/>
<result property="totalProcessed" column="total_processed"/>
@ -29,7 +29,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
failed_bill_codes,
create_by,
create_time,
create_site
create_site,
scan_version
) values (
#{totalProcessed},
#{successCount},
@ -40,8 +41,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{failedBillCodes},
#{createBy},
#{createTime},
#{createSite}
#{createSite},
#{scanVersion}
)
</insert>
</mapper>
</mapper>

View File

@ -39,12 +39,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
ORDER BY w.send_date, COALESCE(bm.start_site_code, w.start_site_code, w.send_site_code)
</select>
<select id="selectStatisticsListV2" parameterType="EmisTmsSiteBatchMissStatistics" resultMap="EmisTmsSiteBatchMissStatisticsResult">
SELECT
COALESCE(bm.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
s.site_name as start_site_name,
STR_TO_DATE(CONCAT(DATE_FORMAT(w.send_date, '%Y-%m'), '-01'), '%Y-%m-%d') as bill_month,
COUNT(*) as ticket_count
FROM emis_tms_site_batch_miss_v2 bm
LEFT JOIN emis_waybill w ON bm.bill_code = w.bill_code AND w.del_flag = '0'
LEFT JOIN emis_site s ON COALESCE(bm.start_site_code, w.start_site_code, w.send_site_code) = s.site_code AND s.del_flag = '0'
WHERE bm.del_flag = '0'
<if test="startSiteCode != null and startSiteCode != ''">
AND COALESCE(bm.start_site_code, w.start_site_code, w.send_site_code) = #{startSiteCode}
</if>
<if test="startTime != null and startTime != ''">
AND DATE_FORMAT(w.send_date, '%Y-%m') <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND DATE_FORMAT(w.send_date, '%Y-%m') <![CDATA[ <= ]]> #{endTime}
</if>
<if test="(startTime == null or startTime == '') and (endTime == null or endTime == '')">
<if test="billMonth != null">
AND DATE_FORMAT(w.send_date, '%Y-%m') = DATE_FORMAT(#{billMonth}, '%Y-%m')
</if>
</if>
GROUP BY COALESCE(bm.start_site_code, w.start_site_code, w.send_site_code), s.site_name, DATE_FORMAT(w.send_date, '%Y-%m')
ORDER BY w.send_date, COALESCE(bm.start_site_code, w.start_site_code, w.send_site_code)
</select>
<select id="selectDetailDataByMonth" parameterType="EmisTmsSiteBatchMiss" resultMap="com.xdadan.erp.emis.mapper.EmisTmsSiteBatchMissMapper.EmisTmsSiteBatchMissResult">
select
m.id, m.bill_code, m.miss_reason, m.create_time, m.update_time, m.remark, m.del_flag,
m.line_code, m.product_type,
COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(m.next_site_code, w.destination_code) as next_site_code,
m.line_code, m.product_type,
COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(m.next_site_code, w.destination_code) as next_site_code,
m.supplier_code, m.start_date, m.end_date,m.miss_type,
w.send_site_code, s1.site_name as send_site_name,s2.site_name as start_site_name,s3.site_name as next_site_name, w.send_country, c1.name as send_country_name, w.destination_code, s4.site_name as dispatch_underling_site_name,w.bl_is_question,
l.line_name as line_name,
@ -93,13 +121,67 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
ORDER BY w.send_date DESC, m.bill_code
</select>
<select id="selectDetailDataByMonthV2" parameterType="EmisTmsSiteBatchMiss" resultMap="com.xdadan.erp.emis.mapper.EmisTmsSiteBatchMissMapper.EmisTmsSiteBatchMissResult">
select
m.id, m.bill_code, m.miss_reason, m.create_time, m.update_time, m.remark, m.del_flag,
m.line_code, m.product_type,
COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(m.next_site_code, w.destination_code) as next_site_code,
m.supplier_code, m.start_date, m.end_date,m.miss_type,
w.send_site_code, s1.site_name as send_site_name,s2.site_name as start_site_name,s3.site_name as next_site_name, w.send_country, c1.name as send_country_name, w.destination_code, s4.site_name as dispatch_underling_site_name,w.bl_is_question,
l.line_name as line_name,
w.receive_country, c2.name as receive_country_name, w.send_date, w.bl_sign, w.sign_date,
w.product_type,w.problem_type,w.virtual_remark,w.dispatch_underling_site_code,
w.payment_type, w.parcel_qty, w.bill_weight, w.total_volume, w.volume_weight,
w.settlement_weight, w.freight, w.remark as waybill_remark, w.fee_remark, m.manager, k.trans_manager,
w.payee, w.salesmen, u.emp_name as registerManName,
(SELECT s.name
FROM emis_supplier s
WHERE FIND_IN_SET(s.code, m.supplier_code)
AND s.del_flag = '0'
LIMIT 1) as supplier_name,
(SELECT p.prod_name
FROM emis_trans_product p
WHERE FIND_IN_SET(p.prod_code, m.product_type)
AND p.del_flag = '0'
LIMIT 1) as product_type_name
from emis_tms_site_batch_miss_v2 m
left join emis_waybill w on m.bill_code = w.bill_code and w.del_flag='0'
left join emis_site s1 on w.send_site_code=s1.site_code and s1.del_flag='0'
left join emis_site s2 on COALESCE(m.start_site_code, w.start_site_code, w.send_site_code)=s2.site_code and s2.del_flag='0'
left join emis_site s3 on COALESCE(m.next_site_code, w.destination_code)=s3.site_code and s3.del_flag='0'
left join emis_country c1 on w.send_country=c1.code and c1.del_flag='0'
left join emis_country c2 on w.receive_country=c2.code and c2.del_flag='0'
left join emis_site s4 on w.dispatch_underling_site_code=s4.site_code and s4.del_flag='0'
left join emis_trans_line k on w.trans_line_type=k.line_code and k.del_flag='0'
left join emis_trans_plan_rule r on m.trans_plan_rule_id=r.id and r.del_flag='0'
left join emis_trans_line l on m.line_code=l.line_code and l.del_flag='0'
left join sys_user u on w.register_man_code=u.emp_code and u.del_flag='0'
WHERE m.del_flag = '0'
<if test="startSiteCode != null and startSiteCode != ''">
AND COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) = #{startSiteCode}
</if>
<if test="startTime != null and startTime != ''">
AND DATE_FORMAT(w.send_date, '%Y-%m') <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND DATE_FORMAT(w.send_date, '%Y-%m') <![CDATA[ <= ]]> #{endTime}
</if>
<if test="(startTime == null or startTime == '') and (endTime == null or endTime == '')">
<if test="billMonth != null">
AND DATE_FORMAT(w.send_date, '%Y-%m') = DATE_FORMAT(#{billMonth}, '%Y-%m')
</if>
</if>
ORDER BY w.send_date DESC, m.bill_code
</select>
<!-- 分页查询漏组详细数据列表(按月份分组)- 性能优化版本 -->
<select id="selectDetailDataByMonthWithPaging" resultMap="com.xdadan.erp.emis.mapper.EmisTmsSiteBatchMissMapper.EmisTmsSiteBatchMissResult">
select
m.id, m.bill_code, m.miss_reason, m.create_time, m.update_time, m.remark, m.del_flag,
m.line_code, m.product_type,
COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(m.next_site_code, w.destination_code) as next_site_code,
m.line_code, m.product_type,
COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) as start_site_code,
COALESCE(m.next_site_code, w.destination_code) as next_site_code,
m.supplier_code, m.start_date, m.end_date,m.miss_type,
w.send_site_code, s1.site_name as send_site_name,s2.site_name as start_site_name,s3.site_name as next_site_name, w.send_country, c1.name as send_country_name, w.destination_code, s4.site_name as dispatch_underling_site_name,w.bl_is_question,
l.line_name as line_name,
@ -171,4 +253,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
</select>
<!-- 统计漏组详细数据总数 -->
<select id="countDetailDataByMonthV2" parameterType="EmisTmsSiteBatchMissStatistics" resultType="long">
SELECT COUNT(*)
FROM emis_tms_site_batch_miss_v2 m
LEFT JOIN emis_waybill w ON m.bill_code = w.bill_code AND w.del_flag='0'
WHERE m.del_flag = '0'
<if test="startSiteCode != null and startSiteCode != ''">
AND COALESCE(m.start_site_code, w.start_site_code, w.send_site_code) = #{startSiteCode}
</if>
<if test="startTime != null and startTime != ''">
AND DATE_FORMAT(w.send_date, '%Y-%m') <![CDATA[ >= ]]> #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND DATE_FORMAT(w.send_date, '%Y-%m') <![CDATA[ <= ]]> #{endTime}
</if>
<if test="(startTime == null or startTime == '') and (endTime == null or endTime == '')">
<if test="billMonth != null">
AND DATE_FORMAT(w.send_date, '%Y-%m') = DATE_FORMAT(#{billMonth}, '%Y-%m')
</if>
</if>
</select>
</mapper>

View File

@ -3925,9 +3925,10 @@
</insert>
<select id="selectWaybillListBySiteBatchStatus" resultMap="EmisWaybillResult">
select a.*,k.trans_type
select a.*,k.trans_type,start.site_name as start_site_name
from emis_waybill a
left join emis_trans_line k on a.trans_line_type=k.line_code and k.del_flag='0'
left join emis_site start on a.start_site_code=start.site_code and start.del_flag='0'
where a.del_flag = '0'
<if test="lineCode != null and lineCode != ''">
and a.trans_line_type = #{lineCode}