package com.hvlink.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hvlink.common.Result;
import com.hvlink.entity.dto.order.CompositeKeyDTO;
import com.hvlink.entity.dto.order.PurchaseOrderDetailsDTO;
import com.hvlink.entity.dto.order.PurchaseOrderMainDTO;
import com.hvlink.entity.param.order.PurchaseOrderParam;
import com.hvlink.entity.po.master.CompanyPO;
import com.hvlink.entity.po.master.FactoryPO;
import com.hvlink.entity.po.master.PartPO;
import com.hvlink.entity.po.order.TbOrderMainPO;
import com.hvlink.entity.vo.order.PurchaseOrderDetailsVO;
import com.hvlink.entity.vo.order.PurchaseOrderExportVO;
import com.hvlink.entity.vo.order.PurchaseOrderMainVO;
import com.hvlink.enums.PurchaseOrderPlanTypeEnum;
import com.hvlink.enums.PurchaseOrderTypeEnum;
import com.hvlink.mapper.master.CompanyMapper;
import com.hvlink.mapper.master.FactoryMapper;
import com.hvlink.mapper.master.PartMapper;
import com.hvlink.mapper.order.TbOrderDetailMapper;
import com.hvlink.mapper.order.TbOrderMainMapper;
import com.hvlink.pagination.PageResult;
import com.hvlink.service.IPurchaseOrderService;
import com.hvlink.utils.BeanCopyUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Slf4j
public class PurchaseOrderServiceImpl extends ServiceImpl<TbOrderMainMapper, TbOrderMainPO> implements IPurchaseOrderService {
private final TbOrderMainMapper orderMainMapper;
private final TbOrderDetailMapper orderDetailMapper;
private final PartMapper partMapper;
private final CompanyMapper companyMapper;
private final FactoryMapper factoryMapper;
@Override
public PageResult<PurchaseOrderMainVO> queryPage(PurchaseOrderParam purchaseOrderParam) {
// 查询主表数据
Page<PurchaseOrderMainDTO> page = new Page<>(purchaseOrderParam.getPageIndex(), purchaseOrderParam.getPageSize());
Page<PurchaseOrderMainDTO> mainPage = orderMainMapper.queryPurchaseOrderPage(page, purchaseOrderParam);
if (CollectionUtils.isEmpty(mainPage.getRecords())) {
return null;
}
// 构建复合键列表用于查询明细
List<CompositeKeyDTO> compositeKeys = mainPage.getRecords().stream()
.map(main -> new CompositeKeyDTO(
main.getPurchaseOrderNo(),
main.getCompanyCode(),
main.getSerialVersionNum()
))
.collect(Collectors.toList());
// 查询明细数据
List<PurchaseOrderDetailsDTO> detailList = orderDetailMapper.queryDetailsByCompositeKeys(compositeKeys);
// 按复合键分组明细数据
Map<String, List<PurchaseOrderDetailsDTO>> detailMap = detailList.stream()
.collect(Collectors.groupingBy(detail ->
detail.getPurchaseOrderNo() + "#" +
detail.getCompanyCode() + "#" +
detail.getSerialVersionNum()
));
// 转换主表数据到VO
List<PurchaseOrderMainVO> mainVOList = mainPage.getRecords().stream().map(mainDTO -> {
PurchaseOrderMainVO mainVO = BeanCopyUtils.copyBean(mainDTO, PurchaseOrderMainVO.class);
mainVO.setSpliceSupplierName(mainDTO.getSupplierCode() + " - " + mainDTO.getSupplierName());
mainVO.setSpliceFactoryName(mainDTO.getFactoryCode() + " - " + mainDTO.getFactoryName());
mainVO.setSpliceWarehouseName(mainDTO.getWarehouseCode() + " - " + mainDTO.getWarehouseName());
// 设置订单类型描述
if (mainDTO.getOrderType() != null) {
PurchaseOrderTypeEnum typeEnum = Arrays.stream(PurchaseOrderTypeEnum.values())
.filter(e -> e.getTypeCode().equals(mainDTO.getOrderType()))
.findFirst()
.orElse(null);
if (typeEnum != null) {
mainVO.setType(typeEnum.getTypeCode());
}
}
// 设置明细数据 - 使用复合键
String compositeKey = mainDTO.getPurchaseOrderNo() + "#" +
mainDTO.getCompanyCode() + "#" +
mainDTO.getSerialVersionNum();
List<PurchaseOrderDetailsVO> detailVOList = new ArrayList<>();
if (detailMap.containsKey(compositeKey)) {
detailVOList = detailMap.get(compositeKey).stream().map(detail -> {
PurchaseOrderDetailsVO detailVO = BeanCopyUtils.copyBean(detail, PurchaseOrderDetailsVO.class);
// 设置类型名称
if (detail.getType() != null) {
PurchaseOrderPlanTypeEnum planTypeEnum = Arrays.stream(PurchaseOrderPlanTypeEnum.values())
.filter(e -> e.getTypeCode().equals(detail.getType()))
.findFirst()
.orElse(null);
if (planTypeEnum != null) {
detailVO.setTypeName(planTypeEnum.getTypeDesc());
}
}
return detailVO;
}).collect(Collectors.toList());
}
mainVO.setDetailsList(detailVOList);
return mainVO;
}).collect(Collectors.toList());
// 构建分页结果
PageResult<PurchaseOrderMainVO> result = new PageResult<>();
result.setRecords(mainVOList);
result.setTotal(mainPage.getTotal());
result.setPageIndex(purchaseOrderParam.getPageIndex());
result.setPageSize(purchaseOrderParam.getPageSize());
return result;
}
@Override
public void exportPurchaseOrderData(PurchaseOrderParam exportQuery, HttpServletResponse response) {
// 获取所有零件代码
Map<String, PartPO> partMap = partMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(PartPO::getPartCode, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(PartPO::getCreateTime))));
// 获取所有公司
Map<String, CompanyPO> companyMap = companyMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(CompanyPO::getCompanyCode, Function.identity(), BinaryOperator.maxBy(Comparator.comparing(CompanyPO::getCreateTime))));
// 获取所有工厂
Map<String, FactoryPO> factoryMap = factoryMapper.selectList(Wrappers.emptyWrapper()).stream().collect(Collectors.toMap(FactoryPO::getFactoryCode, Function.identity(),BinaryOperator.maxBy(Comparator.comparing(FactoryPO::getCreateTime))));
try {
// 查询所有符合条件的数据(不分页)
Page<PurchaseOrderMainDTO> page = new Page<>(1, 9999999, true);
Page<PurchaseOrderMainDTO> mainPage = orderMainMapper.queryPurchaseOrderPage(page, exportQuery);
if (CollectionUtils.isEmpty(mainPage.getRecords())) {
log.warn("没有找到符合条件的采购订单数据");
return;
}
// 构建复合键列表用于查询明细
List<CompositeKeyDTO> compositeKeys = mainPage.getRecords().stream()
.map(main -> new CompositeKeyDTO(
main.getPurchaseOrderNo(),
main.getCompanyCode(),
main.getSerialVersionNum()
))
.collect(Collectors.toList());
// 查询明细数据
List<PurchaseOrderDetailsDTO> detailList = orderDetailMapper.queryDetailsByCompositeKeys(compositeKeys);
// 按复合键分组明细数据
Map<String, List<PurchaseOrderDetailsDTO>> detailMap = detailList.stream()
.collect(Collectors.groupingBy(detail ->
detail.getPurchaseOrderNo() + "#" +
detail.getCompanyCode() + "#" +
detail.getSerialVersionNum()
));
// 准备导出数据
List<PurchaseOrderExportVO> exportList = new ArrayList<>();
// 用于记录合并信息:key为采购订单号+版本号,value为明细行数
Map<String, Integer> mergeInfoMap = new HashMap<>();
for (PurchaseOrderMainDTO mainDTO : mainPage.getRecords()) {
String compositeKey = mainDTO.getPurchaseOrderNo() + "#" +
mainDTO.getCompanyCode() + "#" +
mainDTO.getSerialVersionNum();
List<PurchaseOrderDetailsDTO> details = detailMap.getOrDefault(compositeKey, new ArrayList<>());
// 记录合并信息
String mergeKey = mainDTO.getPurchaseOrderNo() + "#" + mainDTO.getSerialVersionNum();
mergeInfoMap.put(mergeKey, details.size());
// 设置类型描述
String orderTypeDesc = "";
if (mainDTO.getOrderType() != null) {
PurchaseOrderTypeEnum typeEnum = Arrays.stream(PurchaseOrderTypeEnum.values())
.filter(e -> e.getTypeCode().equals(mainDTO.getOrderType()))
.findFirst()
.orElse(null);
if (typeEnum != null) {
orderTypeDesc = typeEnum.getTypeDesc();
}
}
for (PurchaseOrderDetailsDTO detail : details) {
PurchaseOrderExportVO exportVO = new PurchaseOrderExportVO();
// 主表信息
exportVO.setPublishDate(mainDTO.getPublishDate());
exportVO.setPurchaseOrderNo(mainDTO.getPurchaseOrderNo());
exportVO.setSupplier(mainDTO.getSupplierCode() + " - " + mainDTO.getSupplierName());
exportVO.setFactory(mainDTO.getFactoryCode() + " - " + mainDTO.getFactoryName());
exportVO.setWarehouse(mainDTO.getWarehouseCode() + " - " + mainDTO.getWarehouseName());
exportVO.setSerialVersionNum(mainDTO.getSerialVersionNum());
// 明细信息
exportVO.setLineNo(detail.getLineNo());
exportVO.setDeliveryDate(detail.getDeliveryDate());
exportVO.setPartCode(detail.getPartCode());
exportVO.setPartDesc(detail.getPartDesc());
exportVO.setSupplierPartCode(detail.getSupplierPartCode());
exportVO.setUnit(detail.getUnit());
exportVO.setTotalQty(detail.getTotalQty());
exportVO.setRequiredQty(detail.getRequiredQty());
exportVO.setAvailableQty(detail.getAvailableQty());
exportVO.setShippedQty(detail.getShippedQty());
exportVO.setType(detail.getType());
exportVO.setFrequency(detail.getFrequency());
// 设置类型名称
if (detail.getType() != null) {
PurchaseOrderPlanTypeEnum planTypeEnum = Arrays.stream(PurchaseOrderPlanTypeEnum.values())
.filter(e -> e.getTypeCode().equals(detail.getType()))
.findFirst()
.orElse(null);
if (planTypeEnum != null) {
exportVO.setType(planTypeEnum.getTypeDesc());
}
}
exportList.add(exportVO);
}
}
// 设置响应头
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String fileName = "采购订单数据_" + sdf.format(new Date()) + ".xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 创建单元格样式
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
// 创建合并策略
HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 自定义合并策略 - 合并相同订单号+版本号的主表信息
LoopMergeStrategy mergeStrategy = new LoopMergeStrategy(1, 0) {
private final Map<String, Integer> mergeMap = mergeInfoMap;
private int currentIndex = 0;
protected boolean needMerge(int rowIndex, int columnIndex) {
if (columnIndex < 6) { // 只合并前6列(主表信息列)
// 这里需要根据实际数据计算合并范围
// 简化实现:根据mergeInfoMap来确定合并范围
return true;
}
return false;
}
protected int getMergeRange(int rowIndex, int columnIndex) {
// 实际实现需要根据数据来计算合并行数
// 这里返回简化实现
return 1;
}
};
// 导出Excel
EasyExcel.write(response.getOutputStream())
.registerWriteHandler(styleStrategy)
.registerWriteHandler(mergeStrategy)
.head(PurchaseOrderExportVO.class)
.sheet("采购订单数据")
.doWrite(exportList);
} catch (Exception e) {
String errorMessage = "导出采购订单数据出现异常,具体信息是:" + e.getMessage();
log.error(errorMessage, e);
throw new RuntimeException(errorMessage);
}
}
}
<?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.hvlink.mapper.order.TbOrderMainMapper">
<!-- 其他 insert/update 不变 -->
<insert id="batchInsert">
INSERT INTO tb_order_main(
company_code, purchase_order_no, publish_date, supplier_code,
factory_code, warehouse_code, create_time, serial_version_num, order_type
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.companyCode}, #{item.purchaseOrderNo}, #{item.publishDate},
#{item.supplierCode}, #{item.factoryCode}, #{item.warehouseCode},
GETDATE(), #{item.serialVersionNum}, #{item.orderType}
)
</foreach>
</insert>
<update id="batchUpdate">
UPDATE tb_order_main SET
update_time = GETDATE(),
publish_date =
<foreach collection="list" item="item" index="index" separator=" " open="CASE ID" close="END">
WHEN #{item.id} THEN #{item.publishDate}
</foreach>,
serial_version_num =
<foreach collection="list" item="item" index="index" separator=" " open="CASE ID" close="END">
WHEN #{item.id} THEN #{item.serialVersionNum}
</foreach>
WHERE id IN
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{item.id}
</foreach>
</update>
<!-- 主查询:采购订单分页,去除 purchaseOrderNo 重复数据,只保留最新版本 -->
<select id="queryPurchaseOrderPage" resultType="com.hvlink.entity.dto.order.PurchaseOrderMainDTO">
SELECT
m.id,
m.company_code,
m.purchase_order_no,
m.publish_date,
s.supplier_code,
s.supplier_name,
f.factory_code,
f.factory_name,
w.warehouse_code,
w.warehouse_name,
m.serial_version_num,
m.order_type
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY purchase_order_no, company_code ORDER BY serial_version_num DESC) as rn
FROM tb_order_main
WHERE is_deleted = 0
) m
LEFT JOIN tm_supplier s ON m.supplier_code = s.supplier_code AND m.company_code = s.company_code
LEFT JOIN tm_factory f ON m.factory_code = f.factory_code AND m.company_code = f.company_code
LEFT JOIN tm_warehouse w ON m.warehouse_code = w.warehouse_code AND m.company_code = w.company_code
<where>
m.rn = 1 <!-- 只取每个订单的最新版本 -->
<!-- 优化后的模糊匹配组 -->
<if test="param.supplierName != null and param.supplierName != ''
or param.supplierCode != null and param.supplierCode != ''
or param.factoryName != null and param.factoryName != ''
or param.factoryCode != null and param.factoryCode != ''
or param.warehouseName != null and param.warehouseName != ''
or param.warehouseCode != null and param.warehouseCode != ''">
AND (
<trim prefixOverrides="OR">
<!-- 供应商条件 -->
<if test="param.supplierName != null and param.supplierName != ''">
OR s.supplier_name LIKE '%' + #{param.supplierName} + '%'
</if>
<if test="param.supplierCode != null and param.supplierCode != ''">
OR s.supplier_code LIKE '%' + #{param.supplierCode} + '%'
</if>
<!-- 工厂条件 -->
<if test="param.factoryName != null and param.factoryName != ''">
OR f.factory_name LIKE '%' + #{param.factoryName} + '%'
</if>
<if test="param.factoryCode != null and param.factoryCode != ''">
OR f.factory_code LIKE '%' + #{param.factoryCode} + '%'
</if>
<!-- 仓库条件 -->
<if test="param.warehouseName != null and param.warehouseName != ''">
OR w.warehouse_name LIKE '%' + #{param.warehouseName} + '%'
</if>
<if test="param.warehouseCode != null and param.warehouseCode != ''">
OR w.warehouse_code LIKE '%' + #{param.warehouseCode} + '%'
</if>
</trim>
)
</if>
<!-- 订单号模糊查询 -->
<if test="param.purchaseOrderNo != null and param.purchaseOrderNo != ''">
AND m.purchase_order_no LIKE '%' + #{param.purchaseOrderNo} + '%'
</if>
<if test="param.startDate != null">
AND m.publish_date >= #{param.startDate}
</if>
<if test="param.endDate != null">
AND m.publish_date <= #{param.endDate}
</if>
<!-- 订单类型 -->
<if test="param.orderType != null">
AND m.order_type = #{param.orderType}
</if>
<!-- 明细字段关联查询 - 使用EXISTS优化性能 -->
<if test="param.partCode != null and param.partCode != ''">
AND EXISTS (
SELECT 1 FROM tb_order_detail d
WHERE d.purchase_order_no = m.purchase_order_no
AND d.company_code = m.company_code
AND d.serial_version_num = m.serial_version_num
AND d.part_code LIKE '%' + #{param.partCode} + '%'
)
</if>
<if test="param.partDesc != null and param.partDesc != ''">
AND EXISTS (
SELECT 1 FROM tb_order_detail d
WHERE d.purchase_order_no = m.purchase_order_no
AND d.company_code = m.company_code
AND d.serial_version_num = m.serial_version_num
AND d.part_desc LIKE '%' + #{param.partDesc} + '%'
)
</if>
<if test="param.frequency != null and param.frequency != ''">
AND EXISTS (
SELECT 1 FROM tb_order_detail d
WHERE d.purchase_order_no = m.purchase_order_no
AND d.company_code = m.company_code
AND d.serial_version_num = m.serial_version_num
AND d.frequency = #{param.frequency}
)
</if>
</where>
ORDER BY m.create_time DESC
</select>
</mapper>
我现在需要根据前端选择哪些数据就导出哪些数据,比如前端展示的有10行数据,我选中了3,6,8行数据,那么再点击导出,就只导出选中的这三条,请帮我修改代码,参数里已经添加了private List<String> selectedOrderIds;这个ID集合
最新发布