feat(wms,oms):库存管理新增入库排名统计(按仓单+货品汇总,件数降序,与出库排名tab配对);还原出库排名第一版源码

入库排名统计(新增):WMS /inboundRankApi、OMS /executionInboundRankApi 的 queryMaterialRankList 分页+合计、exportMaterialRank 导出;口径:仅已入库(status=5),明细仅level=1主行,日期区间按入仓日期warehouse_date筛选(必填);一行=仓单内某货品合计(GROUP BY 仓单+货品 ORDER BY 件数DESC);字段:入仓日期/仓单编号/客户编号/存货人/货名/件数/毛重(吨=KG/1000)/投保总额(货品档案保额,组内MAX防重复累加)/体积(CBM)+货号/规格/单位;合计行:totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount;登录上下文:日期必填校验+topOrganizationId租户隔离,WMS另注入关联仓库warehouseId。出库排名统计(还原第一版):/outboundRankApi、/executionOutboundRankApi,按货品/线路汇总排名
This commit is contained in:
rcx
2026-08-31 17:47:09 +08:00
parent a983ae639e
commit bf96f5f005
50 changed files with 2887 additions and 0 deletions
@@ -0,0 +1,26 @@
package com.mhd.oms.domain.inboundRank.repository.facade;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import java.util.List;
/**
* 入库排名统计Service接口
*
* @author rcx
* @date 2026-08-31
*/
public interface IExecutionInboundRankService {
/**
* 商品入库排名列表(仓单+货品汇总,按件数降序)
*/
List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO);
/**
* 商品入库排名合计(不分页)
*/
ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO);
}
@@ -0,0 +1,26 @@
package com.mhd.oms.domain.inboundRank.repository.mapper;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import java.util.List;
/**
* 入库排名统计Mapper接口
*
* @author rcx
* @date 2026-08-31
*/
public interface ExecutionInboundRankMapper {
/**
* 商品入库排名列表(仓单+货品汇总,按件数降序)
*/
List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO);
/**
* 商品入库排名合计(不分页,当前筛选条件全量合计)
*/
ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO);
}
@@ -0,0 +1,34 @@
package com.mhd.oms.domain.inboundRank.repository.persistence;
import com.mhd.oms.domain.inboundRank.repository.facade.IExecutionInboundRankService;
import com.mhd.oms.domain.inboundRank.repository.mapper.ExecutionInboundRankMapper;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计Service业务层处理
*
* @author rcx
* @date 2026-08-31
*/
@Service
public class ExecutionInboundRankImpl implements IExecutionInboundRankService {
@Autowired
private ExecutionInboundRankMapper executionInboundRankMapper;
@Override
public List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO) {
return executionInboundRankMapper.queryMaterialRankList(executionInboundRankDO);
}
@Override
public ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO) {
return executionInboundRankMapper.queryMaterialRankTotal(executionInboundRankDO);
}
}
@@ -0,0 +1,79 @@
package com.mhd.oms.domain.inboundRank.repository.po;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 入库排名统计PO(一行 = 某仓单内某货品的合计,按件数降序排名)
*
* <p>字段口径:入仓日期=执行入仓单入仓日期;仓单编号=执行入库单号;客户编号/存货人=入仓单货主;
* 件数/毛重/体积=仓单内该货品明细合计;毛重(吨)=明细总毛重KG/1000;
* 投保总额=货品档案保额(NGWL_TEST_WMS.material_base_info.insure_amount,组内取MAX防多批次明细行重复累加)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "入库排名统计对象", description = "入库排名统计响应对象")
public class ExecutionInboundMaterialRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "入仓日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date warehouseDate;
@ApiModelProperty("仓单编号(执行入库单号)")
@Excel(name = "仓单编号")
private String inOrderNumber;
@ApiModelProperty("客户编号")
@Excel(name = "客户编号")
private String shipperCode;
@ApiModelProperty("存货人(客户名称)")
@Excel(name = "存货人")
private String shipperName;
@ApiModelProperty("商品货号")
@Excel(name = "商品货号")
private String materialCode;
@ApiModelProperty("货名/商品名称")
@Excel(name = "货名")
private String materialName;
@ApiModelProperty("规格型号")
@Excel(name = "规格型号")
private String specificationModel;
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unitName;
@ApiModelProperty("件数(仓单内该货品合计,排名依据)")
@Excel(name = "件数")
private BigDecimal pieceCount;
@ApiModelProperty("毛重(吨)(仓单内该货品合计,KG/1000)")
@Excel(name = "毛重(吨)")
private BigDecimal grossWeightTon;
@ApiModelProperty("投保总额(货品档案保额)")
@Excel(name = "投保总额")
private BigDecimal insureAmount;
@ApiModelProperty("体积(CBM)(仓单内该货品合计)")
@Excel(name = "体积(CBM)")
private BigDecimal totalVolume;
}
@@ -0,0 +1,39 @@
package com.mhd.oms.domain.inboundRank.repository.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 入库排名统计合计PO
*
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回
* totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "入库排名统计合计对象", description = "入库排名统计合计响应对象")
public class ExecutionInboundRankTotalPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("件数合计")
private BigDecimal totalPieceCount;
@ApiModelProperty("毛重(吨)合计")
private BigDecimal totalGrossWeightTon;
@ApiModelProperty("投保总额合计(各仓单+货品行保额之和)")
private BigDecimal totalInsureAmount;
@ApiModelProperty("体积(CBM)合计")
private BigDecimal totalVolume;
@ApiModelProperty("仓单数合计")
private Long totalOrderCount;
}
@@ -0,0 +1,47 @@
package com.mhd.oms.domain.inboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 入库排名统计查询DO
*
* <p>topOrganizationId 由服务端按登录用户注入,前端不传</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class ExecutionInboundRankDO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填)")
private String warehouseDateStart;
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填)")
private String warehouseDateEnd;
@ApiModelProperty("仓单编号/入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("客户编号(模糊)")
private String shipperCode;
@ApiModelProperty("存货人/客户名称(模糊)")
private String shipperName;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
private Long topOrganizationId;
}
@@ -0,0 +1,44 @@
package com.mhd.oms.domain.inboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 入库排名统计入参DTO
*
* <p>数据源为 OMS 执行入库链路(execution_in_material_detail / execution_stock_in_order</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class ExecutionInboundRankDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
private String warehouseDateStart;
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
private String warehouseDateEnd;
@ApiModelProperty("仓单编号/入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("客户编号(模糊)")
private String shipperCode;
@ApiModelProperty("存货人/客户名称(模糊)")
private String shipperName;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
}
@@ -0,0 +1,69 @@
package com.mhd.oms.domain.inboundRank.service;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计应用服务
*
* <p>负责登录用户组织隔离注入、日期区间校验,查询逻辑在 ExecutionInboundRankDomainService → ExecutionInboundRankMapper.xml</p>
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class ExecutionInboundRankApplicationService {
@Autowired
private ExecutionInboundRankDomainService executionInboundRankDomainService;
/**
* 分页查询商品入库排名列表(仓单+货品汇总,按件数降序)
*/
public List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO) {
fillLoginContext(executionInboundRankDO);
return executionInboundRankDomainService.queryMaterialRankList(executionInboundRankDO);
}
/**
* 商品入库排名合计(当前筛选条件全量,不分页)
*/
public ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO) {
return executionInboundRankDomainService.queryMaterialRankTotal(executionInboundRankDO);
}
/**
* 导出商品入库排名(不分页全量)
*/
public List<ExecutionInboundMaterialRankPO> exportMaterialRank(ExecutionInboundRankDO executionInboundRankDO) {
fillLoginContext(executionInboundRankDO);
return executionInboundRankDomainService.queryMaterialRankList(executionInboundRankDO);
}
/**
* 注入登录用户组织隔离,并校验入仓日期区间必填
*/
private void fillLoginContext(ExecutionInboundRankDO executionInboundRankDO) {
if (StringUtils.isEmpty(executionInboundRankDO.getWarehouseDateStart()) || StringUtils.isEmpty(executionInboundRankDO.getWarehouseDateEnd())) {
throw new ServiceException("请选择统计日期区间");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1) {
executionInboundRankDO.setTopOrganizationId(topOrganizationId);
}
}
}
}
@@ -0,0 +1,36 @@
package com.mhd.oms.domain.inboundRank.service;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDTO;
import org.springframework.stereotype.Component;
/**
* 入库排名统计Assembler
*
* @author rcx
* @date 2026-08-31
*/
@Component
public class ExecutionInboundRankAssembler {
/**
* 转换实体
*/
public ExecutionInboundRankDO toDO(ExecutionInboundRankDTO executionInboundRankDTO) {
ExecutionInboundRankDO executionInboundRankDO = new ExecutionInboundRankDO();
// 拷贝
BeanUtils.copyProperties(executionInboundRankDTO, executionInboundRankDO, IgnoreNullUtil.getNullPropertyNames(executionInboundRankDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
return executionInboundRankDO;
}
}
@@ -0,0 +1,39 @@
package com.mhd.oms.domain.inboundRank.service;
import com.mhd.oms.domain.inboundRank.repository.facade.IExecutionInboundRankService;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计领域服务
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class ExecutionInboundRankDomainService {
@Autowired
private IExecutionInboundRankService executionInboundRankService;
/**
* 商品入库排名列表(仓单+货品汇总,按件数降序)
*/
public List<ExecutionInboundMaterialRankPO> queryMaterialRankList(ExecutionInboundRankDO executionInboundRankDO) {
return executionInboundRankService.queryMaterialRankList(executionInboundRankDO);
}
/**
* 商品入库排名合计(不分页)
*/
public ExecutionInboundRankTotalPO queryMaterialRankTotal(ExecutionInboundRankDO executionInboundRankDO) {
return executionInboundRankService.queryMaterialRankTotal(executionInboundRankDO);
}
}
@@ -0,0 +1,37 @@
package com.mhd.oms.domain.outboundRank.repository.facade;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import java.util.List;
/**
* 出库排名统计Service接口
*
* @author rcx
* @date 2026-08-31
*/
public interface IExecutionOutboundRankService {
/**
* 商品出库排名列表
*/
List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO);
/**
* 线路出库排名列表
*/
List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO);
/**
* 商品出库排名合计(不分页)
*/
ExecutionOutboundRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
/**
* 线路出库排名合计(不分页)
*/
ExecutionOutboundRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
}
@@ -0,0 +1,37 @@
package com.mhd.oms.domain.outboundRank.repository.mapper;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import java.util.List;
/**
* 出库排名统计Mapper接口
*
* @author rcx
* @date 2026-08-31
*/
public interface ExecutionOutboundRankMapper {
/**
* 商品出库排名列表(按销售量倒序)
*/
List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO);
/**
* 线路出库排名列表(按销售量倒序)
*/
List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO);
/**
* 商品出库排名合计(不分页,当前筛选条件全量合计)
*/
ExecutionOutboundRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
/**
* 线路出库排名合计(不分页,当前筛选条件全量合计)
*/
ExecutionOutboundRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO);
}
@@ -0,0 +1,45 @@
package com.mhd.oms.domain.outboundRank.repository.persistence;
import com.mhd.oms.domain.outboundRank.repository.facade.IExecutionOutboundRankService;
import com.mhd.oms.domain.outboundRank.repository.mapper.ExecutionOutboundRankMapper;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计Service业务层处理
*
* @author rcx
* @date 2026-08-31
*/
@Service
public class ExecutionOutboundRankImpl implements IExecutionOutboundRankService {
@Autowired
private ExecutionOutboundRankMapper executionOutboundRankMapper;
@Override
public List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankMapper.queryMaterialRankList(executionOutboundRankDO);
}
@Override
public List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankMapper.queryLineRankList(executionOutboundRankDO);
}
@Override
public ExecutionOutboundRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankMapper.queryMaterialRankTotal(executionOutboundRankDO);
}
@Override
public ExecutionOutboundRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankMapper.queryLineRankTotal(executionOutboundRankDO);
}
}
@@ -0,0 +1,45 @@
package com.mhd.oms.domain.outboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 线路出库排名PO
*
* <p>按日期区间对执行出库单聚合:销售量=SUM(出库数量),交易数=COUNT(DISTINCT 出库单号),金额=SUM(明细总价);
* 仅统计带线路的出库单(line_code 为 2026-08-20 新增冗余字段,历史单为空)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "线路出库排名对象", description = "线路出库排名响应对象")
public class ExecutionOutboundLineRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("线路编码")
@Excel(name = "线路编码")
private String lineCode;
@ApiModelProperty("线路名称")
@Excel(name = "线路名称")
private String lineName;
@ApiModelProperty("销售量(出库数量合计)")
@Excel(name = "销售量")
private BigDecimal salesQuantity;
@ApiModelProperty("交易数(出库单据数)")
@Excel(name = "交易数")
private Long tradeCount;
@ApiModelProperty("金额")
@Excel(name = "金额")
private BigDecimal amount;
}
@@ -0,0 +1,52 @@
package com.mhd.oms.domain.outboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 商品出库排名PO
*
* <p>按日期区间对执行出库明细聚合:销售量=SUM(出库数量),交易数=COUNT(DISTINCT 出库单号),金额=SUM(明细总价)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "商品出库排名对象", description = "商品出库排名响应对象")
public class ExecutionOutboundMaterialRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("商品货号")
@Excel(name = "商品货号")
private String materialCode;
@ApiModelProperty("商品名称")
@Excel(name = "商品名称")
private String materialName;
@ApiModelProperty("规格")
@Excel(name = "规格")
private String specificationModel;
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unitName;
@ApiModelProperty("销售量(出库数量合计)")
@Excel(name = "销售量")
private BigDecimal salesQuantity;
@ApiModelProperty("交易数(出库单据数)")
@Excel(name = "交易数")
private Long tradeCount;
@ApiModelProperty("金额")
@Excel(name = "金额")
private BigDecimal amount;
}
@@ -0,0 +1,32 @@
package com.mhd.oms.domain.outboundRank.repository.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 出库排名合计PO
*
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回(totalSalesQuantity/totalTradeCount/totalAmount</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "出库排名合计对象", description = "出库排名合计响应对象")
public class ExecutionOutboundRankTotalPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("销售量合计")
private BigDecimal totalSalesQuantity;
@ApiModelProperty("交易数合计(去重出库单数)")
private Long totalTradeCount;
@ApiModelProperty("金额合计")
private BigDecimal totalAmount;
}
@@ -0,0 +1,50 @@
package com.mhd.oms.domain.outboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 出库排名统计查询DO
*
* <p>topOrganizationId 由服务端按登录用户注入,前端不传</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class ExecutionOutboundRankDO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填)")
private String createTimeStart;
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填)")
private String createTimeEnd;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("商品名称(模糊)")
private String materialName;
@ApiModelProperty("线路编码(模糊)")
private String lineCode;
@ApiModelProperty("线路名称(模糊)")
private String lineName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("客户名称(模糊)")
private String customerName;
@ApiModelProperty("货主名称(模糊)")
private String shipperName;
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
private Long topOrganizationId;
}
@@ -0,0 +1,45 @@
package com.mhd.oms.domain.outboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 出库排名统计入参DTO
*
* <p>按日期区间统计商品/线路出库量排名,数据源为 OMS 执行出库链路(execution_out_material_detail / execution_stock_out_order</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class ExecutionOutboundRankDTO {
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填,按执行出库单创建时间统计)")
private String createTimeStart;
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填,按执行出库单创建时间统计)")
private String createTimeEnd;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("商品名称(模糊)")
private String materialName;
@ApiModelProperty("线路编码(模糊)")
private String lineCode;
@ApiModelProperty("线路名称(模糊)")
private String lineName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("客户名称(模糊)")
private String customerName;
@ApiModelProperty("货主名称(模糊)")
private String shipperName;
}
@@ -0,0 +1,93 @@
package com.mhd.oms.domain.outboundRank.service;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计应用服务
*
* <p>负责登录用户组织隔离注入、日期区间校验,统计逻辑在 ExecutionOutboundRankDomainService → ExecutionOutboundRankMapper.xml</p>
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class ExecutionOutboundRankApplicationService {
@Autowired
private ExecutionOutboundRankDomainService executionOutboundRankDomainService;
/**
* 分页查询商品出库排名列表
*/
public List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
fillLoginContext(executionOutboundRankDO);
return executionOutboundRankDomainService.queryMaterialRankList(executionOutboundRankDO);
}
/**
* 分页查询线路出库排名列表
*/
public List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
fillLoginContext(executionOutboundRankDO);
return executionOutboundRankDomainService.queryLineRankList(executionOutboundRankDO);
}
/**
* 商品出库排名合计(当前筛选条件全量,不分页)
*/
public ExecutionOutboundRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankDomainService.queryMaterialRankTotal(executionOutboundRankDO);
}
/**
* 线路出库排名合计(当前筛选条件全量,不分页)
*/
public ExecutionOutboundRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankDomainService.queryLineRankTotal(executionOutboundRankDO);
}
/**
* 导出商品出库排名(不分页全量)
*/
public List<ExecutionOutboundMaterialRankPO> exportMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
fillLoginContext(executionOutboundRankDO);
return executionOutboundRankDomainService.queryMaterialRankList(executionOutboundRankDO);
}
/**
* 导出线路出库排名(不分页全量)
*/
public List<ExecutionOutboundLineRankPO> exportLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
fillLoginContext(executionOutboundRankDO);
return executionOutboundRankDomainService.queryLineRankList(executionOutboundRankDO);
}
/**
* 注入登录用户组织隔离,并校验日期区间必填
*/
private void fillLoginContext(ExecutionOutboundRankDO executionOutboundRankDO) {
if (StringUtils.isEmpty(executionOutboundRankDO.getCreateTimeStart()) || StringUtils.isEmpty(executionOutboundRankDO.getCreateTimeEnd())) {
throw new ServiceException("请选择统计日期区间");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1) {
executionOutboundRankDO.setTopOrganizationId(topOrganizationId);
}
}
}
}
@@ -0,0 +1,36 @@
package com.mhd.oms.domain.outboundRank.service;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDTO;
import org.springframework.stereotype.Component;
/**
* 出库排名统计Assembler
*
* @author rcx
* @date 2026-08-31
*/
@Component
public class ExecutionOutboundRankAssembler {
/**
* 转换实体
*/
public ExecutionOutboundRankDO toDO(ExecutionOutboundRankDTO executionOutboundRankDTO) {
ExecutionOutboundRankDO executionOutboundRankDO = new ExecutionOutboundRankDO();
// 拷贝
BeanUtils.copyProperties(executionOutboundRankDTO, executionOutboundRankDO, IgnoreNullUtil.getNullPropertyNames(executionOutboundRankDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
return executionOutboundRankDO;
}
}
@@ -0,0 +1,54 @@
package com.mhd.oms.domain.outboundRank.service;
import com.mhd.oms.domain.outboundRank.repository.facade.IExecutionOutboundRankService;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计领域服务
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class ExecutionOutboundRankDomainService {
@Autowired
private IExecutionOutboundRankService executionOutboundRankService;
/**
* 商品出库排名列表
*/
public List<ExecutionOutboundMaterialRankPO> queryMaterialRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankService.queryMaterialRankList(executionOutboundRankDO);
}
/**
* 线路出库排名列表
*/
public List<ExecutionOutboundLineRankPO> queryLineRankList(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankService.queryLineRankList(executionOutboundRankDO);
}
/**
* 商品出库排名合计(不分页)
*/
public ExecutionOutboundRankTotalPO queryMaterialRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankService.queryMaterialRankTotal(executionOutboundRankDO);
}
/**
* 线路出库排名合计(不分页)
*/
public ExecutionOutboundRankTotalPO queryLineRankTotal(ExecutionOutboundRankDO executionOutboundRankDO) {
return executionOutboundRankService.queryLineRankTotal(executionOutboundRankDO);
}
}
@@ -0,0 +1,107 @@
package com.mhd.oms.interfaces.facade.inboundRank;
import com.github.pagehelper.PageInfo;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.utils.PageUtils;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO;
import com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO;
import com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDTO;
import com.mhd.oms.domain.inboundRank.service.ExecutionInboundRankApplicationService;
import com.mhd.oms.domain.inboundRank.service.ExecutionInboundRankAssembler;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
* 入库排名统计Api
*
* <p>按入仓日期区间筛选商品入库量统计排名。统计口径:入仓日期 + 仅已入库状态(5),
* 明细仅取 level=1 主行;一行 = 某仓单内某货品的合计,按件数降序排名。
* 字段:入仓日期/仓单编号/客户编号/存货人/货名/件数/毛重(吨)/投保总额/体积(CBM)。
* 与出库排名(/executionOutboundRankApi)配对,前端用 tab 页区分</p>
*
* @author rcx
* @date 2026-08-31
*/
@RestController
@RequestMapping("/executionInboundRankApi")
@Slf4j
public class ExecutionInboundRankApi {
@Resource
private ExecutionInboundRankAssembler executionInboundRankAssembler;
@Autowired
private ExecutionInboundRankApplicationService executionInboundRankApplicationService;
/**
* 分页查询商品入库排名列表(含合计行)
*/
@ApiOperation("分页查询商品入库排名列表")
@GetMapping("/queryMaterialRankList")
public HashMap<String, Object> queryMaterialRankList(ExecutionInboundRankDTO executionInboundRankDTO) {
//转换实体
ExecutionInboundRankDO executionInboundRankDO = executionInboundRankAssembler.toDO(executionInboundRankDTO);
startPage();
List<ExecutionInboundMaterialRankPO> list = executionInboundRankApplicationService.queryMaterialRankList(executionInboundRankDO);
ExecutionInboundRankTotalPO total = executionInboundRankApplicationService.queryMaterialRankTotal(executionInboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 导出商品入库排名Excel
*/
@ApiOperation("导出商品入库排名Excel")
@GetMapping("/exportMaterialRank")
public void exportMaterialRank(HttpServletResponse response, ExecutionInboundRankDTO executionInboundRankDTO) throws Exception {
//转换实体(不分页,导出全量)
ExecutionInboundRankDO executionInboundRankDO = executionInboundRankAssembler.toDO(executionInboundRankDTO);
List<ExecutionInboundMaterialRankPO> list = executionInboundRankApplicationService.exportMaterialRank(executionInboundRankDO);
String fileName = URLEncoder.encode("入库排名统计", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<ExecutionInboundMaterialRankPO> util = new ExcelUtil<>(ExecutionInboundMaterialRankPO.class);
util.exportExcel(response, list, "入库排名统计");
}
/**
* 分页结果 + 合计行:data/total/code/msg 之外追加
* totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount
*/
private HashMap<String, Object> getDataTableWithTotal(List<?> list, ExecutionInboundRankTotalPO total) {
HashMap<String, Object> result = new HashMap<>();
result.put("data", list);
result.put("total", new PageInfo<>(list).getTotal());
result.put("code", HttpStatus.SUCCESS);
result.put("msg", "查询成功");
if (total != null) {
result.put("totalPieceCount", total.getTotalPieceCount());
result.put("totalGrossWeightTon", total.getTotalGrossWeightTon());
result.put("totalInsureAmount", total.getTotalInsureAmount());
result.put("totalVolume", total.getTotalVolume());
result.put("totalOrderCount", total.getTotalOrderCount());
}
return result;
}
/**
* 设置请求分页数据
*/
protected void startPage() {
PageUtils.startPage();
}
}
@@ -0,0 +1,136 @@
package com.mhd.oms.interfaces.facade.outboundRank;
import com.github.pagehelper.PageInfo;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.utils.PageUtils;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO;
import com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO;
import com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDTO;
import com.mhd.oms.domain.outboundRank.service.ExecutionOutboundRankApplicationService;
import com.mhd.oms.domain.outboundRank.service.ExecutionOutboundRankAssembler;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
* 出库排名统计Api
*
* <p>按日期区间筛选,统计商品/线路出库量排名。统计口径:执行出库单创建时间 + 已出库状态(9),
* 明细仅取 level=1 主行;销售量=SUM(出库数量)、交易数=COUNT(DISTINCT 出库单号)、金额=SUM(明细总价)</p>
*
* @author rcx
* @date 2026-08-31
*/
@RestController
@RequestMapping("/executionOutboundRankApi")
@Slf4j
public class ExecutionOutboundRankApi {
@Resource
private ExecutionOutboundRankAssembler executionOutboundRankAssembler;
@Autowired
private ExecutionOutboundRankApplicationService executionOutboundRankApplicationService;
/**
* 分页查询商品出库排名列表(含合计行)
*/
@ApiOperation("分页查询商品出库排名列表")
@GetMapping("/queryMaterialRankList")
public HashMap<String, Object> queryMaterialRankList(ExecutionOutboundRankDTO executionOutboundRankDTO) {
//转换实体
ExecutionOutboundRankDO executionOutboundRankDO = executionOutboundRankAssembler.toDO(executionOutboundRankDTO);
startPage();
List<ExecutionOutboundMaterialRankPO> list = executionOutboundRankApplicationService.queryMaterialRankList(executionOutboundRankDO);
ExecutionOutboundRankTotalPO total = executionOutboundRankApplicationService.queryMaterialRankTotal(executionOutboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 分页查询线路出库排名列表(含合计行)
*/
@ApiOperation("分页查询线路出库排名列表")
@GetMapping("/queryLineRankList")
public HashMap<String, Object> queryLineRankList(ExecutionOutboundRankDTO executionOutboundRankDTO) {
//转换实体
ExecutionOutboundRankDO executionOutboundRankDO = executionOutboundRankAssembler.toDO(executionOutboundRankDTO);
startPage();
List<ExecutionOutboundLineRankPO> list = executionOutboundRankApplicationService.queryLineRankList(executionOutboundRankDO);
ExecutionOutboundRankTotalPO total = executionOutboundRankApplicationService.queryLineRankTotal(executionOutboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 导出商品出库排名Excel
*/
@ApiOperation("导出商品出库排名Excel")
@GetMapping("/exportMaterialRank")
public void exportMaterialRank(HttpServletResponse response, ExecutionOutboundRankDTO executionOutboundRankDTO) throws Exception {
//转换实体(不分页,导出全量)
ExecutionOutboundRankDO executionOutboundRankDO = executionOutboundRankAssembler.toDO(executionOutboundRankDTO);
List<ExecutionOutboundMaterialRankPO> list = executionOutboundRankApplicationService.exportMaterialRankList(executionOutboundRankDO);
String fileName = URLEncoder.encode("商品出库排名", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<ExecutionOutboundMaterialRankPO> util = new ExcelUtil<>(ExecutionOutboundMaterialRankPO.class);
util.exportExcel(response, list, "商品出库排名");
}
/**
* 导出线路出库排名Excel
*/
@ApiOperation("导出线路出库排名Excel")
@GetMapping("/exportLineRank")
public void exportLineRank(HttpServletResponse response, ExecutionOutboundRankDTO executionOutboundRankDTO) throws Exception {
//转换实体(不分页,导出全量)
ExecutionOutboundRankDO executionOutboundRankDO = executionOutboundRankAssembler.toDO(executionOutboundRankDTO);
List<ExecutionOutboundLineRankPO> list = executionOutboundRankApplicationService.exportLineRankList(executionOutboundRankDO);
String fileName = URLEncoder.encode("线路出库排名", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<ExecutionOutboundLineRankPO> util = new ExcelUtil<>(ExecutionOutboundLineRankPO.class);
util.exportExcel(response, list, "线路出库排名");
}
/**
* 分页结果 + 合计行:data/total/code/msg 之外追加 totalSalesQuantity/totalTradeCount/totalAmount
*/
private HashMap<String, Object> getDataTableWithTotal(List<?> list, ExecutionOutboundRankTotalPO total) {
HashMap<String, Object> result = new HashMap<>();
result.put("data", list);
result.put("total", new PageInfo<>(list).getTotal());
result.put("code", HttpStatus.SUCCESS);
result.put("msg", "查询成功");
if (total != null) {
result.put("totalSalesQuantity", total.getTotalSalesQuantity());
result.put("totalTradeCount", total.getTotalTradeCount());
result.put("totalAmount", total.getTotalAmount());
}
return result;
}
/**
* 设置请求分页数据
*/
protected void startPage() {
PageUtils.startPage();
}
}
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mhd.oms.domain.inboundRank.repository.mapper.ExecutionInboundRankMapper">
<!--
入库排名统计公共查询条件(OMS 执行入库链路)
数据源:执行入库明细 execution_in_material_detail(a,仅 level=1 主行防重复) join 执行入仓单 execution_stock_in_order(b)
left join NGWL_TEST_WMS.material_base_info(c,跨库取货品档案保额作投保总额)
口径:b.status = 5已入库(OMS 入库状态枚举 1~7,排除已取消(6)/已关闭(7)及未完成单据);
日期筛选按执行入仓单入仓日期 b.warehouse_date(与列表"入仓日期"列同源);
一行 = 某仓单(b.in_order_number)内某货品(a.material_code)的合计,按件数降序排名
-->
<sql id="inboundRankConditions">
a.del_flag = 1
and a.level = 1
and b.del_flag = 1
and b.status = 5
<if test="topOrganizationId != null">
and b.top_organization_id = #{topOrganizationId}
</if>
<if test="warehouseName != null and warehouseName != ''">
and b.warehouse_name like concat('%', #{warehouseName}, '%')
</if>
<if test="inOrderNumber != null and inOrderNumber != ''">
and b.in_order_number like concat('%', #{inOrderNumber}, '%')
</if>
<if test="shipperCode != null and shipperCode != ''">
and b.shipper_code like concat('%', #{shipperCode}, '%')
</if>
<if test="shipperName != null and shipperName != ''">
and b.shipper_name like concat('%', #{shipperName}, '%')
</if>
<if test="materialCode != null and materialCode != ''">
and a.material_code like concat('%', #{materialCode}, '%')
</if>
<if test="materialName != null and materialName != ''">
and a.material_name like concat('%', #{materialName}, '%')
</if>
<if test="warehouseDateStart != null and warehouseDateStart != ''">
and date_format(b.warehouse_date,'%Y-%m-%d') &gt;= #{warehouseDateStart}
</if>
<if test="warehouseDateEnd != null and warehouseDateEnd != ''">
and date_format(b.warehouse_date,'%Y-%m-%d') &lt;= #{warehouseDateEnd}
</if>
</sql>
<sql id="inboundRankFrom">
from execution_in_material_detail a
join execution_stock_in_order b on a.in_order_number = b.in_order_number and b.del_flag = 1
left join NGWL_TEST_WMS.material_base_info c on a.material_base_info_id = c.material_base_info_id
</sql>
<!-- 商品入库排名列表(仓单+货品汇总,按件数降序) -->
<select id="queryMaterialRankList" parameterType="com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO"
resultType="com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundMaterialRankPO">
select
MAX(b.warehouse_date) AS warehouseDate,
b.in_order_number AS inOrderNumber,
MAX(b.shipper_code) AS shipperCode,
MAX(b.shipper_name) AS shipperName,
a.material_code AS materialCode,
MAX(a.material_name) AS materialName,
MAX(a.SPECIFICATION_MODEL) AS specificationModel,
MAX(a.unit_name) AS unitName,
IFNULL(SUM(a.PIECE_COUNT), 0) AS pieceCount,
ROUND(IFNULL(SUM(a.TOTAL_GROSS_WEIGHT), 0) / 1000, 3) AS grossWeightTon,
MAX(IFNULL(c.insure_amount, 0)) AS insureAmount,
IFNULL(SUM(a.TOTAL_VOLUME), 0) AS totalVolume
<include refid="inboundRankFrom"/>
<where>
<include refid="inboundRankConditions"/>
</where>
GROUP BY b.in_order_number, a.material_code
order by pieceCount desc, warehouseDate desc, inOrderNumber desc
</select>
<!-- 商品入库排名合计(当前筛选条件全量;外层对分组结果再汇总,保证投保总额合计=各行列值之和) -->
<select id="queryMaterialRankTotal" parameterType="com.mhd.oms.domain.inboundRank.repository.todo.ExecutionInboundRankDO"
resultType="com.mhd.oms.domain.inboundRank.repository.po.ExecutionInboundRankTotalPO">
select
IFNULL(SUM(t.pieceCount), 0) AS totalPieceCount,
ROUND(IFNULL(SUM(t.grossWeightKG), 0) / 1000, 3) AS totalGrossWeightTon,
IFNULL(SUM(t.insureAmount), 0) AS totalInsureAmount,
IFNULL(SUM(t.totalVolume), 0) AS totalVolume,
COUNT(DISTINCT t.inOrderNumber) AS totalOrderCount
from (
select
b.in_order_number AS inOrderNumber,
IFNULL(SUM(a.PIECE_COUNT), 0) AS pieceCount,
IFNULL(SUM(a.TOTAL_GROSS_WEIGHT), 0) AS grossWeightKG,
MAX(IFNULL(c.insure_amount, 0)) AS insureAmount,
IFNULL(SUM(a.TOTAL_VOLUME), 0) AS totalVolume
<include refid="inboundRankFrom"/>
<where>
<include refid="inboundRankConditions"/>
</where>
GROUP BY b.in_order_number, a.material_code
) t
</select>
</mapper>
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mhd.oms.domain.outboundRank.repository.mapper.ExecutionOutboundRankMapper">
<!--
出库排名统计公共查询条件(OMS 执行出库链路)
数据源:执行出库明细 execution_out_material_detail(a,仅 level=1 主行防重复) join 执行出库单 execution_stock_out_order(b)
口径:b.status = 9已出库(OMS 状态枚举 1~11,排除已取消(10)/已关闭(11),无 12/13);
日期按执行出库单创建时间 b.create_time
-->
<sql id="outboundRankConditions">
a.del_flag = 1
and a.level = 1
and b.del_flag = 1
and b.status = 9
<if test="topOrganizationId != null">
and b.top_organization_id = #{topOrganizationId}
</if>
<if test="warehouseName != null and warehouseName != ''">
and b.warehouse_name like concat('%', #{warehouseName}, '%')
</if>
<if test="customerName != null and customerName != ''">
and b.customer_name like concat('%', #{customerName}, '%')
</if>
<if test="shipperName != null and shipperName != ''">
and b.shipper_name like concat('%', #{shipperName}, '%')
</if>
<if test="lineCode != null and lineCode != ''">
and b.line_code like concat('%', #{lineCode}, '%')
</if>
<if test="lineName != null and lineName != ''">
and b.line_name like concat('%', #{lineName}, '%')
</if>
<if test="createTimeStart != null and createTimeStart != ''">
and date_format(b.create_time,'%Y-%m-%d') &gt;= #{createTimeStart}
</if>
<if test="createTimeEnd != null and createTimeEnd != ''">
and date_format(b.create_time,'%Y-%m-%d') &lt;= #{createTimeEnd}
</if>
</sql>
<sql id="outboundRankFrom">
from execution_out_material_detail a
join execution_stock_out_order b on a.out_order_number = b.out_order_number and b.del_flag = 1
</sql>
<!-- 商品出库排名列表 -->
<select id="queryMaterialRankList" parameterType="com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO"
resultType="com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundMaterialRankPO">
select
a.material_code AS materialCode,
MAX(a.material_name) AS materialName,
MAX(a.SPECIFICATION_MODEL) AS specificationModel,
MAX(a.unit_name) AS unitName,
IFNULL(SUM(a.outbound_quantity), 0) AS salesQuantity,
COUNT(DISTINCT a.out_order_number) AS tradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS amount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
<if test="materialCode != null and materialCode != ''">
and a.material_code like concat('%', #{materialCode}, '%')
</if>
<if test="materialName != null and materialName != ''">
and a.material_name like concat('%', #{materialName}, '%')
</if>
</where>
GROUP BY a.material_code
order by salesQuantity desc
</select>
<!-- 线路出库排名列表(线路为空的历史单不参与线路排名) -->
<select id="queryLineRankList" parameterType="com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO"
resultType="com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundLineRankPO">
select
b.line_code AS lineCode,
MAX(b.line_name) AS lineName,
IFNULL(SUM(a.outbound_quantity), 0) AS salesQuantity,
COUNT(DISTINCT b.out_order_number) AS tradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS amount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
and b.line_code is not null and b.line_code != ''
</where>
GROUP BY b.line_code
order by salesQuantity desc
</select>
<!-- 商品出库排名合计(当前筛选条件全量) -->
<select id="queryMaterialRankTotal" parameterType="com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO"
resultType="com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO">
select
IFNULL(SUM(a.outbound_quantity), 0) AS totalSalesQuantity,
COUNT(DISTINCT a.out_order_number) AS totalTradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS totalAmount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
<if test="materialCode != null and materialCode != ''">
and a.material_code like concat('%', #{materialCode}, '%')
</if>
<if test="materialName != null and materialName != ''">
and a.material_name like concat('%', #{materialName}, '%')
</if>
</where>
</select>
<!-- 线路出库排名合计(当前筛选条件全量) -->
<select id="queryLineRankTotal" parameterType="com.mhd.oms.domain.outboundRank.repository.todo.ExecutionOutboundRankDO"
resultType="com.mhd.oms.domain.outboundRank.repository.po.ExecutionOutboundRankTotalPO">
select
IFNULL(SUM(a.outbound_quantity), 0) AS totalSalesQuantity,
COUNT(DISTINCT b.out_order_number) AS totalTradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS totalAmount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
and b.line_code is not null and b.line_code != ''
</where>
</select>
</mapper>
@@ -0,0 +1,76 @@
package com.mhd.wms.application.service.inboundRank;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.domain.cache.AssociationWarehouseCacheDO;
import com.mhd.system.api.domain.cache.SystemServiceCacheUtil;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import com.mhd.wms.domain.inboundRank.service.InboundRankDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计应用服务
*
* <p>负责登录用户组织隔离注入、日期区间校验,查询逻辑在 InboundRankDomainService → InboundRankMapper.xml</p>
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class InboundRankApplicationService {
@Autowired
private InboundRankDomainService inboundRankDomainService;
/**
* 分页查询商品入库排名列表(仓单+货品汇总,按件数降序)
*/
public List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO) {
fillLoginContext(inboundRankDO);
return inboundRankDomainService.queryMaterialRankList(inboundRankDO);
}
/**
* 商品入库排名合计(当前筛选条件全量,不分页)
*/
public InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO) {
return inboundRankDomainService.queryMaterialRankTotal(inboundRankDO);
}
/**
* 导出商品入库排名(不分页全量)
*/
public List<InboundMaterialRankPO> exportMaterialRank(InboundRankDO inboundRankDO) {
fillLoginContext(inboundRankDO);
return inboundRankDomainService.queryMaterialRankList(inboundRankDO);
}
/**
* 注入登录用户组织隔离与关联仓库,并校验入仓日期区间必填
*/
private void fillLoginContext(InboundRankDO inboundRankDO) {
if (StringUtils.isEmpty(inboundRankDO.getWarehouseDateStart()) || StringUtils.isEmpty(inboundRankDO.getWarehouseDateEnd())) {
throw new ServiceException("请选择统计日期区间");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1) {
inboundRankDO.setTopOrganizationId(topOrganizationId);
}
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
if (associationWarehouseCacheDO != null && associationWarehouseCacheDO.getWarehouseId() != null) {
inboundRankDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
}
}
}
}
@@ -0,0 +1,100 @@
package com.mhd.wms.application.service.outboundRank;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.domain.cache.AssociationWarehouseCacheDO;
import com.mhd.system.api.domain.cache.SystemServiceCacheUtil;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import com.mhd.wms.domain.outboundRank.service.OutboundRankDomainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计应用服务
*
* <p>负责登录用户组织隔离注入、日期区间校验,统计逻辑在 OutboundRankDomainService → OutboundRankMapper.xml</p>
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class OutboundRankApplicationService {
@Autowired
private OutboundRankDomainService outboundRankDomainService;
/**
* 分页查询商品出库排名列表
*/
public List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryMaterialRankList(outboundRankDO);
}
/**
* 分页查询线路出库排名列表
*/
public List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryLineRankList(outboundRankDO);
}
/**
* 商品出库排名合计(当前筛选条件全量,不分页)
*/
public OutboundRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankDomainService.queryMaterialRankTotal(outboundRankDO);
}
/**
* 线路出库排名合计(当前筛选条件全量,不分页)
*/
public OutboundRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankDomainService.queryLineRankTotal(outboundRankDO);
}
/**
* 导出商品出库排名(不分页全量)
*/
public List<OutboundMaterialRankPO> exportMaterialRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryMaterialRankList(outboundRankDO);
}
/**
* 导出线路出库排名(不分页全量)
*/
public List<OutboundLineRankPO> exportLineRankList(OutboundRankDO outboundRankDO) {
fillLoginContext(outboundRankDO);
return outboundRankDomainService.queryLineRankList(outboundRankDO);
}
/**
* 注入登录用户组织隔离与关联仓库,并校验日期区间必填
*/
private void fillLoginContext(OutboundRankDO outboundRankDO) {
if (StringUtils.isEmpty(outboundRankDO.getCreateTimeStart()) || StringUtils.isEmpty(outboundRankDO.getCreateTimeEnd())) {
throw new ServiceException("请选择统计日期区间");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null && loginUser.getUserPo() != null) {
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
if (topOrganizationId != null && topOrganizationId != 1) {
outboundRankDO.setTopOrganizationId(topOrganizationId);
}
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
if (associationWarehouseCacheDO != null && associationWarehouseCacheDO.getWarehouseId() != null) {
outboundRankDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
}
}
}
}
@@ -0,0 +1,26 @@
package com.mhd.wms.domain.inboundRank.repository.facade;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import java.util.List;
/**
* 入库排名统计Service接口
*
* @author rcx
* @date 2026-08-31
*/
public interface IInboundRankService {
/**
* 商品入库排名列表(仓单+货品汇总,按件数降序)
*/
List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO);
/**
* 商品入库排名合计(不分页)
*/
InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO);
}
@@ -0,0 +1,26 @@
package com.mhd.wms.domain.inboundRank.repository.mapper;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import java.util.List;
/**
* 入库排名统计Mapper接口
*
* @author rcx
* @date 2026-08-31
*/
public interface InboundRankMapper {
/**
* 商品入库排名列表(仓单+货品汇总,按件数降序)
*/
List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO);
/**
* 商品入库排名合计(不分页,当前筛选条件全量合计)
*/
InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO);
}
@@ -0,0 +1,34 @@
package com.mhd.wms.domain.inboundRank.repository.persistence;
import com.mhd.wms.domain.inboundRank.repository.facade.IInboundRankService;
import com.mhd.wms.domain.inboundRank.repository.mapper.InboundRankMapper;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计Service业务层处理
*
* @author rcx
* @date 2026-08-31
*/
@Service
public class InboundRankImpl implements IInboundRankService {
@Autowired
private InboundRankMapper inboundRankMapper;
@Override
public List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO) {
return inboundRankMapper.queryMaterialRankList(inboundRankDO);
}
@Override
public InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO) {
return inboundRankMapper.queryMaterialRankTotal(inboundRankDO);
}
}
@@ -0,0 +1,79 @@
package com.mhd.wms.domain.inboundRank.repository.po;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 入库排名统计PO(一行 = 某仓单内某货品的合计,按件数降序排名)
*
* <p>字段口径:入仓日期=入仓单入仓日期;仓单编号=入库单号;客户编号/存货人=入仓单货主;
* 件数/毛重/体积=仓单内该货品明细合计;毛重(吨)=明细总毛重KG/1000;
* 投保总额=货品档案保额(material_base_info.insure_amount,组内取MAX防多批次明细行重复累加)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "入库排名统计对象", description = "入库排名统计响应对象")
public class InboundMaterialRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name = "入仓日期", width = 30, dateFormat = "yyyy-MM-dd")
private Date warehouseDate;
@ApiModelProperty("仓单编号(入库单号)")
@Excel(name = "仓单编号")
private String inOrderNumber;
@ApiModelProperty("客户编号")
@Excel(name = "客户编号")
private String shipperCode;
@ApiModelProperty("存货人(客户名称)")
@Excel(name = "存货人")
private String shipperName;
@ApiModelProperty("商品货号")
@Excel(name = "商品货号")
private String materialCode;
@ApiModelProperty("货名/商品名称")
@Excel(name = "货名")
private String materialName;
@ApiModelProperty("规格型号")
@Excel(name = "规格型号")
private String specificationModel;
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unitName;
@ApiModelProperty("件数(仓单内该货品合计,排名依据)")
@Excel(name = "件数")
private BigDecimal pieceCount;
@ApiModelProperty("毛重(吨)(仓单内该货品合计,KG/1000)")
@Excel(name = "毛重(吨)")
private BigDecimal grossWeightTon;
@ApiModelProperty("投保总额(货品档案保额)")
@Excel(name = "投保总额")
private BigDecimal insureAmount;
@ApiModelProperty("体积(CBM)(仓单内该货品合计)")
@Excel(name = "体积(CBM)")
private BigDecimal totalVolume;
}
@@ -0,0 +1,39 @@
package com.mhd.wms.domain.inboundRank.repository.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 入库排名统计合计PO
*
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回
* totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "入库排名统计合计对象", description = "入库排名统计合计响应对象")
public class InboundRankTotalPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("件数合计")
private BigDecimal totalPieceCount;
@ApiModelProperty("毛重(吨)合计")
private BigDecimal totalGrossWeightTon;
@ApiModelProperty("投保总额合计(各仓单+货品行保额之和)")
private BigDecimal totalInsureAmount;
@ApiModelProperty("体积(CBM)合计")
private BigDecimal totalVolume;
@ApiModelProperty("仓单数合计")
private Long totalOrderCount;
}
@@ -0,0 +1,50 @@
package com.mhd.wms.domain.inboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 入库排名统计查询DO
*
* <p>topOrganizationId / warehouseId 由服务端按登录用户注入,前端不传</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class InboundRankDO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填)")
private String warehouseDateStart;
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填)")
private String warehouseDateEnd;
@ApiModelProperty("仓单编号/入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("客户编号(模糊)")
private String shipperCode;
@ApiModelProperty("存货人/客户名称(模糊)")
private String shipperName;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
private Long topOrganizationId;
@ApiModelProperty("仓库id(按用户关联仓库注入)")
private Long warehouseId;
}
@@ -0,0 +1,39 @@
package com.mhd.wms.domain.inboundRank.service;
import com.mhd.wms.domain.inboundRank.repository.facade.IInboundRankService;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 入库排名统计领域服务
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class InboundRankDomainService {
@Autowired
private IInboundRankService inboundRankService;
/**
* 商品入库排名列表(仓单+货品汇总,按件数降序)
*/
public List<InboundMaterialRankPO> queryMaterialRankList(InboundRankDO inboundRankDO) {
return inboundRankService.queryMaterialRankList(inboundRankDO);
}
/**
* 商品入库排名合计(不分页)
*/
public InboundRankTotalPO queryMaterialRankTotal(InboundRankDO inboundRankDO) {
return inboundRankService.queryMaterialRankTotal(inboundRankDO);
}
}
@@ -0,0 +1,37 @@
package com.mhd.wms.domain.outboundRank.repository.facade;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import java.util.List;
/**
* 出库排名统计Service接口
*
* @author rcx
* @date 2026-08-31
*/
public interface IOutboundRankService {
/**
* 商品出库排名列表
*/
List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO);
/**
* 线路出库排名列表
*/
List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO);
/**
* 商品出库排名合计(不分页)
*/
OutboundRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO);
/**
* 线路出库排名合计(不分页)
*/
OutboundRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO);
}
@@ -0,0 +1,37 @@
package com.mhd.wms.domain.outboundRank.repository.mapper;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import java.util.List;
/**
* 出库排名统计Mapper接口
*
* @author rcx
* @date 2026-08-31
*/
public interface OutboundRankMapper {
/**
* 商品出库排名列表(按销售量倒序)
*/
List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO);
/**
* 线路出库排名列表(按销售量倒序)
*/
List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO);
/**
* 商品出库排名合计(不分页,当前筛选条件全量合计)
*/
OutboundRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO);
/**
* 线路出库排名合计(不分页,当前筛选条件全量合计)
*/
OutboundRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO);
}
@@ -0,0 +1,45 @@
package com.mhd.wms.domain.outboundRank.repository.persistence;
import com.mhd.wms.domain.outboundRank.repository.facade.IOutboundRankService;
import com.mhd.wms.domain.outboundRank.repository.mapper.OutboundRankMapper;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计Service业务层处理
*
* @author rcx
* @date 2026-08-31
*/
@Service
public class OutboundRankImpl implements IOutboundRankService {
@Autowired
private OutboundRankMapper outboundRankMapper;
@Override
public List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryMaterialRankList(outboundRankDO);
}
@Override
public List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryLineRankList(outboundRankDO);
}
@Override
public OutboundRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryMaterialRankTotal(outboundRankDO);
}
@Override
public OutboundRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankMapper.queryLineRankTotal(outboundRankDO);
}
}
@@ -0,0 +1,45 @@
package com.mhd.wms.domain.outboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 线路出库排名PO
*
* <p>按日期区间对出库单聚合:销售量=SUM(出库数量),交易数=COUNT(DISTINCT 出库单号),金额=SUM(明细总价);
* 仅统计带线路的出库单(line_code 为 2026-08-20 新增冗余字段,历史单为空)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "线路出库排名对象", description = "线路出库排名响应对象")
public class OutboundLineRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("线路编码")
@Excel(name = "线路编码")
private String lineCode;
@ApiModelProperty("线路名称")
@Excel(name = "线路名称")
private String lineName;
@ApiModelProperty("销售量(出库数量合计)")
@Excel(name = "销售量")
private BigDecimal salesQuantity;
@ApiModelProperty("交易数(出库单据数)")
@Excel(name = "交易数")
private Long tradeCount;
@ApiModelProperty("金额")
@Excel(name = "金额")
private BigDecimal amount;
}
@@ -0,0 +1,52 @@
package com.mhd.wms.domain.outboundRank.repository.po;
import com.mhd.common.core.annotation.Excel;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 商品出库排名PO
*
* <p>按日期区间对出库明细聚合:销售量=SUM(出库数量),交易数=COUNT(DISTINCT 出库单号),金额=SUM(明细总价)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "商品出库排名对象", description = "商品出库排名响应对象")
public class OutboundMaterialRankPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("商品货号")
@Excel(name = "商品货号")
private String materialCode;
@ApiModelProperty("商品名称")
@Excel(name = "商品名称")
private String materialName;
@ApiModelProperty("规格")
@Excel(name = "规格")
private String specificationModel;
@ApiModelProperty("单位")
@Excel(name = "单位")
private String unitName;
@ApiModelProperty("销售量(出库数量合计)")
@Excel(name = "销售量")
private BigDecimal salesQuantity;
@ApiModelProperty("交易数(出库单据数)")
@Excel(name = "交易数")
private Long tradeCount;
@ApiModelProperty("金额")
@Excel(name = "金额")
private BigDecimal amount;
}
@@ -0,0 +1,32 @@
package com.mhd.wms.domain.outboundRank.repository.po;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 出库排名合计PO
*
* <p>当前筛选条件下不分页的全量合计,随分页列表一起返回(totalSalesQuantity/totalTradeCount/totalAmount</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
@ApiModel(value = "出库排名合计对象", description = "出库排名合计响应对象")
public class OutboundRankTotalPO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("销售量合计")
private BigDecimal totalSalesQuantity;
@ApiModelProperty("交易数合计(去重出库单数)")
private Long totalTradeCount;
@ApiModelProperty("金额合计")
private BigDecimal totalAmount;
}
@@ -0,0 +1,53 @@
package com.mhd.wms.domain.outboundRank.repository.todo;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 出库排名统计查询DO
*
* <p>topOrganizationId / warehouseId 由服务端按登录用户注入,前端不传</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class OutboundRankDO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填)")
private String createTimeStart;
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填)")
private String createTimeEnd;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("商品名称(模糊)")
private String materialName;
@ApiModelProperty("线路编码(模糊)")
private String lineCode;
@ApiModelProperty("线路名称(模糊)")
private String lineName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("客户名称(模糊)")
private String customerName;
@ApiModelProperty("货主名称(模糊)")
private String shipperName;
@ApiModelProperty("一级组织表ID(登录用户隔离,非1时注入)")
private Long topOrganizationId;
@ApiModelProperty("仓库id(按用户关联仓库注入)")
private Long warehouseId;
}
@@ -0,0 +1,54 @@
package com.mhd.wms.domain.outboundRank.service;
import com.mhd.wms.domain.outboundRank.repository.facade.IOutboundRankService;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 出库排名统计领域服务
*
* @author rcx
* @date 2026-08-31
*/
@Service
@Slf4j
public class OutboundRankDomainService {
@Autowired
private IOutboundRankService outboundRankService;
/**
* 商品出库排名列表
*/
public List<OutboundMaterialRankPO> queryMaterialRankList(OutboundRankDO outboundRankDO) {
return outboundRankService.queryMaterialRankList(outboundRankDO);
}
/**
* 线路出库排名列表
*/
public List<OutboundLineRankPO> queryLineRankList(OutboundRankDO outboundRankDO) {
return outboundRankService.queryLineRankList(outboundRankDO);
}
/**
* 商品出库排名合计(不分页)
*/
public OutboundRankTotalPO queryMaterialRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankService.queryMaterialRankTotal(outboundRankDO);
}
/**
* 线路出库排名合计(不分页)
*/
public OutboundRankTotalPO queryLineRankTotal(OutboundRankDO outboundRankDO) {
return outboundRankService.queryLineRankTotal(outboundRankDO);
}
}
@@ -0,0 +1,36 @@
package com.mhd.wms.interfaces.assember.inboundRank;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import com.mhd.wms.interfaces.dto.inboundRank.InboundRankDTO;
import org.springframework.stereotype.Component;
/**
* 入库排名统计Assembler
*
* @author rcx
* @date 2026-08-31
*/
@Component
public class InboundRankAssembler {
/**
* 转换实体
*/
public InboundRankDO toDO(InboundRankDTO inboundRankDTO) {
InboundRankDO inboundRankDO = new InboundRankDO();
// 拷贝
BeanUtils.copyProperties(inboundRankDTO, inboundRankDO, IgnoreNullUtil.getNullPropertyNames(inboundRankDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
return inboundRankDO;
}
}
@@ -0,0 +1,36 @@
package com.mhd.wms.interfaces.assember.outboundRank;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.utils.IgnoreNullUtil;
import com.mhd.common.core.utils.bean.BeanUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import com.mhd.wms.interfaces.dto.outboundRank.OutboundRankDTO;
import org.springframework.stereotype.Component;
/**
* 出库排名统计Assembler
*
* @author rcx
* @date 2026-08-31
*/
@Component
public class OutboundRankAssembler {
/**
* 转换实体
*/
public OutboundRankDO toDO(OutboundRankDTO outboundRankDTO) {
OutboundRankDO outboundRankDO = new OutboundRankDO();
// 拷贝
BeanUtils.copyProperties(outboundRankDTO, outboundRankDO, IgnoreNullUtil.getNullPropertyNames(outboundRankDTO));
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
return outboundRankDO;
}
}
@@ -0,0 +1,45 @@
package com.mhd.wms.interfaces.dto.inboundRank;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* 入库排名统计入参DTO
*
* <p>按入仓日期区间筛选商品入库量统计排名,统计口径:入仓日期 + 仅已入库状态(5) + 明细仅取 level=1 主行;
* 一行 = 某仓单内某货品的合计,按件数降序排名</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class InboundRankDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("入仓日期-开始 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
private String warehouseDateStart;
@ApiModelProperty("入仓日期-结束 yyyy-MM-dd(必填,按入仓单入仓日期筛选)")
private String warehouseDateEnd;
@ApiModelProperty("仓单编号/入库单号(模糊)")
private String inOrderNumber;
@ApiModelProperty("客户编号(模糊)")
private String shipperCode;
@ApiModelProperty("存货人/客户名称(模糊)")
private String shipperName;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("货名/商品名称(模糊)")
private String materialName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
}
@@ -0,0 +1,43 @@
package com.mhd.wms.interfaces.dto.outboundRank;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 出库排名统计入参DTO
*
* <p>按日期区间统计商品/线路出库量排名,统计口径:出库单创建时间 + 已出库及之后状态(9,12,13)</p>
*
* @author rcx
* @date 2026-08-31
*/
@Data
public class OutboundRankDTO {
@ApiModelProperty("出库日期-开始 yyyy-MM-dd(必填,按出库单创建时间统计)")
private String createTimeStart;
@ApiModelProperty("出库日期-结束 yyyy-MM-dd(必填,按出库单创建时间统计)")
private String createTimeEnd;
@ApiModelProperty("商品货号(模糊)")
private String materialCode;
@ApiModelProperty("商品名称(模糊)")
private String materialName;
@ApiModelProperty("线路编码(模糊)")
private String lineCode;
@ApiModelProperty("线路名称(模糊)")
private String lineName;
@ApiModelProperty("仓库名称(模糊)")
private String warehouseName;
@ApiModelProperty("客户名称(模糊)")
private String customerName;
@ApiModelProperty("货主名称(模糊)")
private String shipperName;
}
@@ -0,0 +1,105 @@
package com.mhd.wms.interfaces.facadeApi.inboundRank;
import com.github.pagehelper.PageInfo;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.utils.PageUtils;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.mhd.wms.application.service.inboundRank.InboundRankApplicationService;
import com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO;
import com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO;
import com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO;
import com.mhd.wms.interfaces.assember.inboundRank.InboundRankAssembler;
import com.mhd.wms.interfaces.dto.inboundRank.InboundRankDTO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
* 入库排名统计Api
*
* <p>按入仓日期区间筛选商品入库量统计排名。统计口径:入仓日期 + 仅已入库状态(5),
* 明细仅取 level=1 主行;一行 = 某仓单内某货品的合计,按件数降序排名。
* 字段:入仓日期/仓单编号/客户编号/存货人/货名/件数/毛重(吨)/投保总额/体积(CBM)。
* 与出库排名(/outboundRankApi)配对,前端用 tab 页区分</p>
*
* @author rcx
* @date 2026-08-31
*/
@RestController
@RequestMapping("/inboundRankApi")
public class InboundRankApi {
@Resource
private InboundRankAssembler inboundRankAssembler;
@Autowired
private InboundRankApplicationService inboundRankApplicationService;
/**
* 分页查询商品入库排名列表(含合计行)
*/
@ApiOperation("分页查询商品入库排名列表")
@GetMapping("/queryMaterialRankList")
public HashMap<String, Object> queryMaterialRankList(InboundRankDTO inboundRankDTO) {
//转换实体
InboundRankDO inboundRankDO = inboundRankAssembler.toDO(inboundRankDTO);
startPage();
List<InboundMaterialRankPO> list = inboundRankApplicationService.queryMaterialRankList(inboundRankDO);
InboundRankTotalPO total = inboundRankApplicationService.queryMaterialRankTotal(inboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 导出商品入库排名Excel
*/
@ApiOperation("导出商品入库排名Excel")
@GetMapping("/exportMaterialRank")
public void exportMaterialRank(HttpServletResponse response, InboundRankDTO inboundRankDTO) throws Exception {
//转换实体(不分页,导出全量)
InboundRankDO inboundRankDO = inboundRankAssembler.toDO(inboundRankDTO);
List<InboundMaterialRankPO> list = inboundRankApplicationService.exportMaterialRank(inboundRankDO);
String fileName = URLEncoder.encode("入库排名统计", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<InboundMaterialRankPO> util = new ExcelUtil<>(InboundMaterialRankPO.class);
util.exportExcel(response, list, "入库排名统计");
}
/**
* 分页结果 + 合计行:data/total/code/msg 之外追加
* totalPieceCount/totalGrossWeightTon/totalInsureAmount/totalVolume/totalOrderCount
*/
private HashMap<String, Object> getDataTableWithTotal(List<?> list, InboundRankTotalPO total) {
HashMap<String, Object> result = new HashMap<>();
result.put("data", list);
result.put("total", new PageInfo<>(list).getTotal());
result.put("code", HttpStatus.SUCCESS);
result.put("msg", "查询成功");
if (total != null) {
result.put("totalPieceCount", total.getTotalPieceCount());
result.put("totalGrossWeightTon", total.getTotalGrossWeightTon());
result.put("totalInsureAmount", total.getTotalInsureAmount());
result.put("totalVolume", total.getTotalVolume());
result.put("totalOrderCount", total.getTotalOrderCount());
}
return result;
}
/**
* 设置请求分页数据
*/
protected void startPage() {
PageUtils.startPage();
}
}
@@ -0,0 +1,134 @@
package com.mhd.wms.interfaces.facadeApi.outboundRank;
import com.github.pagehelper.PageInfo;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.utils.PageUtils;
import com.mhd.common.core.utils.poi.ExcelUtil;
import com.mhd.wms.application.service.outboundRank.OutboundRankApplicationService;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO;
import com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO;
import com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO;
import com.mhd.wms.interfaces.assember.outboundRank.OutboundRankAssembler;
import com.mhd.wms.interfaces.dto.outboundRank.OutboundRankDTO;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
/**
* 出库排名统计Api
*
* <p>按日期区间筛选,统计商品/线路出库量排名。统计口径:出库单创建时间 + 已出库及之后状态(9,12,13)
* 明细仅取 level=1 主行;销售量=SUM(出库数量)、交易数=COUNT(DISTINCT 出库单号)、金额=SUM(明细总价)</p>
*
* @author rcx
* @date 2026-08-31
*/
@RestController
@RequestMapping("/outboundRankApi")
public class OutboundRankApi {
@Resource
private OutboundRankAssembler outboundRankAssembler;
@Autowired
private OutboundRankApplicationService outboundRankApplicationService;
/**
* 分页查询商品出库排名列表(含合计行)
*/
@ApiOperation("分页查询商品出库排名列表")
@GetMapping("/queryMaterialRankList")
public HashMap<String, Object> queryMaterialRankList(OutboundRankDTO outboundRankDTO) {
//转换实体
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
startPage();
List<OutboundMaterialRankPO> list = outboundRankApplicationService.queryMaterialRankList(outboundRankDO);
OutboundRankTotalPO total = outboundRankApplicationService.queryMaterialRankTotal(outboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 分页查询线路出库排名列表(含合计行)
*/
@ApiOperation("分页查询线路出库排名列表")
@GetMapping("/queryLineRankList")
public HashMap<String, Object> queryLineRankList(OutboundRankDTO outboundRankDTO) {
//转换实体
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
startPage();
List<OutboundLineRankPO> list = outboundRankApplicationService.queryLineRankList(outboundRankDO);
OutboundRankTotalPO total = outboundRankApplicationService.queryLineRankTotal(outboundRankDO);
return getDataTableWithTotal(list, total);
}
/**
* 导出商品出库排名Excel
*/
@ApiOperation("导出商品出库排名Excel")
@GetMapping("/exportMaterialRank")
public void exportMaterialRank(HttpServletResponse response, OutboundRankDTO outboundRankDTO) throws Exception {
//转换实体(不分页,导出全量)
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
List<OutboundMaterialRankPO> list = outboundRankApplicationService.exportMaterialRankList(outboundRankDO);
String fileName = URLEncoder.encode("商品出库排名", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<OutboundMaterialRankPO> util = new ExcelUtil<>(OutboundMaterialRankPO.class);
util.exportExcel(response, list, "商品出库排名");
}
/**
* 导出线路出库排名Excel
*/
@ApiOperation("导出线路出库排名Excel")
@GetMapping("/exportLineRank")
public void exportLineRank(HttpServletResponse response, OutboundRankDTO outboundRankDTO) throws Exception {
//转换实体(不分页,导出全量)
OutboundRankDO outboundRankDO = outboundRankAssembler.toDO(outboundRankDTO);
List<OutboundLineRankPO> list = outboundRankApplicationService.exportLineRankList(outboundRankDO);
String fileName = URLEncoder.encode("线路出库排名", "UTF-8").replaceAll("\\+", "%20");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelUtil<OutboundLineRankPO> util = new ExcelUtil<>(OutboundLineRankPO.class);
util.exportExcel(response, list, "线路出库排名");
}
/**
* 分页结果 + 合计行:data/total/code/msg 之外追加 totalSalesQuantity/totalTradeCount/totalAmount
*/
private HashMap<String, Object> getDataTableWithTotal(List<?> list, OutboundRankTotalPO total) {
HashMap<String, Object> result = new HashMap<>();
result.put("data", list);
result.put("total", new PageInfo<>(list).getTotal());
result.put("code", HttpStatus.SUCCESS);
result.put("msg", "查询成功");
if (total != null) {
result.put("totalSalesQuantity", total.getTotalSalesQuantity());
result.put("totalTradeCount", total.getTotalTradeCount());
result.put("totalAmount", total.getTotalAmount());
}
return result;
}
/**
* 设置请求分页数据
*/
protected void startPage() {
PageUtils.startPage();
}
}
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mhd.wms.domain.inboundRank.repository.mapper.InboundRankMapper">
<!--
入库排名统计公共查询条件
数据源:入库明细 in_material_detail(a,仅 level=1 主行防重复) join 入仓单 stock_in_order(b)
left join 货品档案 material_base_info(c,取保额作投保总额)
口径:b.status = 5已入库(排除已取消(6)/已关闭(7)及未完成单据);
日期筛选按入仓单入仓日期 b.warehouse_date(与列表"入仓日期"列同源);
一行 = 某仓单(b.in_order_number)内某货品(a.material_code)的合计,按件数降序排名
-->
<sql id="inboundRankConditions">
a.del_flag = 1
and a.level = 1
and b.status = 5
<if test="topOrganizationId != null">
and b.top_organization_id = #{topOrganizationId}
</if>
<if test="warehouseId != null">
and b.warehouse_id = #{warehouseId}
</if>
<if test="warehouseName != null and warehouseName != ''">
and b.warehouse_name like concat('%', #{warehouseName}, '%')
</if>
<if test="inOrderNumber != null and inOrderNumber != ''">
and b.in_order_number like concat('%', #{inOrderNumber}, '%')
</if>
<if test="shipperCode != null and shipperCode != ''">
and b.shipper_code like concat('%', #{shipperCode}, '%')
</if>
<if test="shipperName != null and shipperName != ''">
and b.shipper_name like concat('%', #{shipperName}, '%')
</if>
<if test="materialCode != null and materialCode != ''">
and a.material_code like concat('%', #{materialCode}, '%')
</if>
<if test="materialName != null and materialName != ''">
and a.material_name like concat('%', #{materialName}, '%')
</if>
<if test="warehouseDateStart != null and warehouseDateStart != ''">
and date_format(b.warehouse_date,'%Y-%m-%d') &gt;= #{warehouseDateStart}
</if>
<if test="warehouseDateEnd != null and warehouseDateEnd != ''">
and date_format(b.warehouse_date,'%Y-%m-%d') &lt;= #{warehouseDateEnd}
</if>
</sql>
<sql id="inboundRankFrom">
from in_material_detail a
join stock_in_order b on a.in_order_number = b.in_order_number and b.del_flag = 1
left join material_base_info c on a.material_base_info_id = c.material_base_info_id
</sql>
<!-- 商品入库排名列表(仓单+货品汇总,按件数降序) -->
<select id="queryMaterialRankList" parameterType="com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO"
resultType="com.mhd.wms.domain.inboundRank.repository.po.InboundMaterialRankPO">
select
MAX(b.warehouse_date) AS warehouseDate,
b.in_order_number AS inOrderNumber,
MAX(b.shipper_code) AS shipperCode,
MAX(b.shipper_name) AS shipperName,
a.material_code AS materialCode,
MAX(a.material_name) AS materialName,
MAX(a.SPECIFICATION_MODEL) AS specificationModel,
MAX(a.unit_name) AS unitName,
IFNULL(SUM(a.PIECE_COUNT), 0) AS pieceCount,
ROUND(IFNULL(SUM(a.TOTAL_GROSS_WEIGHT), 0) / 1000, 3) AS grossWeightTon,
MAX(IFNULL(c.insure_amount, 0)) AS insureAmount,
IFNULL(SUM(a.TOTAL_VOLUME), 0) AS totalVolume
<include refid="inboundRankFrom"/>
<where>
<include refid="inboundRankConditions"/>
</where>
GROUP BY b.in_order_number, a.material_code
order by pieceCount desc, warehouseDate desc, inOrderNumber desc
</select>
<!-- 商品入库排名合计(当前筛选条件全量;外层对分组结果再汇总,保证投保总额合计=各行列值之和) -->
<select id="queryMaterialRankTotal" parameterType="com.mhd.wms.domain.inboundRank.repository.todo.InboundRankDO"
resultType="com.mhd.wms.domain.inboundRank.repository.po.InboundRankTotalPO">
select
IFNULL(SUM(t.pieceCount), 0) AS totalPieceCount,
ROUND(IFNULL(SUM(t.grossWeightKG), 0) / 1000, 3) AS totalGrossWeightTon,
IFNULL(SUM(t.insureAmount), 0) AS totalInsureAmount,
IFNULL(SUM(t.totalVolume), 0) AS totalVolume,
COUNT(DISTINCT t.inOrderNumber) AS totalOrderCount
from (
select
b.in_order_number AS inOrderNumber,
IFNULL(SUM(a.PIECE_COUNT), 0) AS pieceCount,
IFNULL(SUM(a.TOTAL_GROSS_WEIGHT), 0) AS grossWeightKG,
MAX(IFNULL(c.insure_amount, 0)) AS insureAmount,
IFNULL(SUM(a.TOTAL_VOLUME), 0) AS totalVolume
<include refid="inboundRankFrom"/>
<where>
<include refid="inboundRankConditions"/>
</where>
GROUP BY b.in_order_number, a.material_code
) t
</select>
</mapper>
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mhd.wms.domain.outboundRank.repository.mapper.OutboundRankMapper">
<!--
出库排名统计公共查询条件
数据源:出库明细 out_material_detail(a,仅 level=1 主行防重复) join 出库单 stock_out_order(b)
口径:b.status in (9已出库,12已复核,13已交接),排除已取消(10)/已关闭(11);
日期按出库单创建时间 b.create_time(与出库单列表日期筛选一致)
-->
<sql id="outboundRankConditions">
a.del_flag = 1
and a.level = 1
and b.status in (9, 12, 13)
<if test="topOrganizationId != null">
and b.top_organization_id = #{topOrganizationId}
</if>
<if test="warehouseId != null">
and b.warehouse_id = #{warehouseId}
</if>
<if test="warehouseName != null and warehouseName != ''">
and b.warehouse_name like concat('%', #{warehouseName}, '%')
</if>
<if test="customerName != null and customerName != ''">
and b.customer_name like concat('%', #{customerName}, '%')
</if>
<if test="shipperName != null and shipperName != ''">
and b.shipper_name like concat('%', #{shipperName}, '%')
</if>
<if test="lineCode != null and lineCode != ''">
and b.line_code like concat('%', #{lineCode}, '%')
</if>
<if test="lineName != null and lineName != ''">
and b.line_name like concat('%', #{lineName}, '%')
</if>
<if test="createTimeStart != null and createTimeStart != ''">
and date_format(b.create_time,'%Y-%m-%d') &gt;= #{createTimeStart}
</if>
<if test="createTimeEnd != null and createTimeEnd != ''">
and date_format(b.create_time,'%Y-%m-%d') &lt;= #{createTimeEnd}
</if>
</sql>
<sql id="outboundRankFrom">
from out_material_detail a
join stock_out_order b on a.out_order_number = b.out_order_number and b.del_flag = 1
</sql>
<!-- 商品出库排名列表 -->
<select id="queryMaterialRankList" parameterType="com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO"
resultType="com.mhd.wms.domain.outboundRank.repository.po.OutboundMaterialRankPO">
select
a.material_code AS materialCode,
MAX(a.material_name) AS materialName,
MAX(a.SPECIFICATION_MODEL) AS specificationModel,
MAX(a.unit_name) AS unitName,
IFNULL(SUM(a.outbound_quantity), 0) AS salesQuantity,
COUNT(DISTINCT a.out_order_number) AS tradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS amount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
<if test="materialCode != null and materialCode != ''">
and a.material_code like concat('%', #{materialCode}, '%')
</if>
<if test="materialName != null and materialName != ''">
and a.material_name like concat('%', #{materialName}, '%')
</if>
</where>
GROUP BY a.material_code
order by salesQuantity desc
</select>
<!-- 线路出库排名列表(线路为空的历史单不参与线路排名) -->
<select id="queryLineRankList" parameterType="com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO"
resultType="com.mhd.wms.domain.outboundRank.repository.po.OutboundLineRankPO">
select
b.line_code AS lineCode,
MAX(b.line_name) AS lineName,
IFNULL(SUM(a.outbound_quantity), 0) AS salesQuantity,
COUNT(DISTINCT b.out_order_number) AS tradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS amount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
and b.line_code is not null and b.line_code != ''
</where>
GROUP BY b.line_code
order by salesQuantity desc
</select>
<!-- 商品出库排名合计(当前筛选条件全量) -->
<select id="queryMaterialRankTotal" parameterType="com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO"
resultType="com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO">
select
IFNULL(SUM(a.outbound_quantity), 0) AS totalSalesQuantity,
COUNT(DISTINCT a.out_order_number) AS totalTradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS totalAmount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
<if test="materialCode != null and materialCode != ''">
and a.material_code like concat('%', #{materialCode}, '%')
</if>
<if test="materialName != null and materialName != ''">
and a.material_name like concat('%', #{materialName}, '%')
</if>
</where>
</select>
<!-- 线路出库排名合计(当前筛选条件全量) -->
<select id="queryLineRankTotal" parameterType="com.mhd.wms.domain.outboundRank.repository.todo.OutboundRankDO"
resultType="com.mhd.wms.domain.outboundRank.repository.po.OutboundRankTotalPO">
select
IFNULL(SUM(a.outbound_quantity), 0) AS totalSalesQuantity,
COUNT(DISTINCT b.out_order_number) AS totalTradeCount,
IFNULL(SUM(IFNULL(a.TOTAL_AMOUNT, 0)), 0) AS totalAmount
<include refid="outboundRankFrom"/>
<where>
<include refid="outboundRankConditions"/>
and b.line_code is not null and b.line_code != ''
</where>
</select>
</mapper>