维他奶出入库业务单修改;

This commit is contained in:
王奎兴
2026-05-27 14:05:07 +08:00
parent 591763f99f
commit 1cca869a30
43 changed files with 2017 additions and 233 deletions
@@ -3,14 +3,17 @@ package com.mhd.system.api.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -368,4 +371,9 @@ public class InMaterialDetail extends BaseVOEntity {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -0,0 +1,47 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName(value = "reservation_delivery_details_link")
public class ReservationDeliveryDetailsLink extends BaseVOEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("编号")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "业务单id")
private Long orderId;
@ApiModelProperty(value = "业务单号")
private String orderNumber;
@ApiModelProperty(value = "类别")
private String category;
@ApiModelProperty(value = "发票编号")
private String invoiceNumber;
@ApiModelProperty(value = "客户编号")
private String customerNumber;
@ApiModelProperty(value = "配送名称")
private String deliveryName;
@ApiModelProperty(value = "")
private Integer box;
@ApiModelProperty(value = "")
private Integer bag;
@ApiModelProperty(value = "金额")
private BigDecimal amount;
}
@@ -0,0 +1,48 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.facade;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.todo.ReservationDeliveryDetailsLinkDO;
import java.util.List;
/**
* 预约配送明细关联Service接口
*
* @author gen
* @date 2026-05-26
*/
public interface IReservationDeliveryDetailsLinkService extends IService<ReservationDeliveryDetailsLink>
{
/**
* 分页查询预约配送明细关联列表
*/
public List<ReservationDeliveryDetailsLinkPO> queryList(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO);
/**
* 新增预约配送明细关联
*/
public Boolean insert(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO);
/**
* 修改预约配送明细关联
*/
public Boolean update(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO);
/**
* 批量删除预约配送明细关联
*/
public Boolean delete(Long[] ids);
/**
* 查询预约配送明细关联
*/
public ReservationDeliveryDetailsLinkPO getInfo(Long id);
/**
* 批量新增
*/
Boolean batchInsert(List<ReservationDeliveryDetailsLinkDO> reservationDeliveryDetailsLinkDOList);
}
@@ -0,0 +1,23 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.todo.ReservationDeliveryDetailsLinkDO;
import java.util.List;
/**
* 预约配送明细关联Mapper接口
*
* @author gen
* @date 2026-05-26
*/
public interface ReservationDeliveryDetailsLinkMapper extends BaseMapper<ReservationDeliveryDetailsLink>
{
/**
* 查询预约配送明细关联列表
*/
public List<ReservationDeliveryDetailsLinkPO> queryList(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO);
}
@@ -0,0 +1,95 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.persistence;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.facade.IReservationDeliveryDetailsLinkService;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.mapper.ReservationDeliveryDetailsLinkMapper;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.todo.ReservationDeliveryDetailsLinkDO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 预约配送明细关联Service业务层处理
*
* @author gen
* @date 2026-05-26
*/
@Service
public class ReservationDeliveryDetailsLinkImpl extends ServiceImpl<ReservationDeliveryDetailsLinkMapper, ReservationDeliveryDetailsLink> implements IReservationDeliveryDetailsLinkService {
@Autowired
private ReservationDeliveryDetailsLinkMapper reservationDeliveryDetailsLinkMapper;
/**
* 查询预约配送明细关联列表
*/
@Override
public List<ReservationDeliveryDetailsLinkPO> queryList(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO)
{
return reservationDeliveryDetailsLinkMapper.queryList(reservationDeliveryDetailsLinkDO);
}
/**
* 新增预约配送明细关联
*/
@Override
public Boolean insert(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO) {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
BeanUtils.copyProperties(reservationDeliveryDetailsLinkDO, reservationDeliveryDetailsLink);
return this.save(reservationDeliveryDetailsLink);
}
/**
* 修改预约配送明细关联
*/
@Override
public Boolean update(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO) {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
BeanUtils.copyProperties(reservationDeliveryDetailsLinkDO, reservationDeliveryDetailsLink);
return this.updateById(reservationDeliveryDetailsLink);
}
/**
* 批量删除预约配送明细关联
*/
@Override
public Boolean delete(Long[] ids ) {
List<ReservationDeliveryDetailsLink> list = new ArrayList<>();
for (Long id : ids) {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
reservationDeliveryDetailsLink.setId(id);
reservationDeliveryDetailsLink.setDelFlag(2);
list.add(reservationDeliveryDetailsLink);
}
return this.updateBatchById(list);
}
/**
* 查询预约配送明细关联
*/
@Override
public ReservationDeliveryDetailsLinkPO getInfo(Long id) {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = this.getById(id);
ReservationDeliveryDetailsLinkPO reservationDeliveryDetailsLinkPO = new ReservationDeliveryDetailsLinkPO();
BeanUtils.copyProperties(reservationDeliveryDetailsLink, reservationDeliveryDetailsLinkPO);
return reservationDeliveryDetailsLinkPO;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean batchInsert(List<ReservationDeliveryDetailsLinkDO> reservationDeliveryDetailsLinkDOList) {
List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinkList = reservationDeliveryDetailsLinkDOList.stream().map(reservationDeliveryDetailsLinkDO -> {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
BeanUtils.copyProperties(reservationDeliveryDetailsLinkDO, reservationDeliveryDetailsLink);
return reservationDeliveryDetailsLink;
}).collect(Collectors.toList());
return this.saveBatch(reservationDeliveryDetailsLinkList);
}
}
@@ -0,0 +1,58 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* 预约配送明细关联对象 oms_receipt_info
*
* @author gen
* @date 2026-05-26
*/
@Data
@ApiModel(value = "预约配送明细关联对象", description = "预约配送明细关联响应对象")
public class ReservationDeliveryDetailsLinkPO extends BaseVOEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("编号")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "业务单id")
private Long orderId;
@ApiModelProperty(value = "业务单号")
private String orderNumber;
@ApiModelProperty(value = "类别")
private String category;
@ApiModelProperty(value = "发票编号")
private String invoiceNumber;
@ApiModelProperty(value = "客户编号")
private String customerNumber;
@ApiModelProperty(value = "配送名称")
private String deliveryName;
@ApiModelProperty(value = "")
private Integer box;
@ApiModelProperty(value = "")
private Integer bag;
@ApiModelProperty(value = "金额")
private BigDecimal amount;
}
@@ -0,0 +1,62 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.todo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 预约配送明细关联对象 oms_receipt_info
*
* @author gen
* @date 2026-05-26
*/
@Data
public class ReservationDeliveryDetailsLinkDO extends BaseVOEntity {
private static final long serialVersionUID = 1L;
@ApiModelProperty("编号")
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "业务单id")
private Long orderId;
@ApiModelProperty(value = "业务单号")
private String orderNumber;
@ApiModelProperty(value = "类别")
private String category;
@ApiModelProperty(value = "发票编号")
private String invoiceNumber;
@ApiModelProperty(value = "客户编号")
private String customerNumber;
@ApiModelProperty(value = "配送名称")
private String deliveryName;
@ApiModelProperty(value = "")
private Integer box;
@ApiModelProperty(value = "")
private Integer bag;
@ApiModelProperty(value = "金额")
private BigDecimal amount;
@ApiModelProperty(name = "组织ID集合")
private List<Long> organizationIdList;
@ApiModelProperty(name = "权限菜单ID")
private Long permissionMenuId;
}
@@ -0,0 +1,70 @@
package com.mhd.oms.domain.reservationDeliveryDetailsLink.service;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.facade.IReservationDeliveryDetailsLinkService;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.todo.ReservationDeliveryDetailsLinkDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 预约配送明细关联
*
* @author gen
* @date 2026-05-26
*/
@Service
@Slf4j
public class ReservationDeliveryDetailsLinkDomainService {
@Autowired
private IReservationDeliveryDetailsLinkService reservationDeliveryDetailsLinkService;
/**
* 分页查询预约配送明细关联列表
*/
public List<ReservationDeliveryDetailsLinkPO> queryList(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO) {
return reservationDeliveryDetailsLinkService.queryList(reservationDeliveryDetailsLinkDO);
}
/**
* 新增预约配送明细关联
*/
public Boolean insert(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO) {
return reservationDeliveryDetailsLinkService.insert(reservationDeliveryDetailsLinkDO);
}
/**
* 修改预约配送明细关联
*/
public Boolean update(ReservationDeliveryDetailsLinkDO reservationDeliveryDetailsLinkDO) {
return reservationDeliveryDetailsLinkService.update(reservationDeliveryDetailsLinkDO);
}
/**
* 批量删除预约配送明细关联
*/
public Boolean delete(Long[] ids) {
return reservationDeliveryDetailsLinkService.delete(ids);
}
/**
* 获取预约配送明细关联详细信息
*/
public ReservationDeliveryDetailsLinkPO getInfo(Long id)
{
return reservationDeliveryDetailsLinkService.getInfo(id);
}
/**
* 批量新增
*/
public Boolean batchInsert(List<ReservationDeliveryDetailsLinkDO> reservationDeliveryDetailsLinkDOList) {
return reservationDeliveryDetailsLinkService.batchInsert(reservationDeliveryDetailsLinkDOList);
}
}
@@ -3,14 +3,17 @@ package com.mhd.oms.domain.reservationInMaterialDetail.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -364,4 +367,8 @@ public class ReservationInMaterialDetail extends BaseVOEntity {
@ApiModelProperty("lot编号")
@Excel(name = "lot编号")
private String lotNo;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -8,6 +8,7 @@ import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStock
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@@ -467,5 +468,8 @@ public class ReservationInMaterialDetailPO extends ReservationStockInOrderBasePO
@ApiModelProperty("调整后面积")
@Excel(name = "调整后面积")
private BigDecimal adjustedArea;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -1,13 +1,16 @@
package com.mhd.oms.domain.reservationInMaterialDetail.repository.todo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderBaseDO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@@ -429,4 +432,9 @@ public class ReservationInMaterialDetailDO extends ReservationStockInOrderBaseDO
@Excel(name = "调整后面积")
private BigDecimal adjustedArea;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -1,13 +1,16 @@
package com.mhd.oms.domain.reservationInMaterialDetail.repository.todo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -439,4 +442,9 @@ public class ReservationInMaterialDetailDTO extends BaseVOEntity {
@ApiModelProperty("调整后面积")
@Excel(name = "调整后面积")
private BigDecimal adjustedArea;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -6,6 +6,7 @@ import com.mhd.oms.domain.reservationStockInOrder.entity.ReservationStockInOrder
import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStockInOrderPO;
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.DeliveryDataDTO;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.StockInDataDTO;
import org.springframework.web.multipart.MultipartFile;
@@ -56,4 +57,13 @@ public interface IReservationStockInOrderService extends IService<ReservationSto
* @throws Exception 解析异常
*/
DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName) throws Exception;
/**
* 解析入库单Excel文件
* @param file Excel文件
* @param sheetName 工作表名称(默认:入库模版)
* @return 入库单完整数据(表头+明细)
* @throws Exception 解析异常
*/
StockInDataDTO parseStockInExcel(MultipartFile file, String sheetName) throws Exception;
}
@@ -1,210 +0,0 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.CustomerDeliveryDetail;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.DeliveryHeader;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.ProductDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Excel解析监听器
*/
public class DeliveryExcelListener extends AnalysisEventListener<Object> {
private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryExcelListener.class);
// 数据存储
private DeliveryHeader deliveryHeader = new DeliveryHeader();
private List<ProductDetail> productDetailList = new ArrayList<>();
private List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
// 解析状态标记
private boolean isProductDetail = false; // 是否在解析货品明细
private boolean isCustomerDetail = false; // 是否在解析客户配送明细
@Override
public void invoke(Object data, AnalysisContext context) {
try {
// 将数据转为数组(Excel行数据)
Object[] rowData = (Object[]) data;
// 1. 解析表头信息(第1行)
if (context.readRowHolder().getRowIndex() == 0) {
parseHeaderInfo(rowData);
}
// 2. 识别数据区域(根据关键字判断)
else if (context.readRowHolder().getRowIndex() == 1) {
if (rowData[0] != null && "货品明细".equals(rowData[0].toString().trim())) {
isProductDetail = true;
isCustomerDetail = false;
}
}
else if (context.readRowHolder().getRowIndex() == 7) {
// 解析纪录合共(第8行,索引为7
parseTotalRecord(rowData);
}
else if (context.readRowHolder().getRowIndex() == 8) {
if (rowData[0] != null && "客户配送明细".equals(rowData[0].toString().trim())) {
isProductDetail = false;
isCustomerDetail = true;
}
}
else if (context.readRowHolder().getRowIndex() == 30) {
// 解析发票张数(第31行,索引为30)
parseInvoiceCount(rowData);
}
// 3. 解析货品明细(第4-7行,索引3-6)
else if (isProductDetail && context.readRowHolder().getRowIndex() >= 3 && context.readRowHolder().getRowIndex() <= 6) {
parseProductDetail(rowData);
}
// 4. 解析客户配送明细(第11-30行,索引10-29)
else if (isCustomerDetail && context.readRowHolder().getRowIndex() >= 10 && context.readRowHolder().getRowIndex() <= 29) {
parseCustomerDeliveryDetail(rowData);
}
} catch (ExcelDataConvertException e) {
LOGGER.error("Excel数据转换异常,行号:{},列号:{}",
context.readRowHolder().getRowIndex() + 1,
e.getColumnIndex() + 1);
} catch (Exception e) {
LOGGER.error("Excel解析异常,行号:{}",
context.readRowHolder().getRowIndex() + 1, e);
}
}
/**
* 解析表头信息(第1行)
*/
private void parseHeaderInfo(Object[] rowData) {
// 送货日期(第2列,索引1
if (rowData[1] != null) {
deliveryHeader.setDeliveryDate((Date) rowData[1]);
}
// 送货线号(第4列,索引3
if (rowData[3] != null) {
deliveryHeader.setDeliveryLineNo(Integer.parseInt(rowData[3].toString()));
}
// 车次(第6列,索引5
if (rowData[5] != null) {
deliveryHeader.setTrainNumber(Integer.parseInt(rowData[5].toString()));
}
}
/**
* 解析纪录合共(第8行)
*/
private void parseTotalRecord(Object[] rowData) {
// 纪录合共-货品总数量(第2列,索引1)
if (rowData[1] != null) {
deliveryHeader.setTotalRecordCount(Integer.parseInt(rowData[1].toString()));
}
// 总箱数(第5列,索引4
if (rowData[4] != null) {
deliveryHeader.setTotalBoxCount(Integer.parseInt(rowData[4].toString()));
}
// 总支数(第6列,索引5
if (rowData[5] != null) {
deliveryHeader.setTotalPieceCount(Integer.parseInt(rowData[5].toString()));
}
}
/**
* 解析发票张数(第31行)
*/
private void parseInvoiceCount(Object[] rowData) {
// 发票张数(第2列,索引1
if (rowData[1] != null) {
deliveryHeader.setInvoiceCount(Integer.parseInt(rowData[1].toString()));
}
// 小计-总箱数(第5列,索引4
if (rowData[4] != null) {
deliveryHeader.setSubtotalBoxCount(Integer.parseInt(rowData[4].toString()));
}
// 小计-总支数(第6列,索引5
if (rowData[5] != null) {
deliveryHeader.setSubtotalPieceCount(Integer.parseInt(rowData[5].toString()));
}
// 小计-总金额(第7列,索引6
if (rowData[6] != null) {
deliveryHeader.setSubtotalAmount(new BigDecimal(rowData[6].toString()));
}
}
/**
* 解析货品明细
*/
private void parseProductDetail(Object[] rowData) {
ProductDetail productDetail = new ProductDetail();
// 货品编号(第1列,索引0
if (rowData[0] != null) {
productDetail.setProductCode(rowData[0].toString().trim());
}
// 货品简称(第2列,索引1
if (rowData[1] != null) {
productDetail.setProductName(rowData[1].toString().trim());
}
// 箱数(第5列,索引4
if (rowData[4] != null) {
productDetail.setBoxCount(Integer.parseInt(rowData[4].toString()));
}
// 支数(第6列,索引5
if (rowData[5] != null) {
productDetail.setPieceCount(Integer.parseInt(rowData[5].toString()));
}
productDetailList.add(productDetail);
}
/**
* 解析客户配送明细
*/
private void parseCustomerDeliveryDetail(Object[] rowData) {
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
// 类别(第1列,索引0
if (rowData[0] != null) {
customerDetail.setCategory(rowData[0].toString().trim());
}
// 发票编号(第2列,索引1
if (rowData[1] != null) {
customerDetail.setInvoiceNo(rowData[1].toString().trim());
}
// 客户编号(第3列,索引2
if (rowData[2] != null) {
customerDetail.setCustomerCode(rowData[2].toString().trim());
}
// 客户名称(第4列,索引3
if (rowData[3] != null) {
customerDetail.setCustomerName(rowData[3].toString().trim());
}
// 箱数(第5列,索引4
if (rowData[4] != null) {
customerDetail.setBoxCount(Integer.parseInt(rowData[4].toString()));
}
// 包数(第6列,索引5
if (rowData[5] != null) {
customerDetail.setPackageCount(Integer.parseInt(rowData[5].toString()));
}
// 金额(第7列,索引6
if (rowData[6] != null) {
customerDetail.setAmount(new BigDecimal(rowData[6].toString()));
}
customerDeliveryDetailList.add(customerDetail);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
LOGGER.info("Excel解析完成,共解析货品明细:{}条,客户配送明细:{}条",
productDetailList.size(), customerDeliveryDetailList.size());
}
// Getter方法(供Service层获取解析后的数据)
public DeliveryHeader getDeliveryHeader() { return deliveryHeader; }
public List<ProductDetail> getProductDetailList() { return productDetailList; }
public List<CustomerDeliveryDetail> getCustomerDeliveryDetailList() { return customerDeliveryDetailList; }
}
@@ -0,0 +1,518 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.CustomerDeliveryDetail;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.DeliveryHeader;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.ProductDetail;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 维他奶出库Excel解析监听器(最终完整版)
* 核心功能:
* 1. 正确解析多单元格数据(解决发票张数/纪录合共读取错误)
* 2. 支持动态行数的货品明细/客户配送明细解析
* 3. 自动处理文本型数字转换(String→Integer/BigDecimal
* 4. 完整的异常处理和日志记录
*/
public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<String, Object>> {
// 日志实例
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDeliveryExcelListener.class);
// ---------------------- 常量定义(便于维护) ----------------------
// 正则表达式:匹配整数(支持正负数)
private static final Pattern INTEGER_PATTERN = Pattern.compile("-?\\d+");
// 正则表达式:匹配小数(支持正负数和整数形式)
private static final Pattern DECIMAL_PATTERN = Pattern.compile("-?\\d+(\\.\\d+)?");
// 关键字定义(Excel标签匹配)
private static final String KEYWORD_DELIVERY_DATE = "送貨日期";
private static final String KEYWORD_PRODUCT_DETAIL = "货品明细";
private static final String KEYWORD_TOTAL_RECORD = "紀錄合共";
private static final String KEYWORD_CUSTOMER_DETAIL = "客户配送明细";
private static final String KEYWORD_INVOICE_COUNT = "發票張數";
private static final String KEYWORD_TOTAL_COUNT = "總計";
private static final String KEYWORD_SUBTOTAL = "小計";
// 支持的日期格式(String转Date)
private static final String[] SUPPORTED_DATE_FORMATS = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy-MM-dd HH:mm:ss"};
// ---------------------- 数据存储(解析结果) ----------------------
private DeliveryHeader deliveryHeader = new DeliveryHeader();
private List<ProductDetail> productDetailList = new ArrayList<>();
private List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
// ---------------------- 解析状态管理(动态切换) ----------------------
public enum ParseStatus {
IDLE, // 空闲状态(未进入任何表格)
PRODUCT_DETAIL,// 货品明细解析状态
CUSTOMER_DETAIL// 客户配送明细解析状态
}
private ParseStatus currentStatus = ParseStatus.IDLE;
// ---------------------- 核心解析方法(EasyExcel回调) ----------------------
@Override
public void invoke(Map<String, Object> data, AnalysisContext context) {
try {
int rowNum = context.readRowHolder().getRowIndex() + 1;
LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", rowNum, data);
// 1. 获取第一列数据(用于状态判断,标签通常在第一列)
String firstCellValue = getFirstCellValue(data);
LOGGER.debug("第{}行:第一列数据(状态判断依据):{}", rowNum, firstCellValue);
// 2. 状态切换判断(传递完整data,支持多单元格解析)
if (isStatusSwitchTrigger(firstCellValue)) {
switchStatus(firstCellValue, rowNum, data);
return;
}
// 3. 根据当前状态解析对应表格数据
switch (currentStatus) {
case PRODUCT_DETAIL:
if (isValidProductDetailRow(data, firstCellValue)) {
parseProductDetail(data, rowNum);
}
break;
case CUSTOMER_DETAIL:
if (isValidCustomerDetailRow(data, firstCellValue)) {
parseCustomerDeliveryDetail(data, rowNum);
}
break;
case IDLE:
// 空闲状态:解析表头信息(送货日期行,多单元格数据)
if (firstCellValue.contains(KEYWORD_DELIVERY_DATE)) {
parseHeaderInfo(data, rowNum);
}
break;
}
} catch (ExcelDataConvertException e) {
// Excel内置转换异常(如日期格式错误)
int rowNum = context.readRowHolder().getRowIndex() + 1;
LOGGER.error("第{}行:Excel数据转换异常,列号:{},列名:{}",
rowNum, e.getColumnIndex() + 1, getColumnNameByIndex(data, e.getColumnIndex()), e);
throw new RuntimeException("Excel数据转换异常(行:" + rowNum + ",列:" + (e.getColumnIndex() + 1) + "", e);
} catch (Exception e) {
// 其他解析异常
int rowNum = context.readRowHolder().getRowIndex() + 1;
LOGGER.error("第{}行:Excel解析异常,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("Excel解析异常(行:" + rowNum + "", e);
}
}
// ---------------------- 解析完成回调 ----------------------
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
LOGGER.info("Excel解析全部完成!最终统计结果:");
LOGGER.info("1. 表头信息:送货日期={},发票张数={},总记录数={},小计金额={}",
deliveryHeader.getDeliveryDate(),
deliveryHeader.getInvoiceCount(),
deliveryHeader.getTotalRecordCount(),
deliveryHeader.getSubtotalAmount());
LOGGER.info("2. 货品明细:共{}条记录", productDetailList.size());
LOGGER.info("3. 客户配送明细:共{}条记录", customerDeliveryDetailList.size());
}
// ---------------------- 状态管理方法(传递完整data ----------------------
/**
* 判断是否触发状态切换(根据第一列标签关键字)
*/
private boolean isStatusSwitchTrigger(String firstCellValue) {
return firstCellValue.contains(KEYWORD_PRODUCT_DETAIL)
|| firstCellValue.contains(KEYWORD_TOTAL_RECORD)
|| firstCellValue.contains(KEYWORD_CUSTOMER_DETAIL)
|| firstCellValue.contains(KEYWORD_INVOICE_COUNT);
}
/**
* 切换解析状态(核心:传递完整data,支持多单元格解析)
*/
private void switchStatus(String firstCellValue, int rowNum, Map<String, Object> data) {
if (firstCellValue.contains(KEYWORD_PRODUCT_DETAIL)) {
currentStatus = ParseStatus.PRODUCT_DETAIL;
LOGGER.info("第{}行:检测到【{}】标签,切换到【货品明细】解析状态", rowNum, KEYWORD_PRODUCT_DETAIL);
} else if (firstCellValue.contains(KEYWORD_TOTAL_RECORD)) {
currentStatus = ParseStatus.IDLE;
parseTotalRecord(data, rowNum); // 解析纪录合共(多单元格)
LOGGER.info("第{}行:检测到【{}】标签,切换到【空闲】状态,已解析货品明细{}条",
rowNum, KEYWORD_TOTAL_RECORD, productDetailList.size());
} else if (firstCellValue.contains(KEYWORD_CUSTOMER_DETAIL)) {
currentStatus = ParseStatus.CUSTOMER_DETAIL;
LOGGER.info("第{}行:检测到【{}】标签,切换到【客户配送明细】解析状态", rowNum, KEYWORD_CUSTOMER_DETAIL);
} else if (firstCellValue.contains(KEYWORD_INVOICE_COUNT)) {
currentStatus = ParseStatus.IDLE;
parseInvoiceCount(data, rowNum); // 解析发票张数(多单元格,核心修复)
LOGGER.info("第{}行:检测到【{}】标签,切换到【空闲】状态,已解析客户配送明细{}条",
rowNum, KEYWORD_INVOICE_COUNT, customerDeliveryDetailList.size());
}
}
// ---------------------- 数据有效性校验方法 ----------------------
/**
* 校验货品明细行是否有效(排除表头和空行)
*/
private boolean isValidProductDetailRow(Map<String, Object> data, String firstCellValue) {
// 有效条件:1.货品编号(第一列)不为空 2.不是表头行(表头第一列是"貨品編號")
Object productCode = getValueSafely(data, 0);
return productCode != null
&& !"貨品編號".equals(productCode.toString().trim())
&& !productCode.toString().trim().isEmpty();
}
/**
* 校验客户配送明细行是否有效(排除表头和空行)
*/
private boolean isValidCustomerDetailRow(Map<String, Object> data, String firstCellValue) {
// 有效条件:1.类别(第一列)不为空 2.不是表头行(表头第一列是"類别")
Object category = getValueSafely(data, 0);
return category != null
&& !"類别".equals(category.toString().trim())
&& !category.toString().trim().isEmpty();
}
// ---------------------- 具体数据解析方法(全部支持多单元格) ----------------------
/**
* 解析表头信息(送货日期、送货线号、车次)
* 列对应关系:0=送貨日期标签,1=送貨日期值,2=送貨線號标签,3=送貨線號值,4=車次标签,5=車次值
*/
private void parseHeaderInfo(Map<String, Object> data, int rowNum) {
try {
// 读取多单元格数据(标签在左,数值在右)
Date deliveryDate = getValueSafely(data, 1, Date.class); // 第2列:送货日期值
String deliveryLineNo = getValueSafely(data, 3, String.class); // 第4列:送货线号值
Integer trainNumber = getValueSafely(data, 5, Integer.class); // 第6列:车次值
// 赋值(空值处理)
if (deliveryDate != null) {
deliveryHeader.setDeliveryDate(deliveryDate);
}
deliveryHeader.setDeliveryLineNo(deliveryLineNo);
deliveryHeader.setTrainNumber(getDefaultValue(trainNumber, 0));
LOGGER.debug("第{}行:解析表头信息完成,送货日期={},送货线号={},车次={}",
rowNum, deliveryHeader.getDeliveryDate(),
deliveryHeader.getDeliveryLineNo(), deliveryHeader.getTrainNumber());
} catch (Exception e) {
LOGGER.error("第{}行:解析表头信息失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析表头信息失败(行:" + rowNum + "", e);
}
}
/**
* 解析纪录合共(多单元格版)
* 列对应关系:0=紀錄合共标签,1=总记录数值,4=總計标签,5=总箱数值,6=总支数值
*/
private void parseTotalRecord(Map<String, Object> data, int rowNum) {
try {
// 1. 验证是否为纪录合共行(第一列包含标签)
Object totalRecordLabel = getValueSafely(data, 0, String.class);
if (totalRecordLabel == null || !totalRecordLabel.toString().trim().contains(KEYWORD_TOTAL_RECORD)) {
LOGGER.warn("第{}行:不是纪录合共行,跳过解析,第一列内容:{}", rowNum, totalRecordLabel);
return;
}
// 2. 读取多单元格数值(标签右侧对应列)
Integer totalRecordCount = getValueSafely(data, 1, Integer.class); // 第2列:总记录数
Integer totalBoxCount = getValueSafely(data, 5, Integer.class); // 第6列:总箱数
Integer totalPieceCount = getValueSafely(data, 6, Integer.class); // 第7列:总支数
// 3. 赋值与日志
deliveryHeader.setTotalRecordCount(getDefaultValue(totalRecordCount, 0));
deliveryHeader.setTotalBoxCount(getDefaultValue(totalBoxCount, 0));
deliveryHeader.setTotalPieceCount(getDefaultValue(totalPieceCount, 0));
LOGGER.debug("第{}行:解析纪录合共完成,总记录数={},总箱数={},总支数={}",
rowNum, deliveryHeader.getTotalRecordCount(),
deliveryHeader.getTotalBoxCount(), deliveryHeader.getTotalPieceCount());
} catch (Exception e) {
LOGGER.error("第{}行:解析纪录合共失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析纪录合共失败(行:" + rowNum + "", e);
}
}
/**
* 核心修复:解析发票张数(多单元格版,正确读取右侧单元格)
* 列对应关系:
* - 0=發票張數标签列(A列),1=发票张数数值列(B列)
* - 4=小計标签列(E列),5=小计箱数列(F列),6=小计支数列(G列),7=小计金额列(H列)
*/
private void parseInvoiceCount(Map<String, Object> data, int rowNum) {
try {
// 1. 验证是否为发票张数行(第一列包含标签)
Object invoiceLabel = getValueSafely(data, 0, String.class);
if (invoiceLabel == null || !invoiceLabel.toString().trim().contains(KEYWORD_INVOICE_COUNT)) {
LOGGER.warn("第{}行:不是发票张数行,跳过解析,第一列内容:{}", rowNum, invoiceLabel);
return;
}
// 2. 解析发票张数(标签右侧第1列:B列,索引1)
Integer invoiceCount = getValueSafely(data, 1, Integer.class);
deliveryHeader.setInvoiceCount(getDefaultValue(invoiceCount, 0));
LOGGER.debug("第{}行:解析发票张数,标签列(A列):{},数值列(B列):{}",
rowNum, invoiceLabel, deliveryHeader.getInvoiceCount());
// 3. 解析小计信息(标签在E列,数值在F/G/H列)
Object subtotalLabel = getValueSafely(data, 3, String.class); // E列:小計标签
if (subtotalLabel != null && subtotalLabel.toString().trim().contains(KEYWORD_SUBTOTAL)) {
Integer subtotalBoxCount = getValueSafely(data, 4, Integer.class); // F列:小计箱数
Integer subtotalPieceCount = getValueSafely(data, 5, Integer.class); // G列:小计支数
BigDecimal subtotalAmount = getValueSafely(data, 6, BigDecimal.class); // H列:小计金额
// 赋值(空值处理)
deliveryHeader.setSubtotalBoxCount(getDefaultValue(subtotalBoxCount, 0));
deliveryHeader.setSubtotalPieceCount(getDefaultValue(subtotalPieceCount, 0));
deliveryHeader.setSubtotalAmount(getDefaultValue(subtotalAmount, BigDecimal.ZERO));
LOGGER.debug("第{}行:解析小计信息,标签列(E列):{},箱数列(F列):{},支数列(G列):{},金额列(H列):{}",
rowNum, subtotalLabel,
deliveryHeader.getSubtotalBoxCount(),
deliveryHeader.getSubtotalPieceCount(),
deliveryHeader.getSubtotalAmount());
} else {
LOGGER.warn("第{}行:未找到小计标签(E列),或标签内容不匹配,跳过小计解析", rowNum);
deliveryHeader.setSubtotalBoxCount(0);
deliveryHeader.setSubtotalPieceCount(0);
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
}
// 4. 解析完成日志
LOGGER.debug("第{}行:解析发票张数行完成,最终结果:发票张数={},小计金额={}",
rowNum, deliveryHeader.getInvoiceCount(), deliveryHeader.getSubtotalAmount());
} catch (Exception e) {
LOGGER.error("第{}行:解析发票张数失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析发票张数失败(行:" + rowNum + "),请检查Excel列对应关系", e);
}
}
/**
* 解析货品明细(多单元格,动态行数适配)
* 列对应关系:0=貨品編號,1=簡稱,4=箱,5=支
*/
private void parseProductDetail(Map<String, Object> data, int rowNum) {
try {
ProductDetail productDetail = new ProductDetail();
// 读取对应列的多单元格数据
String productCode = getValueSafely(data, 0, String.class); // 第1列:货品编号
String productName = getValueSafely(data, 1, String.class); // 第2列:货品简称
Integer boxCount = getValueSafely(data, 4, Integer.class); // 第5列:箱数
Integer pieceCount = getValueSafely(data, 5, Integer.class); // 第6列:支数
// 赋值(空值处理+去空格)
productDetail.setProductCode(getDefaultValue(productCode, "").trim());
productDetail.setProductName(getDefaultValue(productName, "").trim());
productDetail.setBoxCount(getDefaultValue(boxCount, 0));
productDetail.setPieceCount(getDefaultValue(pieceCount, 0));
// 添加到列表
productDetailList.add(productDetail);
LOGGER.debug("第{}行:解析货品明细完成,货品编号={},名称={},箱数={},支数={}",
rowNum, productDetail.getProductCode(), productDetail.getProductName(),
productDetail.getBoxCount(), productDetail.getPieceCount());
} catch (Exception e) {
LOGGER.error("第{}行:解析货品明细失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析货品明细失败(行:" + rowNum + "", e);
}
}
/**
* 解析客户配送明细(多单元格,动态行数适配)
* 列对应关系:0=類别,1=發票編號,2=客户編號,3=名稱,4=箱,5=包,6=金額
*/
private void parseCustomerDeliveryDetail(Map<String, Object> data, int rowNum) {
try {
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
// 读取对应列的多单元格数据
String category = getValueSafely(data, 0, String.class); // 第1列:类别
String invoiceNo = getValueSafely(data, 1, String.class); // 第2列:发票编号
String customerCode = getValueSafely(data, 2, String.class); // 第3列:客户编号
String customerName = getValueSafely(data, 3, String.class); // 第4列:客户名称
Integer boxCount = getValueSafely(data, 4, Integer.class); // 第5列:箱数
Integer packageCount = getValueSafely(data, 5, Integer.class); // 第6列:包数
BigDecimal amount = getValueSafely(data, 6, BigDecimal.class); // 第7列:金额
// 赋值(空值处理+去空格)
customerDetail.setCategory(getDefaultValue(category, "").trim());
customerDetail.setInvoiceNo(getDefaultValue(invoiceNo, "").trim());
customerDetail.setCustomerCode(getDefaultValue(customerCode, "").trim());
customerDetail.setCustomerName(getDefaultValue(customerName, "").trim());
customerDetail.setBoxCount(getDefaultValue(boxCount, 0));
customerDetail.setPackageCount(getDefaultValue(packageCount, 0));
customerDetail.setAmount(getDefaultValue(amount, BigDecimal.ZERO));
// 添加到列表
customerDeliveryDetailList.add(customerDetail);
LOGGER.debug("第{}行:解析客户配送明细完成,客户编号={},名称={},金额={}",
rowNum, customerDetail.getCustomerCode(), customerDetail.getCustomerName(),
customerDetail.getAmount());
} catch (Exception e) {
LOGGER.error("第{}行:解析客户配送明细失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析客户配送明细失败(行:" + rowNum + "", e);
}
}
// ---------------------- 核心工具方法(支持多单元格+类型转换) ----------------------
/**
* 获取第一列数据(用于状态判断)
*/
private String getFirstCellValue(Map<String, Object> data) {
if (data == null || data.isEmpty()) {
return "";
}
Object firstValue = data.values().iterator().next();
return firstValue != null ? firstValue.toString().trim() : "";
}
/**
* 根据列索引获取列名(用于异常定位)
*/
private String getColumnNameByIndex(Map<String, Object> data, int columnIndex) {
if (data == null || data.isEmpty()) {
return "未知列";
}
int index = 0;
for (String key : data.keySet()) {
if (index == columnIndex) {
return key;
}
index++;
}
return "" + (columnIndex + 1) + "";
}
/**
* 按列索引安全获取原始值(不做类型转换)
*/
private Object getValueSafely(Map<String, Object> data, int columnIndex) {
if (data == null || data.isEmpty()) {
return null;
}
int index = 0;
for (Object value : data.values()) {
if (index == columnIndex) {
return value;
}
index++;
}
LOGGER.warn("列索引{}超出当前行单元格数量({}列),返回null", columnIndex, data.size());
return null;
}
/**
* 按列索引安全获取值(支持多类型转换,核心工具)
* 功能:1. 直接类型匹配 2. String转数字/日期 3. 数字类型间转换
*/
private <T> T getValueSafely(Map<String, Object> data, int columnIndex, Class<T> expectedType) {
Object value = getValueSafely(data, columnIndex);
if (value == null) {
LOGGER.debug("列索引{}:单元格值为null,返回null", columnIndex);
return null;
}
// 1. 直接类型匹配
if (expectedType.isInstance(value)) {
return expectedType.cast(value);
}
// 2. String类型转目标类型(核心:处理文本型数字)
if (value instanceof String) {
String strValue = ((String) value).trim();
if (strValue.isEmpty()) {
LOGGER.debug("列索引{}:单元格值为空字符串,返回null", columnIndex);
return null;
}
// String → Integer
if (expectedType == Integer.class) {
try {
return expectedType.cast(Integer.parseInt(strValue));
} catch (NumberFormatException e) {
LOGGER.warn("列索引{}String转Integer失败,原始值:{}", columnIndex, strValue, e);
return null;
}
}
// String → Long
if (expectedType == Long.class) {
try {
return expectedType.cast(Long.parseLong(strValue));
} catch (NumberFormatException e) {
LOGGER.warn("列索引{}String转Long失败,原始值:{}", columnIndex, strValue, e);
return null;
}
}
// String → BigDecimal(支持千分位符号,如"1,234.56"
if (expectedType == BigDecimal.class) {
try {
String cleanValue = strValue.replace(",", ""); // 去除千分位逗号
return expectedType.cast(new BigDecimal(cleanValue));
} catch (NumberFormatException e) {
LOGGER.warn("列索引{}String转BigDecimal失败,原始值:{}", columnIndex, strValue, e);
return null;
}
}
// String → Date(支持多种日期格式)
if (expectedType == Date.class) {
try {
return expectedType.cast(DateUtils.parseDate(strValue, SUPPORTED_DATE_FORMATS));
} catch (ParseException e) {
LOGGER.warn("列索引{}String转Date失败,原始值:{},支持格式:{}",
columnIndex, strValue, Arrays.toString(SUPPORTED_DATE_FORMATS), e);
return null;
}
}
}
// 3. 数字类型间转换(如Double→IntegerLong→BigDecimal
if (value instanceof Number) {
Number numValue = (Number) value;
if (expectedType == Integer.class) {
return expectedType.cast(numValue.intValue());
}
if (expectedType == Long.class) {
return expectedType.cast(numValue.longValue());
}
if (expectedType == BigDecimal.class) {
return expectedType.cast(new BigDecimal(numValue.toString()));
}
}
// 4. 不支持的转换
LOGGER.warn("列索引{}:不支持的类型转换,期望类型:{},实际类型:{},原始值:{}",
columnIndex, expectedType.getSimpleName(), value.getClass().getSimpleName(), value);
return null;
}
/**
* 空值处理:返回默认值
*/
private <T> T getDefaultValue(T value, T defaultValue) {
return value != null ? value : defaultValue;
}
// ---------------------- Getter方法(供Service层获取解析结果) ----------------------
public DeliveryHeader getDeliveryHeader() {
return deliveryHeader;
}
public List<ProductDetail> getProductDetailList() {
return productDetailList;
}
public List<CustomerDeliveryDetail> getCustomerDeliveryDetailList() {
return customerDeliveryDetailList;
}
}
@@ -0,0 +1,259 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.StockInHeader;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.StockInProductDetail;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 入库单Excel解析监听器
* 功能:1. 解析第一行表头数据 2. 解析第二行开始的产品明细 3. 支持文本型数字转换
*/
public class StockInExcelListener extends AnalysisEventListener<Map<String, Object>> {
private static final Logger LOGGER = LoggerFactory.getLogger(StockInExcelListener.class);
// 存储解析结果
private final StockInHeader stockInHeader = new StockInHeader();
private final List<StockInProductDetail> productDetailList = new ArrayList<>();
// 支持的日期格式(用于String转Date)
private static final String[] SUPPORTED_DATE_FORMATS = {
"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy-MM-dd HH:mm:ss"
};
// 行号标记(用于区分第一行和表格行)
private int currentRowNum = 0;
@Override
public void invoke(Map<String, Object> data, AnalysisContext context) {
currentRowNum = context.readRowHolder().getRowIndex() + 1; // 行号从1开始
try {
LOGGER.debug("第{}行解析开始,数据:{}", currentRowNum, data);
// 1. 解析第一行:表头数据(入库日期、OrderNO)
if (currentRowNum == 1) {
parseHeaderData(data);
}
// 2. 解析第二行及以后:产品明细(跳过表头行:第2行是表格列名)
else if (currentRowNum >= 3) {
if (isValidProductRow(data)) {
StockInProductDetail detail = parseProductDetail(data);
productDetailList.add(detail);
}
}
} catch (ExcelDataConvertException e) {
// Excel内置转换异常(如日期格式错误)
LOGGER.error("第{}行数据转换异常,列号:{},列名:{}",
currentRowNum, e.getColumnIndex() + 1, getColumnNameByIndex(data, e.getColumnIndex()), e);
throw new RuntimeException("" + currentRowNum + "行列" + (e.getColumnIndex() + 1) + "数据格式错误", e);
} catch (Exception e) {
// 其他解析异常
LOGGER.error("第{}行解析异常,数据:{}", currentRowNum, data, e);
throw new RuntimeException("" + currentRowNum + "行数据解析失败", e);
}
}
/**
* 解析第一行表头数据
* 列对应关系:0=入库日期标签,1=入库日期值,2=OrderNO标签,3=OrderNO值
*/
private void parseHeaderData(Map<String, Object> data) {
// 入库日期(第2列,索引1
Object stockInDateObj = getValueSafely(data, 1);
if (stockInDateObj != null) {
Date stockInDate = convertToDate(stockInDateObj);
stockInHeader.setStockInDate(stockInDate);
LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate);
}
// OrderNO(第4列,索引3
Object orderNoObj = getValueSafely(data, 3);
if (orderNoObj != null) {
String orderNo = orderNoObj.toString().trim();
stockInHeader.setOrderNo(orderNo);
LOGGER.debug("第1行:OrderNO解析完成,值:{}", orderNo);
}
// 校验表头数据完整性
if (stockInHeader.getStockInDate() == null || stockInHeader.getOrderNo().isEmpty()) {
LOGGER.warn("第1行表头数据不完整,入库日期:{},OrderNO:{}",
stockInHeader.getStockInDate(), stockInHeader.getOrderNo());
}
}
/**
* 解析产品明细数据(一行对应一个产品)
*/
private StockInProductDetail parseProductDetail(Map<String, Object> data) {
StockInProductDetail detail = new StockInProductDetail();
// 产品编号(第1列,索引0)- 支持文本型数字转换
detail.setProductCode(convertToInteger(getValueSafely(data, 0)));
// 产品名称(第2列,索引1
detail.setProductName(convertToString(getValueSafely(data, 1)));
// 箱含量(第5列,索引4
detail.setBoxCapacity(convertToInteger(getValueSafely(data, 4)));
// 件数(第6列,索引5
detail.setPieceCount(convertToInteger(getValueSafely(data, 5)));
// 箱数(第7列,索引6
detail.setBoxCount(convertToInteger(getValueSafely(data, 6)));
// 饮用日期(第8列,索引7
detail.setDrinkDate(convertToDate(getValueSafely(data, 7)));
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{}",
currentRowNum, detail.getProductCode(), detail.getProductName());
return detail;
}
/**
* 校验产品行是否有效(排除空行和合计行)
*/
private boolean isValidProductRow(Map<String, Object> data) {
// 有效条件:产品编号不为空 + 不是合计行(合计行产品编号为null,且包含"Total"
Object productCodeObj = getValueSafely(data, 0);
Object productNameObj = getValueSafely(data, 1);
// 排除合计行(如第7行:Total:68)
if (productNameObj != null && productNameObj.toString().contains("Total")) {
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
return false;
}
// 排除空行(产品编号为空)
return productCodeObj != null && !productCodeObj.toString().trim().isEmpty();
}
/**
* 解析完成回调
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
LOGGER.info("Excel解析全部完成!解析结果:");
LOGGER.info("1. 表头信息:入库日期={}OrderNO={}",
stockInHeader.getStockInDate(), stockInHeader.getOrderNo());
LOGGER.info("2. 产品明细:共{}条记录", productDetailList.size());
}
// ---------------------- 工具方法(类型转换+安全取值) ----------------------
/**
* 按列索引安全获取值(支持列索引越界处理)
*/
private Object getValueSafely(Map<String, Object> data, int columnIndex) {
if (data == null || data.isEmpty()) {
return null;
}
int index = 0;
for (Object value : data.values()) {
if (index == columnIndex) {
return value;
}
index++;
}
LOGGER.warn("第{}行列索引{}超出范围(当前行共{}列),返回null",
currentRowNum, columnIndex, data.size());
return null;
}
/**
* 根据列索引获取列名(用于异常定位)
*/
private String getColumnNameByIndex(Map<String, Object> data, int columnIndex) {
if (data == null || data.isEmpty()) {
return "未知列";
}
int index = 0;
for (String key : data.keySet()) {
if (index == columnIndex) {
return key;
}
index++;
}
return "" + (columnIndex + 1) + "";
}
/**
* 通用转换:Object转String
*/
private String convertToString(Object value) {
if (value == null) {
return "";
}
return value.toString().trim();
}
/**
* 通用转换:Object转Integer(支持文本型数字、Number类型)
*/
private Integer convertToInteger(Object value) {
if (value == null) {
return 0;
}
try {
// 文本型数字(如String"1741"
if (value instanceof String) {
String strValue = ((String) value).trim();
return strValue.isEmpty() ? 0 : Integer.parseInt(strValue);
}
// Number类型(如Double、Long
if (value instanceof Number) {
return ((Number) value).intValue();
}
} catch (NumberFormatException e) {
LOGGER.warn("值转换为Integer失败,值:{},返回0", value, e);
}
return 0;
}
/**
* 通用转换:Object转Date(支持Date类型、String类型)
*/
private Date convertToDate(Object value) {
if (value == null) {
return null;
}
// 直接是Date类型
if (value instanceof Date) {
return (Date) value;
}
// String类型转Date(支持多种格式)
if (value instanceof String) {
String strValue = ((String) value).trim();
try {
return DateUtils.parseDate(strValue, SUPPORTED_DATE_FORMATS);
} catch (ParseException e) {
LOGGER.warn("值转换为Date失败,值:{},支持格式:{}",
strValue, String.join(",", SUPPORTED_DATE_FORMATS), e);
}
}
return null;
}
// ---------------------- Getter方法(供Service层获取解析结果) ----------------------
public StockInHeader getStockInHeader() {
return stockInHeader;
}
public List<StockInProductDetail> getProductDetailList() {
return productDetailList;
}
}
@@ -27,6 +27,8 @@ public interface ReservationStockInOrderMapper extends BaseMapper<ReservationSto
public MaterialBaseInfoPO getinfo(@Param("id") Long id);
public MaterialBaseInfoPO getByName(@Param("name") String name);
public List<MaterialBaseInfoPO> getMaterialBaseInfoList(@Param("id") String id);
public MaterialWarehouseControlPO getInfoByMaterialBaseInfoIds(@Param("id") Long id);
@@ -1,15 +1,51 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.persistence;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mhd.common.core.domain.po.UserPo;
import com.mhd.common.core.enums.DictCode;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.OrderSequence;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.facade.IReservationDeliveryDetailsLinkService;
import com.mhd.oms.domain.reservationInMaterialDetail.repository.facade.IReservationInMaterialDetailService;
import com.mhd.oms.domain.reservationInMaterialDetail.repository.po.ReservationInMaterialDetailPO;
import com.mhd.oms.domain.reservationInMaterialDetail.repository.todo.ReservationInMaterialDetailDO;
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.facade.IReservationOutMaterialDetailService;
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.todo.ReservationOutMaterialDetailDO;
import com.mhd.oms.domain.reservationStockInOrder.entity.ReservationStockInOrder;
import com.mhd.oms.domain.reservationStockInOrder.repository.facade.IReservationStockInOrderService;
import com.mhd.oms.domain.reservationStockInOrder.repository.listener.DeliveryExcelListener;
//import com.mhd.oms.domain.reservationStockInOrder.repository.listener.DeliveryExcelListener;
import com.mhd.oms.domain.reservationStockInOrder.repository.listener.DynamicDeliveryExcelListener;
import com.mhd.oms.domain.reservationStockInOrder.repository.listener.StockInExcelListener;
import com.mhd.oms.domain.reservationStockInOrder.repository.mapper.ReservationStockInOrderMapper;
import com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO;
import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStockInOrderPO;
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.DeliveryDataDTO;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.*;
import com.mhd.oms.domain.reservationStockOutOrder.entity.ReservationStockOutOrder;
import com.mhd.oms.domain.reservationStockOutOrder.repository.facade.IReservationStockOutOrderService;
import com.mhd.oms.domain.reservationStockOutOrder.repository.mapper.ReservationStockOutOrderMapper;
import com.mhd.oms.domain.reservationStockOutOrder.repository.todo.ReservationStockOutOrderDO;
import com.mhd.oms.domain.reservationStockOutOrder.service.ReservationStockOutOrderApplicationService;
import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.UserServiceFeign;
import com.mhd.system.api.domain.SysDictData;
import com.mhd.system.api.domain.WarehouseFeignPO;
import com.mhd.system.api.domain.cache.AssociationWarehouseCacheDO;
import com.mhd.system.api.domain.cache.SystemServiceCacheUtil;
import com.mhd.system.api.model.LoginUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -17,8 +53,9 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 入库单Service业务层处理
@@ -30,6 +67,22 @@ import java.util.List;
public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInOrderMapper, ReservationStockInOrder> implements IReservationStockInOrderService {
@Autowired
private ReservationStockInOrderMapper stockInOrderMapper;
@Autowired
private ReservationStockOutOrderMapper stockOutOrderMapper;
@Autowired
private IReservationDeliveryDetailsLinkService reservationDeliveryDetailsLinkService;
@Autowired
private IReservationStockOutOrderService reservationStockOutOrderService;
@Autowired
private IReservationOutMaterialDetailService reservationOutMaterialDetailService;
@Autowired
private ReservationStockOutOrderApplicationService reservationStockOutOrderApplicationService;
@Autowired
private UserServiceFeign userServiceFeign;
@Autowired
private SystemServiceFeign systemServiceFeign;
@Autowired
private IReservationInMaterialDetailService materialDetailService;
/**
* 查询入库单列表
@@ -100,33 +153,405 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
@Override
public DeliveryDataDTO parseExcelFile(MultipartFile file, String sheetName) throws Exception {
// 校验文件
LoginUser loginUser = SecurityUtils.getLoginUser();
// 校验文件和工作表名称(逻辑不变)
if (file.isEmpty()) {
throw new IllegalArgumentException("上传的Excel文件不能为空");
}
// 校验工作表名称
if (!"出库导入".equals(sheetName) && !"参考数据".equals(sheetName)) {
throw new IllegalArgumentException("工作表名称必须为'出库导入'或'参考数据'");
sheetName = "出库导入";
}
// 使用EasyExcel解析Excel
DeliveryExcelListener listener = new DeliveryExcelListener();
// 关键变更:使用动态行数监听器
DynamicDeliveryExcelListener listener = new DynamicDeliveryExcelListener();
try (InputStream inputStream = file.getInputStream()) {
EasyExcel.read(inputStream, listener)
.sheet(sheetName) // 指定工作表
.headRowNumber(0) // 无固定表头,从第1行开始解析
.sheet(sheetName)
.headRowNumber(0) // 无固定表头,全部行交由监听器处理
.doRead();
}
// 封装DTO返回
// 封装DTO(逻辑不变)
DeliveryDataDTO deliveryDataDTO = new DeliveryDataDTO();
deliveryDataDTO.setDeliveryHeader(listener.getDeliveryHeader());
deliveryDataDTO.setProductDetailList(listener.getProductDetailList());
deliveryDataDTO.setCustomerDeliveryDetailList(listener.getCustomerDeliveryDetailList());
List<ProductDetail> productDetailList = deliveryDataDTO.getProductDetailList();
List<Map<String,Object>> shipperIds = new ArrayList<>();
Set<Long> processedShipperIds = new HashSet<>();
for (ProductDetail productDetail : productDetailList) {
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByName(productDetail.getProductName());
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);
}
}
}
if (shipperIds.isEmpty()) {
throw new Exception("出库物料货主不存在!");
}
if (shipperIds.size() > 1) {
throw new Exception("出库物料不是同一个货主!");
}
ReservationStockOutOrderDO reservationStockOutOrderDO = new ReservationStockOutOrderDO();
reservationStockOutOrderDO.setOutOrderNumber(OrderSequence.getOrderCode("CK"));
reservationStockOutOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId"));
reservationStockOutOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName"));
reservationStockOutOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
reservationStockOutOrderDO.setNeedTransportFlag("1");
reservationStockOutOrderDO.setNeedDeclareFlag("1");
reservationStockOutOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
reservationStockOutOrderDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
reservationStockOutOrderDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
reservationStockOutOrderDO.setIssueStatus(0);
reservationStockOutOrderDO.setSalesmanId(loginUser.getUserPo().getUserId().toString());
reservationStockOutOrderDO.setSalesmanName(loginUser.getUserPo().getUserName());
reservationStockOutOrderDO.setCreateTime(new Date());
reservationStockOutOrderDO.setCreateBy(loginUser.getUserPo().getUserId());
reservationStockOutOrderDO.setCreateByName(loginUser.getUserPo().getUserName());
reservationStockOutOrderDO.setDelFlag(1);
reservationStockOutOrderDO.setDocumentCreator(loginUser.getUserPo().getUserId().toString());
reservationStockOutOrderDO.setDocumentCreatorName(loginUser.getUserPo().getUserName());
reservationStockOutOrderDO.setOrderTypeCode("pu_tong_chu_ku");
reservationStockOutOrderDO.setOrderTypeName("普通出库");
DeliveryHeader deliveryHeader = deliveryDataDTO.getDeliveryHeader();
reservationStockOutOrderDO.setTotalAmount(deliveryHeader.getSubtotalAmount());
reservationStockOutOrderDO.setRoute(deliveryHeader.getDeliveryLineNo());
reservationStockOutOrderDO.setOutboundDate(deliveryHeader.getDeliveryDate());
int index = 1;
List<ReservationOutMaterialDetailDO> reservationOutMaterialDetailDOList = new ArrayList<>();
for (ProductDetail productDetail : productDetailList) {
ReservationOutMaterialDetailDO reservationOutMaterialDetailDO = new ReservationOutMaterialDetailDO();
reservationOutMaterialDetailDO.setOutOrderNumber(reservationStockOutOrderDO.getOutOrderNumber());
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByName(productDetail.getProductName());
reservationOutMaterialDetailDO.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId());
reservationOutMaterialDetailDO.setQuantity(new BigDecimal(productDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP).add(new BigDecimal(productDetail.getBoxCount()).setScale(2, BigDecimal.ROUND_HALF_UP)));
reservationOutMaterialDetailDO.setMaterialCode(materialBaseInfoPO.getMaterialCode());
reservationOutMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName());
reservationOutMaterialDetailDO.setBarCode(materialBaseInfoPO.getBarCode());
reservationOutMaterialDetailDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
reservationOutMaterialDetailDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
reservationOutMaterialDetailDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
reservationOutMaterialDetailDO.setCreateBy(loginUser.getUserPo().getUserId());
reservationOutMaterialDetailDO.setCreateByName(loginUser.getUserPo().getUserName());
String lot = generateDateSerialNumber();
lot = lot+"-"+index;
index++;
reservationOutMaterialDetailDO.setLotNo(lot);
reservationOutMaterialDetailDOList.add(reservationOutMaterialDetailDO);
}
reservationStockOutOrderDO.setMaterialDetailList(reservationOutMaterialDetailDOList);
reservationStockOutOrderApplicationService.insert(reservationStockOutOrderDO);
List<CustomerDeliveryDetail> customerDeliveryDetailList = deliveryDataDTO.getCustomerDeliveryDetailList();
for (CustomerDeliveryDetail customerDeliveryDetail : customerDeliveryDetailList) {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
reservationDeliveryDetailsLink.setInvoiceNumber(customerDeliveryDetail.getInvoiceNo());
reservationDeliveryDetailsLink.setAmount(customerDeliveryDetail.getAmount());
reservationDeliveryDetailsLink.setCustomerNumber(customerDeliveryDetail.getCustomerCode());
reservationDeliveryDetailsLink.setDeliveryName(customerDeliveryDetail.getCustomerName());
reservationDeliveryDetailsLink.setBox(customerDeliveryDetail.getBoxCount());
reservationDeliveryDetailsLink.setBag(customerDeliveryDetail.getPackageCount());
reservationDeliveryDetailsLink.setCategory(customerDeliveryDetail.getCategory());
reservationDeliveryDetailsLink.setOrderNumber(reservationStockOutOrderDO.getOutOrderNumber());
List<ReservationStockOutOrder> reservationStockOutOrderDOList = stockOutOrderMapper.selectList(new LambdaQueryWrapper<ReservationStockOutOrder>().eq(ReservationStockOutOrder::getOutOrderNumber, reservationStockOutOrderDO.getOutOrderNumber()));
if (reservationStockOutOrderDOList != null && !reservationStockOutOrderDOList.isEmpty() && reservationStockOutOrderDOList.get(0) != null) {
reservationDeliveryDetailsLink.setOrderId(reservationStockOutOrderDOList.get(0).getOutOrderId());
}
reservationDeliveryDetailsLinkService.save(reservationDeliveryDetailsLink);
}
return deliveryDataDTO;
}
public String generateDateSerialNumber() {
// 1. 获取当前时间,格式化为:yyMMdd (2位年+2位月+2位日)
SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
String datePart = sdf.format(new Date());
// 2. 生成 000~999 的 3 位随机数(不足补零)
Random random = new Random();
int randomNum = random.nextInt(1000); // 0~999
String randomPart = String.format("%03d", randomNum); // 自动补前导零
// 3. 拼接结果
return datePart + randomPart;
}
private static final Logger LOGGER = LoggerFactory.getLogger(ReservationStockInOrderImpl.class);
// 默认工作表名称(与Excel一致)
private static final String DEFAULT_SHEET_NAME = "入库模版";
@Override
public StockInDataDTO parseStockInExcel(MultipartFile file, String sheetName) throws Exception {
LoginUser loginUser = SecurityUtils.getLoginUser();
// 1. 校验文件
if (file.isEmpty()) {
LOGGER.error("上传的Excel文件为空");
throw new IllegalArgumentException("Excel文件不能为空");
}
// 2. 校验工作表名称(默认使用"入库模版")
String targetSheetName = (sheetName == null || sheetName.trim().isEmpty())
? DEFAULT_SHEET_NAME : sheetName.trim();
// 3. 使用EasyExcel解析(指定监听器)
StockInExcelListener listener = new StockInExcelListener();
try (InputStream inputStream = file.getInputStream()) {
EasyExcel.read(inputStream, listener)
.sheet(targetSheetName) // 指定工作表
.headRowNumber(0) // 从第1行开始解析(不跳过任何行)
.doRead();
}
// 4. 封装DTO返回
StockInDataDTO resultDTO = new StockInDataDTO();
resultDTO.setStockInHeader(listener.getStockInHeader());
resultDTO.setProductDetailList(listener.getProductDetailList());
LOGGER.info("Excel解析成功,工作表:{},产品明细数:{}",
targetSheetName, resultDTO.getProductDetailList().size());
List<StockInProductDetail> productDetailList = resultDTO.getProductDetailList();
List<Map<String,Object>> shipperIds = new ArrayList<>();
Set<Long> processedShipperIds = new HashSet<>();
for (StockInProductDetail productDetail : productDetailList) {
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByName(productDetail.getProductName());
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);
}
}
}
ReservationStockInOrderDO reservationStockInOrderDO = new ReservationStockInOrderDO();
reservationStockInOrderDO.setInOrderNumber(OrderSequence.getOrderCode("RK"));
reservationStockInOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
reservationStockInOrderDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
reservationStockInOrderDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
reservationStockInOrderDO.setCreateBy(loginUser.getUserPo().getUserId());
reservationStockInOrderDO.setCreateByName(loginUser.getUserPo().getUserName());
reservationStockInOrderDO.setCreateTime(new Date());
reservationStockInOrderDO.setDelFlag(1);
reservationStockInOrderDO.setDocumentCreator(loginUser.getUserPo().getUserId().toString());
reservationStockInOrderDO.setDocumentCreatorName(loginUser.getUserPo().getUserName());
reservationStockInOrderDO.setOrderTypeCode("pu_tong_ru_ku");
reservationStockInOrderDO.setOrderTypeName("普通入库");
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"));
StockInHeader stockInHeader = resultDTO.getStockInHeader();
reservationStockInOrderDO.setWarehouseDate(stockInHeader.getStockInDate());
reservationStockInOrderDO.setBusinessOrderNo(stockInHeader.getOrderNo());
List<ReservationInMaterialDetailDO> reservationInMaterialDetailDOList = new ArrayList<>();
int index = 1;
for (StockInProductDetail stockInProductDetail : productDetailList) {
ReservationInMaterialDetailDO reservationInMaterialDetailDO = new ReservationInMaterialDetailDO();
reservationInMaterialDetailDO.setInOrderNumber(reservationStockInOrderDO.getInOrderNumber());
reservationInMaterialDetailDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
reservationInMaterialDetailDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
reservationInMaterialDetailDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
reservationInMaterialDetailDO.setCreateBy(loginUser.getUserPo().getUserId());
reservationInMaterialDetailDO.setCreateByName(loginUser.getUserPo().getUserName());
reservationInMaterialDetailDO.setCreateTime(new Date());
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByName(stockInProductDetail.getProductName());
if (materialBaseInfoPO != null) {
reservationInMaterialDetailDO.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId());
reservationInMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName());
reservationInMaterialDetailDO.setMaterialCode(materialBaseInfoPO.getMaterialCode());
}
reservationInMaterialDetailDO.setQuantity(new BigDecimal(stockInProductDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP));
String lot = generateDateSerialNumber();
lot = lot+"-"+index;
index++;
reservationInMaterialDetailDO.setLotNo(lot);
reservationInMaterialDetailDO.setDrinkDate(stockInProductDetail.getDrinkDate());
reservationInMaterialDetailDOList.add(reservationInMaterialDetailDO);
}
reservationStockInOrderDO.setMaterialDetailList(reservationInMaterialDetailDOList);
insertApplication(reservationStockInOrderDO);
return resultDTO;
}
public Boolean insertApplication(ReservationStockInOrderDO stockInOrderDO) {
// 如果仓库ID为空,从当前登录用户的关联仓库中获取
if (stockInOrderDO.getWarehouseId() == null) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null) {
throw new ServiceException("登录已失效,请重新登录");
}
AssociationWarehouseCacheDO associationWarehouseCacheDO = SystemServiceCacheUtil.getAssociationWarehouseCache(loginUser.getUserid());
if (associationWarehouseCacheDO == null || associationWarehouseCacheDO.getWarehouseId() == null) {
throw new ServiceException("当前用户未配置关联仓库,请先配置仓库");
}
stockInOrderDO.setWarehouseId(associationWarehouseCacheDO.getWarehouseId());
}
//设置仓库
setWarehouseInfo(stockInOrderDO);
// 设置货主信息
shipperParam(stockInOrderDO);
// 设置供应商信息
supplierParam(stockInOrderDO);
// 设置数据字典键值
setDataDict(stockInOrderDO);
return insertDomain(stockInOrderDO);
}
@Transactional(rollbackFor = Exception.class)
public Boolean insertDomain(ReservationStockInOrderDO stockInOrderDO) {
if (stockInOrderDO.getInOrderNumber() == null) {
stockInOrderDO.setInOrderNumber(OrderSequence.getOrderCode("RK"));
}
// //统计总计划数
BigDecimal quantity = stockInOrderDO.getMaterialDetailList().stream().map(ReservationInMaterialDetailDO::getQuantity).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
stockInOrderDO.setQuantity(quantity);
this.insert(stockInOrderDO);
return materialDetailService.batchInsert(stockInOrderDO.getInOrderNumber(), stockInOrderDO.getMaterialDetailList());
}
private void setWarehouseInfo(ReservationStockInOrderDO stockInOrderDO) {
if (stockInOrderDO.getWarehouseId() == null) {
throw new ServiceException("仓库ID不能为空");
}
AjaxResult ajaxResult = systemServiceFeign.getWarehouseInfoByWarehouseId(stockInOrderDO.getWarehouseId());
if (ajaxResult == null) {
throw new ServiceException("获取仓库信息失败:返回结果为空,仓库ID:" + stockInOrderDO.getWarehouseId());
}
Object codeObj = ajaxResult.get("code");
if (codeObj == null || !"200".equals(String.valueOf(codeObj))) {
String errorMsg = ajaxResult.get("msg") != null ? String.valueOf(ajaxResult.get("msg")) : "未知错误";
throw new ServiceException("获取仓库信息失败,仓库ID" + stockInOrderDO.getWarehouseId() + ",错误信息:" + errorMsg);
}
Object dataObj = ajaxResult.get("data");
if (dataObj == null) {
throw new ServiceException("获取仓库信息失败:返回数据为空,仓库ID:" + stockInOrderDO.getWarehouseId());
}
WarehouseFeignPO warehouseFeignPO = JSON.parseObject(JSONObject.toJSONString(dataObj), WarehouseFeignPO.class);
if (warehouseFeignPO == null) {
throw new ServiceException("获取仓库信息失败:数据解析失败,仓库ID:" + stockInOrderDO.getWarehouseId());
}
stockInOrderDO.setWarehouseCode(warehouseFeignPO.getWarehouseCode());
stockInOrderDO.setWarehouseName(warehouseFeignPO.getWarehouseName());
}
/**
* @description 封装货主信息(改为与出库单相同的简洁逻辑,使用入库单的字段shipperName
* @author ZhouGY
* @date 2024/5/9 20:43
* @param stockInOrderDO
*/
private void shipperParam(ReservationStockInOrderDO stockInOrderDO){
// 新增/编辑时前端可能只传 shipperId(下拉选中值),shipperName 为空。
// 这里优先按 shipperId 回填货主编码/名称;其次再按 shipperName(名称或数字ID字符串)回填。
// 1) 优先按 shipperId 查询
if (ObjectUtil.isNotNull(stockInOrderDO.getShipperId())) {
AjaxResult ajaxResult = userServiceFeign.getInfo(stockInOrderDO.getShipperId());
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
throw new ServiceException("获取货主信息失败");
}
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
stockInOrderDO.setShipperId(userPo.getUserId());
stockInOrderDO.setShipperCode(userPo.getUserMemberCode());
stockInOrderDO.setShipperName(userPo.getUserName());
return;
}
// 2) shipperId 为空时,按 shipperName 兜底
String shipperName = stockInOrderDO.getShipperName();
if (StringUtils.isEmpty(shipperName)) {
return;
}
if (!shipperName.matches("\\d+")) {
// 按名称查询
AjaxResult ajaxResult = userServiceFeign.getInfoByName(shipperName);
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
throw new ServiceException("获取货主信息失败");
}
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
stockInOrderDO.setShipperId(userPo.getUserId());
stockInOrderDO.setShipperCode(userPo.getUserMemberCode());
stockInOrderDO.setShipperName(userPo.getUserName());
} else {
// shipperName 是数字:按ID查询
AjaxResult ajaxResult = userServiceFeign.getInfo(Long.valueOf(shipperName));
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
throw new ServiceException("获取货主信息失败");
}
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
stockInOrderDO.setShipperId(userPo.getUserId());
stockInOrderDO.setShipperCode(userPo.getUserMemberCode());
stockInOrderDO.setShipperName(userPo.getUserName());
}
}
/**
* @description 封装供应商信息
* @author ZhouGY
* @date 2024/5/9 20:43
* @param stockInOrderDO
*/
private void supplierParam(ReservationStockInOrderDO stockInOrderDO){
if (ObjectUtil.isNotNull(stockInOrderDO.getSupplierId())){
AjaxResult ajaxResult = userServiceFeign.getInfo(stockInOrderDO.getSupplierId());
if(!"200".equals(String.valueOf(ajaxResult.get("code")))){
throw new ServiceException("获取供应商信息失败");
}
UserPo userPo = JSON.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
stockInOrderDO.setSupplierName(userPo.getUserName());
}
}
/**
* @description 设置数据字典键值
* @author ZhouGY
* @date 2024/4/7 14:33
* @param stockInOrderDO
*/
private void setDataDict(ReservationStockInOrderDO stockInOrderDO){
//翻译单据类型
if (StringUtils.isNotBlank(stockInOrderDO.getOrderTypeCode())){
AjaxResult documentTypeResult = systemServiceFeign.selectListByDictType(DictCode.DOCUMENT_TYPE.getCode());
if (!"200".equals(String.valueOf(documentTypeResult.get("code")))) {
throw new ServiceException("单据类型数据字典不存在");
}
List<SysDictData> documentTypeDataList = JSON.parseArray(JSONUtil.toJsonStr(documentTypeResult.get("data")), SysDictData.class);
SysDictData documentTypeData = documentTypeDataList.stream().filter(info -> ObjectUtil.equal(info.getDictValue(), stockInOrderDO.getOrderTypeCode())).findAny().orElse(null);
if (null == documentTypeData) {
throw new ServiceException("单据类型【" + stockInOrderDO.getOrderTypeCode() + "】数据字典不存在 请联系管理员");
}
stockInOrderDO.setOrderTypeName(documentTypeData.getDictLabel());
}
//翻译优先级别类型
if (StringUtils.isNotBlank(stockInOrderDO.getPriorityLevelCode())){
AjaxResult priorityLevelResult = systemServiceFeign.selectListByDictType(DictCode.PRIORITY_LEVEL.getCode());
if (!"200".equals(String.valueOf(priorityLevelResult.get("code")))) {
throw new ServiceException("优先级别类型数据字典不存在");
}
List<SysDictData> priorityLevelDataList = JSON.parseArray(JSONUtil.toJsonStr(priorityLevelResult.get("data")), SysDictData.class);
SysDictData priorityLevelData = priorityLevelDataList.stream().filter(info -> ObjectUtil.equal(info.getDictValue(), stockInOrderDO.getPriorityLevelCode())).findAny().orElse(null);
if (null == priorityLevelData) {
throw new ServiceException("优先级别类型【" + stockInOrderDO.getPriorityLevelCode() + "】数据字典不存在 请联系管理员");
}
stockInOrderDO.setPriorityLevelName(priorityLevelData.getDictLabel());
}
}
}
@@ -12,7 +12,7 @@ public class DeliveryHeader {
// 送货日期
private Date deliveryDate;
// 送货线号
private Integer deliveryLineNo;
private String deliveryLineNo;
// 车次
private Integer trainNumber;
// 纪录合共-货品总数量
@@ -0,0 +1,33 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.vo;
import lombok.Data;
import java.util.List;
/**
* 入库单数据DTO(封装表头信息和产品明细列表,用于Controller层传输)
*/
@Data
public class StockInDataDTO {
// 入库表头信息
private StockInHeader stockInHeader;
// 入库产品明细列表
private List<StockInProductDetail> productDetailList;
// Getter和Setter方法
public StockInHeader getStockInHeader() {
return stockInHeader;
}
public void setStockInHeader(StockInHeader stockInHeader) {
this.stockInHeader = stockInHeader;
}
public List<StockInProductDetail> getProductDetailList() {
return productDetailList;
}
public void setProductDetailList(List<StockInProductDetail> productDetailList) {
this.productDetailList = productDetailList;
}
}
@@ -0,0 +1,34 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.vo;
import lombok.Data;
import java.util.Date;
/**
* 入库单表头实体类对应Excel第一行数据
* 存储入库日期OrderNO
*/
@Data
public class StockInHeader {
// 入库日期Excel第一行第2列
private Date stockInDate;
// 订单编号Excel第一行第4列
private String orderNo;
// Getter和Setter方法
public Date getStockInDate() {
return stockInDate;
}
public void setStockInDate(Date stockInDate) {
this.stockInDate = stockInDate;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
}
@@ -0,0 +1,73 @@
package com.mhd.oms.domain.reservationStockInOrder.repository.vo;
import lombok.Data;
import java.util.Date;
/**
* 入库产品明细实体类对应Excel第二行开始的表格数据
*/
@Data
public class StockInProductDetail {
// 产品编号Excel第1列
private Integer productCode;
// 产品名称Excel第2列
private String productName;
// 箱含量Excel第5列
private Integer boxCapacity;
// 件数Excel第6列
private Integer pieceCount;
// 箱数Excel第7列Qty(cases)
private Integer boxCount;
// 饮用日期Excel第8列
private Date drinkDate;
// Getter和Setter方法
public Integer getProductCode() {
return productCode;
}
public void setProductCode(Integer productCode) {
this.productCode = productCode;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Integer getBoxCapacity() {
return boxCapacity;
}
public void setBoxCapacity(Integer boxCapacity) {
this.boxCapacity = boxCapacity;
}
public Integer getPieceCount() {
return pieceCount;
}
public void setPieceCount(Integer pieceCount) {
this.pieceCount = pieceCount;
}
public Integer getBoxCount() {
return boxCount;
}
public void setBoxCount(Integer boxCount) {
this.boxCount = boxCount;
}
public Date getDrinkDate() {
return drinkDate;
}
public void setDrinkDate(Date drinkDate) {
this.drinkDate = drinkDate;
}
}
@@ -457,5 +457,10 @@ public class ReservationStockOutOrder extends BaseVOEntity {
@ApiModelProperty("推送人姓名")
private String pushByName;
@ApiModelProperty("总金额")
@Excel(name = "总金额")
private BigDecimal totalAmount;
@ApiModelProperty("路线")
private String route;
}
@@ -3,6 +3,8 @@ package com.mhd.oms.domain.reservationStockOutOrder.repository.po;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO;
import com.mhd.oms.domain.reservationOutMaterialDetail.repository.po.ReservationOutMaterialDetailPO;
//import com.mhd.wms.domain.outMaterialDetail.repository.po.OutMaterialCheckPO;
//import com.mhd.wms.domain.outMaterialDetail.repository.po.OutMaterialDetailPO;
@@ -468,4 +470,10 @@ public class ReservationStockOutOrderPO extends BaseVOEntity {
@ApiModelProperty("推送人姓名")
private String pushByName;
@ApiModelProperty("总金额")
@Excel(name = "总金额")
private BigDecimal totalAmount;
@ApiModelProperty("路线")
private String route;
private List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinkList;
}
@@ -504,4 +504,10 @@ public class ReservationStockOutOrderDO extends BaseVOEntity {
@ApiModelProperty("推送人姓名")
private String pushByName;
@ApiModelProperty("总金额")
@Excel(name = "总金额")
private BigDecimal totalAmount;
@ApiModelProperty("路线")
private String route;
}
@@ -493,4 +493,9 @@ public class ReservationStockOutOrderDTO extends ReservationStockOutOrderBaseDTO
@ApiModelProperty("推送人姓名")
private String pushByName;
@ApiModelProperty("总金额")
@Excel(name = "总金额")
private BigDecimal totalAmount;
@ApiModelProperty("路线")
private String route;
}
@@ -18,6 +18,9 @@ import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.oms.domain.gwLog.entity.GwLog;
import com.mhd.oms.domain.gwLog.repository.mapper.GwLogMapper;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.entity.ReservationDeliveryDetailsLink;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.mapper.ReservationDeliveryDetailsLinkMapper;
import com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO;
import com.mhd.oms.domain.reservationInMaterialDetail.entity.ReservationInMaterialDetail;
import com.mhd.oms.domain.reservationInMaterialDetail.repository.facade.IReservationInMaterialDetailService;
import com.mhd.oms.domain.reservationInMaterialDetail.repository.mapper.ReservationInMaterialDetailMapper;
@@ -105,6 +108,8 @@ public class ReservationStockOutOrderApplicationService {
private ReservationStockOutOrderMapper reservationStockOutOrderMapper;
@Autowired
private ReservationOutMaterialDetailMapper reservationOutMaterialDetailMapper;
@Autowired
private ReservationDeliveryDetailsLinkMapper reservationDeliveryDetailsLinkMapper;
@Autowired
private ReservationStockInOrderMapper reservationStockInOrderMapper;
@@ -355,7 +360,9 @@ public class ReservationStockOutOrderApplicationService {
// if (stockOutOrderPO.getStatus() != null && (stockOutOrderPO.getStatus() == 9 || stockOutOrderPO.getStatus() == 12)) {
// filterMaterialDetailByPickingQuantity(detailList, stockOutOrderPO.getReview(), stockOutOrderPO.getOutOrderNumber());
// }
List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinks = reservationDeliveryDetailsLinkMapper.selectList(new LambdaQueryWrapper<ReservationDeliveryDetailsLink>()
.eq(ReservationDeliveryDetailsLink::getOrderNumber, stockOutOrderPO.getOutOrderNumber()));
stockOutOrderPO.setReservationDeliveryDetailsLinkList(reservationDeliveryDetailsLinks);
return stockOutOrderPO;
}
@@ -66,4 +66,8 @@ public class SysTableConfig extends BaseVOEntity {
@ApiModelProperty(value = "物料添加方式")
@Excel(name = "物料添加方式", width = 15)
private String materialAddType;
/**配送明细*/
@ApiModelProperty(value = "配送明细")
@Excel(name = "配送明细", width = 15)
private String deliveryDetail;
}
@@ -64,4 +64,8 @@ public class SysTableConfigPO extends BaseVOEntity {
@ApiModelProperty(value = "组织名称")
@Excel(name = "组织名称", width = 15)
private String organizationName;
/**配送明细*/
@ApiModelProperty(value = "配送明细")
@Excel(name = "配送明细", width = 15)
private String deliveryDetail;
}
@@ -57,6 +57,10 @@ public class SysTableConfigDTO extends BaseVOEntity {
@ApiModelProperty(value = "物料添加方式")
@Excel(name = "物料添加方式", width = 15)
private String materialAddType;
/**配送明细*/
@ApiModelProperty(value = "配送明细")
@Excel(name = "配送明细", width = 15)
private String deliveryDetail;
/**一级组织ID*/
@ApiModelProperty(value = "一级组织ID")
@Excel(name = "一级组织ID", width = 15)
@@ -65,4 +65,8 @@ public class SysTableConfigVO extends BaseVOEntity {
@ApiModelProperty(value = "组织名称")
@Excel(name = "组织名称", width = 15)
private String organizationName;
/**配送明细*/
@ApiModelProperty(value = "配送明细")
@Excel(name = "配送明细", width = 15)
private String deliveryDetail;
}
@@ -9,6 +9,7 @@ import com.mhd.oms.domain.reservationStockInOrder.repository.po.ReservationStock
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDO;
import com.mhd.oms.domain.reservationStockInOrder.repository.todo.ReservationStockInOrderDTO;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.DeliveryDataDTO;
import com.mhd.oms.domain.reservationStockInOrder.repository.vo.StockInDataDTO;
import com.mhd.oms.domain.reservationStockInOrder.service.ReservationStockInOrderApplicationService;
import com.mhd.oms.domain.reservationStockInOrder.service.ReservationStockInOrderAssembler;
import com.mhd.system.api.domain.InMaterialDetail;
@@ -197,12 +198,14 @@ public class ReservationStockInOrderApi extends BaseController {
* @return 解析后的出库数据
*/
@PostMapping("/parseExcel")
public ResponseEntity<DeliveryDataDTO> parseExcel(
@RequestParam("file") MultipartFile file,
@RequestParam("sheetName") String sheetName) {
public ResponseEntity<Map<String, Object>> parseExcel(
@RequestParam("file") MultipartFile file) {
try {
DeliveryDataDTO deliveryDataDTO = reservationStockInOrderService.parseExcelFile(file, sheetName);
return new ResponseEntity<>(deliveryDataDTO, HttpStatus.OK);
DeliveryDataDTO deliveryDataDTO = reservationStockInOrderService.parseExcelFile(file, "");
Map<String, Object> result = new HashMap<>();
result.put("result", deliveryDataDTO);
result.put("code",200);
return new ResponseEntity<>(result, HttpStatus.OK);
} catch (IllegalArgumentException e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} catch (Exception e) {
@@ -210,6 +213,31 @@ public class ReservationStockInOrderApi extends BaseController {
}
}
/**
* 接口1仅解析Excel文件返回解析结果不保存
* 用途预览Excel解析数据
*/
@PostMapping("/parse-excel")
public ResponseEntity<Map<String, Object>> parseStockInExcel(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "sheetName", required = false) String sheetName) {
try {
StockInDataDTO resultDTO = reservationStockInOrderService.parseStockInExcel(file, sheetName);
Map<String, Object> result = new HashMap<>();
result.put("result", resultDTO);
result.put("code",200);
return new ResponseEntity<>(result, HttpStatus.OK);
} catch (IllegalArgumentException e) {
// 参数错误如文件为空
// LOGGER.error("Excel解析参数错误:{}", e.getMessage());
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
} catch (Exception e) {
// 其他解析异常
// LOGGER.error("Excel解析失败:{}", e.getMessage(), e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@ApiOperation("批量推送")
@PostMapping("/batchPush")
@@ -0,0 +1,62 @@
<?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.reservationDeliveryDetailsLink.repository.mapper.ReservationDeliveryDetailsLinkMapper">
<resultMap type="com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.po.ReservationDeliveryDetailsLinkPO" id="ReservationDeliveryDetailsLinkResult">
<result property="id" column="id" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
<result property="delFlag" column="del_flag" />
<result property="orderId" column="order_id" />
<result property="category" column="category" />
<result property="invoiceNumber" column="invoice_number" />
<result property="customerNumber" column="customer_number" />
<result property="deliveryName" column="delivery_name" />
<result property="box" column="box" />
<result property="bag" column="bag" />
<result property="amount" column="amount" />
</resultMap>
<sql id="selectReservationDeliveryDetailsLinkPo">
select id, create_by, create_time, update_by, update_time, del_flag, order_id, category, invoice_number, customer_number, delivery_name, box, bag, amount from RESERVATION_DELIVERY_DETAILS_LINK
</sql>
<sql id="selectReservationDeliveryDetailsLinkPo1">
<where>
<if test="orderId != null ">
and order_id = #{orderId}
</if>
<if test="category != null and category != ''">
and category = #{category}
</if>
<if test="invoiceNumber != null and invoiceNumber != ''">
and invoice_number = #{invoiceNumber}
</if>
<if test="customerNumber != null and customerNumber != ''">
and customer_number = #{customerNumber}
</if>
<if test="deliveryName != null and deliveryName != ''">
and delivery_name like concat('%', #{deliveryName}, '%')
</if>
<if test="box != null ">
and box = #{box}
</if>
<if test="bag != null ">
and bag = #{bag}
</if>
<if test="amount != null ">
and amount = #{amount}
</if>
</where>
</sql>
<select id="queryList" parameterType="com.mhd.oms.domain.reservationDeliveryDetailsLink.repository.todo.ReservationDeliveryDetailsLinkDO" resultMap="ReservationDeliveryDetailsLinkResult">
<include refid="selectReservationDeliveryDetailsLinkPo"/>
<include refid="selectReservationDeliveryDetailsLinkPo1"/>
</select>
</mapper>
@@ -454,6 +454,10 @@
<select id="getinfo" parameterType="java.lang.Long" resultType="com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO">
select * from "NGWL_TEST_WMS".MATERIAL_BASE_INFO where MATERIAL_BASE_INFO_ID = #{id}
</select>
<select id="getByName" parameterType="java.lang.String" resultType="com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO">
select * from "NGWL_TEST_WMS".MATERIAL_BASE_INFO where MATERIAL_NAME = #{name}
</select>
<select id="getMaterialBaseInfoList" parameterType="java.lang.String" resultType="com.mhd.oms.domain.reservationStockInOrder.repository.po.MaterialBaseInfoPO">
select * from "NGWL_TEST_WMS".MATERIAL_BASE_INFO where material_code = #{id}
</select>
@@ -3,14 +3,17 @@ package com.mhd.wms.domain.inMaterialDetail.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
@@ -372,4 +375,10 @@ public class InMaterialDetail extends BaseVOEntity {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -12,6 +12,7 @@ import java.math.BigDecimal;
import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
/**
@@ -417,4 +418,9 @@ public class InMaterialDetailPO extends StockInOrderBasePO {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -1,5 +1,6 @@
package com.mhd.wms.domain.inMaterialDetail.repository.todo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
@@ -8,8 +9,10 @@ import com.mhd.wms.domain.materialMoreDetail.repository.todo.MaterialMoreDetailD
import com.mhd.wms.domain.stockInOrder.repository.todo.StockInOrderBaseDO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@@ -380,4 +383,10 @@ public class InMaterialDetailDO extends StockInOrderBaseDO {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -10,6 +10,7 @@ import com.mhd.common.core.annotation.Excel;
import com.mhd.common.core.web.domain.BaseVOEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
@@ -366,4 +367,10 @@ public class ReceiptMaterialDetail extends BaseVOEntity {
@Excel(name = "抄码数量克重详情")
private String copyDetails;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -14,6 +14,7 @@ import java.math.BigDecimal;
import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
/**
@@ -363,4 +364,10 @@ public class ReceiptMaterialDetailPO extends BaseVOEntity {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -13,6 +13,7 @@ import java.math.BigDecimal;
import java.util.List;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
/**
@@ -358,4 +359,9 @@ public class ReceiptMaterialDetailDO extends BaseVOEntity {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -570,6 +570,7 @@ public class StockInOrderDomainService {
planQty = inMaterialDetailPO.getInboundQuantity();
}
receiptMaterialDetailDO.setQuantity(planQty);
receiptMaterialDetailDO.setDrinkDate(inMaterialDetailPO.getDrinkDate());
receiptMaterialDetailDOList.add(receiptMaterialDetailDO);
});
stockReceiptOrderDO.setMaterialDetailList(receiptMaterialDetailDOList);
@@ -1,5 +1,6 @@
package com.mhd.wms.interfaces.dto.inMaterialDetail;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.mhd.common.core.annotation.Excel;
@@ -8,8 +9,10 @@ import com.mhd.wms.interfaces.dto.inMaterialDetailSerialNumber.InMaterialDetailS
import com.mhd.wms.interfaces.dto.materialMoreDetail.MaterialMoreDetailDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -380,4 +383,8 @@ public class InMaterialDetailDTO extends BaseVOEntity {
@Excel(name = "lot编号")
private String lotNo;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}
@@ -1,5 +1,6 @@
package com.mhd.wms.interfaces.dto.receiptMaterialDetail;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
@@ -9,8 +10,10 @@ import com.mhd.wms.domain.inMaterialDetailSerialNumber.repository.todo.InMateria
import com.mhd.wms.interfaces.dto.materialMoreDetail.MaterialMoreDetailDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
@@ -307,4 +310,10 @@ public class ReceiptMaterialDetailDTO extends BaseVOEntity {
@ApiModelProperty("oms入库单详情id")
@Excel(name = "oms入库单详情id")
private String omsInMaterialDetail;
@ApiModelProperty(value = "饮用时间")
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
private Date drinkDate;
}