demand: 月结客户达标统计相关需求
This commit is contained in:
parent
6dd335a11b
commit
1335ae8890
@ -495,7 +495,19 @@ public class EmisCustomerUserController extends EmisBaseController
|
||||
setPrivParams(emisWaybill);
|
||||
List<MonthlyCustomerShipmentStatVO> list = emisCustomerUserService
|
||||
.selectMonthlyCustomerShipmentStat(emisWaybill);
|
||||
return getDataTable(list);
|
||||
TableDataInfo dataTable = getDataTable(list);
|
||||
|
||||
// 如果使用了应用层过滤(complianceStatType=3/4/5/6),需要手动设置总数
|
||||
if (emisWaybill.getParams() != null && emisWaybill.getParams().containsKey("_filteredTotal")) {
|
||||
Object filteredTotal = emisWaybill.getParams().get("_filteredTotal");
|
||||
if (filteredTotal instanceof Long) {
|
||||
dataTable.setTotal((Long) filteredTotal);
|
||||
} else if (filteredTotal instanceof Number) {
|
||||
dataTable.setTotal(((Number) filteredTotal).longValue());
|
||||
}
|
||||
}
|
||||
|
||||
return dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -203,5 +203,14 @@ public class MonthlyCustomerShipmentStatVO implements Serializable {
|
||||
|
||||
/* 创建日期(现金客户导出使用,对应 create_time) */
|
||||
private String createTime;
|
||||
|
||||
/* 是否达标;1:已达标,2:未达标 */
|
||||
private String blCompliance;
|
||||
|
||||
/* 寄件月份 */
|
||||
private String sendMonth;
|
||||
|
||||
/* 总运费 */
|
||||
private Integer totalBillFee;
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ package com.xdadan.erp.emis.mapper;
|
||||
import java.util.List;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.xdadan.erp.emis.domain.EmisCustomerUser;
|
||||
import com.xdadan.erp.emis.domain.EmisWaybill;
|
||||
import com.xdadan.erp.emis.domain.vo.MonthlyCustomerShipmentStatVO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
/**
|
||||
* @ClassName EmisCustomerUserMapper
|
||||
@ -134,4 +136,12 @@ public interface EmisCustomerUserMapper extends BaseMapper<EmisCustomerUser>
|
||||
*/
|
||||
public int clearMoneyUserIdByMoneyUserIds(@Param("moneyUserIds") Long[] moneyUserIds);
|
||||
|
||||
/**
|
||||
* 月结客户达标统计
|
||||
*
|
||||
* @param emisWaybill 查询条件
|
||||
* @return
|
||||
*/
|
||||
List<MonthlyCustomerShipmentStatVO> selectMonthlyCustomerShipmentStatIsCompliance(EmisWaybill emisWaybill);
|
||||
|
||||
}
|
||||
|
||||
@ -70,9 +70,31 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
MonthlyCustomerShipmentStatVO::getOtherWeightCompareFlag)
|
||||
);
|
||||
|
||||
private static final String[] BASE_HEADERS = {"客户名称", "首次签约日期", "总票数", "总重量", "销售联系人"};
|
||||
/**
|
||||
* 默认(月结/现金)出货统计基础列(原有导出保持不变)
|
||||
* 客户名称、首次签约日期(或创建日期)、总票数、总重量、销售联系人
|
||||
*/
|
||||
private static final String[] BASE_HEADERS = {
|
||||
"客户名称", "首次签约日期", "总票数", "总重量", "销售联系人"
|
||||
};
|
||||
private static final int[] BASE_WIDTHS = {28, 18, 12, 16, 24};
|
||||
|
||||
/**
|
||||
* 月结客户达标统计基础列(仅 statType=5 使用)
|
||||
*
|
||||
* 列顺序要求:
|
||||
* 1)寄件月份 字段放在“首次签约日期”后边;
|
||||
* 2)总运费 字段放在“总重量”后边;
|
||||
* 3)是否达标 字段放在“寄件月份”后边;
|
||||
*
|
||||
* 最终基础表头顺序:
|
||||
* 客户名称、首次签约日期、寄件月份、是否达标、总票数、总重量、总运费、销售联系人
|
||||
*/
|
||||
private static final String[] COMPLIANCE_HEADERS = {
|
||||
"客户名称", "首次签约日期", "寄件月份", "是否达标", "总票数", "总重量", "总运费", "销售联系人"
|
||||
};
|
||||
private static final int[] COMPLIANCE_BASE_WIDTHS = {28, 18, 16, 12, 12, 16, 16, 24};
|
||||
|
||||
private MonthlyShipmentStatExcelUtil() {
|
||||
}
|
||||
|
||||
@ -94,21 +116,36 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
|
||||
private static void exportInternal(HttpServletResponse response, List<MonthlyCustomerShipmentStatVO> dataList,
|
||||
int statType, boolean moneyCustomer) {
|
||||
boolean transportLayout = statType == 1 || statType == 3;
|
||||
boolean transportLayout = statType == 1 || statType == 3 || statType == 5;
|
||||
boolean compareMode = statType == 3 || statType == 4;
|
||||
// 是否为月结达标统计(仅 statType=5)
|
||||
boolean complianceLayout = !moneyCustomer && statType == 5;
|
||||
|
||||
List<MonthlyCustomerShipmentStatVO> safeList = dataList == null ? new ArrayList<>() : dataList;
|
||||
|
||||
// 为保证 Excel 导出稳定排序:先按总重量降序,再按总票数降序,最后按客户名称升序
|
||||
sortForExport(safeList);
|
||||
if(!complianceLayout){
|
||||
sortForExport(safeList);
|
||||
}
|
||||
|
||||
List<MonthlyCustomerShipmentStatVO> exportList = new ArrayList<>(safeList);
|
||||
exportList.add(buildTotalRow(safeList));
|
||||
|
||||
String sheetName = moneyCustomer ? "现金客户出货统计" : "月结客户出货统计";
|
||||
String[] headers = moneyCustomer
|
||||
? new String[]{"客户名称", "创建日期", "总票数", "总重量", "销售联系人"}
|
||||
: BASE_HEADERS;
|
||||
|
||||
|
||||
String[] headers;
|
||||
int[] baseWidths;
|
||||
if (moneyCustomer) {
|
||||
headers = new String[]{"客户名称", "创建日期", "总票数", "总重量", "销售联系人"};
|
||||
baseWidths = BASE_WIDTHS;
|
||||
} else if (complianceLayout) {
|
||||
headers = COMPLIANCE_HEADERS;
|
||||
baseWidths = COMPLIANCE_BASE_WIDTHS;
|
||||
} else {
|
||||
headers = BASE_HEADERS;
|
||||
baseWidths = BASE_WIDTHS;
|
||||
}
|
||||
|
||||
try {
|
||||
SXSSFWorkbook workbook = new SXSSFWorkbook(200);
|
||||
@ -131,7 +168,7 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
// 样式缓存:避免在对比模式下创建过多样式导致超过Excel样式限制
|
||||
Map<String, CellStyle> styleCache = new HashMap<>();
|
||||
|
||||
createHeader(sheet, headers, transportLayout ? TRANSPORT_GROUPS : COUNTRY_GROUPS, headerStyle);
|
||||
createHeader(sheet, headers, baseWidths, transportLayout ? TRANSPORT_GROUPS : COUNTRY_GROUPS, headerStyle);
|
||||
|
||||
int dataStartRow = 2;
|
||||
for (int i = 0; i < exportList.size(); i++) {
|
||||
@ -143,17 +180,64 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
int col = 0;
|
||||
// 客户名称:月结用 customerName;现金用 company
|
||||
String customerName = moneyCustomer ? vo.getCompany() : vo.getCustomerName();
|
||||
String dateValue = moneyCustomer ? vo.getCreateTime() : vo.getFirstSigningDay();
|
||||
|
||||
col = writeTextCell(row, col, customerName, totalRow ? totalTextStyle : textStyle);
|
||||
col = writeTextCell(row, col, dateValue, totalRow ? totalTextStyle : textStyle);
|
||||
col = writeIntegerCell(row, col, vo.getTotalTickets(), vo.getTotalTicketsCompareFlag(),
|
||||
totalRow ? totalNumberStyle : numberStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeDecimalCell(row, col, vo.getTotalWeight(), vo.getTotalWeightCompareFlag(),
|
||||
totalRow ? totalDecimalStyle : decimalStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeTextCell(row, col, vo.getSalesmen(), totalRow ? totalTextStyle : textStyle);
|
||||
|
||||
if (moneyCustomer) {
|
||||
// 现金客户出货统计:保持原有列顺序不变
|
||||
String dateValue = vo.getCreateTime();
|
||||
col = writeTextCell(row, col, dateValue, totalRow ? totalTextStyle : textStyle);
|
||||
col = writeIntegerCell(row, col, vo.getTotalTickets(), vo.getTotalTicketsCompareFlag(),
|
||||
totalRow ? totalNumberStyle : numberStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeDecimalCell(row, col, vo.getTotalWeight(), vo.getTotalWeightCompareFlag(),
|
||||
totalRow ? totalDecimalStyle : decimalStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeTextCell(row, col, vo.getSalesmen(), totalRow ? totalTextStyle : textStyle);
|
||||
} else if (complianceLayout) {
|
||||
// 月结客户达标统计:使用新增三列
|
||||
String dateValue = vo.getFirstSigningDay();
|
||||
// 首次签约日期
|
||||
col = writeTextCell(row, col, dateValue, totalRow ? totalTextStyle : textStyle);
|
||||
// 寄件月份
|
||||
col = writeTextCell(row, col, vo.getSendMonth(), totalRow ? totalTextStyle : textStyle);
|
||||
// 是否达标(1=已达标,2=未达标)
|
||||
String complianceText;
|
||||
if (vo.getBlCompliance() == null || vo.getBlCompliance().isEmpty()) {
|
||||
complianceText = "";
|
||||
} else if ("1".equals(vo.getBlCompliance())) {
|
||||
complianceText = "已达标";
|
||||
} else if ("2".equals(vo.getBlCompliance())) {
|
||||
complianceText = "未达标";
|
||||
} else {
|
||||
complianceText = vo.getBlCompliance();
|
||||
}
|
||||
col = writeTextCell(row, col, complianceText, totalRow ? totalTextStyle : textStyle);
|
||||
|
||||
// 总票数、总重量
|
||||
col = writeIntegerCell(row, col, vo.getTotalTickets(), vo.getTotalTicketsCompareFlag(),
|
||||
totalRow ? totalNumberStyle : numberStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeDecimalCell(row, col, vo.getTotalWeight(), vo.getTotalWeightCompareFlag(),
|
||||
totalRow ? totalDecimalStyle : decimalStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
// 总运费(达标统计专用)
|
||||
col = writeIntegerCell(row, col, vo.getTotalBillFee(), null,
|
||||
totalRow ? totalNumberStyle : numberStyle, false, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
// 销售联系人
|
||||
col = writeTextCell(row, col, vo.getSalesmen(), totalRow ? totalTextStyle : textStyle);
|
||||
} else {
|
||||
// 普通月结出货统计:保持原有 5 列
|
||||
String dateValue = vo.getFirstSigningDay();
|
||||
col = writeTextCell(row, col, dateValue, totalRow ? totalTextStyle : textStyle);
|
||||
col = writeIntegerCell(row, col, vo.getTotalTickets(), vo.getTotalTicketsCompareFlag(),
|
||||
totalRow ? totalNumberStyle : numberStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeDecimalCell(row, col, vo.getTotalWeight(), vo.getTotalWeightCompareFlag(),
|
||||
totalRow ? totalDecimalStyle : decimalStyle, compareMode && !totalRow, workbook,
|
||||
redArrowFont, greenArrowFont, blueArrowFont, styleCache);
|
||||
col = writeTextCell(row, col, vo.getSalesmen(), totalRow ? totalTextStyle : textStyle);
|
||||
}
|
||||
|
||||
List<MetricGroup> groups = transportLayout ? TRANSPORT_GROUPS : COUNTRY_GROUPS;
|
||||
for (MetricGroup group : groups) {
|
||||
@ -174,7 +258,8 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static void createHeader(Sheet sheet, String[] baseHeaders, List<MetricGroup> groups, CellStyle headerStyle) {
|
||||
private static void createHeader(Sheet sheet, String[] baseHeaders, int[] baseWidths,
|
||||
List<MetricGroup> groups, CellStyle headerStyle) {
|
||||
Row topRow = sheet.createRow(0);
|
||||
Row subRow = sheet.createRow(1);
|
||||
topRow.setHeightInPoints(22);
|
||||
@ -186,7 +271,7 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
cell.setCellValue(baseHeaders[i]);
|
||||
cell.setCellStyle(headerStyle);
|
||||
sheet.addMergedRegion(new CellRangeAddress(0, 1, col, col));
|
||||
sheet.setColumnWidth(col, BASE_WIDTHS[i] * 256);
|
||||
sheet.setColumnWidth(col, baseWidths[i] * 256);
|
||||
col++;
|
||||
}
|
||||
|
||||
@ -400,9 +485,12 @@ public class MonthlyShipmentStatExcelUtil {
|
||||
total.setCustomerName("合计");
|
||||
total.setFirstSigningDay("");
|
||||
total.setSalesmen("");
|
||||
total.setSendMonth("");
|
||||
total.setBlCompliance("");
|
||||
|
||||
total.setTotalTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getTotalTickets));
|
||||
total.setTotalWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getTotalWeight));
|
||||
total.setTotalBillFee(sumInt(source, MonthlyCustomerShipmentStatVO::getTotalBillFee));
|
||||
|
||||
total.setExpressTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getExpressTickets));
|
||||
total.setExpressWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getExpressWeight));
|
||||
|
||||
@ -11,7 +11,15 @@
|
||||
package com.xdadan.erp.emis.service.impl;
|
||||
|
||||
import java.util.*;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.xdadan.erp.common.core.page.PageDomain;
|
||||
import com.xdadan.erp.common.core.page.TableSupport;
|
||||
import com.xdadan.erp.common.utils.DateUtils;
|
||||
import com.xdadan.erp.common.utils.SecurityUtils;
|
||||
import com.xdadan.erp.emis.domain.exception.EmisBizError;
|
||||
import com.xdadan.erp.emis.domain.enumtype.EmisBizErrorType;
|
||||
@ -175,14 +183,119 @@ public class EmisCustomerUserServiceImpl extends EmisBaseService implements IEmi
|
||||
} else if ("4".equals(emisWaybill.getParams().get("statType"))) {
|
||||
// 按目的国家统计,增加与上一个时间段的票数、重量对比标记(根据客户类型自动选择 key)
|
||||
result = buildShipmentStatWithCompare(emisWaybill, false);
|
||||
} else if ("5".equals(emisWaybill.getParams().get("statType"))) {
|
||||
// 月结客户达标统计
|
||||
// 校验:当complianceStatType=3/4/5/6时,寄件时间段需大于等于3个月
|
||||
checkDateForComplianceStat(emisWaybill);
|
||||
if(Objects.isNull(emisWaybill.getParams().get("beginSendDate"))){
|
||||
emisWaybill.getParams().put("beginSendDate","2024-01-01 00:00:00");
|
||||
}
|
||||
if(Objects.isNull(emisWaybill.getParams().get("endSendDate"))){
|
||||
emisWaybill.getParams().put("endSendDate", DateUtils.getDate("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
Object complianceStatTypeObj = emisWaybill.getParams().get("complianceStatType");
|
||||
String complianceStatType = complianceStatTypeObj != null ? String.valueOf(complianceStatTypeObj) : null;
|
||||
|
||||
// 统一使用应用层过滤:先清除 PageHelper 分页,查询所有数据
|
||||
PageHelper.clearPage();
|
||||
List<MonthlyCustomerShipmentStatVO> allResults = emisCustomerUserMapper.selectMonthlyCustomerShipmentStatIsCompliance(emisWaybill);
|
||||
|
||||
// 先根据 totalTickets / totalBillFee 在代码中计算 blCompliance(1=达标,2=未达标)
|
||||
if (allResults != null) {
|
||||
for (MonthlyCustomerShipmentStatVO vo : allResults) {
|
||||
if (vo == null) {
|
||||
continue;
|
||||
}
|
||||
Integer tickets = vo.getTotalTickets();
|
||||
Integer fee = vo.getTotalBillFee();
|
||||
int safeTickets = tickets == null ? 0 : tickets;
|
||||
int safeFee = fee == null ? 0 : fee;
|
||||
// 规则:票数>=5 或 总运费>=3000 视为达标
|
||||
vo.setBlCompliance((safeTickets >= 5 || safeFee >= 3000) ? "1" : "2");
|
||||
}
|
||||
}
|
||||
|
||||
// 按 complianceStatType 在应用层过滤(1~6 全走代码逻辑)
|
||||
if (complianceStatType != null && !complianceStatType.trim().isEmpty()) {
|
||||
result = filterComplianceStat(allResults, complianceStatType.trim());
|
||||
} else {
|
||||
result = allResults;
|
||||
}
|
||||
|
||||
// 保存过滤后的总数到 params 中,供 Controller 使用
|
||||
if (emisWaybill.getParams() == null) {
|
||||
emisWaybill.setParams(new HashMap<>());
|
||||
}
|
||||
emisWaybill.getParams().put("_filteredTotal", result == null ? 0L : (long) result.size());
|
||||
|
||||
// 手动分页
|
||||
if (result == null) {
|
||||
result = Collections.emptyList();
|
||||
}
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
Integer pageNum = pageDomain.getPageNum();
|
||||
Integer pageSize = pageDomain.getPageSize();
|
||||
if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
|
||||
int start = (pageNum - 1) * pageSize;
|
||||
int end = Math.min(start + pageSize, result.size());
|
||||
if (start < result.size()) {
|
||||
result = result.subList(start, end);
|
||||
} else {
|
||||
result = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = Collections.emptyList();
|
||||
}
|
||||
|
||||
sortShipmentStat(result);
|
||||
if(!"5".equals(emisWaybill.getParams().get("statType"))){
|
||||
sortShipmentStat(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void checkDateForComplianceStat(EmisWaybill emisWaybill) throws EmisBizError {
|
||||
Object complianceStatTypeObj = emisWaybill.getParams().get("complianceStatType");
|
||||
if (complianceStatTypeObj != null) {
|
||||
String complianceStatType = String.valueOf(complianceStatTypeObj);
|
||||
if ("3".equals(complianceStatType) || "4".equals(complianceStatType)
|
||||
|| "5".equals(complianceStatType) || "6".equals(complianceStatType)) {
|
||||
// 验证时间段必须大于等于3个月
|
||||
Object beginSendObj = emisWaybill.getParams().get("beginSendDate");
|
||||
Object endSendObj = emisWaybill.getParams().get("endSendDate");
|
||||
|
||||
if (beginSendObj == null || endSendObj == null) {
|
||||
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR, "请选择寄件日期");
|
||||
}
|
||||
|
||||
try {
|
||||
String beginSendDate = String.valueOf(beginSendObj).trim();
|
||||
String endSendDate = String.valueOf(endSendObj).trim();
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
LocalDate beginDate = LocalDate.parse(beginSendDate, formatter);
|
||||
LocalDate endDate = LocalDate.parse(endSendDate, formatter);
|
||||
|
||||
long monthsBetween = ChronoUnit.MONTHS.between(
|
||||
beginDate.withDayOfMonth(1),
|
||||
endDate.withDayOfMonth(1)
|
||||
);
|
||||
|
||||
if (monthsBetween < 3) {
|
||||
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
|
||||
"当达标统计类型为" + getComplianceStatTypeName(complianceStatType) + "时,寄件时间段需大于等于3个月");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof EmisBizError) {
|
||||
throw e;
|
||||
}
|
||||
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR, "日期格式错误,请使用yyyy-MM-dd HH:mm:ss格式");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询现金客户出货统计
|
||||
*
|
||||
@ -429,6 +542,184 @@ public class EmisCustomerUserServiceImpl extends EmisBaseService implements IEmi
|
||||
return v == null ? 0 : v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取达标统计类型名称
|
||||
*/
|
||||
private String getComplianceStatTypeName(String complianceStatType) {
|
||||
switch (complianceStatType) {
|
||||
case "3": return "连续3个月未出货";
|
||||
case "4": return "累计3个月未出货";
|
||||
case "5": return "连续3个月出货未达标";
|
||||
case "6": return "累计3个月出货未达标";
|
||||
default: return complianceStatType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用层过滤:根据 complianceStatType 过滤统计结果
|
||||
* 优化:将复杂的 SQL EXISTS 子查询移到应用层处理,提升查询性能
|
||||
*
|
||||
* @param allResults 所有查询结果(已按客户+月份分组)
|
||||
* @param complianceStatType 达标统计类型:3-连续3个月未出货,4-累计3个月未出货,5-连续3个月未达标,6-累计3个月未达标
|
||||
* @return 过滤后的结果列表
|
||||
*/
|
||||
private List<MonthlyCustomerShipmentStatVO> filterComplianceStat(
|
||||
List<MonthlyCustomerShipmentStatVO> allResults, String complianceStatType) {
|
||||
if (allResults == null || allResults.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 1/2:简单按 blCompliance 过滤(1=已达标,2=未达标)
|
||||
if ("1".equals(complianceStatType)) {
|
||||
return allResults.stream()
|
||||
.filter(m -> "1".equals(m.getBlCompliance()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
if ("2".equals(complianceStatType)) {
|
||||
return allResults.stream()
|
||||
.filter(m -> "2".equals(m.getBlCompliance()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 按客户分组,便于后续判断连续/累计
|
||||
Map<String, List<MonthlyCustomerShipmentStatVO>> customerMap = allResults.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
item -> item.getCustNo() != null ? item.getCustNo() : "",
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()
|
||||
));
|
||||
|
||||
List<MonthlyCustomerShipmentStatVO> filteredResults = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, List<MonthlyCustomerShipmentStatVO>> entry : customerMap.entrySet()) {
|
||||
List<MonthlyCustomerShipmentStatVO> customerMonths = entry.getValue();
|
||||
// 按月份排序(升序)
|
||||
customerMonths.sort(Comparator.comparing(MonthlyCustomerShipmentStatVO::getSendMonth));
|
||||
|
||||
switch (complianceStatType) {
|
||||
case "3":
|
||||
// 连续3个月以上出货为0
|
||||
filteredResults.addAll(filterConsecutiveZeroTickets(customerMonths));
|
||||
break;
|
||||
case "4":
|
||||
// 累计超过3个月出货为0
|
||||
filteredResults.addAll(filterAccumulatedZeroTickets(customerMonths, 3));
|
||||
break;
|
||||
case "5":
|
||||
// 连续3个月以上出货未达标
|
||||
filteredResults.addAll(filterConsecutiveNonCompliance(customerMonths));
|
||||
break;
|
||||
case "6":
|
||||
// 累计3个月以上出货未达标
|
||||
filteredResults.addAll(filterAccumulatedNonCompliance(customerMonths, 3));
|
||||
break;
|
||||
default:
|
||||
filteredResults.addAll(customerMonths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return filteredResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤连续3个月以上出货为0的月份
|
||||
*/
|
||||
private List<MonthlyCustomerShipmentStatVO> filterConsecutiveZeroTickets(
|
||||
List<MonthlyCustomerShipmentStatVO> customerMonths) {
|
||||
List<MonthlyCustomerShipmentStatVO> result = new ArrayList<>();
|
||||
for (int i = 0; i < customerMonths.size(); i++) {
|
||||
MonthlyCustomerShipmentStatVO current = customerMonths.get(i);
|
||||
if (current.getTotalTickets() == null || current.getTotalTickets() == 0) {
|
||||
// 检查前两个月是否也为0
|
||||
boolean prev1Zero = (i >= 1 && (customerMonths.get(i - 1).getTotalTickets() == null
|
||||
|| customerMonths.get(i - 1).getTotalTickets() == 0));
|
||||
boolean prev2Zero = (i >= 2 && (customerMonths.get(i - 2).getTotalTickets() == null
|
||||
|| customerMonths.get(i - 2).getTotalTickets() == 0));
|
||||
// 检查后两个月是否也为0
|
||||
boolean next1Zero = (i < customerMonths.size() - 1
|
||||
&& (customerMonths.get(i + 1).getTotalTickets() == null
|
||||
|| customerMonths.get(i + 1).getTotalTickets() == 0));
|
||||
boolean next2Zero = (i < customerMonths.size() - 2
|
||||
&& (customerMonths.get(i + 2).getTotalTickets() == null
|
||||
|| customerMonths.get(i + 2).getTotalTickets() == 0));
|
||||
|
||||
// 连续口径:某月在连续>=3的窗口内即可返回(包含窗口的前两个月或后两个月)
|
||||
if ((prev1Zero && prev2Zero) || (prev1Zero && next1Zero) || (next1Zero && next2Zero)) {
|
||||
result.add(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤累计超过指定月数出货为0的月份
|
||||
*/
|
||||
private List<MonthlyCustomerShipmentStatVO> filterAccumulatedZeroTickets(
|
||||
List<MonthlyCustomerShipmentStatVO> customerMonths, int minMonths) {
|
||||
// 统计该客户出货为0的月份总数
|
||||
long zeroTicketMonths = customerMonths.stream()
|
||||
.filter(m -> m.getTotalTickets() == null || m.getTotalTickets() == 0)
|
||||
.count();
|
||||
|
||||
if (zeroTicketMonths >= minMonths) {
|
||||
// 返回所有出货为0的月份
|
||||
return customerMonths.stream()
|
||||
.filter(m -> m.getTotalTickets() == null || m.getTotalTickets() == 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤连续3个月以上出货未达标的月份
|
||||
*/
|
||||
private List<MonthlyCustomerShipmentStatVO> filterConsecutiveNonCompliance(
|
||||
List<MonthlyCustomerShipmentStatVO> customerMonths) {
|
||||
List<MonthlyCustomerShipmentStatVO> result = new ArrayList<>();
|
||||
for (int i = 0; i < customerMonths.size(); i++) {
|
||||
MonthlyCustomerShipmentStatVO current = customerMonths.get(i);
|
||||
if ("2".equals(current.getBlCompliance())) {
|
||||
// 检查前两个月是否也未达标
|
||||
boolean prev1NonCompliance = (i >= 1 && "2".equals(customerMonths.get(i - 1).getBlCompliance()));
|
||||
boolean prev2NonCompliance = (i >= 2 && "2".equals(customerMonths.get(i - 2).getBlCompliance()));
|
||||
// 检查后两个月是否也未达标
|
||||
boolean next1NonCompliance = (i < customerMonths.size() - 1
|
||||
&& "2".equals(customerMonths.get(i + 1).getBlCompliance()));
|
||||
boolean next2NonCompliance = (i < customerMonths.size() - 2
|
||||
&& "2".equals(customerMonths.get(i + 2).getBlCompliance()));
|
||||
|
||||
// 连续口径:某月在连续>=3的窗口内即可返回
|
||||
if ((prev1NonCompliance && prev2NonCompliance)
|
||||
|| (prev1NonCompliance && next1NonCompliance)
|
||||
|| (next1NonCompliance && next2NonCompliance)) {
|
||||
result.add(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤累计超过指定月数出货未达标的月份
|
||||
*/
|
||||
private List<MonthlyCustomerShipmentStatVO> filterAccumulatedNonCompliance(
|
||||
List<MonthlyCustomerShipmentStatVO> customerMonths, int minMonths) {
|
||||
// 统计该客户未达标的月份总数
|
||||
long nonComplianceMonths = customerMonths.stream()
|
||||
.filter(m -> "2".equals(m.getBlCompliance()))
|
||||
.count();
|
||||
|
||||
if (nonComplianceMonths >= minMonths) {
|
||||
// 返回所有未达标的月份
|
||||
return customerMonths.stream()
|
||||
.filter(m -> "2".equals(m.getBlCompliance()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询月结客户出货明细
|
||||
*
|
||||
|
||||
@ -674,4 +674,160 @@
|
||||
AND del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- 月结客户达标统计 -->
|
||||
|
||||
<select id="selectMonthlyCustomerShipmentStatIsCompliance" parameterType="EmisWaybill" resultType="com.xdadan.erp.emis.domain.vo.MonthlyCustomerShipmentStatVO">
|
||||
SELECT
|
||||
ms.id,
|
||||
ms.customerCode,
|
||||
ms.customerName,
|
||||
ms.custNo,
|
||||
ms.salesmen,
|
||||
ms.firstSigningDay,
|
||||
ms.sendMonth,
|
||||
ms.totalTickets,
|
||||
ms.totalWeight,
|
||||
ms.totalBillFee,
|
||||
ms.expressTickets,
|
||||
ms.expressWeight,
|
||||
ms.airTickets,
|
||||
ms.airWeight,
|
||||
ms.landTickets,
|
||||
ms.landWeight,
|
||||
ms.seaTickets,
|
||||
ms.seaWeight,
|
||||
ms.importTickets,
|
||||
ms.importWeight,
|
||||
ms.otherTickets,
|
||||
ms.otherWeight
|
||||
FROM (
|
||||
/*
|
||||
先生成查询区间内的月份维表(m),再做 客户×月份 的骨架,
|
||||
最后 LEFT JOIN 运单并按“客户+月份”聚合,才能产出 totalTickets=0 且 sendMonth 有值的行。
|
||||
用数字表(0..36)模拟月份递增。
|
||||
*/
|
||||
SELECT
|
||||
cu.id AS id,
|
||||
cu.customer_code AS customerCode,
|
||||
cu.customer_name AS customerName,
|
||||
cu.monthly_pay_code AS custNo,
|
||||
cu.salesmen AS salesmen,
|
||||
cu.first_signing_day AS firstSigningDay,
|
||||
m.sendMonth AS sendMonth,
|
||||
COUNT(DISTINCT a.bill_code) AS totalTickets,
|
||||
IFNULL(SUM(a.bill_weight), 0) AS totalWeight,
|
||||
CAST(IFNULL(SUM(IFNULL(a.freight, 0)), 0) AS UNSIGNED) AS totalBillFee,
|
||||
SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type = '1' THEN 1 ELSE 0 END) AS expressTickets,
|
||||
IFNULL(SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type = '1' THEN a.bill_weight ELSE 0 END), 0) AS expressWeight,
|
||||
SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type = '2' THEN 1 ELSE 0 END) AS airTickets,
|
||||
IFNULL(SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type = '2' THEN a.bill_weight ELSE 0 END), 0) AS airWeight,
|
||||
SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type in ('3','5') THEN 1 ELSE 0 END) AS landTickets,
|
||||
IFNULL(SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type in ('3','5') THEN a.bill_weight ELSE 0 END), 0) AS landWeight,
|
||||
SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type = '4' THEN 1 ELSE 0 END) AS seaTickets,
|
||||
IFNULL(SUM(CASE WHEN a.send_country = '0086' AND tl.trans_type = '4' THEN a.bill_weight ELSE 0 END), 0) AS seaWeight,
|
||||
SUM(CASE WHEN a.receive_country = '0086' THEN 1 ELSE 0 END) AS importTickets,
|
||||
IFNULL(SUM(CASE WHEN a.receive_country = '0086' THEN a.bill_weight ELSE 0 END), 0) AS importWeight,
|
||||
SUM(CASE WHEN a.receive_country != '0086' AND NOT (a.send_country = '0086' AND tl.trans_type IN ('1','2','3','4','5')) THEN 1 ELSE 0 END) AS otherTickets,
|
||||
IFNULL(SUM(CASE WHEN a.receive_country != '0086' AND NOT (a.send_country = '0086' AND tl.trans_type IN ('1','2','3','4','5')) THEN a.bill_weight ELSE 0 END), 0) AS otherWeight
|
||||
FROM emis_customer_user cu
|
||||
JOIN (
|
||||
SELECT
|
||||
DATE_FORMAT(DATE_ADD(STR_TO_DATE(#{params.beginSendDate}, '%Y-%m-%d %H:%i:%s'), INTERVAL n.n MONTH), '%Y-%m') AS sendMonth,
|
||||
DATE_FORMAT(DATE_ADD(STR_TO_DATE(#{params.beginSendDate}, '%Y-%m-%d %H:%i:%s'), INTERVAL n.n MONTH), '%Y-%m-01') AS monthStart
|
||||
FROM (
|
||||
SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL
|
||||
SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL
|
||||
SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL
|
||||
SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL
|
||||
SELECT 24 UNION ALL SELECT 25 UNION ALL SELECT 26 UNION ALL SELECT 27 UNION ALL SELECT 28 UNION ALL SELECT 29 UNION ALL
|
||||
SELECT 30 UNION ALL SELECT 31 UNION ALL SELECT 32 UNION ALL SELECT 33 UNION ALL SELECT 34 UNION ALL SELECT 35 UNION ALL
|
||||
SELECT 36
|
||||
) n
|
||||
WHERE DATE_ADD(STR_TO_DATE(#{params.beginSendDate}, '%Y-%m-%d %H:%i:%s'), INTERVAL n.n MONTH)
|
||||
<![CDATA[ <= ]]> STR_TO_DATE(#{params.endSendDate}, '%Y-%m-%d %H:%i:%s')
|
||||
) m
|
||||
LEFT JOIN emis_waybill a
|
||||
ON cu.monthly_pay_code = a.cust_no
|
||||
AND a.del_flag = '0'
|
||||
AND a.order_status != '0'
|
||||
AND a.order_status != '2'
|
||||
AND a.cust_no IS NOT NULL
|
||||
AND a.cust_no != ''
|
||||
<!-- 剔除问题件类型为面单删除、出口虚拟单、返货虚拟单的运单 -->
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM emis_waybill_problem_info ewpi
|
||||
WHERE ewpi.bill_code = a.bill_code
|
||||
AND ewpi.del_flag = '0'
|
||||
AND ewpi.problem_type IN (151, 170, 173)
|
||||
)
|
||||
-- 只统计该月份内的运单
|
||||
AND a.send_date <![CDATA[ >= ]]> STR_TO_DATE(m.monthStart, '%Y-%m-%d')
|
||||
AND a.send_date <![CDATA[ < ]]> DATE_ADD(STR_TO_DATE(m.monthStart, '%Y-%m-%d'), INTERVAL 1 MONTH)
|
||||
<!-- 寄件国家 -->
|
||||
<if test="sendCountry != null and sendCountry != ''">
|
||||
AND a.send_country = #{sendCountry}
|
||||
</if>
|
||||
<!-- 目的国家 -->
|
||||
<if test="receiveCountry != null and receiveCountry != ''">
|
||||
<choose>
|
||||
<when test="receiveCountry == 'other'">
|
||||
AND a.receive_country NOT IN ('0086','0084','0855','0095','0880','0856','0066')
|
||||
</when>
|
||||
<otherwise>
|
||||
AND a.receive_country = #{receiveCountry}
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
LEFT JOIN emis_trans_line tl ON a.trans_line_type = tl.line_code AND tl.del_flag = '0'
|
||||
<where>
|
||||
cu.del_flag = '0'
|
||||
AND cu.customer_type = '2'
|
||||
<!-- 客户名称 -->
|
||||
<if test="customerName != null and customerName != ''">
|
||||
AND (cu.customer_name LIKE CONCAT('%', #{custName}, '%') OR a.cust_name LIKE CONCAT('%', #{custName}, '%'))
|
||||
</if>
|
||||
<if test="customerCode != null and customerCode != ''">
|
||||
AND (cu.customer_code = #{customerCode} OR a.customer_code = #{customerCode})
|
||||
</if>
|
||||
<if test="custNo != null and custNo != ''">
|
||||
AND cu.monthly_pay_code = #{custNo}
|
||||
</if>
|
||||
<!-- 销售主管 -->
|
||||
<if test="salesExecutive != null and salesExecutive != ''">
|
||||
AND cu.sales_executive LIKE CONCAT('%', #{salesExecutive}, '%')
|
||||
</if>
|
||||
<!-- 销售支持 -->
|
||||
<if test="salesSupport != null and salesSupport != ''">
|
||||
AND cu.sales_support LIKE CONCAT('%', #{salesSupport}, '%')
|
||||
</if>
|
||||
<!-- 销售联系人 -->
|
||||
<if test="salesmen != null and salesmen != ''">
|
||||
AND cu.salesmen LIKE CONCAT('%', #{salesmen}, '%')
|
||||
</if>
|
||||
<!-- 权限控制 -->
|
||||
<if test="params.privSiteCode != null and params.privSiteCode != '88888'">
|
||||
AND (
|
||||
cu.sales_executive = #{params.privEmpName}
|
||||
OR cu.sales_support = #{params.privEmpName}
|
||||
OR cu.salesmen = #{params.privEmpName}
|
||||
OR cu.salesmen IN (
|
||||
SELECT salesmen FROM emis_salesmen_rel esr
|
||||
WHERE esr.del_flag = '0'
|
||||
AND esr.bl_open = '1'
|
||||
AND NOW() <![CDATA[ >= ]]> esr.start_date
|
||||
AND NOW() <![CDATA[ <= ]]> esr.end_date
|
||||
AND esr.sales_ass = #{params.privEmpName}
|
||||
)
|
||||
)
|
||||
</if>
|
||||
<!-- 客户状态 -->
|
||||
<if test="params.status != null and params.status != ''">
|
||||
AND cu.status = #{params.status}
|
||||
</if>
|
||||
</where>
|
||||
GROUP BY cu.id, cu.customer_code, cu.customer_name, cu.monthly_pay_code, cu.salesmen, cu.first_signing_day, m.sendMonth
|
||||
) ms
|
||||
ORDER BY ms.customerName ASC,ms.sendMonth DESC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@ -3183,6 +3183,9 @@
|
||||
or a.take_piece_employee_code=#{params.privEmpCode}
|
||||
)
|
||||
</if>
|
||||
<if test="params.billMonth != null and params.billMonth != ''">
|
||||
and DATE_FORMAT(a.send_date,'%Y%m')= #{params.billMonth}
|
||||
</if>
|
||||
|
||||
</where>
|
||||
|
||||
@ -5083,6 +5086,9 @@
|
||||
)
|
||||
</if>
|
||||
</if>
|
||||
<if test="params.sendMonth != null and params.sendMonth != ''">
|
||||
and DATE_FORMAT(a.send_date,'%Y-%m')= #{params.sendMonth}
|
||||
</if>
|
||||
<!-- 权限控制 -->
|
||||
<if test="params.privSiteCode != null and params.privSiteCode != '88888'">
|
||||
AND EXISTS (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user