Merge branch 'refs/heads/dev-ty1.2' into dev
# Conflicts: # mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/BmsServiceFeign.java # mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/OmsServiceFeign.java # mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WlhyServiceFeign.java # mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/WmsServiceFeign.java # mhd-api/mhd-api-system/src/main/java/com/mhd/system/api/YmsServiceFeign.java # mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/feign/ThirdPartyServiceFeign.java # mhd_oms/src/main/java/com/mhd/oms/infrastructure/feign/ThirdPartyServiceFeign.java # mhd_oms/src/main/resources/bootstrap.yml
This commit is contained in:
@@ -18,7 +18,7 @@ import java.util.Set;
|
||||
/**
|
||||
* bms财务结算系统feign调用接口
|
||||
*/
|
||||
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.102.192.3:8019",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "BmsService", value = ServiceNameConstants.BMS_SERVICE, url = "http://10.33.0.129:8012",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface BmsServiceFeign {
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.102.192.4:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "OmsService", value = ServiceNameConstants.OMS_SERVICE,url = "http://10.33.0.99:8017",fallbackFactory = RemoteOmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface OmsServiceFeign {
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(contextId = "commonWlhyServiceFeign",url = "http://10.102.192.30:8014",value = ServiceNameConstants.WLHY_SERVICE,configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "commonWlhyServiceFeign",value = ServiceNameConstants.WLHY_SERVICE,url = "http://10.33.0.129:8014",configuration = FeignAutoConfiguration.class)
|
||||
public interface WlhyServiceFeign {
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.util.Map;
|
||||
* @description: TODO
|
||||
* @date 2024/5/10 8:58
|
||||
**/
|
||||
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.102.192.3:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "remoteWmsServiceFeign",value = ServiceNameConstants.WMS_SERVICE, url = "http://10.33.0.109:8016",fallbackFactory = RemoteWmsFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface WmsServiceFeign {
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.mhd.system.api.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.102.192.4:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(contextId = "YmsService", value = ServiceNameConstants.YMS_SERVICE,url = "http://10.33.0.99:8018",fallbackFactory = RemoteBmsFeignFallbackFactory.class, configuration = FeignAutoConfiguration.class)
|
||||
public interface YmsServiceFeign {
|
||||
|
||||
/**
|
||||
|
||||
+2
@@ -83,6 +83,8 @@ public class MaterialInventoryOmsParamDO extends BaseVOEntity {
|
||||
private Date productionDate;
|
||||
//过期日期
|
||||
private Date expiryDate;
|
||||
//进货日期
|
||||
private Date purchaseDate;
|
||||
|
||||
private String executioOrderNumber;
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(value = "mhd-third-party-service",url = "http://10.102.192.30:8012", configuration = FeignAutoConfiguration.class)
|
||||
@FeignClient(value = "mhd-third-party-service",url = "http://10.33.0.129:8012",configuration = FeignAutoConfiguration.class)
|
||||
public interface ThirdPartyServiceFeign {
|
||||
|
||||
//查询当前组织所有三方接口信息
|
||||
|
||||
+51
@@ -13,6 +13,7 @@ import com.mhd.basic.application.service.contractManage.ContractManageApplicatio
|
||||
import com.mhd.basic.domain.expenseAccount.service.ExpenseAccountDomainService;
|
||||
import com.mhd.basic.infrastructure.feign.vo.SysDictDataVo;
|
||||
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountDTO;
|
||||
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountExportVO;
|
||||
import com.mhd.basic.interfaces.dto.expenseAccount.SearchExpenseAccountDTO;
|
||||
import com.mhd.common.core.enums.DictCode;
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
@@ -27,6 +28,7 @@ import com.mhd.system.api.SystemServiceFeign;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.mhd.common.core.domain.po.UserPo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -378,6 +380,55 @@ public class ExpenseAccountApplicationService {
|
||||
return expenseAccountDomainService.getInfo(expenseAccountId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制费用科目-获取原科目数据供前端回显
|
||||
* <p>前端拿到数据后回显到新增弹窗,用户填写新科目编码后通过 /edit 接口(id 为空走新增)提交保存。
|
||||
* 清空科目编码是为了避免直接复制出重复编码(费用科目编码无唯一性校验)。</p>
|
||||
*/
|
||||
public ExpenseAccountPO copy(ExpenseAccountDTO expenseAccountDTO) {
|
||||
if (expenseAccountDTO == null || expenseAccountDTO.getExpenseAccountId() == null) {
|
||||
throw new ServiceException("原费用科目id不能为空");
|
||||
}
|
||||
// 获取原费用科目信息
|
||||
ExpenseAccountPO original = expenseAccountDomainService.getInfo(expenseAccountDTO.getExpenseAccountId());
|
||||
if (null == original) {
|
||||
throw new ServiceException("原费用科目信息未找到");
|
||||
}
|
||||
// 清空主键,前端提交时走新增
|
||||
original.setExpenseAccountId(null);
|
||||
// 清空科目编码,强制用户输入新编码,避免编码重复
|
||||
original.setSubjectCode(null);
|
||||
// 清空审计字段,新增时由 ExpenseAccountAssembler 重新生成
|
||||
original.setCreateBy(null);
|
||||
original.setCreateByName(null);
|
||||
original.setCreateTime(null);
|
||||
original.setUpdateBy(null);
|
||||
original.setUpdateByName(null);
|
||||
original.setUpdateTime(null);
|
||||
original.setDelFlag(null);
|
||||
// 清空非表字段
|
||||
original.setUnit(null);
|
||||
original.setExpenseAccountPOList(null);
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建费用科目导出数据(不分页全量,按列表页查询条件 + 数据权限过滤)
|
||||
*/
|
||||
public List<ExpenseAccountExportVO> buildExportList(ExpenseAccountDO expenseAccountDO) {
|
||||
List<ExpenseAccountExportVO> exportList = new ArrayList<>();
|
||||
List<ExpenseAccountPO> list = this.queryList(expenseAccountDO);
|
||||
if (list == null || list.isEmpty()) {
|
||||
return exportList;
|
||||
}
|
||||
for (ExpenseAccountPO po : list) {
|
||||
ExpenseAccountExportVO vo = new ExpenseAccountExportVO();
|
||||
BeanUtils.copyProperties(po, vo);
|
||||
exportList.add(vo);
|
||||
}
|
||||
return exportList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改状态
|
||||
* @param expenseAccountDTO
|
||||
|
||||
+34
@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSON;
|
||||
import com.mhd.basic.domain.storageLocation.repository.po.StorageLocationPO;
|
||||
import com.mhd.basic.domain.storageLocation.repository.todo.StorageLocationDO;
|
||||
import com.mhd.basic.domain.storageLocation.service.StorageLocationDomainService;
|
||||
import com.mhd.basic.interfaces.dto.storageLocation.StorageLocationDTO;
|
||||
import com.mhd.basic.domain.storageSection.repository.todo.StorageSectionDO;
|
||||
import com.mhd.basic.infrastructure.feign.vo.SysDictDataVo;
|
||||
import com.mhd.common.core.domain.po.UserPo;
|
||||
@@ -207,6 +208,39 @@ public class StorageLocationApplicationService {
|
||||
return storageLocationDomainService.getInfo(storageLocationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制库位-获取原库位数据供前端回显
|
||||
* 前端拿到数据后跳转到新增库位页面,用户修改编码后通过edit接口提交新增
|
||||
*/
|
||||
public StorageLocationPO copyStorageLocation(StorageLocationDTO storageLocationDTO) {
|
||||
if (storageLocationDTO.getStorageLocationId() == null) {
|
||||
throw new ServiceException("原库位id不能为空");
|
||||
}
|
||||
// 获取原库位信息
|
||||
StorageLocationPO originalStorageLocation = storageLocationDomainService.getInfo(storageLocationDTO.getStorageLocationId());
|
||||
if (null == originalStorageLocation) {
|
||||
throw new ServiceException("原库位信息未找到");
|
||||
}
|
||||
// 清空唯一性字段:库位id不清空会导致提交后走修改分支覆盖原库位;
|
||||
// 库位编码同组织同仓库唯一,须由用户重新填写;条码为物理唯一标识,一并清空
|
||||
originalStorageLocation.setStorageLocationId(null);
|
||||
originalStorageLocation.setStorageLocationCode(null);
|
||||
originalStorageLocation.setLocationBarCode(null);
|
||||
// 状态重置为初始值:新库位无库存(空)、启用、未作废
|
||||
originalStorageLocation.setStatus(1);
|
||||
originalStorageLocation.setOpeningUp(1);
|
||||
originalStorageLocation.setNullify(1);
|
||||
// 清空审计字段,提交新增时由Assembler按当前登录人重新生成
|
||||
originalStorageLocation.setCreateBy(null);
|
||||
originalStorageLocation.setCreateByName(null);
|
||||
originalStorageLocation.setCreateTime(null);
|
||||
originalStorageLocation.setUpdateBy(null);
|
||||
originalStorageLocation.setUpdateByName(null);
|
||||
originalStorageLocation.setUpdateTime(null);
|
||||
// 其余业务字段(仓库/库区/库位组、分类/种类/混放策略、限制、长宽高、坐标、备注)保留回显
|
||||
return originalStorageLocation;
|
||||
}
|
||||
|
||||
public int getStorageLocationCount(StorageLocationDO storageLocationDO)
|
||||
{
|
||||
return storageLocationDomainService.getStorageLocationCount(storageLocationDO);
|
||||
|
||||
+23
-2
@@ -21,6 +21,7 @@ import com.mhd.system.service.ISysDictTypeService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
/**
|
||||
* 库区ApplicationService
|
||||
@@ -42,11 +43,32 @@ public class StorageSectionApplicationService {
|
||||
*/
|
||||
|
||||
public List<StorageSectionPO> queryList(StorageSectionDO storageSectionDO) {
|
||||
//设置查询数据权限
|
||||
applyQueryDataPermission(storageSectionDO);
|
||||
return storageSectionDomainService.queryList(storageSectionDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 库区面积合计:与 /list 相同的数据权限和筛选条件,SQL 直接 SUM(库区面积),
|
||||
* 保证合计数与列表中各行库区面积之和一致
|
||||
*/
|
||||
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO) {
|
||||
//与 /list 共用同一数据权限,保证合计与列表口径一致
|
||||
applyQueryDataPermission(storageSectionDO);
|
||||
return storageSectionDomainService.sumStorageRegion(storageSectionDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 设置查询数据权限(queryList 与 sumStorageRegion 共用)
|
||||
* @author ZhouGY
|
||||
* @param storageSectionDO
|
||||
*/
|
||||
private void applyQueryDataPermission(StorageSectionDO storageSectionDO) {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||
Long userOrganizationId = loginUser.getUserPo().getOrganizationId();
|
||||
Long frontendOrganizationId = storageSectionDO.getOrganizationId(); // 前端传递的 organizationId
|
||||
|
||||
|
||||
// 数据权限:根据 organizationId 来设置查询权限
|
||||
// 当前登录用户的 organizationId 为 2827 时,可查全部组织数据;其他组织只能查自己
|
||||
if (userOrganizationId != null && userOrganizationId == 2827L) {
|
||||
@@ -91,7 +113,6 @@ public class StorageSectionApplicationService {
|
||||
}
|
||||
}
|
||||
}
|
||||
return storageSectionDomainService.queryList(storageSectionDO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
@@ -5,6 +5,7 @@ import com.mhd.basic.domain.storageSection.entity.StorageSection;
|
||||
import com.mhd.basic.domain.storageSection.repository.po.StorageSectionPO;
|
||||
import com.mhd.basic.domain.storageSection.repository.todo.StorageSectionDO;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -20,6 +21,11 @@ public interface IStorageSectionService extends IService<StorageSection>
|
||||
*/
|
||||
public List<StorageSectionPO> queryList(StorageSectionDO storageSectionDO);
|
||||
|
||||
/**
|
||||
* 库区面积合计(与 queryList 相同筛选条件)
|
||||
*/
|
||||
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO);
|
||||
|
||||
/**
|
||||
* 新增库区
|
||||
*/
|
||||
|
||||
+6
@@ -1,5 +1,6 @@
|
||||
package com.mhd.basic.domain.storageSection.repository.mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mhd.basic.domain.storageSection.entity.StorageSection;
|
||||
@@ -19,4 +20,9 @@ public interface StorageSectionMapper extends BaseMapper<StorageSection>
|
||||
*/
|
||||
public List<StorageSectionPO> queryList(StorageSectionDO storageSectionDO);
|
||||
|
||||
/**
|
||||
* 库区面积合计(与 queryList 相同筛选条件)
|
||||
*/
|
||||
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO);
|
||||
|
||||
}
|
||||
|
||||
+10
@@ -10,6 +10,7 @@ import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,6 +34,15 @@ public class StorageSectionImpl extends ServiceImpl<StorageSectionMapper, Storag
|
||||
return storageSectionMapper.queryList(storageSectionDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 库区面积合计(与 queryList 相同筛选条件)
|
||||
*/
|
||||
@Override
|
||||
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO)
|
||||
{
|
||||
return storageSectionMapper.sumStorageRegion(storageSectionDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增库区
|
||||
*/
|
||||
|
||||
+8
@@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -53,6 +54,13 @@ public class StorageSectionDomainService {
|
||||
return storageSectionService.queryList(storageSectionDO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 库区面积合计(与 queryList 相同筛选条件)
|
||||
*/
|
||||
public BigDecimal sumStorageRegion(StorageSectionDO storageSectionDO) {
|
||||
return storageSectionService.sumStorageRegion(storageSectionDO);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 新增库区
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.mhd.basic.interfaces.dto.expenseAccount;
|
||||
|
||||
import com.mhd.common.core.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 费用科目导出对象 expense_account
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-28
|
||||
*/
|
||||
@Data
|
||||
public class ExpenseAccountExportVO {
|
||||
|
||||
/** 费用类别(关联服务项名称) */
|
||||
@Excel(name = "费用类别")
|
||||
private String serviceItemsName;
|
||||
|
||||
@Excel(name = "上级科目")
|
||||
private String upperSubject;
|
||||
|
||||
@Excel(name = "费用代码")
|
||||
private String subjectCode;
|
||||
|
||||
@Excel(name = "费用名称")
|
||||
private String subjectName;
|
||||
|
||||
@Excel(name = "税率")
|
||||
private BigDecimal taxRate;
|
||||
|
||||
@Excel(name = "创建订单固定显示", readConverterExp = "0=否,1=是")
|
||||
private Integer fixedOrderDisplay;
|
||||
|
||||
@Excel(name = "入库显示", readConverterExp = "0=否,1=是")
|
||||
private Integer inOrderDisplay;
|
||||
|
||||
@Excel(name = "出库显示", readConverterExp = "0=否,1=是")
|
||||
private Integer outOrderDisplay;
|
||||
|
||||
@Excel(name = "自有车辆固定显示", readConverterExp = "1=是,2=否")
|
||||
private Integer ownStatus;
|
||||
|
||||
@Excel(name = "社会车辆固定显示", readConverterExp = "1=是,2=否")
|
||||
private Integer societyStatus;
|
||||
|
||||
@Excel(name = "备注信息")
|
||||
private String remark;
|
||||
|
||||
@Excel(name = "状态", readConverterExp = "1=启用,2=停用")
|
||||
private Integer status;
|
||||
|
||||
@Excel(name = "创建人")
|
||||
private String createByName;
|
||||
|
||||
@Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
@Excel(name = "更新人")
|
||||
private String updateByName;
|
||||
|
||||
@Excel(name = "更新时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
@Excel(name = "所属组织")
|
||||
private String organizationName;
|
||||
}
|
||||
+45
@@ -1,5 +1,7 @@
|
||||
package com.mhd.basic.interfaces.facade.expenseAccount;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -13,9 +15,11 @@ import com.mhd.basic.domain.expenseAccount.repository.todo.SearchExpenseAccountD
|
||||
import com.mhd.basic.domain.sysMultistageDict.repository.po.SysMultistageDictPo;
|
||||
import com.mhd.basic.interfaces.assember.expenseAccount.ExpenseAccountAssembler;
|
||||
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountDTO;
|
||||
import com.mhd.basic.interfaces.dto.expenseAccount.ExpenseAccountExportVO;
|
||||
import com.mhd.basic.interfaces.dto.expenseAccount.SearchExpenseAccountDTO;
|
||||
import com.mhd.common.core.constant.HttpStatus;
|
||||
import com.mhd.common.core.domain.entity.R;
|
||||
import com.mhd.common.core.utils.poi.ExcelUtil;
|
||||
import com.sun.org.apache.bcel.internal.generic.NEW;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
@@ -26,6 +30,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import com.mhd.common.core.web.controller.BaseController;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.core.web.page.TableDataInfo;
|
||||
@@ -336,5 +341,45 @@ public class ExpenseAccountApi extends BaseController{
|
||||
return AjaxResult.success(expenseAccountPO.getUnit());
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制费用科目
|
||||
* 返回原科目数据(已清空主键、科目编码等字段)供前端回显到新增弹窗,用户填写新科目编码后通过 /edit 接口提交新增
|
||||
*/
|
||||
@ApiOperation("复制费用科目-获取原科目数据用于回显")
|
||||
@PostMapping("/copy")
|
||||
public AjaxResult copy(@RequestBody ExpenseAccountDTO expenseAccountDTO)
|
||||
{
|
||||
try {
|
||||
ExpenseAccountPO expenseAccountPO = expenseAccountApplicationService.copy(expenseAccountDTO);
|
||||
return AjaxResult.success(expenseAccountPO);
|
||||
}catch (Exception e){
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出费用科目
|
||||
* 按列表页当前查询条件 + 数据权限过滤,不分页全量导出
|
||||
*/
|
||||
@ApiOperation("导出费用科目")
|
||||
@RequestMapping(value = "/export", method = {RequestMethod.GET, RequestMethod.POST},
|
||||
produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
public void export(ExpenseAccountDTO expenseAccountDTO, HttpServletResponse response) throws IOException
|
||||
{
|
||||
//转换实体(不用Assembler,避免其自动填充createByName/updateByName干扰查询条件,与/list一致)
|
||||
ExpenseAccountDO expenseAccountDO = new ExpenseAccountDO();
|
||||
BeanUtils.copyProperties(expenseAccountDTO, expenseAccountDO);
|
||||
//查询全量(不分页),并转换为导出对象
|
||||
List<ExpenseAccountExportVO> list = expenseAccountApplicationService.buildExportList(expenseAccountDO);
|
||||
|
||||
//设置下载头,防止浏览器乱码
|
||||
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<ExpenseAccountExportVO> util = new ExcelUtil<>(ExpenseAccountExportVO.class);
|
||||
util.exportExcel(response, list, "费用科目");
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -19,6 +19,7 @@ import com.mhd.basic.domain.storageLocation.repository.todo.StorageLocationDO;
|
||||
import com.mhd.basic.domain.storageLocation.repository.po.StorageLocationPO;
|
||||
import com.mhd.basic.application.service.storageLocation.StorageLocationApplicationService;
|
||||
import com.mhd.basic.interfaces.assember.storageLocation.StorageLocationAssembler;
|
||||
import com.mhd.common.core.exception.ServiceException;
|
||||
import javax.annotation.Resource;
|
||||
import com.mhd.common.core.web.controller.BaseController;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
@@ -127,6 +128,24 @@ public class StorageLocationApi extends BaseController{
|
||||
{
|
||||
return AjaxResult.success(storageLocationApplicationService.getInfo(storageLocationId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制库位-获取原库位数据用于回显
|
||||
*/
|
||||
@ApiOperation("复制库位-获取原库位数据用于回显")
|
||||
@PostMapping("/copyStorageLocation")
|
||||
public AjaxResult copyStorageLocation(@RequestBody StorageLocationDTO storageLocationDTO)
|
||||
{
|
||||
if(storageLocationDTO.getStorageLocationId() == null){
|
||||
throw new ServiceException("原库位id不能为空");
|
||||
}
|
||||
try {
|
||||
StorageLocationPO result = storageLocationApplicationService.copyStorageLocation(storageLocationDTO);
|
||||
return AjaxResult.success(result);
|
||||
}catch (Exception e){
|
||||
return AjaxResult.error("复制库位失败:"+e.getMessage());
|
||||
}
|
||||
}
|
||||
@ApiOperation("获取库位总数")
|
||||
@PostMapping(value = "/getStorageLocationCount")
|
||||
public AjaxResult getStorageLocationCount(@RequestBody StorageLocationDTO storageLocationDTO)
|
||||
|
||||
+12
@@ -55,6 +55,18 @@ public class StorageSectionApi extends BaseController{
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 库区面积合计:与 /list 相同的筛选条件,返回库区面积总和
|
||||
*/
|
||||
@ApiOperation("查询库区面积合计(与库区列表相同筛选条件,SUM(库区面积))")
|
||||
@GetMapping("/sumStorageRegion")
|
||||
public AjaxResult sumStorageRegion(StorageSectionDTO storageSectionDTO)
|
||||
{
|
||||
//转换实体
|
||||
StorageSectionDO storageSectionDO = storageSectionAssembler.toDO(storageSectionDTO);
|
||||
return AjaxResult.success(storageSectionApplicationService.sumStorageRegion(storageSectionDO));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询库区列表
|
||||
*/
|
||||
|
||||
+8
@@ -102,4 +102,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="selectStorageSectionPo1"/>
|
||||
order by a.storage_name asc, a.create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 库区面积合计:与 queryList 相同筛选条件,SUM(库区面积),无数据时返回 0 -->
|
||||
<select id="sumStorageRegion" parameterType="com.mhd.basic.domain.storageSection.repository.todo.StorageSectionDO" resultType="java.math.BigDecimal">
|
||||
select IFNULL(SUM(a.storage_region), 0)
|
||||
from storage_section a
|
||||
LEFT JOIN warehouse w ON a.warehouse_id = w.warehouse_id AND w.del_flag = 1
|
||||
<include refid="selectStorageSectionPo1"/>
|
||||
</select>
|
||||
</mapper>
|
||||
+26
@@ -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);
|
||||
}
|
||||
+26
@@ -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);
|
||||
}
|
||||
+34
@@ -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);
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
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>数据源跨库取 WMS 入库表(NGWL_TEST_WMS.stock_in_order / in_material_detail,OMS 执行单状态/上架量不回传),
|
||||
* 口径与 WMS 端 /inboundRankApi 完全一致,两边数字一致。
|
||||
* 字段口径:入仓日期=WMS入仓单入仓日期(为空兜底建档时间);仓单编号=WMS入库单号;客户编号/存货人=入仓单货主;
|
||||
* 件数/毛重/体积=仓单内该货品明细合计(件数取实际上架量 SHELVES_QUANTITY);毛重(吨)=明细总毛重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("仓单编号(WMS入库单号)")
|
||||
@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("件数(仓单内该货品实际上架量合计 SHELVES_QUANTITY,排名依据)")
|
||||
@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;
|
||||
}
|
||||
+39
@@ -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;
|
||||
}
|
||||
+47
@@ -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;
|
||||
}
|
||||
+44
@@ -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>数据源跨库取 WMS 入库链路(NGWL_TEST_WMS.stock_in_order / in_material_detail,OMS 执行单状态/上架量不回传)</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;
|
||||
}
|
||||
+69
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
}
|
||||
+39
@@ -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);
|
||||
}
|
||||
}
|
||||
+37
@@ -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);
|
||||
}
|
||||
+37
@@ -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);
|
||||
}
|
||||
+45
@@ -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);
|
||||
}
|
||||
}
|
||||
+45
@@ -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;
|
||||
}
|
||||
+52
@@ -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;
|
||||
}
|
||||
+32
@@ -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;
|
||||
}
|
||||
+50
@@ -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;
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package com.mhd.oms.domain.outboundRank.repository.todo;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 出库排名统计入参DTO
|
||||
*
|
||||
* <p>按日期区间统计商品/线路出库量排名。OMS 库出库单状态不流转(无"已出库"数据),
|
||||
* 数据源跨库取 WMS 库出库表(NGWL_TEST_WMS.stock_out_order / out_material_detail),与 WMS 端 /outboundRankApi 同口径</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;
|
||||
}
|
||||
+93
@@ -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(跨库取 WMS 出库数据)</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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
}
|
||||
+54
@@ -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);
|
||||
}
|
||||
}
|
||||
+6
@@ -210,6 +210,12 @@ public class ReservationMaterialInventory extends BaseVOEntity {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+6
@@ -2,6 +2,7 @@ package com.mhd.oms.domain.reservationMaterialInventory.repository.facade;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialInventoryParamDO;
|
||||
@@ -113,4 +114,9 @@ public interface IReservationMaterialInventoryService extends IService<Reservati
|
||||
public void xgkc(MaterialInventoryOmsParamDO materialInventoryParamDO);
|
||||
|
||||
public void kctzjl(MaterialInventoryOmsParamDO materialInventoryParamDO,String adjustAction,LoginUser loginUser);
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
*/
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode, Long topOrganizationId);
|
||||
}
|
||||
+4
@@ -2,6 +2,7 @@ package com.mhd.oms.domain.reservationMaterialInventory.repository.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBarCodePO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBaseInfo;
|
||||
@@ -36,4 +37,7 @@ public interface ReservationMaterialInventoryMapper extends BaseMapper<Reservati
|
||||
public MaterialBaseInfo getByInfoId(@Param("id") Long id);
|
||||
|
||||
public List<MaterialBarCodePO> getInfoByMaterialBaseInfoIds(@Param("id") Long id);
|
||||
|
||||
//入库业务单新增:根据物料编号查询库存批次去重后的过期日期+批次号(最近5条),供前端下拉回显
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(@Param("materialCode") String materialCode, @Param("topOrganizationId") Long topOrganizationId);
|
||||
}
|
||||
+18
@@ -13,6 +13,7 @@ import com.mhd.oms.domain.reservationInventoryAdjustmentRecord.repository.mapper
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.facade.IReservationMaterialInventoryService;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBarCodePO;
|
||||
@@ -120,6 +121,14 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
return materialInventoryPO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
*/
|
||||
@Override
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode, Long topOrganizationId) {
|
||||
return materialInventoryMapper.selectRecentExpiryBatches(materialCode, topOrganizationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean omsEdit(MaterialInventoryOmsParamDO materialInventoryOmsParamDO) {
|
||||
String inOrderNumber = materialInventoryOmsParamDO.getInOrderNumber();
|
||||
@@ -127,6 +136,9 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
String batchNumber = materialInventoryOmsParamDO.getBatchNumber();
|
||||
Long materialBaseInfoId = materialInventoryOmsParamDO.getMaterialBaseInfoId();
|
||||
Long warehouseId = materialInventoryOmsParamDO.getWarehouseId();
|
||||
// 进货日期参与行匹配:WMS 库存行按批次属性(含进货日期)分行后,OMS 同步按进货日期区分,避免不同进货日期误合并;
|
||||
// 为空时匹配 purchase_date 为空的行,存量行为不变
|
||||
Date purchaseDate = materialInventoryOmsParamDO.getPurchaseDate();
|
||||
LambdaQueryWrapper<ReservationMaterialInventory> param = new LambdaQueryWrapper<ReservationMaterialInventory>()
|
||||
// .eq(ReservationMaterialInventory::getInOrderNumber, inOrderNumber)
|
||||
//.eq(ReservationMaterialInventory::getLotNumber, lotNumber)
|
||||
@@ -148,6 +160,11 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
} else {
|
||||
param.isNull(ReservationMaterialInventory::getBatchNumber);
|
||||
}
|
||||
if (purchaseDate != null) {
|
||||
param.eq(ReservationMaterialInventory::getPurchaseDate, purchaseDate);
|
||||
} else {
|
||||
param.isNull(ReservationMaterialInventory::getPurchaseDate);
|
||||
}
|
||||
List<ReservationMaterialInventory> reservationMaterialInventories = materialInventoryMapper.selectList(param);
|
||||
if (reservationMaterialInventories != null && reservationMaterialInventories.size() > 0) {
|
||||
ReservationMaterialInventory reservationMaterialInventory = reservationMaterialInventories.get(0);
|
||||
@@ -542,6 +559,7 @@ public class ReservationMaterialInventoryImpl extends ServiceImpl<ReservationMat
|
||||
// materialInventory.setExtAttr4(materialInventoryParamDO.getExtAttr4());
|
||||
materialInventory.setExpiryDate(materialInventoryParamDO.getExpiryDate());
|
||||
// materialInventory.setInventoryDate(materialInventoryParamDO.getInventoryDate());
|
||||
materialInventory.setPurchaseDate(materialInventoryParamDO.getPurchaseDate());
|
||||
materialInventory.setBatchNumber(materialInventoryParamDO.getBatchNumber());
|
||||
String batchNo = materialInventoryParamDO.getBatchNo();
|
||||
if (batchNo != null) {
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.mhd.oms.domain.reservationMaterialInventory.repository.po;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 物料库存批次过期日期对象(入库业务单新增页面下拉回显)
|
||||
*
|
||||
* @author rcx
|
||||
* @date 2026-08-31
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "物料库存批次过期日期", description = "物料库存批次过期日期响应对象")
|
||||
public class MaterialInventoryExpiryBatchPO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("过期日期 yyyy-MM-dd")
|
||||
private String expiryDate;
|
||||
|
||||
@ApiModelProperty("批次号")
|
||||
private String batchNumber;
|
||||
}
|
||||
+6
@@ -192,6 +192,12 @@ public class ReservationMaterialInventoryPO extends MaterialBasePO {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+15
@@ -217,6 +217,12 @@ public class ReservationMaterialInventoryDO extends MaterialBaseDO {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@@ -253,9 +259,18 @@ public class ReservationMaterialInventoryDO extends MaterialBaseDO {
|
||||
private String createTimeEnd;
|
||||
private String expiryDateStr;
|
||||
private String productionDateStr;
|
||||
private String purchaseDateStr;
|
||||
private String expiryDateStart;
|
||||
private String expiryDateEnd;
|
||||
private String purchaseDateStart;
|
||||
private String purchaseDateEnd;
|
||||
|
||||
@ApiModelProperty("导出按到期日分组排序标识:仅 queryRule=wlExpiry 时生效,true-到期日倒序(日期近的在前、空值最后、0库存靠后);不传-保持原有排序")
|
||||
private Boolean orderByExpiryDate;
|
||||
|
||||
@ApiModelProperty("导出排除库存为0标识:true-排除库存数量汇总为0的数据(所有查询维度 queryRule 均生效);不传-不过滤")
|
||||
private Boolean excludeZeroInventoryQuantity;
|
||||
|
||||
@ApiModelProperty("导出标识:导出与查询共用 /list,true-本次为导出请求,启用导出融合排序(到期日倒序优先→同到期日按物料编码升序→0库存靠后→更新时间倒序);不传-保持原有排序")
|
||||
private Boolean isExport;
|
||||
}
|
||||
+2
@@ -193,5 +193,7 @@ public class ReservationMaterialInventoryDTO extends MaterialBaseDTO {
|
||||
private String createTimeEnd;
|
||||
private String expiryDate;
|
||||
private String productionDate;
|
||||
/** 进货日期(yyyy-MM-dd,单日精确查询) */
|
||||
private String purchaseDate;
|
||||
|
||||
}
|
||||
+17
@@ -8,6 +8,7 @@ import com.mhd.common.core.utils.bean.BeanUtils;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.mapper.ReservationMaterialInventoryMapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.MaterialBarCodePO;
|
||||
@@ -185,6 +186,22 @@ public class ReservationMaterialInventoryApplicationService {
|
||||
return materialInventoryDomainService.getInventoryByStorageLocationIdAndMaterialBaseInfoId(storageLocationId, materialBaseInfoId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
* 与 queryList 一致:按登录用户一级组织(租户)过滤,一级组织(平台)不限制
|
||||
*/
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode) {
|
||||
Long topOrganizationId = null;
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (loginUser != null && loginUser.getUserPo() != null) {
|
||||
Long loginTopOrgId = loginUser.getUserPo().getTopOrganizationId();
|
||||
if (loginTopOrgId != null && loginTopOrgId != 1) {
|
||||
topOrganizationId = loginTopOrgId;
|
||||
}
|
||||
}
|
||||
return materialInventoryDomainService.selectRecentExpiryBatches(materialCode, topOrganizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:库存查询列表
|
||||
* @param materialInventoryQueryListDO
|
||||
|
||||
+8
@@ -3,6 +3,7 @@ package com.mhd.oms.domain.reservationMaterialInventory.service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.entity.ReservationMaterialInventory;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.facade.IReservationMaterialInventoryService;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO;
|
||||
import com.mhd.oms.domain.reservationMaterialInventory.repository.todo.ReservationMaterialInventoryDO;
|
||||
@@ -88,6 +89,13 @@ public class ReservationMaterialInventoryDomainService {
|
||||
return materialInventory.getInventoryQuantity();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料编号查询库存批次最近5条过期日期+批次号(入库业务单新增页面下拉回显)
|
||||
*/
|
||||
public List<MaterialInventoryExpiryBatchPO> selectRecentExpiryBatches(String materialCode, Long topOrganizationId) {
|
||||
return materialInventoryService.selectRecentExpiryBatches(materialCode, topOrganizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description:库存查询列表
|
||||
* @param materialInventoryQueryListDO
|
||||
|
||||
+350
-167
@@ -21,20 +21,47 @@ import java.util.Map;
|
||||
/**
|
||||
* 维他奶出库Excel解析监听器(新模板版)
|
||||
* 新模板特点:货品明细与客户配送明细左右分栏展示
|
||||
* 核心功能:
|
||||
* 1. 左侧栏:第1-3行解析表头(送货日期/送货线号/车次),第5行解析纪录合共,第7行起解析货品明细
|
||||
* 2. 右侧栏:第2行解析发票张数/小计,第4行起解析客户配送明细
|
||||
* 3. 自动处理文本型数字转换(String→Integer/BigDecimal)
|
||||
* 4. 完整的异常处理和日志记录
|
||||
* 核心功能(行列位置均不写死,模板调整行/列顺序不影响解析):
|
||||
* 1. 表头标签(送货日期/送货线号/车次/发票张数/小计/纪录合共):按标签文字定位标签所在列,取右侧相邻列的值
|
||||
* 2. 货品明细:按列头文字(貨品編號/簡稱/箱/支等,繁简体归一化)定位各列,列头行之后的行按数据解析
|
||||
* 3. 客户配送明细:按列头文字(類别/發票編號/客户編號/名稱/箱/包/金額等)定位各列
|
||||
* 4. 列头行识别失败时兜底:按老模板固定布局(左栏A-D列、右栏F-L列、行号1-7)解析,保证老模板不受影响
|
||||
* 5. 自动处理文本型数字转换(String→Integer/BigDecimal)
|
||||
*/
|
||||
public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<String, Object>> {
|
||||
// 日志实例
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDeliveryExcelListener.class);
|
||||
|
||||
// 关键字定义(Excel标签匹配)
|
||||
private static final String KEYWORD_TOTAL_RECORD = "紀錄合共";
|
||||
private static final String KEYWORD_INVOICE_COUNT = "發票張數";
|
||||
private static final String KEYWORD_SUBTOTAL = "小計";
|
||||
// ---------------------- 关键字/列头别名定义(归一化后匹配,兼容简繁体) ----------------------
|
||||
// 表头标签关键字
|
||||
private static final String[] KEYWORD_DELIVERY_DATE = {"送货日期"};
|
||||
private static final String[] KEYWORD_DELIVERY_LINE = {"送货线号", "送货线路"};
|
||||
private static final String[] KEYWORD_TRAIN = {"车次"};
|
||||
private static final String[] KEYWORD_TOTAL_RECORD = {"纪录合共"};
|
||||
private static final String[] KEYWORD_INVOICE_COUNT = {"发票张数"};
|
||||
private static final String[] KEYWORD_SUBTOTAL = {"小计"};
|
||||
|
||||
// 货品明细列头别名
|
||||
private static final String[] PRODUCT_CODE_ALIASES = {"货品编号", "产品编号", "物料编号", "货品编码", "产品编码", "物料编码", "料号"};
|
||||
private static final String[] PRODUCT_NAME_ALIASES = {"简称", "货品名称", "产品名称", "物料名称", "品名", "名称"};
|
||||
private static final String[] PRODUCT_BOX_ALIASES = {"箱数", "箱"};
|
||||
private static final String[] PRODUCT_PIECE_ALIASES = {"支数", "件数", "支"};
|
||||
|
||||
// 客户配送明细列头别名
|
||||
private static final String[] CATEGORY_ALIASES = {"类别"};
|
||||
private static final String[] INVOICE_NO_ALIASES = {"发票编号", "发票号", "发票"};
|
||||
private static final String[] CUSTOMER_CODE_ALIASES = {"客户编号", "客户编码", "客户代码"};
|
||||
private static final String[] CUSTOMER_NAME_ALIASES = {"客户名称", "名称"};
|
||||
private static final String[] CUSTOMER_BOX_ALIASES = {"箱数", "箱"};
|
||||
private static final String[] CUSTOMER_PACKAGE_ALIASES = {"包数", "包"};
|
||||
private static final String[] CUSTOMER_AMOUNT_ALIASES = {"金额", "总额"};
|
||||
|
||||
// 列头行认定门槛:锚点列(编号列)必须识别到,且识别列总数达标,防止数据行误判为列头行
|
||||
private static final int HEADER_MIN_MATCH_COUNT = 2;
|
||||
// 老模板兜底:货品明细数据固定从第7行、客户配送明细数据固定从第4行开始(列头未识别到时启用固定列位)
|
||||
private static final int PRODUCT_DATA_START_ROW = 7;
|
||||
private static final int CUSTOMER_DATA_START_ROW = 4;
|
||||
|
||||
// 支持的日期格式(String转Date)
|
||||
private static final String[] SUPPORTED_DATE_FORMATS = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy-MM-dd HH:mm:ss"};
|
||||
|
||||
@@ -43,6 +70,26 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
private final List<ProductDetail> productDetailList = new ArrayList<>();
|
||||
private final List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
|
||||
|
||||
// 货品明细列索引(-1表示未识别该列)
|
||||
private int productCodeCol = -1;
|
||||
private int productNameCol = -1;
|
||||
private int productBoxCol = -1;
|
||||
private int productPieceCol = -1;
|
||||
private boolean productColumnsResolved = false;
|
||||
|
||||
// 客户配送明细列索引(-1表示未识别该列)
|
||||
private int categoryCol = -1;
|
||||
private int invoiceNoCol = -1;
|
||||
private int customerCodeCol = -1;
|
||||
private int customerNameCol = -1;
|
||||
private int customerBoxCol = -1;
|
||||
private int customerPackageCol = -1;
|
||||
private int customerAmountCol = -1;
|
||||
private boolean customerColumnsResolved = false;
|
||||
|
||||
// 发票张数/小计行是否已解析
|
||||
private boolean invoiceRowParsed = false;
|
||||
|
||||
// 当前行号(1-based,与Excel行号一致)
|
||||
private int currentRowNum = 0;
|
||||
|
||||
@@ -53,10 +100,10 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
try {
|
||||
LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", currentRowNum, data);
|
||||
|
||||
// 左侧区域解析(A-E列)
|
||||
// 左侧区域解析(货品相关信息)
|
||||
parseLeftSection(data);
|
||||
|
||||
// 右侧区域解析(F-M列)
|
||||
// 右侧区域解析(客户配送相关信息)
|
||||
parseRightSection(data);
|
||||
|
||||
} catch (ExcelDataConvertException e) {
|
||||
@@ -71,67 +118,66 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
|
||||
// ---------------------- 左侧区域解析(货品相关信息) ----------------------
|
||||
private void parseLeftSection(Map<String, Object> data) {
|
||||
switch (currentRowNum) {
|
||||
case 1:
|
||||
// 第1行:送货日期(A列=标签,B列=值)
|
||||
parseDeliveryDate(data);
|
||||
break;
|
||||
case 2:
|
||||
// 第2行:送货线号(A列=标签,B列=值)
|
||||
parseDeliveryLineNo(data);
|
||||
break;
|
||||
case 3:
|
||||
// 第3行:车次(A列=标签,B列=值)
|
||||
parseTrainNumber(data);
|
||||
break;
|
||||
case 4:
|
||||
// 第4行:货品明细标题行,无需解析
|
||||
LOGGER.debug("第{}行:货品明细标题行,跳过", currentRowNum);
|
||||
break;
|
||||
case 5:
|
||||
// 第5行:纪录合共(A列=标签,B列=总记录数,D列=总箱数,E列=总支数)
|
||||
parseTotalRecord(data);
|
||||
break;
|
||||
case 6:
|
||||
// 第6行:货品明细列标题(貨品編號/簡稱/箱/支),跳过
|
||||
LOGGER.debug("第{}行:货品明细列标题行,跳过", currentRowNum);
|
||||
break;
|
||||
default:
|
||||
// 第7行起:货品明细数据行(遇到空行自动结束)
|
||||
if (currentRowNum >= 7 && isValidProductDetailRow(data)) {
|
||||
parseProductDetail(data, currentRowNum);
|
||||
}
|
||||
break;
|
||||
// 1. 表头标签:送货日期/送货线号/车次(按标签文字定位,行位置不限)
|
||||
parseHeaderLabels(data);
|
||||
// 2. 纪录合共(按标签文字定位,行位置不限)
|
||||
parseTotalRecord(data);
|
||||
// 3. 货品明细:列布局已确定时按数据行解析
|
||||
if (productColumnsResolved) {
|
||||
if (isValidProductDetailRow(data)) {
|
||||
parseProductDetail(data, currentRowNum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 4. 尝试识别货品明细列头行;识别成功则本行即列头行,跳过数据解析
|
||||
if (tryResolveProductColumns(data)) {
|
||||
return;
|
||||
}
|
||||
// 5. 超过老模板数据起始行仍未识别到列头行:启用固定列位兜底,从当前行开始按数据行解析
|
||||
if (currentRowNum >= PRODUCT_DATA_START_ROW) {
|
||||
applyProductFallbackColumns();
|
||||
if (isValidProductDetailRow(data)) {
|
||||
parseProductDetail(data, currentRowNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 右侧区域解析(客户配送相关信息) ----------------------
|
||||
private void parseRightSection(Map<String, Object> data) {
|
||||
switch (currentRowNum) {
|
||||
case 1:
|
||||
// 第1行:客户配送明细标题行,无需解析
|
||||
LOGGER.debug("第{}行:客户配送明细标题行,跳过", currentRowNum);
|
||||
break;
|
||||
case 2:
|
||||
// 第2行:发票张数/小计(F列=發票張數标签,G列=数值;J列=小計标签,K/L/M列=数值)
|
||||
parseInvoiceCount(data);
|
||||
break;
|
||||
case 3:
|
||||
// 第3行:客户配送明细列标题(類别/發票編號/客户編號/名稱/箱/包/金額),跳过
|
||||
LOGGER.debug("第{}行:客户配送明细列标题行,跳过", currentRowNum);
|
||||
break;
|
||||
default:
|
||||
// 第4行起:客户配送明细数据行(遇到空行自动结束)
|
||||
if (currentRowNum >= 4 && isValidCustomerDetailRow(data)) {
|
||||
parseCustomerDeliveryDetail(data, currentRowNum);
|
||||
}
|
||||
break;
|
||||
// 1. 发票张数/小计(按标签文字定位,行位置不限,解析成功后不再重复解析)
|
||||
if (!invoiceRowParsed) {
|
||||
parseInvoiceRow(data);
|
||||
}
|
||||
// 2. 客户配送明细:列布局已确定时按数据行解析
|
||||
if (customerColumnsResolved) {
|
||||
if (isValidCustomerDetailRow(data)) {
|
||||
parseCustomerDeliveryDetail(data, currentRowNum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 3. 尝试识别客户配送明细列头行;识别成功则本行即列头行,跳过数据解析
|
||||
if (tryResolveCustomerColumns(data)) {
|
||||
return;
|
||||
}
|
||||
// 4. 超过老模板数据起始行仍未识别到列头行:启用固定列位兜底,从当前行开始按数据行解析
|
||||
if (currentRowNum >= CUSTOMER_DATA_START_ROW) {
|
||||
applyCustomerFallbackColumns();
|
||||
if (isValidCustomerDetailRow(data)) {
|
||||
parseCustomerDeliveryDetail(data, currentRowNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 解析完成回调 ----------------------
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
// 小计标签始终未识别到时保持与原逻辑一致:小计相关字段置0
|
||||
if (deliveryHeader.getSubtotalAmount() == null) {
|
||||
LOGGER.warn("未找到【小计】标签,小计信息置空");
|
||||
deliveryHeader.setSubtotalBoxCount(0);
|
||||
deliveryHeader.setSubtotalPieceCount(0);
|
||||
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
|
||||
}
|
||||
LOGGER.info("Excel解析全部完成!最终统计结果:");
|
||||
LOGGER.info("1. 表头信息:送货日期={},送货线号={},车次={},发票张数={},总记录数={},小计金额={}",
|
||||
deliveryHeader.getDeliveryDate(),
|
||||
@@ -144,47 +190,52 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
LOGGER.info("3. 客户配送明细:共{}条记录", customerDeliveryDetailList.size());
|
||||
}
|
||||
|
||||
// ---------------------- 左侧:表头信息解析 ----------------------
|
||||
private void parseDeliveryDate(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
if (value != null) {
|
||||
Date deliveryDate = convertToDate(value);
|
||||
deliveryHeader.setDeliveryDate(deliveryDate);
|
||||
LOGGER.debug("第{}行:送货日期解析完成,值={}", currentRowNum, deliveryDate);
|
||||
// ---------------------- 左侧:表头信息解析(标签定位,行位置不限) ----------------------
|
||||
private void parseHeaderLabels(Map<String, Object> data) {
|
||||
// 送货日期
|
||||
if (deliveryHeader.getDeliveryDate() == null) {
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_DELIVERY_DATE);
|
||||
if (keywordIndex != null) {
|
||||
Date deliveryDate = convertToDate(getValueSafely(data, keywordIndex + 1));
|
||||
if (deliveryDate != null) {
|
||||
deliveryHeader.setDeliveryDate(deliveryDate);
|
||||
LOGGER.debug("第{}行:送货日期解析完成,值={}", currentRowNum, deliveryDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 送货线号
|
||||
if (deliveryHeader.getDeliveryLineNo() == null || deliveryHeader.getDeliveryLineNo().trim().isEmpty()) {
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_DELIVERY_LINE);
|
||||
if (keywordIndex != null) {
|
||||
Object value = getValueSafely(data, keywordIndex + 1);
|
||||
if (value != null && !value.toString().trim().isEmpty()) {
|
||||
deliveryHeader.setDeliveryLineNo(value.toString().trim());
|
||||
LOGGER.debug("第{}行:送货线号解析完成,值={}", currentRowNum, deliveryHeader.getDeliveryLineNo());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 车次
|
||||
if (deliveryHeader.getTrainNumber() == null) {
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_TRAIN);
|
||||
if (keywordIndex != null) {
|
||||
deliveryHeader.setTrainNumber(convertToInteger(getValueSafely(data, keywordIndex + 1)));
|
||||
LOGGER.debug("第{}行:车次解析完成,值={}", currentRowNum, deliveryHeader.getTrainNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseDeliveryLineNo(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
if (value != null) {
|
||||
String deliveryLineNo = value.toString().trim();
|
||||
deliveryHeader.setDeliveryLineNo(deliveryLineNo);
|
||||
LOGGER.debug("第{}行:送货线号解析完成,值={}", currentRowNum, deliveryLineNo);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseTrainNumber(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
Integer trainNumber = convertToInteger(value);
|
||||
deliveryHeader.setTrainNumber(trainNumber);
|
||||
LOGGER.debug("第{}行:车次解析完成,值={}", currentRowNum, trainNumber);
|
||||
}
|
||||
|
||||
// ---------------------- 左侧:纪录合共解析 ----------------------
|
||||
// ---------------------- 左侧:纪录合共解析(标签定位,行位置不限) ----------------------
|
||||
/**
|
||||
* 第5行:纪录合共
|
||||
* 列对应关系:0=A列=紀錄合共标签,1=B列=总记录数,3=D列=总箱数,4=E列=总支数
|
||||
* 纪录合共:标签右侧第1列=总记录数,第3列=总箱数,第4列=总支数(与老模板B/D/E列偏移一致)
|
||||
*/
|
||||
private void parseTotalRecord(Map<String, Object> data) {
|
||||
Object totalRecordLabel = getValueSafely(data, 0);
|
||||
if (totalRecordLabel == null || !totalRecordLabel.toString().trim().contains(KEYWORD_TOTAL_RECORD)) {
|
||||
LOGGER.warn("第{}行:不是纪录合共行,跳过解析,第一列内容:{}", currentRowNum, totalRecordLabel);
|
||||
Integer keywordIndex = findKeywordIndex(data, KEYWORD_TOTAL_RECORD);
|
||||
if (keywordIndex == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Integer totalRecordCount = convertToInteger(getValueSafely(data, 1)); // B列
|
||||
Integer totalBoxCount = convertToInteger(getValueSafely(data, 3)); // D列
|
||||
Integer totalPieceCount = convertToInteger(getValueSafely(data, 4)); // E列
|
||||
Integer totalRecordCount = convertToInteger(getValueSafely(data, keywordIndex + 1));
|
||||
Integer totalBoxCount = convertToInteger(getValueSafely(data, keywordIndex + 3));
|
||||
Integer totalPieceCount = convertToInteger(getValueSafely(data, keywordIndex + 4));
|
||||
|
||||
deliveryHeader.setTotalRecordCount(totalRecordCount);
|
||||
deliveryHeader.setTotalBoxCount(totalBoxCount);
|
||||
@@ -194,27 +245,66 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
currentRowNum, totalRecordCount, totalBoxCount, totalPieceCount);
|
||||
}
|
||||
|
||||
// ---------------------- 左侧:货品明细解析 ----------------------
|
||||
// ---------------------- 左侧:货品明细列头识别与数据解析 ----------------------
|
||||
/**
|
||||
* 校验货品明细行是否有效(排除表头和空行)
|
||||
* 尝试将当前行识别为货品明细列头行:以货品编号列为锚点,按列头文字(简繁归一化)匹配各列
|
||||
*/
|
||||
private boolean isValidProductDetailRow(Map<String, Object> data) {
|
||||
Object productCode = getValueSafely(data, 0); // A列
|
||||
return productCode != null
|
||||
&& !productCode.toString().trim().isEmpty()
|
||||
&& !"貨品編號".equals(productCode.toString().trim());
|
||||
private boolean tryResolveProductColumns(Map<String, Object> data) {
|
||||
int codeCol = findColumnByAliases(data, PRODUCT_CODE_ALIASES);
|
||||
if (codeCol < 0) {
|
||||
return false;
|
||||
}
|
||||
int nameCol = findColumnByAliases(data, PRODUCT_NAME_ALIASES);
|
||||
int boxCol = findColumnByAliases(data, PRODUCT_BOX_ALIASES);
|
||||
int pieceCol = findColumnByAliases(data, PRODUCT_PIECE_ALIASES);
|
||||
int matched = 1 + (nameCol >= 0 ? 1 : 0) + (boxCol >= 0 ? 1 : 0) + (pieceCol >= 0 ? 1 : 0);
|
||||
if (matched < HEADER_MIN_MATCH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
productCodeCol = codeCol;
|
||||
productNameCol = nameCol;
|
||||
productBoxCol = boxCol;
|
||||
productPieceCol = pieceCol;
|
||||
productColumnsResolved = true;
|
||||
LOGGER.info("第{}行识别为货品明细列头行,列映射:货品编号={},名称={},箱={},支={}",
|
||||
currentRowNum, productCodeCol, productNameCol, productBoxCol, productPieceCol);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析货品明细(第7行起)
|
||||
* 列对应关系:0=A列=貨品編號,1=B列=簡稱,2=C列=箱,3=D列=支
|
||||
* 兜底列位:老模板货品明细固定布局(0=A列=貨品編號,1=B列=簡稱,2=C列=箱,3=D列=支)
|
||||
*/
|
||||
private void applyProductFallbackColumns() {
|
||||
productCodeCol = 0;
|
||||
productNameCol = 1;
|
||||
productBoxCol = 2;
|
||||
productPieceCol = 3;
|
||||
productColumnsResolved = true;
|
||||
LOGGER.warn("第{}行前未识别到货品明细列头行,启用老模板固定列位兜底解析", currentRowNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验货品明细行是否有效(排除列头行和空行)
|
||||
*/
|
||||
private boolean isValidProductDetailRow(Map<String, Object> data) {
|
||||
Object productCode = getValueSafely(data, productCodeCol);
|
||||
if (productCode == null || productCode.toString().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 列头文字本身不算数据行(防止列头行被当作数据重复解析)
|
||||
String text = normalizeHeaderText(productCode);
|
||||
return !matchesAny(text, PRODUCT_CODE_ALIASES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析货品明细(列头行之后的行)
|
||||
*/
|
||||
private void parseProductDetail(Map<String, Object> data, int rowNum) {
|
||||
ProductDetail productDetail = new ProductDetail();
|
||||
productDetail.setProductCode(convertToString(getValueSafely(data, 0)));
|
||||
productDetail.setProductName(convertToString(getValueSafely(data, 1)));
|
||||
productDetail.setBoxCount(convertToInteger(getValueSafely(data, 2)));
|
||||
productDetail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
|
||||
productDetail.setProductCode(convertToString(getValueSafely(data, productCodeCol)));
|
||||
productDetail.setProductName(convertToString(getValueSafely(data, productNameCol)));
|
||||
productDetail.setBoxCount(convertToInteger(getValueSafely(data, productBoxCol)));
|
||||
productDetail.setPieceCount(convertToInteger(getValueSafely(data, productPieceCol)));
|
||||
|
||||
productDetailList.add(productDetail);
|
||||
LOGGER.debug("第{}行:解析货品明细完成,货品编号={},名称={},箱数={},支数={}",
|
||||
@@ -222,67 +312,105 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
productDetail.getBoxCount(), productDetail.getPieceCount());
|
||||
}
|
||||
|
||||
// ---------------------- 右侧:发票张数与小计解析 ----------------------
|
||||
/**
|
||||
* 第2行:发票张数与小计
|
||||
* 通过关键字定位标签,再读取右侧相邻列,避免空列导致索引偏移
|
||||
*/
|
||||
private void parseInvoiceCount(Map<String, Object> data) {
|
||||
// 1. 定位并解析发票张数
|
||||
Integer invoiceCount = findValueAfterKeyword(data, KEYWORD_INVOICE_COUNT);
|
||||
if (invoiceCount != null) {
|
||||
deliveryHeader.setInvoiceCount(invoiceCount);
|
||||
// ---------------------- 右侧:发票张数与小计解析(标签定位,行位置不限) ----------------------
|
||||
private void parseInvoiceRow(Map<String, Object> data) {
|
||||
// 1. 发票张数:标签右侧相邻列
|
||||
Integer invoiceIndex = findKeywordIndex(data, KEYWORD_INVOICE_COUNT);
|
||||
if (invoiceIndex != null) {
|
||||
deliveryHeader.setInvoiceCount(convertToInteger(getValueSafely(data, invoiceIndex + 1)));
|
||||
}
|
||||
|
||||
// 2. 定位并解析小计(标签右侧第1列=箱,第2列=支,第3列=金额)
|
||||
Integer subtotalStartIndex = findKeywordIndex(data, KEYWORD_SUBTOTAL);
|
||||
if (subtotalStartIndex != null && subtotalStartIndex >= 0) {
|
||||
Integer subtotalBoxCount = convertToInteger(getValueSafely(data, subtotalStartIndex + 1));
|
||||
Integer subtotalPieceCount = convertToInteger(getValueSafely(data, subtotalStartIndex + 2));
|
||||
BigDecimal subtotalAmount = convertToBigDecimal(getValueSafely(data, subtotalStartIndex + 3));
|
||||
// 2. 小计:标签右侧第1列=箱,第2列=支,第3列=金额
|
||||
Integer subtotalIndex = findKeywordIndex(data, KEYWORD_SUBTOTAL);
|
||||
if (subtotalIndex != null && subtotalIndex >= 0) {
|
||||
Integer subtotalBoxCount = convertToInteger(getValueSafely(data, subtotalIndex + 1));
|
||||
Integer subtotalPieceCount = convertToInteger(getValueSafely(data, subtotalIndex + 2));
|
||||
BigDecimal subtotalAmount = convertToBigDecimal(getValueSafely(data, subtotalIndex + 3));
|
||||
|
||||
deliveryHeader.setSubtotalBoxCount(subtotalBoxCount);
|
||||
deliveryHeader.setSubtotalPieceCount(subtotalPieceCount);
|
||||
deliveryHeader.setSubtotalAmount(subtotalAmount);
|
||||
invoiceRowParsed = true;
|
||||
|
||||
LOGGER.debug("第{}行:解析小计完成,箱数={},支数={},金额={}",
|
||||
currentRowNum, subtotalBoxCount, subtotalPieceCount, subtotalAmount);
|
||||
} else {
|
||||
LOGGER.warn("第{}行:未找到小计标签,小计信息置空", currentRowNum);
|
||||
deliveryHeader.setSubtotalBoxCount(0);
|
||||
deliveryHeader.setSubtotalPieceCount(0);
|
||||
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
|
||||
LOGGER.debug("第{}行:解析发票张数行完成,发票张数={},小计箱数={},支数={},金额={}",
|
||||
currentRowNum, deliveryHeader.getInvoiceCount(), subtotalBoxCount, subtotalPieceCount, subtotalAmount);
|
||||
}
|
||||
|
||||
LOGGER.debug("第{}行:解析发票张数行完成,发票张数={},小计金额={}",
|
||||
currentRowNum, deliveryHeader.getInvoiceCount(), deliveryHeader.getSubtotalAmount());
|
||||
}
|
||||
|
||||
// ---------------------- 右侧:客户配送明细解析 ----------------------
|
||||
// ---------------------- 右侧:客户配送明细列头识别与数据解析 ----------------------
|
||||
/**
|
||||
* 校验客户配送明细行是否有效(排除表头和空行)
|
||||
* 尝试将当前行识别为客户配送明细列头行:以客户编号列为锚点,按列头文字(简繁归一化)匹配各列
|
||||
*/
|
||||
private boolean tryResolveCustomerColumns(Map<String, Object> data) {
|
||||
int codeCol = findColumnByAliases(data, CUSTOMER_CODE_ALIASES);
|
||||
if (codeCol < 0) {
|
||||
return false;
|
||||
}
|
||||
int categoryColTmp = findColumnByAliases(data, CATEGORY_ALIASES);
|
||||
int invoiceNoColTmp = findColumnByAliases(data, INVOICE_NO_ALIASES);
|
||||
int nameColTmp = findColumnByAliases(data, CUSTOMER_NAME_ALIASES);
|
||||
int boxColTmp = findColumnByAliases(data, CUSTOMER_BOX_ALIASES);
|
||||
int packageColTmp = findColumnByAliases(data, CUSTOMER_PACKAGE_ALIASES);
|
||||
int amountColTmp = findColumnByAliases(data, CUSTOMER_AMOUNT_ALIASES);
|
||||
int matched = 1 + (categoryColTmp >= 0 ? 1 : 0) + (invoiceNoColTmp >= 0 ? 1 : 0) + (nameColTmp >= 0 ? 1 : 0)
|
||||
+ (boxColTmp >= 0 ? 1 : 0) + (packageColTmp >= 0 ? 1 : 0) + (amountColTmp >= 0 ? 1 : 0);
|
||||
if (matched < HEADER_MIN_MATCH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
categoryCol = categoryColTmp;
|
||||
invoiceNoCol = invoiceNoColTmp;
|
||||
customerCodeCol = codeCol;
|
||||
customerNameCol = nameColTmp;
|
||||
customerBoxCol = boxColTmp;
|
||||
customerPackageCol = packageColTmp;
|
||||
customerAmountCol = amountColTmp;
|
||||
customerColumnsResolved = true;
|
||||
LOGGER.info("第{}行识别为客户配送明细列头行,列映射:类别={},发票编号={},客户编号={},名称={},箱={},包={},金额={}",
|
||||
currentRowNum, categoryCol, invoiceNoCol, customerCodeCol, customerNameCol, customerBoxCol, customerPackageCol, customerAmountCol);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底列位:老模板客户配送明细固定布局(5=F列=類别,6=G列=發票編號,7=H列=客户編號,8=I列=名稱,
|
||||
* 9=J列=箱,10=K列=包,11=L列=金額)
|
||||
*/
|
||||
private void applyCustomerFallbackColumns() {
|
||||
categoryCol = 5;
|
||||
invoiceNoCol = 6;
|
||||
customerCodeCol = 7;
|
||||
customerNameCol = 8;
|
||||
customerBoxCol = 9;
|
||||
customerPackageCol = 10;
|
||||
customerAmountCol = 11;
|
||||
customerColumnsResolved = true;
|
||||
LOGGER.warn("第{}行前未识别到客户配送明细列头行,启用老模板固定列位兜底解析", currentRowNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验客户配送明细行是否有效(排除列头行和空行)
|
||||
*/
|
||||
private boolean isValidCustomerDetailRow(Map<String, Object> data) {
|
||||
Object category = getValueSafely(data, 5); // F列
|
||||
return category != null
|
||||
&& !category.toString().trim().isEmpty()
|
||||
&& !"類别".equals(category.toString().trim());
|
||||
Object category = getValueSafely(data, categoryCol);
|
||||
if (category == null || category.toString().trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// 列头文字本身不算数据行(防止列头行被当作数据重复解析)
|
||||
String text = normalizeHeaderText(category);
|
||||
return !matchesAny(text, CATEGORY_ALIASES);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析客户配送明细(第4行起)
|
||||
* 列对应关系:5=F列=類别,6=G列=發票編號,7=H列=客户編號,8=I列=名稱,
|
||||
* 9=J列=箱,10=K列=包,11=L列=金額
|
||||
* 解析客户配送明细(列头行之后的行)
|
||||
*/
|
||||
private void parseCustomerDeliveryDetail(Map<String, Object> data, int rowNum) {
|
||||
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
|
||||
customerDetail.setCategory(convertToString(getValueSafely(data, 5)));
|
||||
customerDetail.setInvoiceNo(convertToString(getValueSafely(data, 6)));
|
||||
customerDetail.setCustomerCode(convertToString(getValueSafely(data, 7)));
|
||||
customerDetail.setCustomerName(convertToString(getValueSafely(data, 8)));
|
||||
customerDetail.setBoxCount(convertToInteger(getValueSafely(data, 9)));
|
||||
customerDetail.setPackageCount(convertToInteger(getValueSafely(data, 10)));
|
||||
customerDetail.setAmount(convertToBigDecimal(getValueSafely(data, 11)));
|
||||
customerDetail.setCategory(convertToString(getValueSafely(data, categoryCol)));
|
||||
customerDetail.setInvoiceNo(convertToString(getValueSafely(data, invoiceNoCol)));
|
||||
customerDetail.setCustomerCode(convertToString(getValueSafely(data, customerCodeCol)));
|
||||
customerDetail.setCustomerName(convertToString(getValueSafely(data, customerNameCol)));
|
||||
customerDetail.setBoxCount(convertToInteger(getValueSafely(data, customerBoxCol)));
|
||||
customerDetail.setPackageCount(convertToInteger(getValueSafely(data, customerPackageCol)));
|
||||
customerDetail.setAmount(convertToBigDecimal(getValueSafely(data, customerAmountCol)));
|
||||
|
||||
customerDeliveryDetailList.add(customerDetail);
|
||||
LOGGER.debug("第{}行:解析客户配送明细完成,类别={},发票编号={},客户编号={},名称={},箱数={},包数={},金额={}",
|
||||
@@ -299,7 +427,7 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
* 此时key不是数字字符串,需跳过这些key,走位置兜底逻辑
|
||||
*/
|
||||
private Object getValueSafely(Map<String, Object> data, int columnIndex) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
if (columnIndex < 0 || data == null || data.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -333,9 +461,9 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
return "未知列";
|
||||
}
|
||||
int index = 0;
|
||||
for (String key : data.keySet()) {
|
||||
for (Object key : data.keySet()) {
|
||||
if (index == columnIndex) {
|
||||
return key;
|
||||
return String.valueOf(key);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@@ -343,22 +471,28 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定行中查找关键字所在的列索引
|
||||
* 注意:Map的key可能是非数字字符串(如表头文本"客户配送明细"),此时跳过该key,不抛出异常
|
||||
* 在指定行中查找关键字所在的列索引(关键字归一化后包含匹配,兼容简繁体与冒号等符号)
|
||||
* 注意:Map的key可能是非数字字符串(如表头文本),此时跳过该key,不抛出异常
|
||||
*/
|
||||
private Integer findKeywordIndex(Map<String, Object> data, String keyword) {
|
||||
if (data == null || data.isEmpty() || keyword == null) {
|
||||
private Integer findKeywordIndex(Map<String, Object> data, String[] keywords) {
|
||||
if (data == null || data.isEmpty() || keywords == null) {
|
||||
return null;
|
||||
}
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value != null && value.toString().trim().contains(keyword)) {
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = normalizeHeaderText(value);
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (matchesAny(text, keywords)) {
|
||||
try {
|
||||
return Integer.parseInt(keyObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// key不是数字(可能是表头文本如"客户配送明细"),跳过,不打印警告避免日志刷屏
|
||||
LOGGER.debug("第{}行:关键字'{}'所在列key不是数字,key={},跳过",
|
||||
currentRowNum, keyword, keyObj);
|
||||
// key不是数字(可能是表头文本),跳过,不打印警告避免日志刷屏
|
||||
LOGGER.debug("第{}行:关键字所在列key不是数字,key={},跳过", currentRowNum, keyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,15 +500,64 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找关键字所在列的右侧相邻列值,并转为Integer
|
||||
* 在指定行中按列头别名查找列索引(列头文字归一化后包含匹配,返回首个命中的列;未命中返回-1)
|
||||
*/
|
||||
private Integer findValueAfterKeyword(Map<String, Object> data, String keyword) {
|
||||
Integer keywordIndex = findKeywordIndex(data, keyword);
|
||||
if (keywordIndex == null) {
|
||||
LOGGER.warn("第{}行:未找到关键字'{}'", currentRowNum, keyword);
|
||||
return null;
|
||||
private int findColumnByAliases(Map<String, Object> data, String[] aliases) {
|
||||
if (data == null || data.isEmpty() || aliases == null) {
|
||||
return -1;
|
||||
}
|
||||
return convertToInteger(getValueSafely(data, keywordIndex + 1));
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = normalizeHeaderText(value);
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (matchesAny(text, aliases)) {
|
||||
try {
|
||||
return Integer.parseInt(keyObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// key不是数字(可能是表头文本),跳过
|
||||
LOGGER.debug("第{}行:列头所在列key不是数字,key={},跳过", currentRowNum, keyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列头/标签文字归一化:去空格/冒号/括号等符号、转小写、繁体转简体(覆盖模板常见用字)
|
||||
*/
|
||||
private String normalizeHeaderText(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String s = value.toString().trim().toLowerCase();
|
||||
s = s.replace(":", "").replace(":", "")
|
||||
.replace(" ", "").replace(" ", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("/", "").replace("\\", "");
|
||||
s = s.replace('貨', '货').replace('編', '编').replace('號', '号').replace('稱', '称')
|
||||
.replace('數', '数').replace('飲', '饮').replace('規', '规').replace('戶', '户')
|
||||
.replace('紀', '纪').replace('錄', '录').replace('張', '张').replace('計', '计')
|
||||
.replace('車', '车').replace('線', '线').replace('類', '类').replace('別', '别')
|
||||
.replace('簡', '简').replace('產', '产').replace('單', '单');
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断归一化后的文字是否命中任一别名
|
||||
*/
|
||||
private boolean matchesAny(String text, String[] aliases) {
|
||||
for (String alias : aliases) {
|
||||
if (text.contains(alias)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+206
-40
@@ -17,7 +17,11 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 入库单Excel解析监听器
|
||||
* 功能:1. 解析第一行表头数据 2. 跳过第二行合计行、第三行列名行 3. 解析第四行开始的产品明细 4. 支持文本型数字转换
|
||||
* 功能:1. 解析第一行表头数据(入库日期、OrderNO,按标签定位,位置不敏感)
|
||||
* 2. 动态定位列头行:扫描前几行,识别出"產品編號"等已知列头文字的行即认定为列头行(行位置不敏感)
|
||||
* 3. 列按列头文字动态定位(兼容繁体列头:產品編號/客戶編號/件數/飲用日期等),列顺序调整不影响解析
|
||||
* 4. 列头无法识别时兜底:按老模板固定列位(0=编号,1=名称,2=规格,3=件数,4=饮用日期)
|
||||
* 5. 支持文本型数字转换
|
||||
*/
|
||||
public class StockInExcelListener extends AnalysisEventListener<Map<String, Object>> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StockInExcelListener.class);
|
||||
@@ -35,6 +39,29 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
"dd.MM.yyyy", "d-MMM-yyyy", "yyyyMMddHHmmss"
|
||||
};
|
||||
|
||||
// ====== 列头别名(归一化后匹配,兼容简繁体及常见叫法) ======
|
||||
private static final String[] PRODUCT_CODE_ALIASES = {"产品编号", "物料编号", "产品编码", "物料编码", "料号"};
|
||||
private static final String[] PRODUCT_NAME_ALIASES = {"产品名称", "物料名称", "商品名称", "品名"};
|
||||
private static final String[] CUSTOMER_CODE_ALIASES = {"客户编号", "客户编码", "客户代码"};
|
||||
private static final String[] DRINK_DATE_ALIASES = {"饮用日期", "饮用时间"};
|
||||
private static final String[] PIECE_COUNT_ALIASES = {"件数"};
|
||||
private static final String[] SPEC_ALIASES = {"规格", "箱含量"};
|
||||
|
||||
// 列头行至少需识别出的列数(防止把日期标签行/合计行误判为列头行)
|
||||
private static final int HEADER_MIN_MATCH_COUNT = 2;
|
||||
// 老模板列头行固定在第3行:超过该行仍未识别到列头行时,启用固定列位兜底
|
||||
private static final int FALLBACK_AFTER_ROW_NUM = 3;
|
||||
|
||||
// 动态解析出的列索引(-1表示未识别该列)
|
||||
private int productCodeCol = -1;
|
||||
private int productNameCol = -1;
|
||||
private int customerCodeCol = -1;
|
||||
private int drinkDateCol = -1;
|
||||
private int pieceCountCol = -1;
|
||||
private int specCol = -1;
|
||||
// 列布局是否已确定(列头行识别成功或已启用兜底)
|
||||
private boolean columnResolved = false;
|
||||
|
||||
// 行号标记(用于区分第一行和表格行)
|
||||
private int currentRowNum = 0;
|
||||
|
||||
@@ -48,21 +75,21 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
if (currentRowNum == 1) {
|
||||
parseHeaderData(data);
|
||||
}
|
||||
// 2. 跳过第二行:合计行(Total: xxx)
|
||||
else if (currentRowNum == 2) {
|
||||
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
|
||||
}
|
||||
// 3. 跳过第三行:表格列名行(產品編號、產品名稱...)
|
||||
else if (currentRowNum == 3) {
|
||||
LOGGER.debug("第{}行是表格列名行,跳过解析", currentRowNum);
|
||||
}
|
||||
// 4. 解析第四行及以后:产品明细
|
||||
else if (currentRowNum >= 4) {
|
||||
if (isValidProductRow(data)) {
|
||||
StockInProductDetail detail = parseProductDetail(data);
|
||||
productDetailList.add(detail);
|
||||
// 2. 列布局未确定:尝试识别列头行;识别成功则本行即为列头行,跳过数据解析
|
||||
else if (!columnResolved) {
|
||||
if (!tryResolveHeaderRow(data)) {
|
||||
// 3. 超过老模板列头行位置仍未识别到列头行:启用固定列位兜底,从当前行开始按数据行解析
|
||||
if (currentRowNum > FALLBACK_AFTER_ROW_NUM) {
|
||||
applyFallbackColumns();
|
||||
parseDataRow(data);
|
||||
}
|
||||
// 否则(合计行Total等):跳过
|
||||
}
|
||||
}
|
||||
// 4. 列布局已确定:按数据行解析
|
||||
else {
|
||||
parseDataRow(data);
|
||||
}
|
||||
|
||||
} catch (ExcelDataConvertException e) {
|
||||
// Excel内置转换异常(如日期格式错误)
|
||||
@@ -79,6 +106,16 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据行解析:排除合计行/空行后解析产品明细
|
||||
*/
|
||||
private void parseDataRow(Map<String, Object> data) {
|
||||
if (isValidProductRow(data)) {
|
||||
StockInProductDetail detail = parseProductDetail(data);
|
||||
productDetailList.add(detail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析第一行表头数据
|
||||
* 模板中标签与值相邻,分别搜索"入库日期"和"OrderNO"标签后一列的值,兼容位置调整
|
||||
@@ -86,15 +123,17 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
private void parseHeaderData(Map<String, Object> data) {
|
||||
// 入库日期:查找包含"入库日期"标签的下一个值
|
||||
Object stockInDateObj = getValueAfterLabel(data, "入庫日期:");
|
||||
if (stockInDateObj == null) {
|
||||
stockInDateObj = getValueAfterLabel(data, "入库日期");
|
||||
}
|
||||
if (stockInDateObj != null) {
|
||||
Date stockInDate = convertToDate(stockInDateObj);
|
||||
stockInHeader.setStockInDate(stockInDate);
|
||||
LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate);
|
||||
}
|
||||
|
||||
// OrderNO:优先查找"OrderNO"标签后的值(老模板布局,标签与值相邻);
|
||||
// 新模板第1行无"OrderNO"标签,单号值直接放在单元格中(如E1),
|
||||
// 此时取第1行中除标签、入库日期以外的第一个非空字符串值
|
||||
// OrderNO:优先查找"OrderNO"标签后的值(标签与值相邻);
|
||||
// 新模板第1行无"OrderNO"标签时,取第1行中除标签、入库日期以外的第一个非空字符串值
|
||||
Object orderNoObj = getValueAfterLabel(data, "OrderNO");
|
||||
if (orderNoObj == null) {
|
||||
for (Object value : data.values()) {
|
||||
@@ -103,7 +142,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
String str = value.toString().trim();
|
||||
// 跳过标签单元格与已被识别为入库日期的值
|
||||
if (str.isEmpty() || str.contains("入庫日期") || str.contains("OrderNO") || str.contains("Total")) {
|
||||
if (str.isEmpty() || str.contains("入庫日期") || str.contains("入库日期") || str.contains("OrderNO") || str.contains("Total")) {
|
||||
continue;
|
||||
}
|
||||
if (value == stockInDateObj) {
|
||||
@@ -125,26 +164,113 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试将当前行识别为列头行:按归一化后的列头文字匹配已知列别名
|
||||
* 识别成功(至少识别出产品编号列且总数达标)返回true并记录各列索引
|
||||
*/
|
||||
private boolean tryResolveHeaderRow(Map<String, Object> data) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
int tmpProductCodeCol = -1;
|
||||
int tmpProductNameCol = -1;
|
||||
int tmpCustomerCodeCol = -1;
|
||||
int tmpDrinkDateCol = -1;
|
||||
int tmpPieceCountCol = -1;
|
||||
int tmpSpecCol = -1;
|
||||
int matched = 0;
|
||||
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
String text = normalizeHeaderText(value);
|
||||
if (text.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
int colIndex;
|
||||
try {
|
||||
colIndex = Integer.parseInt(keyObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
// key不是数字(如表头文本),跳过
|
||||
continue;
|
||||
}
|
||||
// 按组依次匹配(先具体后一般,避免同一列头命中多组别名时归属错乱)
|
||||
if (tmpProductCodeCol < 0 && matchesAny(text, PRODUCT_CODE_ALIASES)) {
|
||||
tmpProductCodeCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpCustomerCodeCol < 0 && matchesAny(text, CUSTOMER_CODE_ALIASES)) {
|
||||
tmpCustomerCodeCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpProductNameCol < 0 && matchesAny(text, PRODUCT_NAME_ALIASES)) {
|
||||
tmpProductNameCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpDrinkDateCol < 0 && matchesAny(text, DRINK_DATE_ALIASES)) {
|
||||
tmpDrinkDateCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpPieceCountCol < 0 && matchesAny(text, PIECE_COUNT_ALIASES)) {
|
||||
tmpPieceCountCol = colIndex;
|
||||
matched++;
|
||||
} else if (tmpSpecCol < 0 && matchesAny(text, SPEC_ALIASES)) {
|
||||
tmpSpecCol = colIndex;
|
||||
matched++;
|
||||
}
|
||||
}
|
||||
|
||||
// 产品编号列是数据解析的锚点,必须识别到;且识别总数达标才认定为列头行
|
||||
if (tmpProductCodeCol < 0 || matched < HEADER_MIN_MATCH_COUNT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
productCodeCol = tmpProductCodeCol;
|
||||
productNameCol = tmpProductNameCol;
|
||||
customerCodeCol = tmpCustomerCodeCol;
|
||||
drinkDateCol = tmpDrinkDateCol;
|
||||
pieceCountCol = tmpPieceCountCol;
|
||||
specCol = tmpSpecCol;
|
||||
columnResolved = true;
|
||||
LOGGER.info("第{}行识别为列头行,列映射:产品编号={},产品名称={},客户编号={},规格={},件数={},饮用日期={}",
|
||||
currentRowNum, productCodeCol, productNameCol, customerCodeCol, specCol, pieceCountCol, drinkDateCol);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底列位:老模板固定布局(0=编号,1=名称,2=规格,3=件数,4=饮用日期),无客户编号列
|
||||
*/
|
||||
private void applyFallbackColumns() {
|
||||
productCodeCol = 0;
|
||||
productNameCol = 1;
|
||||
customerCodeCol = -1;
|
||||
specCol = 2;
|
||||
pieceCountCol = 3;
|
||||
drinkDateCol = 4;
|
||||
columnResolved = true;
|
||||
LOGGER.warn("第{}行前未识别到列头行,启用老模板固定列位兜底解析", currentRowNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析产品明细数据(一行对应一个产品)
|
||||
* 新模板列顺序:0=产品编号,1=产品名称,2=规格(箱含量),3=件数,4=饮用日期
|
||||
* 各列按列头行动态定位的索引取值,未识别的列取null/默认值
|
||||
*/
|
||||
private StockInProductDetail parseProductDetail(Map<String, Object> data) {
|
||||
StockInProductDetail detail = new StockInProductDetail();
|
||||
|
||||
// 产品编号(第1列,索引0)- 支持文本型数字转换
|
||||
detail.setProductCode(convertToString(getValueSafely(data, 0)));
|
||||
// 产品名称(第2列,索引1)
|
||||
detail.setProductName(convertToString(getValueSafely(data, 1)));
|
||||
// 箱含量/规格(第3列,索引2)
|
||||
detail.setBoxCapacity(convertToInteger(getValueSafely(data, 2)));
|
||||
// 件数(第4列,索引3)
|
||||
detail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
|
||||
// 饮用日期(第5列,索引4)
|
||||
detail.setDrinkDate(convertToDate(getValueSafely(data, 4)));
|
||||
// 产品编号(可不填名称,导入时按编号自动匹配库中物料名称)
|
||||
detail.setProductCode(convertToString(getValueSafely(data, productCodeCol)));
|
||||
// 产品名称(列头未识别或单元格未填时为空串,由服务端按编号自动匹配覆盖)
|
||||
detail.setProductName(convertToString(getValueSafely(data, productNameCol)));
|
||||
// 客户编号(新模板新增列,可不填;填写后由服务端校验客户存在性并自动匹配客户名称)
|
||||
detail.setCustomerCode(convertToString(getValueSafely(data, customerCodeCol)));
|
||||
// 箱含量/规格
|
||||
detail.setBoxCapacity(convertToInteger(getValueSafely(data, specCol)));
|
||||
// 件数
|
||||
detail.setPieceCount(convertToInteger(getValueSafely(data, pieceCountCol)));
|
||||
// 饮用日期
|
||||
detail.setDrinkDate(convertToDate(getValueSafely(data, drinkDateCol)));
|
||||
|
||||
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{}",
|
||||
currentRowNum, detail.getProductCode(), detail.getProductName());
|
||||
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{},客户编号:{}",
|
||||
currentRowNum, detail.getProductCode(), detail.getProductName(), detail.getCustomerCode());
|
||||
return detail;
|
||||
}
|
||||
|
||||
@@ -152,9 +278,6 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
* 校验产品行是否有效(排除空行和合计行)
|
||||
*/
|
||||
private boolean isValidProductRow(Map<String, Object> data) {
|
||||
// 有效条件:产品编号不为空 + 不是合计行
|
||||
Object productCodeObj = getValueSafely(data, 0);
|
||||
|
||||
// 排除合计行(整行包含"Total"字样)
|
||||
if (containsAnyValue(data, "Total")) {
|
||||
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
|
||||
@@ -162,6 +285,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
|
||||
// 排除空行(产品编号为空)
|
||||
Object productCodeObj = getValueSafely(data, productCodeCol);
|
||||
return productCodeObj != null && !productCodeObj.toString().trim().isEmpty();
|
||||
}
|
||||
|
||||
@@ -178,13 +302,24 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
|
||||
// ---------------------- 工具方法(类型转换+安全取值) ----------------------
|
||||
/**
|
||||
* 按列索引安全获取值(支持列索引越界处理)
|
||||
* 按列索引安全获取值
|
||||
* 优先按Map的key直接查找(适配EasyExcel以列号为key的情况,避免空单元格导致的位置偏移),
|
||||
* 找不到再按位置顺序查找(保持与原逻辑兼容)
|
||||
*/
|
||||
private Object getValueSafely(Map<String, Object> data, int columnIndex) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
if (columnIndex < 0 || data == null || data.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String strIndex = String.valueOf(columnIndex);
|
||||
// 1. 直接按key查找(key为String或Integer,跳过非数字key)
|
||||
for (Object keyObj : data.keySet()) {
|
||||
if (keyObj != null && strIndex.equals(keyObj.toString())) {
|
||||
return data.get(keyObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 兜底:按顺序位置查找
|
||||
int index = 0;
|
||||
for (Object value : data.values()) {
|
||||
if (index == columnIndex) {
|
||||
@@ -193,7 +328,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
index++;
|
||||
}
|
||||
|
||||
LOGGER.warn("第{}行列索引{}超出范围(当前行共{}列),返回null",
|
||||
LOGGER.warn("第{}行列索引{}超出当前行单元格数量({}列),返回null",
|
||||
currentRowNum, columnIndex, data.size());
|
||||
return null;
|
||||
}
|
||||
@@ -207,9 +342,9 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (String key : data.keySet()) {
|
||||
for (Object key : data.keySet()) {
|
||||
if (index == columnIndex) {
|
||||
return key;
|
||||
return String.valueOf(key);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@@ -251,6 +386,37 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列头文字归一化:去空格/冒号/括号等符号、转小写、繁体转简体(覆盖模板常见用字)
|
||||
*/
|
||||
private String normalizeHeaderText(Object value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
String s = value.toString().trim().toLowerCase();
|
||||
s = s.replace(":", "").replace(":", "")
|
||||
.replace(" ", "").replace(" ", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("(", "").replace(")", "")
|
||||
.replace("/", "").replace("\\", "");
|
||||
s = s.replace('產', '产').replace('編', '编').replace('號', '号').replace('稱', '称')
|
||||
.replace('數', '数').replace('飲', '饮').replace('規', '规').replace('戶', '户')
|
||||
.replace('貨', '货').replace('單', '单').replace('類', '类').replace('倉', '仓');
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断归一化后的列头文字是否命中任一别名
|
||||
*/
|
||||
private boolean matchesAny(String text, String[] aliases) {
|
||||
for (String alias : aliases) {
|
||||
if (text.contains(alias)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用转换:Object转String
|
||||
*/
|
||||
@@ -347,4 +513,4 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
public List<StockInProductDetail> getProductDetailList() {
|
||||
return productDetailList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+175
-38
@@ -172,10 +172,10 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
String lineCode = deliveryHeader.getDeliveryLineNo();
|
||||
Integer trainNumber = deliveryHeader.getTrainNumber();
|
||||
if (StringUtils.isBlank(lineCode)) {
|
||||
throw new Exception("Excel未识别到【送貨線號】(线路),请检查模板第2行后重新导入");
|
||||
throw new Exception("Excel未识别到【送貨線號】(线路),请检查模板表头后重新导入");
|
||||
}
|
||||
if (trainNumber == null || trainNumber <= 0) {
|
||||
throw new Exception("Excel未识别到【車次】,请检查模板第3行后重新导入");
|
||||
throw new Exception("Excel未识别到【車次】,请检查模板表头后重新导入");
|
||||
}
|
||||
AjaxResult lineResult = wmsServiceFeign.listLineManagement(lineCode);
|
||||
if (lineResult == null || lineResult.get(AjaxResult.DATA_TAG) == null) {
|
||||
@@ -259,17 +259,64 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
|
||||
List<Map<String,Object>> shipperIds = new ArrayList<>();
|
||||
Set<Long> processedShipperIds = new HashSet<>();
|
||||
// ====== 第一步:物料编号存在性校验(落库前,先收集全部缺失编号,不立即报错) ======
|
||||
Map<String, MaterialBaseInfoPO> materialCache = new LinkedHashMap<>();
|
||||
List<String> missingMaterials = new ArrayList<>();
|
||||
for (ProductDetail productDetail : productDetailList) {
|
||||
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(productDetail.getProductCode(),loginUser.getUserPo().getOrganizationId());
|
||||
if (materialBaseInfoPO != null && materialBaseInfoPO.getShipperId() != null) {
|
||||
Long shipperId = materialBaseInfoPO.getShipperId();
|
||||
if (processedShipperIds.add(shipperId)) {
|
||||
Map<String, Object> shipper = new HashMap<>();
|
||||
shipper.put("shipperId", shipperId);
|
||||
shipper.put("shipperName", materialBaseInfoPO.getShipperName());
|
||||
shipper.put("shipperCode", materialBaseInfoPO.getShipperCode());
|
||||
shipperIds.add(shipper);
|
||||
}
|
||||
String productCode = productDetail.getProductCode() == null ? "" : productDetail.getProductCode().trim();
|
||||
if (productCode.isEmpty()) {
|
||||
throw new Exception("出库明细存在未填写產品編號的行,请补全后重新导入");
|
||||
}
|
||||
if (materialCache.containsKey(productCode)) {
|
||||
continue;
|
||||
}
|
||||
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(productCode, loginUser.getUserPo().getOrganizationId());
|
||||
if (materialBaseInfoPO == null) {
|
||||
missingMaterials.add(productCode);
|
||||
} else {
|
||||
materialCache.put(productCode, materialBaseInfoPO);
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 第二步:客户编号存在性校验(落库前,先收集全部缺失编号,不立即报错) ======
|
||||
Map<String, UserPo> customerCache = new HashMap<>();
|
||||
List<String> missingCustomers = new ArrayList<>();
|
||||
for (CustomerDeliveryDetail customerDeliveryDetail : customerDeliveryDetailList) {
|
||||
String customerCode = customerDeliveryDetail.getCustomerCode() == null ? "" : customerDeliveryDetail.getCustomerCode().trim();
|
||||
if (customerCode.isEmpty()) {
|
||||
throw new Exception("客户配送明细存在未填写客户编号的行,请补全后重新导入");
|
||||
}
|
||||
if (customerCache.containsKey(customerCode)) {
|
||||
continue;
|
||||
}
|
||||
UserPo customer = stockOutOrderMapper.queryCustomerByCode(customerCode, loginUser.getUserPo().getOrganizationId());
|
||||
if (customer == null || customer.getUserId() == null) {
|
||||
missingCustomers.add(customerCode);
|
||||
} else {
|
||||
customerCache.put(customerCode, customer);
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 第三步:物料/客户存在性校验全部完成后,统一提示两类编号各自有哪些不存在 ======
|
||||
if (!missingMaterials.isEmpty() || !missingCustomers.isEmpty()) {
|
||||
List<String> problems = new ArrayList<>();
|
||||
if (!missingMaterials.isEmpty()) {
|
||||
problems.add("物料[" + String.join("、", missingMaterials) + "]不存在,请先维护物料信息");
|
||||
}
|
||||
if (!missingCustomers.isEmpty()) {
|
||||
problems.add("客户[" + String.join("、", missingCustomers) + "]不存在,请先维护客户信息");
|
||||
}
|
||||
throw new Exception(String.join(";", problems) + ",维护完成后重新导入");
|
||||
}
|
||||
|
||||
// ====== 后续处理(编号均已存在):货主归属校验、建单落库 ======
|
||||
for (MaterialBaseInfoPO materialBaseInfoPO : materialCache.values()) {
|
||||
if (materialBaseInfoPO.getShipperId() != null && processedShipperIds.add(materialBaseInfoPO.getShipperId())) {
|
||||
Map<String, Object> shipper = new HashMap<>();
|
||||
shipper.put("shipperId", materialBaseInfoPO.getShipperId());
|
||||
shipper.put("shipperName", materialBaseInfoPO.getShipperName());
|
||||
shipper.put("shipperCode", materialBaseInfoPO.getShipperCode());
|
||||
shipperIds.add(shipper);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +377,8 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
for (ProductDetail productDetail : productDetailList) {
|
||||
ReservationOutMaterialDetailDO reservationOutMaterialDetailDO = new ReservationOutMaterialDetailDO();
|
||||
reservationOutMaterialDetailDO.setOutOrderNumber(reservationStockOutOrderDO.getOutOrderNumber());
|
||||
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(productDetail.getProductCode(),loginUser.getUserPo().getOrganizationId());
|
||||
// 物料信息取自前置校验的缓存(已确认存在,避免逐行重复查库)
|
||||
MaterialBaseInfoPO materialBaseInfoPO = materialCache.get(productDetail.getProductCode());
|
||||
reservationOutMaterialDetailDO.setQuantity(new BigDecimal(productDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP));
|
||||
if (materialBaseInfoPO != null) {
|
||||
Long materialBaseInfoId = materialBaseInfoPO.getMaterialBaseInfoId();
|
||||
@@ -398,9 +446,14 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
reservationDeliveryDetailsLink.setAmount(customerDeliveryDetail.getAmount());
|
||||
reservationDeliveryDetailsLink.setCustomerNumber(customerDeliveryDetail.getCustomerCode());
|
||||
reservationDeliveryDetailsLink.setDeliveryName(customerDeliveryDetail.getCustomerName());
|
||||
String customerCode = customerDeliveryDetail.getCustomerCode();
|
||||
Long userId = stockOutOrderMapper.queryShipperId(customerCode, loginUser.getUserPo().getOrganizationId());
|
||||
reservationDeliveryDetailsLink.setCustomerId(userId);
|
||||
String customerCode = customerDeliveryDetail.getCustomerCode() == null ? "" : customerDeliveryDetail.getCustomerCode().trim();
|
||||
// 客户已在前置校验通过:客户ID取库中匹配结果,名称以库中数据为准自动匹配(覆盖模板填写值;库中名称为空时保留模板值)
|
||||
UserPo customer = customerCache.get(customerCode);
|
||||
reservationDeliveryDetailsLink.setCustomerId(customer.getUserId());
|
||||
if (customer.getUserName() != null && !customer.getUserName().trim().isEmpty()) {
|
||||
customerDeliveryDetail.setCustomerName(customer.getUserName());
|
||||
reservationDeliveryDetailsLink.setDeliveryName(customer.getUserName());
|
||||
}
|
||||
reservationDeliveryDetailsLink.setBox(customerDeliveryDetail.getBoxCount());
|
||||
reservationDeliveryDetailsLink.setBag(customerDeliveryDetail.getPackageCount());
|
||||
reservationDeliveryDetailsLink.setCategory(customerDeliveryDetail.getCategory());
|
||||
@@ -462,30 +515,103 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
LOGGER.info("Excel解析成功,工作表:{},产品明细数:{}",
|
||||
targetSheetName, resultDTO.getProductDetailList().size());
|
||||
List<StockInProductDetail> productDetailList = resultDTO.getProductDetailList();
|
||||
if (productDetailList == null || productDetailList.isEmpty()) {
|
||||
throw new ServiceException("未解析到产品明细数据,请检查模板中是否填写了產品編號");
|
||||
}
|
||||
StockInHeader stockInHeader = resultDTO.getStockInHeader();
|
||||
// ====== 第一步:物料编号存在性校验(模板只填物料编号即可,名称按编号自动匹配) ======
|
||||
// 查询结果为空说明库中没有该物料,先收集全部缺失编号,不立即报错
|
||||
Map<String, MaterialBaseInfoPO> materialCache = new LinkedHashMap<>();
|
||||
List<String> missingMaterials = new ArrayList<>();
|
||||
for (StockInProductDetail productDetail : productDetailList) {
|
||||
String productCode = productDetail.getProductCode();
|
||||
if (materialCache.containsKey(productCode)) {
|
||||
continue;
|
||||
}
|
||||
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(productCode, loginUser.getUserPo().getOrganizationId());
|
||||
if (materialBaseInfoPO == null) {
|
||||
missingMaterials.add(productCode);
|
||||
} else {
|
||||
materialCache.put(productCode, materialBaseInfoPO);
|
||||
}
|
||||
}
|
||||
// ====== 第二步:客户编号存在性校验:先收集全部缺失编号,不立即报错 ======
|
||||
Set<String> customerCodes = new LinkedHashSet<>();
|
||||
for (StockInProductDetail productDetail : productDetailList) {
|
||||
if (productDetail.getCustomerCode() != null && !productDetail.getCustomerCode().trim().isEmpty()) {
|
||||
customerCodes.add(productDetail.getCustomerCode().trim());
|
||||
}
|
||||
}
|
||||
Map<String, UserPo> customerCache = new HashMap<>();
|
||||
List<String> missingCustomers = new ArrayList<>();
|
||||
for (String customerCode : customerCodes) {
|
||||
UserPo customer = stockOutOrderMapper.queryCustomerByCode(customerCode, loginUser.getUserPo().getOrganizationId());
|
||||
if (customer == null || customer.getUserId() == null) {
|
||||
missingCustomers.add(customerCode);
|
||||
} else {
|
||||
customerCache.put(customerCode, customer);
|
||||
}
|
||||
}
|
||||
// ====== 第三步:物料/客户存在性校验全部完成后,统一提示两类编号各自有哪些不存在 ======
|
||||
if (!missingMaterials.isEmpty() || !missingCustomers.isEmpty()) {
|
||||
List<String> problems = new ArrayList<>();
|
||||
if (!missingMaterials.isEmpty()) {
|
||||
problems.add("物料[" + String.join("、", missingMaterials) + "]不存在,请先维护物料信息");
|
||||
}
|
||||
if (!missingCustomers.isEmpty()) {
|
||||
problems.add("客户[" + String.join("、", missingCustomers) + "]不存在,请先维护客户信息");
|
||||
}
|
||||
throw new ServiceException(String.join(";", problems) + ",维护完成后重新导入");
|
||||
}
|
||||
// ====== 后续处理(编号均已存在):名称自动匹配、货主归属与一致性校验、建单 ======
|
||||
// 明细回填库中匹配到的物料名称(自动匹配,模板中的名称列可不填/以库中为准;库中名称为空时保留模板值)
|
||||
for (StockInProductDetail productDetail : productDetailList) {
|
||||
String materialName = materialCache.get(productDetail.getProductCode()).getMaterialName();
|
||||
if (materialName != null && !materialName.trim().isEmpty()) {
|
||||
productDetail.setProductName(materialName);
|
||||
}
|
||||
}
|
||||
// 物料归属货主集合(用于客户编号一致性校验,及未填客户编号时的兜底取值)
|
||||
List<Map<String,Object>> shipperIds = new ArrayList<>();
|
||||
Set<Long> processedShipperIds = new HashSet<>();
|
||||
for (StockInProductDetail productDetail : productDetailList) {
|
||||
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(productDetail.getProductCode(),loginUser.getUserPo().getOrganizationId());
|
||||
// 物料在当前登录组织下不存在时给出明确提示,避免后续货主取值越界导致500
|
||||
if (materialBaseInfoPO == null) {
|
||||
throw new ServiceException("产品编号[" + productDetail.getProductCode() + "]在当前组织下不存在,请核对产品编号或使用物料所属组织的账号导入");
|
||||
}
|
||||
if (materialBaseInfoPO.getShipperId() != null) {
|
||||
Long shipperId = materialBaseInfoPO.getShipperId();
|
||||
if (processedShipperIds.add(shipperId)) {
|
||||
Map<String, Object> shipper = new HashMap<>();
|
||||
shipper.put("shipperId", shipperId);
|
||||
shipper.put("shipperName", materialBaseInfoPO.getShipperName());
|
||||
shipper.put("shipperCode", materialBaseInfoPO.getShipperCode());
|
||||
shipperIds.add(shipper);
|
||||
}
|
||||
for (MaterialBaseInfoPO materialBaseInfoPO : materialCache.values()) {
|
||||
if (materialBaseInfoPO.getShipperId() != null && processedShipperIds.add(materialBaseInfoPO.getShipperId())) {
|
||||
Map<String, Object> shipper = new HashMap<>();
|
||||
shipper.put("shipperId", materialBaseInfoPO.getShipperId());
|
||||
shipper.put("shipperName", materialBaseInfoPO.getShipperName());
|
||||
shipper.put("shipperCode", materialBaseInfoPO.getShipperCode());
|
||||
shipperIds.add(shipper);
|
||||
}
|
||||
}
|
||||
if (shipperIds.isEmpty()) {
|
||||
throw new ServiceException("导入的产品均未配置货主,请先在物料档案中维护货主信息");
|
||||
if (shipperIds.size() > 1) {
|
||||
StringBuilder shipperCodeStr = new StringBuilder();
|
||||
for (Map<String, Object> shipper : shipperIds) {
|
||||
if (shipperCodeStr.length() > 0) {
|
||||
shipperCodeStr.append("、");
|
||||
}
|
||||
shipperCodeStr.append(shipper.get("shipperCode"));
|
||||
}
|
||||
throw new ServiceException("导入的物料不属于同一货主(货主编号:" + shipperCodeStr + "),一个入库单只能属于一个货主");
|
||||
}
|
||||
// 客户取值:模板填了客户编号时取库中匹配结果(编号/名称均为自动匹配值)
|
||||
UserPo customer = null;
|
||||
if (customerCodes.size() > 1) {
|
||||
throw new ServiceException("导入明细中存在多个客户编号(" + String.join("、", customerCodes) + "),一个入库单只能属于一个客户");
|
||||
}
|
||||
if (!customerCodes.isEmpty()) {
|
||||
String customerCode = customerCodes.iterator().next();
|
||||
customer = customerCache.get(customerCode);
|
||||
// 客户编号与物料归属货主一致性校验(不一致拦截,提示两者编号;货主编码未维护时提示货主名称)
|
||||
if (!shipperIds.isEmpty() && !customer.getUserId().equals(shipperIds.get(0).get("shipperId"))) {
|
||||
Object materialShipperCode = shipperIds.get(0).get("shipperCode");
|
||||
String materialShipperDisplay = (materialShipperCode != null && !materialShipperCode.toString().trim().isEmpty())
|
||||
? "货主编号[" + materialShipperCode + "]" : "货主[" + shipperIds.get(0).get("shipperName") + "]";
|
||||
throw new ServiceException("客户编号[" + customerCode + "]与物料归属" + materialShipperDisplay + "]不一致,请核对后再导入");
|
||||
}
|
||||
stockInHeader.setCustomerCode(customerCode);
|
||||
stockInHeader.setCustomerName(customer.getUserName());
|
||||
}
|
||||
ReservationStockInOrderDO reservationStockInOrderDO = new ReservationStockInOrderDO();
|
||||
StockInHeader stockInHeader = resultDTO.getStockInHeader();
|
||||
// 业务单号:优先使用模板自带OrderNO(不做格式规则校验),与系统已有业务单号重复时提示不允许导入;
|
||||
// 入库业务单号(in_order_number)不使用模板单号,统一由系统原本逻辑生成(见insertDomain);
|
||||
// 模板未填单号时保持系统逻辑:业务单号默认=入库业务单号
|
||||
@@ -515,9 +641,19 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
reservationStockInOrderDO.setIssueStatus(0);
|
||||
reservationStockInOrderDO.setSalesmanId(loginUser.getUserPo().getUserId().toString());
|
||||
reservationStockInOrderDO.setSalesmanName(loginUser.getUserPo().getUserName());
|
||||
reservationStockInOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId"));
|
||||
reservationStockInOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName"));
|
||||
reservationStockInOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
|
||||
// 单据货主:模板填了客户编号时以客户为准(编号/名称均为库中自动匹配值),未填时取物料归属货主
|
||||
if (customer != null) {
|
||||
reservationStockInOrderDO.setShipperId(customer.getUserId());
|
||||
reservationStockInOrderDO.setShipperName(customer.getUserName());
|
||||
reservationStockInOrderDO.setShipperCode(customer.getUserMemberCode());
|
||||
} else {
|
||||
if (shipperIds.isEmpty()) {
|
||||
throw new ServiceException("导入的产品均未配置货主,请先在物料档案中维护货主信息,或在模板中填写客户编号");
|
||||
}
|
||||
reservationStockInOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId"));
|
||||
reservationStockInOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName"));
|
||||
reservationStockInOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
|
||||
}
|
||||
reservationStockInOrderDO.setWarehouseDate(stockInHeader.getStockInDate());
|
||||
reservationStockInOrderDO.setStorageTime(stockInHeader.getStockInDate());
|
||||
reservationStockInOrderDO.setExpectTime(stockInHeader.getStockInDate());
|
||||
@@ -533,7 +669,8 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
reservationInMaterialDetailDO.setCreateBy(loginUser.getUserPo().getUserId());
|
||||
reservationInMaterialDetailDO.setCreateByName(loginUser.getUserPo().getUserName());
|
||||
reservationInMaterialDetailDO.setCreateTime(new Date());
|
||||
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(stockInProductDetail.getProductCode(),loginUser.getUserPo().getOrganizationId());
|
||||
// 物料信息取自前置校验的缓存(已确认存在,避免逐行重复查库)
|
||||
MaterialBaseInfoPO materialBaseInfoPO = materialCache.get(stockInProductDetail.getProductCode());
|
||||
// 计划数量取模版"件数"列,同时件数字段落库用于明细列表回显
|
||||
reservationInMaterialDetailDO.setQuantity(new BigDecimal(stockInProductDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP));
|
||||
reservationInMaterialDetailDO.setPieceCount(new BigDecimal(stockInProductDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP));
|
||||
|
||||
+20
@@ -14,6 +14,10 @@ public class StockInHeader {
|
||||
private Date stockInDate;
|
||||
// 订单编号(Excel第一行第4列)
|
||||
private String orderNo;
|
||||
// 客户编号(明细列头动态定位,导入校验客户存在性时回填)
|
||||
private String customerCode;
|
||||
// 客户名称(按客户编号自动匹配库中名称后回填)
|
||||
private String customerName;
|
||||
|
||||
// Getter和Setter方法
|
||||
public Date getStockInDate() {
|
||||
@@ -31,4 +35,20 @@ public class StockInHeader {
|
||||
public void setOrderNo(String orderNo) {
|
||||
this.orderNo = orderNo;
|
||||
}
|
||||
|
||||
public String getCustomerCode() {
|
||||
return customerCode;
|
||||
}
|
||||
|
||||
public void setCustomerCode(String customerCode) {
|
||||
this.customerCode = customerCode;
|
||||
}
|
||||
|
||||
public String getCustomerName() {
|
||||
return customerName;
|
||||
}
|
||||
|
||||
public void setCustomerName(String customerName) {
|
||||
this.customerName = customerName;
|
||||
}
|
||||
}
|
||||
+12
-2
@@ -9,10 +9,12 @@ import java.util.Date;
|
||||
*/
|
||||
@Data
|
||||
public class StockInProductDetail {
|
||||
// 产品编号(Excel第1列)
|
||||
// 产品编号(按列头动态定位)
|
||||
private String productCode;
|
||||
// 产品名称(Excel第2列)
|
||||
// 产品名称(按列头动态定位,可不填,导入时按产品编号自动匹配库中物料名称)
|
||||
private String productName;
|
||||
// 客户编号(按列头动态定位,可不填,导入时按客户编号自动匹配库中客户名称)
|
||||
private String customerCode;
|
||||
// 箱含量(Excel第3列)
|
||||
private Integer boxCapacity;
|
||||
// 件数(Excel第4列)
|
||||
@@ -37,6 +39,14 @@ public class StockInProductDetail {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public String getCustomerCode() {
|
||||
return customerCode;
|
||||
}
|
||||
|
||||
public void setCustomerCode(String customerCode) {
|
||||
this.customerCode = customerCode;
|
||||
}
|
||||
|
||||
public Integer getBoxCapacity() {
|
||||
return boxCapacity;
|
||||
}
|
||||
|
||||
+7
-1
@@ -2,6 +2,7 @@ package com.mhd.oms.domain.reservationStockOutOrder.repository.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
//import com.mhd.oms.domain.reservationReservationStockOutOrder.entity.ReservationReservationStockOutOrder;
|
||||
import com.mhd.common.core.domain.po.UserPo;
|
||||
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
|
||||
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
|
||||
import com.mhd.oms.domain.reservationStockOutOrder.repository.po.ReservationStockOutOrderPO;
|
||||
@@ -36,7 +37,12 @@ public interface ReservationStockOutOrderMapper extends BaseMapper<ReservationSt
|
||||
*/
|
||||
List<ReservationOutMaterialDetailPO> queryOutStockNoticePrintDetail(ReservationStockOutOrderDO stockOutOrderDO);
|
||||
|
||||
Long queryShipperId(@Param("userMemberCode") String userMemberCode,@Param("organizationId") Long organizationId);
|
||||
/**
|
||||
* 按客户编号+组织查询客户(用户表),返回客户ID/编号/名称
|
||||
* 客户编号兼容两种编码:USER.USER_MEMBER_CODE(客户编码)或 USER_SHIPPER.CUSTOMER_NC_CODE(NC客户代码/khdm),任一命中即可
|
||||
* 用于出入库导入时校验客户是否存在并自动匹配客户名称;结果为空表示库中无该客户
|
||||
*/
|
||||
UserPo queryCustomerByCode(@Param("userMemberCode") String userMemberCode, @Param("organizationId") Long organizationId);
|
||||
|
||||
int countByOrderNumberPrefix(@Param("prefix") String prefix);
|
||||
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
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>按入仓日期区间筛选商品入库量统计排名。OMS 库执行入库单状态/上架数量均不回传,真实数据在 WMS 库,
|
||||
* 故数据源跨库取 WMS 入库表(NGWL_TEST_WMS.stock_in_order/in_material_detail,与 OMS 出库排名跨库修法一致);
|
||||
* 口径与 WMS 端 /inboundRankApi 完全一致,两边数字一致:仅已入库(5) + 明细 level=1 主行 +
|
||||
* 入仓日期(为空的历史单按建档时间兜底) + 件数=实际上架量(SHELVES_QUANTITY);
|
||||
* 一行 = 某仓单内某货品的合计,按件数降序排名。
|
||||
* 字段:入仓日期/仓单编号/客户编号/存货人/货名/件数/毛重(吨)/投保总额/体积(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();
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
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>按日期区间筛选,统计商品/线路出库量排名。OMS 库出库单状态不流转(无"已出库"数据),
|
||||
* 数据源跨库取 WMS 库出库表(NGWL_TEST_WMS.stock_out_order / out_material_detail),
|
||||
* 口径与 WMS 端 /outboundRankApi 完全一致,两边数字一致;
|
||||
* 统计条件:出库单创建时间 + 已出库及之后状态(9,12,13),明细仅取 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();
|
||||
}
|
||||
}
|
||||
+31
-4
@@ -47,13 +47,18 @@ public class ReservationMaterialInventoryApi extends BaseController {
|
||||
/**
|
||||
* 分页查询物料库存列表
|
||||
*/
|
||||
@ApiOperation("查询物料库存列表")
|
||||
@ApiOperation(value = "查询物料库存列表", notes = "导出复用本接口:传 isExport=true 标识导出请求(导出显示:商品饮用有效期expiryDate/物料分类materialClassifyName/进货日期purchaseDate,所有查询维度均返回);excludeZeroInventoryQuantity=true 排除库存为0的数据(所有维度生效);orderByExpiryDate=true 按到期日分组排序")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(ReservationMaterialInventoryDTO materialInventoryDTO,
|
||||
@RequestParam(value = "customerName", required = false) String customerName,
|
||||
@RequestParam(value = "expiryDateStart", required = false) String expiryDateStart,
|
||||
@RequestParam(value = "expiryDateEnd", required = false) String expiryDateEnd,
|
||||
@RequestParam(value = "orderByExpiryDate", required = false) Boolean orderByExpiryDate)
|
||||
@RequestParam(value = "purchaseDateStart", required = false) String purchaseDateStart,
|
||||
@RequestParam(value = "purchaseDateEnd", required = false) String purchaseDateEnd,
|
||||
@RequestParam(value = "excludeZeroInventoryQuantity", required = false) Boolean excludeZeroInventoryQuantity,
|
||||
@RequestParam(value = "orderByExpiryDate", required = false) Boolean orderByExpiryDate,
|
||||
// 导出标识:导出与查询共用本接口,isExport=true 表示本次请求为导出(导出专属排序等逻辑以此开关)
|
||||
@RequestParam(value = "isExport", required = false) Boolean isExport)
|
||||
{
|
||||
// 出库分配前端传的是客户名称 customerName,这里通过名称查询货主ID,使用精确匹配(不模糊匹配)
|
||||
String shipperName = null; // 保存查询到的货主名称
|
||||
@@ -81,17 +86,25 @@ public class ReservationMaterialInventoryApi extends BaseController {
|
||||
ReservationMaterialInventoryDO materialInventoryDO = materialInventoryAssembler.toDO(materialInventoryDTO);
|
||||
materialInventoryDO.setExpiryDateStr(materialInventoryDTO.getExpiryDate());
|
||||
materialInventoryDO.setProductionDateStr(materialInventoryDTO.getProductionDate());
|
||||
materialInventoryDO.setPurchaseDateStr(materialInventoryDTO.getPurchaseDate());
|
||||
materialInventoryDO.setExpiryDateStart(expiryDateStart);
|
||||
materialInventoryDO.setExpiryDateEnd(expiryDateEnd);
|
||||
materialInventoryDO.setPurchaseDateStart(purchaseDateStart);
|
||||
materialInventoryDO.setPurchaseDateEnd(purchaseDateEnd);
|
||||
// 导出场景:queryRule=wlExpiry + orderByExpiryDate=true 时 SQL 按到期日倒序分组
|
||||
// (日期近的在前、空值最后、0库存靠后、同到期日按更新时间倒序)
|
||||
materialInventoryDO.setOrderByExpiryDate(orderByExpiryDate);
|
||||
// 导出场景:前端"库存为0是否导出"选"是"时传 excludeZeroInventoryQuantity=true,
|
||||
// SQL 用 HAVING 排除汇总库存为0的数据(与 WMS 同名参数语义一致)
|
||||
materialInventoryDO.setExcludeZeroInventoryQuantity(excludeZeroInventoryQuantity);
|
||||
// 导出标识:isExport=true 启用导出融合排序(到期日倒序优先 → 同到期日按物料编码升序,所有维度生效)
|
||||
materialInventoryDO.setIsExport(isExport);
|
||||
startPage();
|
||||
// 使用带批次属性的查询方法,批次属性值从库存表中获取
|
||||
List<ReservationMaterialInventoryPO> list = materialInventoryApplicationService.queryListWithBatchAttributes(materialInventoryDO);
|
||||
// 按更新时间倒序排序;update_time 为空的数据放最后
|
||||
// (导出 orderByExpiryDate=true 时跳过该重排,避免覆盖 SQL 的到期日分组排序)
|
||||
if (!Boolean.TRUE.equals(orderByExpiryDate)) {
|
||||
// (导出 orderByExpiryDate=true 或 isExport=true 时跳过该重排,避免覆盖 SQL 的导出排序)
|
||||
if (!Boolean.TRUE.equals(orderByExpiryDate) && !Boolean.TRUE.equals(isExport)) {
|
||||
list.sort((o1, o2) -> {
|
||||
Date t1 = o1.getUpdateTime();
|
||||
Date t2 = o2.getUpdateTime();
|
||||
@@ -168,6 +181,20 @@ public class ReservationMaterialInventoryApi extends BaseController {
|
||||
return AjaxResult.success(materialInventoryApplicationService.getInventoryByStorageLocationIdAndMaterialBaseInfoId(storageLocationId, materialBaseInfoId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 入库业务单新增页面:根据物料编号查询其库存批次过期日期+批次号,去重取最近5条,
|
||||
* 供前端下拉回显;用户也可点击右侧"日期"小图标手动选择日期
|
||||
*/
|
||||
@ApiOperation("根据物料编号查询库存批次最近5条过期日期+批次号")
|
||||
@GetMapping(value = "/queryRecentExpiryDates/{materialCode}")
|
||||
public AjaxResult queryRecentExpiryDates(@PathVariable("materialCode") String materialCode)
|
||||
{
|
||||
if (StringUtils.isBlank(materialCode)) {
|
||||
return AjaxResult.error("物料编号不能为空");
|
||||
}
|
||||
return AjaxResult.success(materialInventoryApplicationService.selectRecentExpiryBatches(materialCode));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,15 +18,15 @@ spring:
|
||||
discovery:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
# server-addr: 10.33.0.129:6010
|
||||
server-addr: 10.102.192.30:6848
|
||||
server-addr: 10.33.0.129:6010
|
||||
# server-addr: 10.102.192.30:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
config:
|
||||
# server-addr: 127.0.0.1:8848
|
||||
# 线上测试环境配置 容器名+端口号
|
||||
# server-addr: 10.33.0.129:6010
|
||||
server-addr: 10.102.192.30:6848
|
||||
server-addr: 10.33.0.129:6010
|
||||
# server-addr: 10.102.192.30:6848
|
||||
username: nacos
|
||||
password: manhuoda@2023
|
||||
#线上正式环境
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<?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 端,跨库取 WMS 入库数据)
|
||||
数据源:WMS 库入库明细 NGWL_TEST_WMS.in_material_detail(a,仅 level=1 主行防重复)
|
||||
join WMS 库入仓单 NGWL_TEST_WMS.stock_in_order(b)
|
||||
left join WMS 库货品档案 NGWL_TEST_WMS.material_base_info(c,取保额作投保总额)
|
||||
说明:OMS 库执行入库单状态/上架数量均不回传(状态长期停留 status=1,上架量回填代码未启用),
|
||||
"已入库/实际上架量"的真实数据在 WMS 库,故跨库取数
|
||||
(与 OMS 出库排名跨库取 NGWL_TEST_WMS 出库表、OMS 库存查询跨库 join material_base_info 同一模式),
|
||||
口径与 WMS 端 /inboundRankApi 完全一致,两边数字一致;
|
||||
条件:b.status = 5已入库(排除已取消(6)/已关闭(7)及未完成单据);
|
||||
日期筛选按入仓单入仓日期 b.warehouse_date(与列表"入仓日期"列同源,为空的历史单按建档时间兜底);
|
||||
一行 = 某仓单(b.in_order_number)内某货品(a.material_code)的合计,
|
||||
按件数(实际上架量 SUM(a.SHELVES_QUANTITY))降序排名
|
||||
-->
|
||||
<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="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(IFNULL(b.warehouse_date, b.create_time),'%Y-%m-%d') >= #{warehouseDateStart}
|
||||
</if>
|
||||
<if test="warehouseDateEnd != null and warehouseDateEnd != ''">
|
||||
and date_format(IFNULL(b.warehouse_date, b.create_time),'%Y-%m-%d') <= #{warehouseDateEnd}
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<sql id="inboundRankFrom">
|
||||
from NGWL_TEST_WMS.in_material_detail a
|
||||
join NGWL_TEST_WMS.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
|
||||
IFNULL(MAX(b.warehouse_date), MAX(b.create_time)) 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.SHELVES_QUANTITY), 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.SHELVES_QUANTITY), 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,121 @@
|
||||
<?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 端,跨库取 WMS 出库数据)
|
||||
数据源:WMS 库出库明细 NGWL_TEST_WMS.out_material_detail(a,仅 level=1 主行防重复)
|
||||
join WMS 库出库单 NGWL_TEST_WMS.stock_out_order(b)
|
||||
说明:OMS 库出库单状态不流转(无"已出库"数据),"已出库"状态的真实数据在 WMS 库,
|
||||
故跨库取数(与 OMS 库存查询跨库 join NGWL_TEST_WMS.material_base_info 同一模式),
|
||||
口径与 WMS 端 /outboundRankApi 完全一致,两边数字一致;
|
||||
条件: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="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="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="createTimeStart != null and createTimeStart != ''">
|
||||
and date_format(b.create_time,'%Y-%m-%d') >= #{createTimeStart}
|
||||
</if>
|
||||
<if test="createTimeEnd != null and createTimeEnd != ''">
|
||||
and date_format(b.create_time,'%Y-%m-%d') <= #{createTimeEnd}
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<sql id="outboundRankFrom">
|
||||
from NGWL_TEST_WMS.out_material_detail a
|
||||
join NGWL_TEST_WMS.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"/>
|
||||
</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"/>
|
||||
</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>
|
||||
+98
-9
@@ -56,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="productionDate" column="production_date" />
|
||||
<result property="expiryDate" column="expiry_date" />
|
||||
<result property="inventoryDate" column="inventory_date" />
|
||||
<result property="purchaseDate" column="purchase_date" />
|
||||
<result property="batchRefNo" column="batch_ref_no" />
|
||||
<result property="sheetRefNo" column="sheet_ref_no" />
|
||||
<result property="boxPalletNo" column="box_pallet_no" />
|
||||
@@ -64,6 +65,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="barCode" column="inv_bar_code" />
|
||||
<result property="inOrderNumber" column="in_order_number" />
|
||||
<result property="lotNumber" column="lot_number" />
|
||||
<result property="materialClassifyCode" column="material_classify_code"
|
||||
typeHandler="com.mhd.common.core.handler.ListTypeHandler" />
|
||||
<result property="materialClassifyName" column="material_classify_name"
|
||||
typeHandler="com.mhd.common.core.handler.ListTypeHandler" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="JoinMaterialBaseInfoPo">
|
||||
@@ -138,7 +143,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
IFNULL (sum(a.GROSS_WEIGHT), 0) GROSS_WEIGHT,
|
||||
IFNULL (sum(a.VOLUME), 0) VOLUME,
|
||||
a.ext_attr_1, a.ext_attr_2, a.ext_attr_3, a.ext_attr_4,
|
||||
a.production_date, a.expiry_date, a.inventory_date,
|
||||
a.production_date, a.expiry_date, a.inventory_date, a.purchase_date,
|
||||
a.BATCH_REF_NO, a.SHEET_REF_NO, a.BOX_PALLET_NO,nvl(a.unit_name,b.unit_name) as unit_name,
|
||||
a.bar_code as inv_bar_code,
|
||||
a.in_order_number,a.lot_number
|
||||
@@ -280,6 +285,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="expiryDateEnd != null and expiryDateEnd != ''">
|
||||
and a.expiry_date <![CDATA[ < ]]> DATEADD(DAY, 1, #{expiryDateEnd})
|
||||
</if>
|
||||
<if test="purchaseDateStr != null and purchaseDateStr != ''">
|
||||
AND TO_CHAR(a.purchase_date, 'yyyy-MM-dd') = #{purchaseDateStr}
|
||||
</if>
|
||||
<if test="purchaseDateStart != null and purchaseDateStart != ''">
|
||||
and a.purchase_date >= #{purchaseDateStart}
|
||||
</if>
|
||||
<if test="purchaseDateEnd != null and purchaseDateEnd != ''">
|
||||
and a.purchase_date <![CDATA[ < ]]> DATEADD(DAY, 1, #{purchaseDateEnd})
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<!-- 导出排除库存为0:所有维度查询(all/hz/wl/kw/wlkw/wlExpiry)的库存数量均为SUM聚合值,
|
||||
统一在各语句 GROUP BY 之后用 HAVING 过滤(与sort_order对0库存的定义保持一致);
|
||||
仅前端导出传 excludeZeroInventoryQuantity=true 时拼接,正常列表查询不传则完全不受影响 -->
|
||||
<sql id="excludeZeroInventoryHaving">
|
||||
<if test="excludeZeroInventoryQuantity != null and excludeZeroInventoryQuantity == true">
|
||||
HAVING IFNULL(SUM(a.inventory_quantity), 0) > 0
|
||||
</if>
|
||||
</sql>
|
||||
|
||||
<!-- 导出融合排序(isExport=true):保留"到期日优先"第一优先级(到期日倒序、空值最后),
|
||||
融合新增"同到期日内按物料编码升序",再保留 0库存靠后、更新时间倒序 兜底,
|
||||
末位以 MAX(物料库存ID) 保证分页序稳定;不传 isExport 各语句保持原有排序 -->
|
||||
<sql id="exportOrderByMaterialCode">
|
||||
order by CASE WHEN MAX(a.expiry_date) IS NULL THEN 1 ELSE 0 END ASC, MAX(a.expiry_date) DESC,
|
||||
MAX(b.material_code) ASC,
|
||||
CASE WHEN IFNULL(SUM(a.inventory_quantity), 0) = 0 THEN 1 ELSE 0 END ASC,
|
||||
MAX(a.update_time) DESC,
|
||||
MAX(a.material_inventory_id) DESC
|
||||
</sql>
|
||||
|
||||
<select id="queryList" parameterType="com.mhd.oms.domain.reservationMaterialInventory.repository.todo.ReservationMaterialInventoryDO" resultMap="MaterialInventoryResult">
|
||||
@@ -292,7 +326,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="selectMaterialInventoryPo1"/>
|
||||
<include refid="JoinMaterialBaseInfoPoWhere"/>
|
||||
</where>
|
||||
group by a.MATERIAL_INVENTORY_ID order by a.update_time desc
|
||||
group by a.MATERIAL_INVENTORY_ID <include refid="excludeZeroInventoryHaving"/>
|
||||
<choose>
|
||||
<when test="isExport != null and isExport == true">
|
||||
<include refid="exportOrderByMaterialCode"/>
|
||||
</when>
|
||||
<otherwise> order by a.update_time desc </otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<select id="queryListByWlKw" parameterType="com.mhd.oms.domain.reservationMaterialInventory.repository.todo.ReservationMaterialInventoryDO" resultMap="MaterialInventoryResult">
|
||||
@@ -306,6 +346,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="JoinMaterialBaseInfoPoWhere"/>
|
||||
</where>
|
||||
GROUP BY a.MATERIAL_BASE_INFO_ID,a.WAREHOUSE_ID,a.STORAGE_SECTION_ID,a.STORAGE_LOCATION_ID
|
||||
<include refid="excludeZeroInventoryHaving"/>
|
||||
<if test="isExport != null and isExport == true">
|
||||
<include refid="exportOrderByMaterialCode"/>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="queryListByKw" parameterType="com.mhd.oms.domain.reservationMaterialInventory.repository.todo.ReservationMaterialInventoryDO" resultMap="MaterialInventoryResult">
|
||||
@@ -319,6 +363,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="JoinMaterialBaseInfoPoWhere"/>
|
||||
</where>
|
||||
GROUP BY a.WAREHOUSE_ID,a.STORAGE_SECTION_ID,a.STORAGE_LOCATION_ID
|
||||
<include refid="excludeZeroInventoryHaving"/>
|
||||
<if test="isExport != null and isExport == true">
|
||||
<include refid="exportOrderByMaterialCode"/>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="queryListByWl" parameterType="com.mhd.oms.domain.reservationMaterialInventory.repository.todo.ReservationMaterialInventoryDO" resultMap="MaterialInventoryResult">
|
||||
@@ -332,6 +380,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="JoinMaterialBaseInfoPoWhere"/>
|
||||
</where>
|
||||
GROUP BY a.MATERIAL_BASE_INFO_ID
|
||||
<include refid="excludeZeroInventoryHaving"/>
|
||||
<if test="isExport != null and isExport == true">
|
||||
<include refid="exportOrderByMaterialCode"/>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<!-- 按物料+过期日期汇总 -->
|
||||
@@ -353,12 +405,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
IFNULL(SUM(a.VOLUME), 0) AS VOLUME,
|
||||
MAX(a.unit) AS unit,
|
||||
MAX(a.unit_name) AS unit_name,
|
||||
MAX(a.create_time) AS create_time,
|
||||
MAX(a.update_time) AS update_time,
|
||||
MAX(a.production_date) AS production_date,
|
||||
MAX(a.inventory_date) AS inventory_date,
|
||||
MAX(a.purchase_date) AS purchase_date,
|
||||
MAX(a.batch_ref_no) AS batch_ref_no, MAX(a.sheet_ref_no) AS sheet_ref_no, MAX(a.box_pallet_no) AS box_pallet_no,
|
||||
MAX(a.bar_code) AS inv_bar_code,
|
||||
MAX(a.in_order_number) AS in_order_number, MAX(a.lot_number) AS lot_number,
|
||||
b.material_code, b.material_name,
|
||||
MAX(b.material_classify_code) AS material_classify_code,
|
||||
MAX(b.material_classify_name) AS material_classify_name,
|
||||
CASE WHEN IFNULL(SUM(a.inventory_quantity), 0) = 0 THEN 1 ELSE 0 END AS sort_order
|
||||
from reservation_material_inventory a
|
||||
left join NGWL_TEST_WMS.material_base_info b on a.material_base_info_id = b.material_base_info_id
|
||||
@@ -369,16 +426,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
GROUP BY a.MATERIAL_BASE_INFO_ID, a.EXPIRY_DATE, a.shipper_id, a.shipper_code, a.shipper_name,
|
||||
a.organization_id, a.organization_name, a.top_organization_id,
|
||||
b.material_code, b.material_name
|
||||
<!-- 导出(queryRule=wlExpiry + orderByExpiryDate=true)按到期日分组归类:到期日倒序(日期近的在前)、
|
||||
空值放最后、0库存靠后、同到期日按更新时间倒序;相同物料+相同到期日合并为一行,数量类字段SUM汇总。
|
||||
不传 orderByExpiryDate 保持原有排序 -->
|
||||
<include refid="excludeZeroInventoryHaving"/>
|
||||
<!-- 导出排序两级开关:isExport=true 用融合排序(到期日优先→同到期日按物料编码升序,与 orderByExpiryDate 融合不取代);
|
||||
否则保持原有 orderByExpiryDate 分支排序 -->
|
||||
<choose>
|
||||
<when test="orderByExpiryDate != null and orderByExpiryDate == true">
|
||||
order by CASE WHEN a.expiry_date IS NULL THEN 1 ELSE 0 END ASC, a.expiry_date DESC,
|
||||
sort_order, MAX(a.update_time) DESC
|
||||
<when test="isExport != null and isExport == true">
|
||||
<include refid="exportOrderByMaterialCode"/>
|
||||
</when>
|
||||
<otherwise>
|
||||
order by sort_order, MAX(a.update_time) desc
|
||||
<!-- 导出(queryRule=wlExpiry + orderByExpiryDate=true)按到期日分组归类:到期日倒序(日期近的在前)、
|
||||
空值放最后、0库存靠后、同到期日按更新时间倒序;相同物料+相同到期日合并为一行,数量类字段SUM汇总。
|
||||
不传 orderByExpiryDate 保持原有排序 -->
|
||||
<choose>
|
||||
<when test="orderByExpiryDate != null and orderByExpiryDate == true">
|
||||
order by CASE WHEN a.expiry_date IS NULL THEN 1 ELSE 0 END ASC, a.expiry_date DESC,
|
||||
sort_order, MAX(a.update_time) DESC
|
||||
</when>
|
||||
<otherwise>
|
||||
order by sort_order, MAX(a.update_time) desc
|
||||
</otherwise>
|
||||
</choose>
|
||||
</otherwise>
|
||||
</choose>
|
||||
</select>
|
||||
@@ -394,6 +461,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<include refid="JoinMaterialBaseInfoPoWhere"/>
|
||||
</where>
|
||||
GROUP BY a.SHIPPER_ID
|
||||
<include refid="excludeZeroInventoryHaving"/>
|
||||
<if test="isExport != null and isExport == true">
|
||||
<include refid="exportOrderByMaterialCode"/>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<resultMap type="com.mhd.oms.domain.reservationMaterialInventory.repository.po.ReservationMaterialInventoryQueryListPO" id="MaterialQueryListResult">
|
||||
@@ -447,6 +518,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="productionDate" column="production_date" />
|
||||
<result property="expiryDate" column="expiry_date" />
|
||||
<result property="inventoryDate" column="inventory_date" />
|
||||
<result property="purchaseDate" column="purchase_date" />
|
||||
<result property="extAttr3" column="ext_attr_3" />
|
||||
<result property="extAttr4" column="ext_attr_4" />
|
||||
<result property="remark" column="remark" />
|
||||
@@ -527,4 +599,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
FROM NGWL_TEST_WMS.material_bar_code
|
||||
WHERE material_base_info_id = #{id} AND del_flag = 1
|
||||
</select>
|
||||
|
||||
<!-- 入库业务单新增:根据物料编号查询其库存批次的过期日期+批次号,去重后取最近5条,供前端下拉回显 -->
|
||||
<select id="selectRecentExpiryBatches" resultType="com.mhd.oms.domain.reservationMaterialInventory.repository.po.MaterialInventoryExpiryBatchPO">
|
||||
select distinct
|
||||
date_format(a.expiry_date, '%Y-%m-%d') as expiryDate,
|
||||
a.batch_number as batchNumber
|
||||
from reservation_material_inventory a
|
||||
left join NGWL_TEST_WMS.material_base_info b on a.material_base_info_id = b.material_base_info_id
|
||||
where a.del_flag = 1
|
||||
and a.expiry_date is not null
|
||||
and b.material_code = #{materialCode}
|
||||
<if test="topOrganizationId != null">
|
||||
and a.top_organization_id = #{topOrganizationId}
|
||||
</if>
|
||||
order by expiryDate desc, batchNumber desc
|
||||
limit 5
|
||||
</select>
|
||||
</mapper>
|
||||
+10
-2
@@ -470,8 +470,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="boxPalletNo" column="box_pallet_no"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="queryShipperId" resultType="java.lang.Long">
|
||||
select max(user_id) as user_id from NGWL_TEST_USER."USER" where ORGANIZATION_ID = #{organizationId} and USER_MEMBER_CODE = #{userMemberCode} and DEL_FLAG = 1
|
||||
<!-- 按客户编号+组织查询客户(用户表):出入库导入校验客户存在性并自动匹配客户名称,结果为空表示客户不存在。
|
||||
客户编码有两处维护:USER.USER_MEMBER_CODE(客户编码)与 USER_SHIPPER.CUSTOMER_NC_CODE(NC客户代码,即单据khdm),
|
||||
模板填写的客户编号两者任一命中即可匹配到客户 -->
|
||||
<select id="queryCustomerByCode" resultType="com.mhd.common.core.domain.po.UserPo">
|
||||
select max(u.user_id) as user_id, max(u.user_name) as user_name, max(u.user_member_code) as user_member_code
|
||||
from NGWL_TEST_USER."USER" u
|
||||
left join NGWL_TEST_USER.USER_SHIPPER us
|
||||
on us.USER_ID = u.USER_ID and us.DEL_FLAG = 1
|
||||
where u.ORGANIZATION_ID = #{organizationId} and u.DEL_FLAG = 1
|
||||
and (u.USER_MEMBER_CODE = #{userMemberCode} or us.CUSTOMER_NC_CODE = #{userMemberCode})
|
||||
</select>
|
||||
|
||||
<select id="countByOrderNumberPrefix" resultType="java.lang.Integer" parameterType="java.lang.String">
|
||||
|
||||
+28
@@ -32,6 +32,7 @@ import com.linke.transport.interfaces.assemble.vehicle.VehicleAssembler;
|
||||
import com.mhd.common.core.constant.Constants;
|
||||
import com.mhd.common.core.domain.dto.SyncTmsVehicleDTO;
|
||||
import com.linke.transport.interfaces.dto.vehicle.VehicleDto;
|
||||
import com.linke.transport.interfaces.dto.vehicle.VehicleExportVO;
|
||||
import com.mhd.common.core.annotation.fieldsetvalue.NeedSetValueField;
|
||||
import com.mhd.common.core.annotation.fieldsetvalue.NeedSetValueMethod;
|
||||
import com.mhd.common.core.constant.NoticeConstants;
|
||||
@@ -238,6 +239,33 @@ public class VehicleApplicationService {
|
||||
return vehiclePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建车辆管理导出数据(不分页,按列表查询条件导出)
|
||||
*
|
||||
* @param vehiclePo 查询条件
|
||||
* @param dataPermission 数据权限
|
||||
* @return 导出数据
|
||||
*/
|
||||
public List<VehicleExportVO> buildExportList(VehiclePo vehiclePo, DataPermission dataPermission) {
|
||||
List<VehiclePo> vehiclePos = selectVehicleList(vehiclePo, dataPermission);
|
||||
List<VehicleExportVO> exportList = new ArrayList<>();
|
||||
if (CollUtil.isEmpty(vehiclePos)) {
|
||||
return exportList;
|
||||
}
|
||||
for (VehiclePo po : vehiclePos) {
|
||||
VehicleExportVO vo = new VehicleExportVO();
|
||||
BeanUtils.copyProperties(po, vo);
|
||||
// 司机名称集合拼接为字符串(List与String类型不匹配,copyProperties不会复制,需手动设置)
|
||||
if (CollUtil.isNotEmpty(po.getDriverNames())) {
|
||||
vo.setDriverNames(String.join(",", po.getDriverNames()));
|
||||
}
|
||||
// 所属组织与归属组织一致
|
||||
vo.setBelongOrganizationName(po.getOrganizationName());
|
||||
exportList.add(vo);
|
||||
}
|
||||
return exportList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询未绑定车辆列表
|
||||
*
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package com.linke.transport.interfaces.dto.vehicle;
|
||||
|
||||
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;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 车辆管理导出对象(基础数据-运营数据-车辆管理列表,列顺序与页面列表一致)
|
||||
*
|
||||
* @author gen
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "车辆管理导出对象", description = "车辆管理导出对象")
|
||||
public class VehicleExportVO implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "车辆主体", readConverterExp = "1=主车,2=挂车")
|
||||
@ApiModelProperty(name = "车辆主体(1-主车,2-挂车)")
|
||||
private Integer vehicleBody;
|
||||
|
||||
@Excel(name = "车牌号码")
|
||||
@ApiModelProperty(name = "车牌号码")
|
||||
private String vehicleLicensePlateNumber;
|
||||
|
||||
@Excel(name = "跨境车牌")
|
||||
@ApiModelProperty(name = "跨境车牌")
|
||||
private String crossPlate;
|
||||
|
||||
@Excel(name = "车辆类型")
|
||||
@ApiModelProperty(name = "车辆类型")
|
||||
private String vehicleTypeName;
|
||||
|
||||
@Excel(name = "类型", readConverterExp = "0=自由车辆,1=社会车辆")
|
||||
@ApiModelProperty(name = "类型(车辆来源)")
|
||||
private Integer vehicleSource;
|
||||
|
||||
@Excel(name = "部门")
|
||||
@ApiModelProperty(name = "部门")
|
||||
private String departmentName;
|
||||
|
||||
@Excel(name = "车辆认证", readConverterExp = "0=无状态,1=未认证,2=等待审核,3=已通过,4=已驳回")
|
||||
@ApiModelProperty(name = "车辆认证状态")
|
||||
private Integer authenticationStatus;
|
||||
|
||||
@Excel(name = "司机姓名")
|
||||
@ApiModelProperty(name = "司机姓名(多个以逗号分隔)")
|
||||
private String driverNames;
|
||||
|
||||
@Excel(name = "副驾1")
|
||||
@ApiModelProperty(name = "副驾1")
|
||||
private String pilot1;
|
||||
|
||||
@Excel(name = "副驾2")
|
||||
@ApiModelProperty(name = "副驾2")
|
||||
private String pilot2;
|
||||
|
||||
@Excel(name = "车队名称")
|
||||
@ApiModelProperty(name = "车队名称")
|
||||
private String motorcadeName;
|
||||
|
||||
@Excel(name = "年检有效期", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "年检有效期")
|
||||
private Date annualValidityPeriod;
|
||||
|
||||
@Excel(name = "保险有效期", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "保险有效期")
|
||||
private Date insuranceValidityPeriod;
|
||||
|
||||
@Excel(name = "运载状态", readConverterExp = "0=无状态,1=空载,2=负载")
|
||||
@ApiModelProperty(name = "运载状态")
|
||||
private Integer carryStatus;
|
||||
|
||||
@Excel(name = "使用性质")
|
||||
@ApiModelProperty(name = "使用性质")
|
||||
private String vehicleUseNature;
|
||||
|
||||
@Excel(name = "总质量(kg)")
|
||||
@ApiModelProperty(name = "总质量(kg)")
|
||||
private BigDecimal vehicleTotalMass;
|
||||
|
||||
@Excel(name = "核定载质量(kg)")
|
||||
@ApiModelProperty(name = "核定载质量(kg)")
|
||||
private BigDecimal vehicleApprovedLoadQuantity;
|
||||
|
||||
@Excel(name = "车轴数")
|
||||
@ApiModelProperty(name = "车轴数")
|
||||
private String otherAxlesNumberName;
|
||||
|
||||
@Excel(name = "车长(米)")
|
||||
@ApiModelProperty(name = "车长(米)")
|
||||
private String otherCarLengthName;
|
||||
|
||||
@Excel(name = "容积(方)")
|
||||
@ApiModelProperty(name = "容积(方)")
|
||||
private String otherVehicleVolumeName;
|
||||
|
||||
@Excel(name = "车牌颜色")
|
||||
@ApiModelProperty(name = "车牌颜色")
|
||||
private String otherLicensePlateColorName;
|
||||
|
||||
@Excel(name = "行驶证有效期限", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "行驶证有效期限")
|
||||
private Date vehicleCheckoutPeriodValidity;
|
||||
|
||||
@Excel(name = "道路运输证号")
|
||||
@ApiModelProperty(name = "道路运输证号")
|
||||
private String roadTransportCertificateNo;
|
||||
|
||||
@Excel(name = "道路运输证有效期限", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "道路运输证有效期限")
|
||||
private Date roadTransportCertificateExpireDate;
|
||||
|
||||
@Excel(name = "道路运输证校验状态", readConverterExp = "0=无状态,1=未核验,2=核验通过,3=核验失败")
|
||||
@ApiModelProperty(name = "道路运输证校验状态")
|
||||
private Integer roadTransportCertificateCheckoutStatus;
|
||||
|
||||
@Excel(name = "经营许可证号")
|
||||
@ApiModelProperty(name = "经营许可证号")
|
||||
private String businessLicenseNumber;
|
||||
|
||||
@Excel(name = "经营许可证业户名称")
|
||||
@ApiModelProperty(name = "经营许可证业户名称")
|
||||
private String specializedHouseholdsName;
|
||||
|
||||
@Excel(name = "道路运输经营许可证有效期", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "道路运输经营许可证有效期(截止日期)")
|
||||
private Date businessLicenseExpireEndDate;
|
||||
|
||||
@Excel(name = "经营许可证校验状态", readConverterExp = "0=无状态,1=未核验,2=核验通过,3=核验失败")
|
||||
@ApiModelProperty(name = "经营许可证校验状态")
|
||||
private Integer businessLicenseCheckoutStatus;
|
||||
|
||||
@Excel(name = "入网校验状态", readConverterExp = "0=无状态,1=未核验,2=已入网,3=未入网")
|
||||
@ApiModelProperty(name = "入网校验状态")
|
||||
private Integer netInStatus;
|
||||
|
||||
@Excel(name = "入网查询时间", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "入网查询时间")
|
||||
private Date netInDate;
|
||||
|
||||
@Excel(name = "使用状态", readConverterExp = "0=无状态,1=正常,2=黑名单")
|
||||
@ApiModelProperty(name = "使用状态")
|
||||
private Integer useStatus;
|
||||
|
||||
@Excel(name = "归属组织")
|
||||
@ApiModelProperty(name = "归属组织")
|
||||
private String organizationName;
|
||||
|
||||
@Excel(name = "添加人员")
|
||||
@ApiModelProperty(name = "添加人员")
|
||||
private String createByName;
|
||||
|
||||
@Excel(name = "添加时间", width = 25, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty(name = "添加时间")
|
||||
private Date createTime;
|
||||
|
||||
@Excel(name = "审核人员")
|
||||
@ApiModelProperty(name = "审核人员")
|
||||
private String checkoutPersonName;
|
||||
|
||||
@Excel(name = "审核时间", width = 20, dateFormat = "yyyy-MM-dd")
|
||||
@ApiModelProperty(name = "审核时间")
|
||||
private Date checkoutDate;
|
||||
|
||||
@Excel(name = "更新时间", width = 25, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@ApiModelProperty(name = "更新时间")
|
||||
private Date updateTime;
|
||||
|
||||
@Excel(name = "所属组织")
|
||||
@ApiModelProperty(name = "所属组织(与归属组织一致)")
|
||||
private String belongOrganizationName;
|
||||
}
|
||||
+32
@@ -9,6 +9,7 @@ import com.linke.transport.domain.vehicle.repository.po.VehiclePo;
|
||||
import com.linke.transport.interfaces.assemble.vehicle.VehicleAssembler;
|
||||
import com.mhd.common.core.domain.dto.SyncTmsVehicleDTO;
|
||||
import com.linke.transport.interfaces.dto.vehicle.VehicleDto;
|
||||
import com.linke.transport.interfaces.dto.vehicle.VehicleExportVO;
|
||||
import com.mhd.common.core.domain.dto.StatisticalMattersDTO;
|
||||
import com.mhd.common.core.domain.entity.R;
|
||||
import com.mhd.common.core.domain.todo.StatisticalMattersDo;
|
||||
@@ -16,6 +17,7 @@ import com.mhd.common.core.exception.ServiceException;
|
||||
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
|
||||
import com.mhd.common.core.utils.IgnoreNullUtil;
|
||||
import com.mhd.common.core.utils.bean.BeanUtils;
|
||||
import com.mhd.common.core.utils.poi.ExcelUtil;
|
||||
import com.mhd.common.core.web.controller.BaseController;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.core.web.page.TableDataInfo;
|
||||
@@ -28,6 +30,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -65,6 +70,33 @@ public class VehicleApi extends BaseController
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出车辆管理列表
|
||||
*/
|
||||
@Log(title = "车辆管理-导出", description = "导出车辆管理列表", businessType = BusinessType.EXPORT)
|
||||
@ApiOperation("导出车辆管理列表")
|
||||
// 同时支持 GET(query 参数)与 POST(form 表单,兼容前端 this.download() 工具)
|
||||
@RequestMapping(value = "/export", method = {RequestMethod.GET, RequestMethod.POST}, produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
public void export(VehicleDto vehicleDto, HttpServletResponse response) throws IOException
|
||||
{
|
||||
VehiclePo vehiclePo = new VehiclePo();
|
||||
//转换实体
|
||||
BeanUtils.copyProperties(vehicleDto, vehiclePo);
|
||||
//判断当前登录人数据权限
|
||||
DataPermission dataPermission = commonApplicationService.dataPermission(vehiclePo.getPermissionMenuId());
|
||||
//查询全量(不分页)
|
||||
List<VehicleExportVO> list = vehicleApplicationService.buildExportList(vehiclePo, dataPermission);
|
||||
|
||||
//设置下载头,防止浏览器乱码
|
||||
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<VehicleExportVO> util = new ExcelUtil<>(VehicleExportVO.class);
|
||||
util.exportExcel(response, list, "车辆管理数据");
|
||||
}
|
||||
|
||||
/**
|
||||
* 车辆绑定副驾
|
||||
*/
|
||||
|
||||
+76
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -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);
|
||||
}
|
||||
+26
@@ -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);
|
||||
}
|
||||
+34
@@ -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);
|
||||
}
|
||||
}
|
||||
+79
@@ -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>字段口径:入仓日期=入仓单入仓日期(为空兜底建档时间);仓单编号=入库单号;客户编号/存货人=入仓单货主;
|
||||
* 件数/毛重/体积=仓单内该货品明细合计(件数取实际上架量 SHELVES_QUANTITY);毛重(吨)=明细总毛重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("件数(仓单内该货品实际上架量合计 SHELVES_QUANTITY,排名依据)")
|
||||
@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;
|
||||
}
|
||||
+39
@@ -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;
|
||||
}
|
||||
+50
@@ -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;
|
||||
}
|
||||
+39
@@ -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);
|
||||
}
|
||||
}
|
||||
+6
@@ -210,6 +210,12 @@ public class MaterialInventory extends BaseVOEntity {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+1
@@ -874,6 +874,7 @@ public class MaterialInventoryImpl extends ServiceImpl<MaterialInventoryMapper,
|
||||
materialInventory.setExtAttr4(materialInventoryParamDO.getExtAttr4());
|
||||
materialInventory.setExpiryDate(materialInventoryParamDO.getExpiryDate());
|
||||
materialInventory.setInventoryDate(materialInventoryParamDO.getInventoryDate());
|
||||
materialInventory.setPurchaseDate(materialInventoryParamDO.getPurchaseDate());
|
||||
materialInventory.setBatchNumber(materialInventoryParamDO.getBatchNumber());
|
||||
materialInventory.setUnit(materialBaseInfo.getUnitCode());
|
||||
materialInventory.setUnitName(materialBaseInfo.getUnitName());
|
||||
|
||||
+6
@@ -194,6 +194,12 @@ public class MaterialInventoryPO extends MaterialBasePO {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+17
@@ -218,6 +218,12 @@ public class MaterialInventoryDO extends MaterialBaseDO {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@@ -253,6 +259,7 @@ public class MaterialInventoryDO extends MaterialBaseDO {
|
||||
private String expiryDateStr;
|
||||
private String productionDateStr;
|
||||
private String updateTimeStr;
|
||||
private String purchaseDateStr;
|
||||
|
||||
private String expiryDateStart;
|
||||
private String expiryDateEnd;
|
||||
@@ -263,6 +270,13 @@ public class MaterialInventoryDO extends MaterialBaseDO {
|
||||
private String updateTimeStart;
|
||||
private String updateTimeEnd;
|
||||
|
||||
private String purchaseDateStart;
|
||||
private String purchaseDateEnd;
|
||||
|
||||
/** 入库日期(库存行创建时间)区间查询 */
|
||||
private String createTimeStart;
|
||||
private String createTimeEnd;
|
||||
|
||||
@ApiModelProperty("库存分配匹配:箱号/卡板号 token 列表(明细多值拆分后,OR 匹配;空则不参与匹配)")
|
||||
private List<String> boxPalletNoList;
|
||||
|
||||
@@ -271,4 +285,7 @@ public class MaterialInventoryDO extends MaterialBaseDO {
|
||||
|
||||
@ApiModelProperty("库存分配匹配:Batch Ref NO's token 列表(默认不启用,仅显式赋值时参与匹配)")
|
||||
private List<String> batchRefNoList;
|
||||
|
||||
@ApiModelProperty("导出标识:导出与查询共用 /list,true-本次为导出请求,启用导出融合排序(到期日倒序优先→同到期日按物料编码升序→0库存靠后→更新时间倒序);不传-保持原有排序")
|
||||
private Boolean isExport;
|
||||
}
|
||||
+2
@@ -88,6 +88,8 @@ public class MaterialInventoryParamDO extends MaterialBaseDO {
|
||||
private Date expiryDate;
|
||||
//库存日期
|
||||
private Date inventoryDate;
|
||||
//进货日期
|
||||
private Date purchaseDate;
|
||||
//扩展字段3
|
||||
private Date extAttr3;
|
||||
//扩展字段4
|
||||
|
||||
+37
@@ -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);
|
||||
}
|
||||
+37
@@ -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);
|
||||
}
|
||||
+45
@@ -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);
|
||||
}
|
||||
}
|
||||
+45
@@ -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;
|
||||
}
|
||||
+52
@@ -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;
|
||||
}
|
||||
+32
@@ -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;
|
||||
}
|
||||
+53
@@ -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;
|
||||
}
|
||||
+54
@@ -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);
|
||||
}
|
||||
}
|
||||
+5
@@ -286,6 +286,11 @@ public class ReceiptMaterialDetail extends BaseVOEntity {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("扩展属性1")
|
||||
@Excel(name = "扩展属性1")
|
||||
@TableField("ext_attr_1")
|
||||
|
||||
+18
@@ -770,6 +770,15 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
receiptMaterialDetailDO.setInventoryDate(date);
|
||||
} else if ("进货日期".equals(batchLabels)) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date = null;
|
||||
try {
|
||||
date = sdf.parse(attributeValue);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
receiptMaterialDetailDO.setPurchaseDate(date);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1274,6 +1283,15 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
log.warn("库存日期格式转换失败,物料:{},原始值:{}", receiptMaterialDetailDO.getMaterialName(), attributeValue);
|
||||
}
|
||||
break;
|
||||
case "进货日期":
|
||||
try {
|
||||
if (attributeValue.matches("\\d{4}-\\d{2}-\\d{2}.*")) {
|
||||
receiptMaterialDetailDO.setPurchaseDate(DateUtils.parseDate(attributeValue));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("进货日期格式转换失败,物料:{},原始值:{}", receiptMaterialDetailDO.getMaterialName(), attributeValue);
|
||||
}
|
||||
break;
|
||||
case "总净重":
|
||||
case "净重(KG)":
|
||||
case "净重":
|
||||
|
||||
+5
@@ -316,6 +316,11 @@ public class ReceiptMaterialDetailPO extends BaseVOEntity {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("扩展属性1")
|
||||
@Excel(name = "扩展属性1")
|
||||
private String extAttr1;
|
||||
|
||||
+5
@@ -300,6 +300,11 @@ public class ReceiptMaterialDetailDO extends BaseVOEntity {
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("扩展属性1")
|
||||
@Excel(name = "扩展属性1")
|
||||
private String extAttr1;
|
||||
|
||||
+7
@@ -301,6 +301,13 @@ public class ShelfMaterialDetail extends BaseVOEntity {
|
||||
@TableField("inventory_date")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
@TableField("purchase_date")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+6
@@ -313,6 +313,12 @@ public class ShelfMaterialDetailDO extends BaseVOEntity {
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("进货日期")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Excel(name = "进货日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date purchaseDate;
|
||||
|
||||
@ApiModelProperty("ExtAttr3")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
+9
-3
@@ -1493,6 +1493,7 @@ public class StockReceiptOrderDomainService {
|
||||
shelfMaterialDetailDO.setTotalGrossWeight(receiptMaterialDetailDO.getTotalGrossWeight());
|
||||
shelfMaterialDetailDO.setExpiryDate(receiptMaterialDetailDO.getExpiryDate());
|
||||
shelfMaterialDetailDO.setInventoryDate(receiptMaterialDetailDO.getInventoryDate());
|
||||
shelfMaterialDetailDO.setPurchaseDate(receiptMaterialDetailDO.getPurchaseDate());
|
||||
shelfMaterialDetailDO.setBatchRefNo(receiptMaterialDetailDO.getBatchRefNo());
|
||||
shelfMaterialDetailDO.setSheetRefNo(receiptMaterialDetailDO.getSheetRefNo());
|
||||
shelfMaterialDetailDO.setBoxPalletNo(receiptMaterialDetailDO.getBoxPalletNo());
|
||||
@@ -1533,6 +1534,7 @@ public class StockReceiptOrderDomainService {
|
||||
shelfMaterialDetailDOChildren.setProductionDate(receiptMaterialDetailDOChildren.getProductionDate());
|
||||
shelfMaterialDetailDOChildren.setExpiryDate(receiptMaterialDetailDOChildren.getExpiryDate());
|
||||
shelfMaterialDetailDOChildren.setInventoryDate(receiptMaterialDetailDOChildren.getInventoryDate());
|
||||
shelfMaterialDetailDOChildren.setPurchaseDate(receiptMaterialDetailDOChildren.getPurchaseDate());
|
||||
shelfMaterialDetailDOChildren.setBatchRefNo(receiptMaterialDetailDOChildren.getBatchRefNo());
|
||||
shelfMaterialDetailDOChildren.setSheetRefNo(receiptMaterialDetailDOChildren.getSheetRefNo());
|
||||
shelfMaterialDetailDOChildren.setBoxPalletNo(receiptMaterialDetailDOChildren.getBoxPalletNo());
|
||||
@@ -1634,10 +1636,11 @@ public class StockReceiptOrderDomainService {
|
||||
shelfMaterialDetailDO.setTotalVolume(receiptMaterialDetailPO.getTotalVolume());
|
||||
shelfMaterialDetailDO.setTotalNetWeight(receiptMaterialDetailPO.getTotalNetWeight());
|
||||
shelfMaterialDetailDO.setTotalGrossWeight(receiptMaterialDetailPO.getTotalGrossWeight());
|
||||
// 传递生产日期、过期日期、存货日期、Batch Ref NO's、Sheet Ref NO's、箱号/卡板号、扩展属性1-4
|
||||
// 传递生产日期、过期日期、存货日期、进货日期、Batch Ref NO's、Sheet Ref NO's、箱号/卡板号、扩展属性1-4
|
||||
shelfMaterialDetailDO.setProductionDate(receiptMaterialDetailPO.getProductionDate());
|
||||
shelfMaterialDetailDO.setExpiryDate(receiptMaterialDetailPO.getExpiryDate());
|
||||
shelfMaterialDetailDO.setInventoryDate(receiptMaterialDetailPO.getInventoryDate());
|
||||
shelfMaterialDetailDO.setPurchaseDate(receiptMaterialDetailPO.getPurchaseDate());
|
||||
shelfMaterialDetailDO.setBatchRefNo(receiptMaterialDetailPO.getBatchRefNo());
|
||||
shelfMaterialDetailDO.setSheetRefNo(receiptMaterialDetailPO.getSheetRefNo());
|
||||
shelfMaterialDetailDO.setBoxPalletNo(receiptMaterialDetailPO.getBoxPalletNo());
|
||||
@@ -1686,6 +1689,7 @@ public class StockReceiptOrderDomainService {
|
||||
shelfMaterialDetailDOChildren.setProductionDate(receiptMaterialDetailPOChildren.getProductionDate());
|
||||
shelfMaterialDetailDOChildren.setExpiryDate(receiptMaterialDetailPOChildren.getExpiryDate());
|
||||
shelfMaterialDetailDOChildren.setInventoryDate(receiptMaterialDetailPOChildren.getInventoryDate());
|
||||
shelfMaterialDetailDOChildren.setPurchaseDate(receiptMaterialDetailPOChildren.getPurchaseDate());
|
||||
shelfMaterialDetailDOChildren.setBatchRefNo(receiptMaterialDetailPOChildren.getBatchRefNo());
|
||||
shelfMaterialDetailDOChildren.setSheetRefNo(receiptMaterialDetailPOChildren.getSheetRefNo());
|
||||
shelfMaterialDetailDOChildren.setBoxPalletNo(receiptMaterialDetailPOChildren.getBoxPalletNo());
|
||||
@@ -1767,10 +1771,11 @@ public class StockReceiptOrderDomainService {
|
||||
shelfMaterialDetailDO.setQuantity(receiptMaterialDetailPO.getReceiptQuantity());
|
||||
shelfMaterialDetailDO.setInUniqueId(receiptMaterialDetailPO.getInUniqueId());
|
||||
shelfMaterialDetailDO.setReceiptUniqueId(receiptMaterialDetailPO.getUniqueId());
|
||||
// 传递生产日期、过期日期、存货日期、Batch Ref NO's、Sheet Ref NO's、箱号/卡板号、扩展属性1-4
|
||||
// 传递生产日期、过期日期、存货日期、进货日期、Batch Ref NO's、Sheet Ref NO's、箱号/卡板号、扩展属性1-4
|
||||
shelfMaterialDetailDO.setProductionDate(receiptMaterialDetailPO.getProductionDate());
|
||||
shelfMaterialDetailDO.setExpiryDate(receiptMaterialDetailPO.getExpiryDate());
|
||||
shelfMaterialDetailDO.setInventoryDate(receiptMaterialDetailPO.getInventoryDate());
|
||||
shelfMaterialDetailDO.setPurchaseDate(receiptMaterialDetailPO.getPurchaseDate());
|
||||
shelfMaterialDetailDO.setBatchRefNo(receiptMaterialDetailPO.getBatchRefNo());
|
||||
shelfMaterialDetailDO.setSheetRefNo(receiptMaterialDetailPO.getSheetRefNo());
|
||||
shelfMaterialDetailDO.setBoxPalletNo(receiptMaterialDetailPO.getBoxPalletNo());
|
||||
@@ -1809,10 +1814,11 @@ public class StockReceiptOrderDomainService {
|
||||
shelfMaterialDetailDOChildren.setInUniqueId(receiptMaterialDetailPO.getInUniqueId());
|
||||
// 子项使用自身的 uniqueId 作为 receiptUniqueId,以便正确关联 material_more_detail 中的扩展属性
|
||||
shelfMaterialDetailDOChildren.setReceiptUniqueId(receiptMaterialDetailDOChildren.getUniqueId());
|
||||
// 传递生产日期、过期日期、存货日期、Batch Ref NO's、Sheet Ref NO's、箱号/卡板号、扩展属性1-4
|
||||
// 传递生产日期、过期日期、存货日期、进货日期、Batch Ref NO's、Sheet Ref NO's、箱号/卡板号、扩展属性1-4
|
||||
shelfMaterialDetailDOChildren.setProductionDate(receiptMaterialDetailDOChildren.getProductionDate());
|
||||
shelfMaterialDetailDOChildren.setExpiryDate(receiptMaterialDetailDOChildren.getExpiryDate());
|
||||
shelfMaterialDetailDOChildren.setInventoryDate(receiptMaterialDetailDOChildren.getInventoryDate());
|
||||
shelfMaterialDetailDOChildren.setPurchaseDate(receiptMaterialDetailDOChildren.getPurchaseDate());
|
||||
shelfMaterialDetailDOChildren.setBatchRefNo(receiptMaterialDetailDOChildren.getBatchRefNo());
|
||||
shelfMaterialDetailDOChildren.setSheetRefNo(receiptMaterialDetailDOChildren.getSheetRefNo());
|
||||
shelfMaterialDetailDOChildren.setBoxPalletNo(receiptMaterialDetailDOChildren.getBoxPalletNo());
|
||||
|
||||
+5
@@ -1262,6 +1262,7 @@ public class StockShelfOrderDomainService {
|
||||
inventoryParamDO.setInventoryDate(shelfMaterialDetail.getInventoryDate());
|
||||
inventoryParamDO.setExpiryDate(shelfMaterialDetail.getExpiryDate());
|
||||
inventoryParamDO.setProductionDate(shelfMaterialDetail.getProductionDate());
|
||||
inventoryParamDO.setPurchaseDate(shelfMaterialDetail.getPurchaseDate());
|
||||
inventoryParamDO.setBoxPalletNo(shelfMaterialDetail.getBoxPalletNo());
|
||||
inventoryParamDO.setSheetRefNo(shelfMaterialDetail.getSheetRefNo());
|
||||
inventoryParamDO.setBatchRefNo(shelfMaterialDetail.getBatchRefNo());
|
||||
@@ -1348,6 +1349,7 @@ public class StockShelfOrderDomainService {
|
||||
BeanUtils.copyProperties(inventoryParamDO, materialInventoryOmsParamDO);
|
||||
materialInventoryOmsParamDO.setProductionDate(materialInventoryPO.getProductionDate());
|
||||
materialInventoryOmsParamDO.setExpiryDate(materialInventoryPO.getExpiryDate());
|
||||
materialInventoryOmsParamDO.setPurchaseDate(materialInventoryPO.getPurchaseDate());
|
||||
materialInventoryOmsParamDO.setMaterialBaseInfoId(materialInventoryPO.getMaterialBaseInfoId());
|
||||
materialInventoryOmsParamDO.setWarehouseId(materialInventoryPO.getWarehouseId());
|
||||
materialInventoryOmsParamDO.setLotNumber(materialInventoryPO.getLotNumber());
|
||||
@@ -1510,6 +1512,9 @@ public class StockShelfOrderDomainService {
|
||||
case "production_date":
|
||||
param.setProductionDate(shelfMaterialDetail.getProductionDate());
|
||||
break;
|
||||
case "purchase_date":
|
||||
param.setPurchaseDate(shelfMaterialDetail.getPurchaseDate());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+36
@@ -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;
|
||||
}
|
||||
}
|
||||
+36
@@ -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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user