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:
commit
5f77fdaba4
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询月结客户出货明细
|
||||
*
|
||||
|
||||
@ -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() + "]已经对应了其他销售主管,一个销售支持只能对应一个销售主管");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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++;
|
||||
|
||||
@ -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>
|
||||
|
||||
<!-- 按目的国家查询月结客户出货统计 -->
|
||||
|
||||
Loading…
Reference in New Issue
Block a user