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

Reviewed-on: http://git.xdadan.loc/tanex/emis-service/pulls/251
This commit is contained in:
heyu 2025-12-03 16:28:20 +08:00
commit 5f77fdaba4
6 changed files with 569 additions and 40 deletions

View File

@ -34,7 +34,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xdadan.erp.common.annotation.Log;
import com.xdadan.erp.common.core.controller.BaseController;
import com.xdadan.erp.common.core.domain.AjaxResult;
import com.xdadan.erp.common.core.page.TableDataInfo;
import com.xdadan.erp.common.enums.BusinessType;
@ -44,6 +43,7 @@ import com.xdadan.erp.common.utils.poi.ExcelUtil;
import com.xdadan.erp.emis.service.IEmisCustomerUserService;
import com.xdadan.erp.emis.domain.EmisWaybill;
import com.xdadan.erp.emis.domain.vo.MonthlyCustomerShipmentStatVO;
import com.xdadan.erp.emis.service.excelCellStrategy.MonthlyShipmentStatExcelUtil;
/**
* 快递客户用户Controller
@ -518,8 +518,22 @@ public class EmisCustomerUserController extends EmisBaseController
setPrivParams(emisWaybill);
List<MonthlyCustomerShipmentStatVO> list = emisCustomerUserService
.selectMonthlyCustomerShipmentStat(emisWaybill);
ExcelUtil<MonthlyCustomerShipmentStatVO> util = new ExcelUtil<MonthlyCustomerShipmentStatVO>(
MonthlyCustomerShipmentStatVO.class);
util.exportExcel(response, list, "月结客户出货统计");
int statType = resolveStatType(emisWaybill);
MonthlyShipmentStatExcelUtil.export(response, list, statType);
}
private int resolveStatType(EmisWaybill emisWaybill) {
if (emisWaybill == null || emisWaybill.getParams() == null) {
return 1;
}
Object statTypeObj = emisWaybill.getParams().get("statType");
if (statTypeObj == null) {
return 1;
}
try {
return Integer.parseInt(statTypeObj.toString());
} catch (NumberFormatException e) {
return 1;
}
}
}

View File

@ -0,0 +1,489 @@
package com.xdadan.erp.emis.service.excelCellStrategy;
import com.xdadan.erp.emis.domain.vo.MonthlyCustomerShipmentStatVO;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.Collator;
import java.util.*;
import java.util.function.Function;
/**
* 自定义月结客户出货统计导出工具
*
* <p>支持两套布局(运输方式/国家维度)和两种模式(普通/同比对比箭头)。</p>
*/
public class MonthlyShipmentStatExcelUtil {
private static final List<MetricGroup> TRANSPORT_GROUPS = Arrays.asList(
new MetricGroup("快递", MonthlyCustomerShipmentStatVO::getExpressTickets,
MonthlyCustomerShipmentStatVO::getExpressWeight, MonthlyCustomerShipmentStatVO::getExpressTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getExpressWeightCompareFlag),
new MetricGroup("空运", MonthlyCustomerShipmentStatVO::getAirTickets,
MonthlyCustomerShipmentStatVO::getAirWeight, MonthlyCustomerShipmentStatVO::getAirTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getAirWeightCompareFlag),
new MetricGroup("陆运", MonthlyCustomerShipmentStatVO::getLandTickets,
MonthlyCustomerShipmentStatVO::getLandWeight, MonthlyCustomerShipmentStatVO::getLandTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getLandWeightCompareFlag),
new MetricGroup("进口", MonthlyCustomerShipmentStatVO::getImportTickets,
MonthlyCustomerShipmentStatVO::getImportWeight, MonthlyCustomerShipmentStatVO::getImportTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getImportWeightCompareFlag),
new MetricGroup("海运", MonthlyCustomerShipmentStatVO::getSeaTickets,
MonthlyCustomerShipmentStatVO::getSeaWeight, MonthlyCustomerShipmentStatVO::getSeaTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getSeaWeightCompareFlag),
new MetricGroup("其他", MonthlyCustomerShipmentStatVO::getOtherTickets,
MonthlyCustomerShipmentStatVO::getOtherWeight, MonthlyCustomerShipmentStatVO::getOtherTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getOtherWeightCompareFlag)
);
private static final List<MetricGroup> COUNTRY_GROUPS = Arrays.asList(
new MetricGroup("中国", MonthlyCustomerShipmentStatVO::getChinaTickets,
MonthlyCustomerShipmentStatVO::getChinaWeight, MonthlyCustomerShipmentStatVO::getChinaTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getChinaWeightCompareFlag),
new MetricGroup("越南", MonthlyCustomerShipmentStatVO::getVietnamTickets,
MonthlyCustomerShipmentStatVO::getVietnamWeight, MonthlyCustomerShipmentStatVO::getVietnamTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getVietnamWeightCompareFlag),
new MetricGroup("柬埔寨", MonthlyCustomerShipmentStatVO::getCambodiaTickets,
MonthlyCustomerShipmentStatVO::getCambodiaWeight, MonthlyCustomerShipmentStatVO::getCambodiaTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getCambodiaWeightCompareFlag),
new MetricGroup("缅甸", MonthlyCustomerShipmentStatVO::getMyanmarTickets,
MonthlyCustomerShipmentStatVO::getMyanmarWeight, MonthlyCustomerShipmentStatVO::getMyanmarTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getMyanmarWeightCompareFlag),
new MetricGroup("孟加拉", MonthlyCustomerShipmentStatVO::getBangladeshTickets,
MonthlyCustomerShipmentStatVO::getBangladeshWeight, MonthlyCustomerShipmentStatVO::getBangladeshTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getBangladeshWeightCompareFlag),
new MetricGroup("老挝", MonthlyCustomerShipmentStatVO::getLaosTickets,
MonthlyCustomerShipmentStatVO::getLaosWeight, MonthlyCustomerShipmentStatVO::getLaosTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getLaosWeightCompareFlag),
new MetricGroup("泰国", MonthlyCustomerShipmentStatVO::getThailandTickets,
MonthlyCustomerShipmentStatVO::getThailandWeight, MonthlyCustomerShipmentStatVO::getThailandTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getThailandWeightCompareFlag),
new MetricGroup("其他", MonthlyCustomerShipmentStatVO::getOtherTickets,
MonthlyCustomerShipmentStatVO::getOtherWeight, MonthlyCustomerShipmentStatVO::getOtherTicketsCompareFlag,
MonthlyCustomerShipmentStatVO::getOtherWeightCompareFlag)
);
private static final String[] BASE_HEADERS = {"客户名称", "首次签约日期", "总票数", "总重量", "销售联系人"};
private static final int[] BASE_WIDTHS = {28, 18, 12, 16, 24};
private MonthlyShipmentStatExcelUtil() {
}
public static void export(HttpServletResponse response, List<MonthlyCustomerShipmentStatVO> dataList, int statType) {
boolean transportLayout = statType == 1 || statType == 3;
boolean compareMode = statType == 3 || statType == 4;
List<MonthlyCustomerShipmentStatVO> safeList = dataList == null ? new ArrayList<>() : dataList;
// 为保证 Excel 导出稳定排序:先按总重量降序,再按总票数降序,最后按客户名称升序
sortForExport(safeList);
List<MonthlyCustomerShipmentStatVO> exportList = new ArrayList<>(safeList);
exportList.add(buildTotalRow(safeList));
try {
SXSSFWorkbook workbook = new SXSSFWorkbook(200);
Sheet sheet = workbook.createSheet("月结客户出货统计");
sheet.createFreezePane(0, 2);
// styles
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle textStyle = createTextStyle(workbook);
CellStyle numberStyle = createNumberStyle(workbook, false);
CellStyle decimalStyle = createNumberStyle(workbook, true);
CellStyle totalTextStyle = cloneWithBold(workbook, textStyle);
CellStyle totalNumberStyle = cloneWithBold(workbook, numberStyle);
CellStyle totalDecimalStyle = cloneWithBold(workbook, decimalStyle);
Font redArrowFont = createArrowFont(workbook, IndexedColors.RED.getIndex());
Font greenArrowFont = createArrowFont(workbook, IndexedColors.GREEN.getIndex());
Font blueArrowFont = createArrowFont(workbook, IndexedColors.BLUE.getIndex());
createHeader(sheet, transportLayout ? TRANSPORT_GROUPS : COUNTRY_GROUPS, headerStyle);
int dataStartRow = 2;
for (int i = 0; i < exportList.size(); i++) {
MonthlyCustomerShipmentStatVO vo = exportList.get(i);
boolean totalRow = i == exportList.size() - 1;
Row row = sheet.createRow(dataStartRow + i);
row.setHeightInPoints(20);
int col = 0;
col = writeTextCell(row, col, vo.getCustomerName(), totalRow ? totalTextStyle : textStyle);
col = writeTextCell(row, col, vo.getFirstSigningDay(), totalRow ? totalTextStyle : textStyle);
col = writeIntegerCell(row, col, vo.getTotalTickets(), vo.getTotalTicketsCompareFlag(),
totalRow ? totalNumberStyle : numberStyle, compareMode && !totalRow, workbook,
redArrowFont, greenArrowFont, blueArrowFont);
col = writeDecimalCell(row, col, vo.getTotalWeight(), vo.getTotalWeightCompareFlag(),
totalRow ? totalDecimalStyle : decimalStyle, compareMode && !totalRow, workbook,
redArrowFont, greenArrowFont, blueArrowFont);
col = writeTextCell(row, col, vo.getSalesmen(), totalRow ? totalTextStyle : textStyle);
List<MetricGroup> groups = transportLayout ? TRANSPORT_GROUPS : COUNTRY_GROUPS;
for (MetricGroup group : groups) {
col = writeIntegerCell(row, col, group.ticketGetter.apply(vo), group.ticketFlagGetter.apply(vo),
totalRow ? totalNumberStyle : numberStyle, compareMode && !totalRow, workbook,
redArrowFont, greenArrowFont, blueArrowFont);
col = writeDecimalCell(row, col, group.weightGetter.apply(vo), group.weightFlagGetter.apply(vo),
totalRow ? totalDecimalStyle : decimalStyle, compareMode && !totalRow, workbook,
redArrowFont, greenArrowFont, blueArrowFont);
}
}
autoSize(sheet);
writeToResponse(response, workbook, "月结客户出货统计");
} catch (IOException e) {
throw new RuntimeException("导出月结客户出货统计失败:" + e.getMessage(), e);
}
}
private static void createHeader(Sheet sheet, List<MetricGroup> groups, CellStyle headerStyle) {
Row topRow = sheet.createRow(0);
Row subRow = sheet.createRow(1);
topRow.setHeightInPoints(22);
subRow.setHeightInPoints(22);
int col = 0;
for (int i = 0; i < BASE_HEADERS.length; i++) {
Cell cell = topRow.createCell(col);
cell.setCellValue(BASE_HEADERS[i]);
cell.setCellStyle(headerStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 1, col, col));
sheet.setColumnWidth(col, BASE_WIDTHS[i] * 256);
col++;
}
for (MetricGroup group : groups) {
Cell groupCell = topRow.createCell(col);
groupCell.setCellValue(group.label);
groupCell.setCellStyle(headerStyle);
sheet.addMergedRegion(new CellRangeAddress(0, 0, col, col + 1));
Cell ticketsCell = subRow.createCell(col);
ticketsCell.setCellValue("票数");
ticketsCell.setCellStyle(headerStyle);
sheet.setColumnWidth(col, 12 * 256);
col++;
Cell weightCell = subRow.createCell(col);
weightCell.setCellValue("实际重量");
weightCell.setCellStyle(headerStyle);
sheet.setColumnWidth(col, 16 * 256);
col++;
}
}
private static int writeTextCell(Row row, int colIndex, String value, CellStyle style) {
Cell cell = row.createCell(colIndex);
cell.setCellStyle(style);
cell.setCellValue(value == null ? "" : value);
return colIndex + 1;
}
private static int writeIntegerCell(Row row, int colIndex, Integer value, Integer compareFlag, CellStyle style,
boolean compareMode, Workbook workbook, Font redArrowFont,
Font greenArrowFont, Font blueArrowFont) {
String text = value == null ? "" : value.toString();
applyRichText(row.createCell(colIndex), text, compareFlag, style, compareMode, workbook,
redArrowFont, greenArrowFont, blueArrowFont);
return colIndex + 1;
}
private static int writeDecimalCell(Row row, int colIndex, BigDecimal value, Integer compareFlag, CellStyle style,
boolean compareMode, Workbook workbook, Font redArrowFont,
Font greenArrowFont, Font blueArrowFont) {
String text = formatDecimal(value);
applyRichText(row.createCell(colIndex), text, compareFlag, style, compareMode, workbook,
redArrowFont, greenArrowFont, blueArrowFont);
return colIndex + 1;
}
private static void applyRichText(Cell cell, String valueText, Integer compareFlag, CellStyle style,
boolean compareMode, Workbook workbook, Font redArrowFont,
Font greenArrowFont, Font blueArrowFont) {
if (!compareMode) {
cell.setCellStyle(style);
cell.setCellValue(valueText);
return;
}
CompareArrow arrow = CompareArrow.of(compareFlag);
if (arrow == null) {
cell.setCellStyle(style);
cell.setCellValue(valueText);
return;
}
// 为兼容 SXSSFWorkbook,这里不再使用富文本局部着色,而是整格字体着色
Font arrowFont = arrow.pickFont(redArrowFont, greenArrowFont, blueArrowFont);
CellStyle coloredStyle = workbook.createCellStyle();
coloredStyle.cloneStyleFrom(style);
coloredStyle.setFont(arrowFont);
String display = (valueText == null || valueText.isEmpty())
? arrow.symbol
: valueText + " " + arrow.symbol;
cell.setCellStyle(coloredStyle);
cell.setCellValue(display);
}
private static String formatDecimal(BigDecimal value) {
if (value == null) {
return "";
}
BigDecimal stripped = value.stripTrailingZeros();
if (stripped.scale() < 0) {
stripped = stripped.setScale(0);
}
return stripped.toPlainString();
}
private static void autoSize(Sheet sheet) {
// SXSSF不支持autoSize,手动设置的宽度已满足,此处预留接口
}
private static CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
setBorder(style);
Font font = workbook.createFont();
font.setBold(true);
font.setFontName("宋体");
font.setFontHeightInPoints((short) 12);
style.setFont(font);
return style;
}
private static CellStyle createTextStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
setBorder(style);
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
style.setFont(font);
return style;
}
private static CellStyle createNumberStyle(Workbook workbook, boolean decimal) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.RIGHT);
style.setVerticalAlignment(VerticalAlignment.CENTER);
setBorder(style);
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
style.setFont(font);
if (decimal) {
style.setDataFormat(workbook.createDataFormat().getFormat("@"));
}
return style;
}
private static CellStyle cloneWithBold(Workbook workbook, CellStyle source) {
CellStyle style = workbook.createCellStyle();
style.cloneStyleFrom(source);
Font font = workbook.createFont();
font.setBold(true);
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
style.setFont(font);
return style;
}
private static void setBorder(CellStyle style) {
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
}
private static Font createArrowFont(Workbook workbook, short colorIndex) {
Font font = workbook.createFont();
font.setFontName("宋体");
font.setFontHeightInPoints((short) 11);
font.setBold(true);
font.setColor(colorIndex);
return font;
}
/**
* 导出排序:总重量 desc、总票数 desc、客户名称 asc(中文按本地 Collator)
*/
private static void sortForExport(List<MonthlyCustomerShipmentStatVO> list) {
if (list == null || list.isEmpty()) {
return;
}
final Collator collator = Collator.getInstance(Locale.CHINA);
list.sort(Comparator
.comparing((MonthlyCustomerShipmentStatVO vo) -> nvl(vo.getTotalWeight()), Comparator.reverseOrder())
.thenComparing(vo -> nvl(vo.getTotalTickets()), Comparator.reverseOrder())
.thenComparing(
vo -> Optional.ofNullable(vo.getCustomerName()).orElse(""),
collator
));
}
private static BigDecimal nvl(BigDecimal v) {
return v == null ? BigDecimal.ZERO : v;
}
private static Integer nvl(Integer v) {
return v == null ? 0 : v;
}
private static void writeToResponse(HttpServletResponse response, SXSSFWorkbook workbook, String sheetName)
throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode(sheetName + ".xlsx", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
try (OutputStream outputStream = response.getOutputStream()) {
workbook.write(outputStream);
workbook.close();
workbook.dispose();
}
}
private static MonthlyCustomerShipmentStatVO buildTotalRow(List<MonthlyCustomerShipmentStatVO> source) {
MonthlyCustomerShipmentStatVO total = new MonthlyCustomerShipmentStatVO();
total.setCustomerName("合计");
total.setFirstSigningDay("");
total.setSalesmen("");
total.setTotalTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getTotalTickets));
total.setTotalWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getTotalWeight));
total.setExpressTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getExpressTickets));
total.setExpressWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getExpressWeight));
total.setAirTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getAirTickets));
total.setAirWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getAirWeight));
total.setLandTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getLandTickets));
total.setLandWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getLandWeight));
total.setImportTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getImportTickets));
total.setImportWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getImportWeight));
total.setSeaTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getSeaTickets));
total.setSeaWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getSeaWeight));
total.setOtherTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getOtherTickets));
total.setOtherWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getOtherWeight));
total.setChinaTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getChinaTickets));
total.setChinaWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getChinaWeight));
total.setVietnamTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getVietnamTickets));
total.setVietnamWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getVietnamWeight));
total.setCambodiaTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getCambodiaTickets));
total.setCambodiaWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getCambodiaWeight));
total.setMyanmarTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getMyanmarTickets));
total.setMyanmarWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getMyanmarWeight));
total.setBangladeshTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getBangladeshTickets));
total.setBangladeshWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getBangladeshWeight));
total.setLaosTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getLaosTickets));
total.setLaosWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getLaosWeight));
total.setThailandTickets(sumInt(source, MonthlyCustomerShipmentStatVO::getThailandTickets));
total.setThailandWeight(sumDecimal(source, MonthlyCustomerShipmentStatVO::getThailandWeight));
return total;
}
private static Integer sumInt(List<MonthlyCustomerShipmentStatVO> source,
Function<MonthlyCustomerShipmentStatVO, Integer> getter) {
long sum = 0;
for (MonthlyCustomerShipmentStatVO item : source) {
Integer value = getter.apply(item);
if (value != null) {
sum += value;
}
}
if (sum > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;
}
return (int) sum;
}
private static BigDecimal sumDecimal(List<MonthlyCustomerShipmentStatVO> source,
Function<MonthlyCustomerShipmentStatVO, BigDecimal> getter) {
BigDecimal sum = BigDecimal.ZERO;
for (MonthlyCustomerShipmentStatVO item : source) {
BigDecimal value = getter.apply(item);
if (value != null) {
sum = sum.add(value);
}
}
if (sum.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
return sum.setScale(2, RoundingMode.HALF_UP);
}
private static class MetricGroup {
private final String label;
private final Function<MonthlyCustomerShipmentStatVO, Integer> ticketGetter;
private final Function<MonthlyCustomerShipmentStatVO, BigDecimal> weightGetter;
private final Function<MonthlyCustomerShipmentStatVO, Integer> ticketFlagGetter;
private final Function<MonthlyCustomerShipmentStatVO, Integer> weightFlagGetter;
private MetricGroup(String label,
Function<MonthlyCustomerShipmentStatVO, Integer> ticketGetter,
Function<MonthlyCustomerShipmentStatVO, BigDecimal> weightGetter,
Function<MonthlyCustomerShipmentStatVO, Integer> ticketFlagGetter,
Function<MonthlyCustomerShipmentStatVO, Integer> weightFlagGetter) {
this.label = label;
this.ticketGetter = ticketGetter;
this.weightGetter = weightGetter;
this.ticketFlagGetter = ticketFlagGetter;
this.weightFlagGetter = weightFlagGetter;
}
}
private enum CompareArrow {
UP(1, "\u2191"),
DOWN(2, "\u2193"),
FLAT(0, "\u2192");
private final int flag;
private final String symbol;
CompareArrow(int flag, String symbol) {
this.flag = flag;
this.symbol = symbol;
}
private static CompareArrow of(Integer flag) {
if (flag == null) {
return null;
}
for (CompareArrow arrow : values()) {
if (arrow.flag == flag) {
return arrow;
}
}
return null;
}
private Font pickFont(Font red, Font green, Font blue) {
switch (this) {
case UP:
return red;
case DOWN:
return green;
case FLAT:
return blue;
default:
return red;
}
}
}
}

View File

@ -162,24 +162,25 @@ public class EmisCustomerUserServiceImpl extends EmisBaseService implements IEmi
*/
@Override
public List<MonthlyCustomerShipmentStatVO> selectMonthlyCustomerShipmentStat(EmisWaybill emisWaybill) throws EmisBizError {
if("1".equals(emisWaybill.getParams().get("statType"))){
List<MonthlyCustomerShipmentStatVO> result;
if ("1".equals(emisWaybill.getParams().get("statType"))) {
// 按运输方式统计
return emisWaybillMapper.selectMonthlyCustomerShipmentStat(emisWaybill);
}
if("2".equals(emisWaybill.getParams().get("statType"))){
result = emisWaybillMapper.selectMonthlyCustomerShipmentStat(emisWaybill);
} else if ("2".equals(emisWaybill.getParams().get("statType"))) {
// 按目的国家统计
return emisWaybillMapper.selectMonthlyCustomerShipmentStatByCountry(emisWaybill);
}
if("3".equals(emisWaybill.getParams().get("statType"))){
result = emisWaybillMapper.selectMonthlyCustomerShipmentStatByCountry(emisWaybill);
} else if ("3".equals(emisWaybill.getParams().get("statType"))) {
// 按运输方式统计,增加与上一个时间段的票数、重量对比标记
return buildShipmentStatWithCompare(emisWaybill, true);
}
if("4".equals(emisWaybill.getParams().get("statType"))){
result = buildShipmentStatWithCompare(emisWaybill, true);
} else if ("4".equals(emisWaybill.getParams().get("statType"))) {
// 按目的国家统计,增加与上一个时间段的票数、重量对比标记
return buildShipmentStatWithCompare(emisWaybill, false);
result = buildShipmentStatWithCompare(emisWaybill, false);
} else {
result = Collections.emptyList();
}
return Collections.emptyList();
sortShipmentStat(result);
return result;
}
/**
@ -341,6 +342,33 @@ public class EmisCustomerUserServiceImpl extends EmisBaseService implements IEmi
return 0;
}
/**
* 统一排序规则:总重量 desc、总票数 desc、客户名称 asc
*
*/
private void sortShipmentStat(List<MonthlyCustomerShipmentStatVO> list) {
if (list == null || list.isEmpty()) {
return;
}
final java.text.Collator collator = java.text.Collator.getInstance(Locale.CHINA);
list.sort(Comparator
.comparing((MonthlyCustomerShipmentStatVO vo) -> nvl(vo.getTotalWeight()), Comparator.reverseOrder())
.thenComparing(vo -> nvl(vo.getTotalTickets()), Comparator.reverseOrder())
.thenComparing(
vo -> Optional.ofNullable(vo.getCustomerName()).orElse(""),
collator
));
}
private java.math.BigDecimal nvl(java.math.BigDecimal v) {
return v == null ? java.math.BigDecimal.ZERO : v;
}
private Integer nvl(Integer v) {
return v == null ? 0 : v;
}
/**
* 查询月结客户出货明细
*

View File

@ -23,7 +23,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 根据主键ID查询销售计提比例配置
*
*
* @param id 主键ID
* @return 配置实体
*/
@ -34,7 +34,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 查询销售计提比例配置列表
*
*
* @param query 查询条件
* @return 配置列表
*/
@ -45,7 +45,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 新增销售计提比例配置
*
*
* @param entity 配置实体
* @return 影响行数
* @throws IllegalArgumentException 占比校验不通过时抛出
@ -67,7 +67,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 修改销售计提比例配置
*
*
* @param entity 配置实体
* @return 影响行数
* @throws IllegalArgumentException 占比校验不通过时抛出
@ -89,7 +89,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 批量删除销售计提比例配置
*
*
* @param ids 主键ID数组
* @return 影响行数
*/
@ -100,7 +100,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 删除单个销售计提比例配置
*
*
* @param id 主键ID
* @return 影响行数
*/
@ -111,7 +111,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 校验销售计提比例总和是否有效(<=100%)
*
*
* @param entity 配置实体
* @return true-有效,false-无效
*/
@ -133,7 +133,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 验证一个销售联系人只能对应一个销售主管
*
*
* @param entity 配置实体
* @throws EmisBizError 如果销售联系人已经对应了其他销售主管
*/
@ -142,7 +142,7 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
&& entity.getSalesExecutive() != null && !entity.getSalesExecutive().trim().isEmpty()) {
int count = emisSalesCommissionRatioMapper.countBySalesmenAndDifferentExecutive(entity);
if (count > 0) {
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
"销售联系人[" + entity.getSalesmen() + "]已经对应了其他销售主管,一个销售联系人只能对应一个销售主管");
}
}
@ -150,24 +150,24 @@ public class EmisSalesCommissionRatioServiceImpl implements IEmisSalesCommission
/**
* 验证一个销售支持只能对应一个销售联系人、只能对应一个销售主管
*
*
* @param entity 配置实体
* @throws EmisBizError 如果销售支持已经对应了其他销售联系人或销售主管
*/
private void validateSalesSupport(EmisSalesCommissionRatio entity) throws EmisBizError {
if (entity.getSalesSupport() != null && !entity.getSalesSupport().trim().isEmpty()) {
// 检查销售支持是否已经对应了其他销售联系人
int countSalesmen = emisSalesCommissionRatioMapper.countBySalesSupportAndDifferentSalesmen(entity);
if (countSalesmen > 0) {
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
"销售支持[" + entity.getSalesSupport() + "]已经对应了其他销售联系人,一个销售支持只能对应一个销售联系人");
}
// // 检查销售支持是否已经对应了其他销售联系人
// int countSalesmen = emisSalesCommissionRatioMapper.countBySalesSupportAndDifferentSalesmen(entity);
// if (countSalesmen > 0) {
// throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
// "销售支持[" + entity.getSalesSupport() + "]已经对应了其他销售联系人,一个销售支持只能对应一个销售联系人");
// }
// 检查销售支持是否已经对应了其他销售主管
int countExecutive = emisSalesCommissionRatioMapper.countBySalesSupportAndDifferentExecutive(entity);
if (countExecutive > 0) {
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
throw new EmisBizError(EmisBizErrorType.PARAM_ERROR,
"销售支持[" + entity.getSalesSupport() + "]已经对应了其他销售主管,一个销售支持只能对应一个销售主管");
}
}
}
}
}

View File

@ -902,12 +902,10 @@ public class EmisTmsSiteBatchMissServiceImpl extends EmisBaseService implements
// 创建漏组记录
EmisTmsSiteBatchMiss miss = createMissRecord(waybill);
miss.setMissReason(waybill.getStartSiteName() +" 尚未完成组批次,请尽快完成,避免影响下一程同事组批次进度" );
miss.setMissReason("当前运单没有组批次");
miss.setMissType("2");
miss.setLineCode(waybill.getTransLineType());
miss.setProductType(waybill.getProductType());
miss.setStartSiteCode(waybill.getStartSiteCode());
miss.setNextSiteCode("");
miss.setMissType("0");
missRecords.add(miss);
successCount++;

View File

@ -4114,8 +4114,8 @@
)
</if>
</where>
GROUP BY cu.monthly_pay_code
ORDER BY totalWeight desc,totalTickets desc
GROUP BY cu.monthly_pay_code, cu.customer_name
ORDER BY totalWeight DESC, totalTickets DESC, customerName ASC
</select>
<!-- 按目的国家查询月结客户出货统计 -->