feat(aps): 新增排产单明细合并功能及优化界面展示
- 在排产单明细表格中添加多选合并功能 - 实现排产单明细合并对话框及合并逻辑 - 优化排产单明细表格列配置和表单布局 - 添加合并校验和接收产需单API接口 - 重构订单绑定解绑逻辑提升用户体验 - 添加ScheduleDetailCoilBind组件引入
This commit is contained in:
@@ -18,8 +18,11 @@ import com.klp.common.core.validate.EditGroup;
|
||||
import com.klp.common.enums.BusinessType;
|
||||
import com.klp.common.utils.poi.ExcelUtil;
|
||||
import com.klp.flow.domain.vo.SchProdScheduleItemVo;
|
||||
import com.klp.flow.domain.vo.SchProdScheduleItemMergeValidateVo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemMergeBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemReceiveBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemMergeValidateBo;
|
||||
import com.klp.flow.service.ISchProdScheduleItemService;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
|
||||
@@ -117,4 +120,22 @@ public class SchProdScheduleItemController extends BaseController {
|
||||
public R<Void> merge(@Validated @RequestBody SchProdScheduleItemMergeBo mergeBo) {
|
||||
return toAjax(iSchProdScheduleItemService.mergeByBo(mergeBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收产需单:从 sch_prod_schedule + sch_prod_schedule_detail 全字段复制到 sch_prod_schedule_item
|
||||
*/
|
||||
@Log(title = "排产单主加明细可合并", businessType = BusinessType.INSERT)
|
||||
@RepeatSubmit()
|
||||
@PostMapping("/receive")
|
||||
public R<Void> receive(@Validated @RequestBody SchProdScheduleItemReceiveBo receiveBo) {
|
||||
return toAjax(iSchProdScheduleItemService.receiveByBo(receiveBo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并校验:检查待合并记录来源产需单的 header 字段是否一致
|
||||
*/
|
||||
@PostMapping("/mergeValidate")
|
||||
public R<SchProdScheduleItemMergeValidateVo> mergeValidate(@Validated @RequestBody SchProdScheduleItemMergeValidateBo validateBo) {
|
||||
return R.ok(iSchProdScheduleItemService.validateMerge(validateBo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,13 +128,9 @@ public class SchProdScheduleItem extends BaseEntity {
|
||||
*/
|
||||
private String returnReason;
|
||||
/**
|
||||
* 排产单主表ID
|
||||
* 排产明细关联主表ID(逗号分隔,未合并存单值,合并后存多个)
|
||||
*/
|
||||
private String scheduleIds;
|
||||
/**
|
||||
* 来源销售订单明细ID(溯源原始订单规格)
|
||||
*/
|
||||
private String orderDetailIds;
|
||||
private String scheduleDetailIds;
|
||||
/**
|
||||
* 规格 例:1.0X1250
|
||||
*/
|
||||
|
||||
@@ -154,14 +154,9 @@ public class SchProdScheduleItemBo extends BaseEntity {
|
||||
private String returnReason;
|
||||
|
||||
/**
|
||||
* 排产单主表ID
|
||||
* 排产明细关联主表ID(逗号分隔,未合并存单值,合并后存多个)
|
||||
*/
|
||||
private String scheduleIds;
|
||||
|
||||
/**
|
||||
* 来源销售订单明细ID(溯源原始订单规格)
|
||||
*/
|
||||
private String orderDetailIds;
|
||||
private String scheduleDetailIds;
|
||||
|
||||
/**
|
||||
* 规格 例:1.0X1250
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.klp.flow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 排产单合并校验请求对象
|
||||
*
|
||||
* @author klp
|
||||
* @date 2026-06-29
|
||||
*/
|
||||
@Data
|
||||
public class SchProdScheduleItemMergeValidateBo {
|
||||
|
||||
/**
|
||||
* 待合并记录ID列表
|
||||
*/
|
||||
@NotEmpty(message = "待合并记录ID不能为空")
|
||||
private List<Long> ids;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.klp.flow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 产需单接收请求对象
|
||||
*
|
||||
* @author klp
|
||||
* @date 2026-06-29
|
||||
*/
|
||||
@Data
|
||||
public class SchProdScheduleItemReceiveBo {
|
||||
|
||||
/**
|
||||
* 排产单主表ID(产需单ID)
|
||||
*/
|
||||
@NotNull(message = "排产单主表ID不能为空")
|
||||
private Long scheduleId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.klp.flow.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 排产单合并校验视图对象
|
||||
*
|
||||
* @author klp
|
||||
* @date 2026-06-29
|
||||
*/
|
||||
@Data
|
||||
public class SchProdScheduleItemMergeValidateVo {
|
||||
|
||||
/** 是否所有 header 字段一致 */
|
||||
private Boolean consistent;
|
||||
|
||||
/** 有差异的字段列表 */
|
||||
private List<FieldDiff> diffs;
|
||||
|
||||
/** 被合并记录快照 */
|
||||
private List<ItemInfo> items;
|
||||
|
||||
/** 汇总信息 */
|
||||
private MergedSummary summary;
|
||||
|
||||
/**
|
||||
* 字段差异
|
||||
*/
|
||||
@Data
|
||||
public static class FieldDiff {
|
||||
/** 字段名(Java属性名) */
|
||||
private String fieldName;
|
||||
/** 字段中文标签 */
|
||||
private String fieldLabel;
|
||||
/** scheduleId → 值 */
|
||||
private Map<String, Object> values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 被合并记录快照
|
||||
*/
|
||||
@Data
|
||||
public static class ItemInfo {
|
||||
/** sch_prod_schedule_item 主键 */
|
||||
private Long itemId;
|
||||
/** 第一个源 schedule_id(从 scheduleIds 解析) */
|
||||
private Long sourceScheduleId;
|
||||
/** 排产单号 */
|
||||
private String scheduleNo;
|
||||
/** 订货单位 */
|
||||
private String customerName;
|
||||
/** 品名 */
|
||||
private String productType;
|
||||
/** 规格 */
|
||||
private String spec;
|
||||
/** 材质 */
|
||||
private String material;
|
||||
/** 排产吨数 */
|
||||
private BigDecimal scheduleWeight;
|
||||
/** 来源排产明细ID串 */
|
||||
private String scheduleDetailIds;
|
||||
/** 业务员 */
|
||||
private String businessUser;
|
||||
/** 联系电话 */
|
||||
private String businessPhone;
|
||||
/** 交货期(天) */
|
||||
private Long deliveryCycle;
|
||||
/** 产品用途 */
|
||||
private String usePurpose;
|
||||
/** 厚度公差 */
|
||||
private String thicknessTolerance;
|
||||
/** 宽度公差 */
|
||||
private String widthTolerance;
|
||||
/** 表面质量 */
|
||||
private String surfaceQuality;
|
||||
/** 表面处理 */
|
||||
private String surfaceTreatment;
|
||||
/** 内径尺寸 */
|
||||
private String innerDiameter;
|
||||
/** 外径要求 */
|
||||
private String outerDiameter;
|
||||
/** 包装要求 */
|
||||
private String packReq;
|
||||
/** 切边要求 */
|
||||
private String cutEdgeReq;
|
||||
/** 单件重量 */
|
||||
private String singleCoilWeight;
|
||||
/** 交货重量偏差 */
|
||||
private String weightDeviation;
|
||||
/** 其他技术要求 */
|
||||
private String otherTechReq;
|
||||
/** 付款情况说明 */
|
||||
private String paymentDesc;
|
||||
/** 关联销售合同号 */
|
||||
private String relContractNo;
|
||||
/** 排产总计划吨数 */
|
||||
private BigDecimal totalPlanWeight;
|
||||
/** 订单日期 */
|
||||
private java.util.Date orderDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并汇总
|
||||
*/
|
||||
@Data
|
||||
public static class MergedSummary {
|
||||
/** 明细条数 */
|
||||
private int itemCount;
|
||||
/** 合计排产吨数 */
|
||||
private BigDecimal totalScheduleWeight;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.klp.common.annotation.ExcelDictFormat;
|
||||
import com.klp.common.convert.ExcelDictConvert;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
|
||||
/**
|
||||
@@ -40,6 +41,8 @@ public class SchProdScheduleItemVo {
|
||||
*/
|
||||
@ExcelProperty(value = "生产日期", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "和=合同号组成业务关联键")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private Date prodDate;
|
||||
|
||||
/**
|
||||
@@ -181,17 +184,10 @@ public class SchProdScheduleItemVo {
|
||||
private String returnReason;
|
||||
|
||||
/**
|
||||
* 排产单主表ID
|
||||
* 排产明细关联主表ID(逗号分隔)
|
||||
*/
|
||||
@ExcelProperty(value = "排产单主表ID")
|
||||
private String scheduleIds;
|
||||
|
||||
/**
|
||||
* 来源销售订单明细ID(溯源原始订单规格)
|
||||
*/
|
||||
@ExcelProperty(value = "来源销售订单明细ID", converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(readConverterExp = "溯=源原始订单规格")
|
||||
private String orderDetailIds;
|
||||
@ExcelProperty(value = "排产明细关联主表ID")
|
||||
private String scheduleDetailIds;
|
||||
|
||||
/**
|
||||
* 规格 例:1.0X1250
|
||||
|
||||
@@ -2,8 +2,11 @@ package com.klp.flow.service;
|
||||
|
||||
import com.klp.flow.domain.SchProdScheduleItem;
|
||||
import com.klp.flow.domain.vo.SchProdScheduleItemVo;
|
||||
import com.klp.flow.domain.vo.SchProdScheduleItemMergeValidateVo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemMergeBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemReceiveBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemMergeValidateBo;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
|
||||
@@ -57,4 +60,14 @@ public interface ISchProdScheduleItemService {
|
||||
* 合并多条排产单主加明细可合并记录:先删除被合并的记录,再插入合并后的新记录
|
||||
*/
|
||||
Boolean mergeByBo(SchProdScheduleItemMergeBo mergeBo);
|
||||
|
||||
/**
|
||||
* 接收产需单:从 sch_prod_schedule + sch_prod_schedule_detail 全字段复制到 sch_prod_schedule_item
|
||||
*/
|
||||
Boolean receiveByBo(SchProdScheduleItemReceiveBo receiveBo);
|
||||
|
||||
/**
|
||||
* 合并校验:检查待合并记录来源产需单的 header 字段是否一致
|
||||
*/
|
||||
SchProdScheduleItemMergeValidateVo validateMerge(SchProdScheduleItemMergeValidateBo validateBo);
|
||||
}
|
||||
|
||||
@@ -11,16 +11,27 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemMergeBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemReceiveBo;
|
||||
import com.klp.flow.domain.bo.SchProdScheduleItemMergeValidateBo;
|
||||
import com.klp.flow.domain.vo.SchProdScheduleItemVo;
|
||||
import com.klp.flow.domain.vo.SchProdScheduleItemMergeValidateVo;
|
||||
import com.klp.flow.domain.SchProdScheduleItem;
|
||||
import com.klp.flow.domain.SchProdSchedule;
|
||||
import com.klp.flow.domain.SchProdScheduleDetail;
|
||||
import com.klp.flow.mapper.SchProdScheduleItemMapper;
|
||||
import com.klp.flow.mapper.SchProdScheduleMapper;
|
||||
import com.klp.flow.mapper.SchProdScheduleDetailMapper;
|
||||
import com.klp.flow.service.ISchProdScheduleItemService;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 排产单主加明细可合并Service业务层处理
|
||||
@@ -33,6 +44,8 @@ import java.util.Collection;
|
||||
public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemService {
|
||||
|
||||
private final SchProdScheduleItemMapper baseMapper;
|
||||
private final SchProdScheduleMapper schProdScheduleMapper;
|
||||
private final SchProdScheduleDetailMapper schProdScheduleDetailMapper;
|
||||
|
||||
/**
|
||||
* 查询排产单主加明细可合并
|
||||
@@ -89,8 +102,7 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOtherTechReq()), SchProdScheduleItem::getOtherTechReq, bo.getOtherTechReq());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getPaymentDesc()), SchProdScheduleItem::getPaymentDesc, bo.getPaymentDesc());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getReturnReason()), SchProdScheduleItem::getReturnReason, bo.getReturnReason());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getScheduleIds()), SchProdScheduleItem::getScheduleIds, bo.getScheduleIds());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOrderDetailIds()), SchProdScheduleItem::getOrderDetailIds, bo.getOrderDetailIds());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getScheduleDetailIds()), SchProdScheduleItem::getScheduleDetailIds, bo.getScheduleDetailIds());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getSpec()), SchProdScheduleItem::getSpec, bo.getSpec());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getMaterial()), SchProdScheduleItem::getMaterial, bo.getMaterial());
|
||||
lqw.eq(bo.getScheduleWeight() != null, SchProdScheduleItem::getScheduleWeight, bo.getScheduleWeight());
|
||||
@@ -179,4 +191,287 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收产需单:从 sch_prod_schedule + sch_prod_schedule_detail 全字段复制到 sch_prod_schedule_item
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean receiveByBo(SchProdScheduleItemReceiveBo receiveBo) {
|
||||
Long scheduleId = receiveBo.getScheduleId();
|
||||
|
||||
// 1. 查询产需单主表
|
||||
SchProdSchedule header = schProdScheduleMapper.selectById(scheduleId);
|
||||
if (header == null) {
|
||||
throw new RuntimeException("产需单不存在,scheduleId=" + scheduleId);
|
||||
}
|
||||
|
||||
// 2. 查询产需单明细列表
|
||||
List<SchProdScheduleDetail> details = schProdScheduleDetailMapper.selectList(
|
||||
Wrappers.<SchProdScheduleDetail>lambdaQuery()
|
||||
.eq(SchProdScheduleDetail::getScheduleId, scheduleId)
|
||||
);
|
||||
if (details == null || details.isEmpty()) {
|
||||
throw new RuntimeException("产需单无明细数据,scheduleId=" + scheduleId);
|
||||
}
|
||||
|
||||
// 3. 遍历每条 detail,构建 SchProdScheduleItem 列表
|
||||
List<SchProdScheduleItem> addList = new ArrayList<>(details.size());
|
||||
for (SchProdScheduleDetail detail : details) {
|
||||
SchProdScheduleItem item = new SchProdScheduleItem();
|
||||
|
||||
// 从 header 复制所有字段
|
||||
item.setScheduleNo(header.getScheduleNo());
|
||||
item.setProdDate(header.getProdDate());
|
||||
item.setScheduleStatus(2L); // 已下达
|
||||
item.setTotalPlanWeight(header.getTotalPlanWeight());
|
||||
item.setRelContractNo(header.getRelContractNo());
|
||||
item.setBusinessUser(header.getBusinessUser());
|
||||
item.setBusinessPhone(header.getBusinessPhone());
|
||||
item.setOrderDate(header.getOrderDate());
|
||||
item.setCustomerName(header.getCustomerName());
|
||||
item.setDeliveryCycle(header.getDeliveryCycle());
|
||||
item.setUsePurpose(header.getUsePurpose());
|
||||
item.setProductType(header.getProductType());
|
||||
item.setThicknessTolerance(header.getThicknessTolerance());
|
||||
item.setWidthTolerance(header.getWidthTolerance());
|
||||
item.setSurfaceQuality(header.getSurfaceQuality());
|
||||
item.setSurfaceTreatment(header.getSurfaceTreatment());
|
||||
item.setInnerDiameter(header.getInnerDiameter());
|
||||
item.setOuterDiameter(header.getOuterDiameter());
|
||||
item.setPackReq(header.getPackReq());
|
||||
item.setCutEdgeReq(header.getCutEdgeReq());
|
||||
item.setSingleCoilWeight(header.getSingleCoilWeight());
|
||||
item.setWeightDeviation(header.getWeightDeviation());
|
||||
item.setOtherTechReq(header.getOtherTechReq());
|
||||
item.setPaymentDesc(header.getPaymentDesc());
|
||||
item.setRemark(header.getRemark());
|
||||
// 不复制 returnReason
|
||||
|
||||
// 从 detail 复制
|
||||
item.setSpec(detail.getSpec());
|
||||
item.setMaterial(detail.getMaterial());
|
||||
item.setScheduleWeight(detail.getScheduleWeight());
|
||||
item.setProductItem(detail.getProductType());
|
||||
item.setRowRemark(detail.getRemark());
|
||||
|
||||
// 来源追溯(未合并,各存明细ID)
|
||||
item.setScheduleDetailIds(String.valueOf(detail.getScheduleDetailId()));
|
||||
|
||||
validEntityBeforeSave(item);
|
||||
addList.add(item);
|
||||
}
|
||||
|
||||
// 4. 批量插入
|
||||
baseMapper.insertBatch(addList);
|
||||
|
||||
// 5. 更新产需单状态为 2(已下达)
|
||||
header.setScheduleStatus(2L);
|
||||
schProdScheduleMapper.updateById(header);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并校验:检查待合并记录来源产需单的 header 字段是否一致
|
||||
*/
|
||||
@Override
|
||||
public SchProdScheduleItemMergeValidateVo validateMerge(SchProdScheduleItemMergeValidateBo validateBo) {
|
||||
List<Long> ids = validateBo.getIds();
|
||||
|
||||
// 1. 查询所有待合并的 sch_prod_schedule_item 记录
|
||||
List<SchProdScheduleItem> items = baseMapper.selectBatchIds(ids);
|
||||
if (items == null || items.size() < 2) {
|
||||
throw new RuntimeException("待合并记录不足,至少需要2条");
|
||||
}
|
||||
|
||||
// 2. 解析每条记录的 scheduleIds(逗号分隔),取第一个作为源 schedule_id
|
||||
// 查询所有源 sch_prod_schedule 记录
|
||||
List<Long> sourceScheduleIds = new ArrayList<>();
|
||||
for (SchProdScheduleItem item : items) {
|
||||
String sids = item.getScheduleDetailIds();
|
||||
if (sids != null && !sids.isEmpty()) {
|
||||
String[] parts = sids.split(",");
|
||||
if (parts.length > 0) {
|
||||
String firstId = parts[0].trim();
|
||||
if (!firstId.isEmpty()) {
|
||||
sourceScheduleIds.add(Long.valueOf(firstId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
List<Long> distinctSourceIds = sourceScheduleIds.stream().distinct().collect(Collectors.toList());
|
||||
List<SchProdSchedule> sourceHeaders = schProdScheduleMapper.selectBatchIds(distinctSourceIds);
|
||||
Map<Long, SchProdSchedule> headerMap = new LinkedHashMap<>();
|
||||
for (SchProdSchedule h : sourceHeaders) {
|
||||
headerMap.put(h.getScheduleId(), h);
|
||||
}
|
||||
|
||||
// 3. 定义要比较的字段列表(字段名 → 中文标签)
|
||||
Map<String, String> compareFields = new LinkedHashMap<>();
|
||||
compareFields.put("scheduleNo", "排产单号");
|
||||
compareFields.put("customerName", "订货单位");
|
||||
compareFields.put("businessUser", "业务员");
|
||||
compareFields.put("businessPhone", "联系电话");
|
||||
compareFields.put("deliveryCycle", "交货期(天)");
|
||||
compareFields.put("usePurpose", "产品用途");
|
||||
compareFields.put("productType", "品名");
|
||||
compareFields.put("thicknessTolerance", "厚度公差");
|
||||
compareFields.put("widthTolerance", "宽度公差");
|
||||
compareFields.put("surfaceQuality", "表面质量");
|
||||
compareFields.put("surfaceTreatment", "表面处理");
|
||||
compareFields.put("innerDiameter", "内径尺寸");
|
||||
compareFields.put("outerDiameter", "外径要求");
|
||||
compareFields.put("packReq", "包装要求");
|
||||
compareFields.put("cutEdgeReq", "切边要求");
|
||||
compareFields.put("singleCoilWeight", "单件重量");
|
||||
compareFields.put("weightDeviation", "交货重量偏差");
|
||||
compareFields.put("otherTechReq", "其他技术要求");
|
||||
compareFields.put("paymentDesc", "付款情况说明");
|
||||
compareFields.put("relContractNo", "关联销售合同号");
|
||||
compareFields.put("totalPlanWeight", "排产总计划吨数");
|
||||
compareFields.put("orderDate", "订单日期");
|
||||
|
||||
// 4. 逐一比较字段
|
||||
List<SchProdScheduleItemMergeValidateVo.FieldDiff> diffs = new ArrayList<>();
|
||||
boolean allConsistent = true;
|
||||
|
||||
for (Map.Entry<String, String> entry : compareFields.entrySet()) {
|
||||
String fieldName = entry.getKey();
|
||||
String fieldLabel = entry.getValue();
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
|
||||
for (SchProdSchedule header : sourceHeaders) {
|
||||
Long headerId = header.getScheduleId();
|
||||
Object val = getFieldValue(header, fieldName);
|
||||
values.put(String.valueOf(headerId), val);
|
||||
}
|
||||
|
||||
// 判断所有值是否一致
|
||||
boolean same = values.values().stream().distinct().count() <= 1;
|
||||
if (!same) {
|
||||
allConsistent = false;
|
||||
SchProdScheduleItemMergeValidateVo.FieldDiff diff = new SchProdScheduleItemMergeValidateVo.FieldDiff();
|
||||
diff.setFieldName(fieldName);
|
||||
diff.setFieldLabel(fieldLabel);
|
||||
diff.setValues(values);
|
||||
diffs.add(diff);
|
||||
}
|
||||
}
|
||||
|
||||
// 4.1 比对明细级字段(spec, material, productItem)—— 这些字段来自 item 自身,不来自 header
|
||||
Map<String, String> itemCompareFields = new LinkedHashMap<>();
|
||||
itemCompareFields.put("spec", "规格");
|
||||
itemCompareFields.put("material", "材质");
|
||||
itemCompareFields.put("productItem", "品名项");
|
||||
|
||||
for (Map.Entry<String, String> entry : itemCompareFields.entrySet()) {
|
||||
String fieldName = entry.getKey();
|
||||
String fieldLabel = entry.getValue();
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
|
||||
for (SchProdScheduleItem item : items) {
|
||||
Object val = getItemFieldValue(item, fieldName);
|
||||
values.put(String.valueOf(item.getScheduleId()), val);
|
||||
}
|
||||
|
||||
boolean same = values.values().stream().distinct().count() <= 1;
|
||||
if (!same) {
|
||||
allConsistent = false;
|
||||
SchProdScheduleItemMergeValidateVo.FieldDiff diff = new SchProdScheduleItemMergeValidateVo.FieldDiff();
|
||||
diff.setFieldName(fieldName);
|
||||
diff.setFieldLabel(fieldLabel);
|
||||
diff.setValues(values);
|
||||
diffs.add(diff);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 构建 ItemInfo 快照列表
|
||||
List<SchProdScheduleItemMergeValidateVo.ItemInfo> itemInfos = new ArrayList<>();
|
||||
BigDecimal totalWeight = BigDecimal.ZERO;
|
||||
for (SchProdScheduleItem item : items) {
|
||||
SchProdScheduleItemMergeValidateVo.ItemInfo info = new SchProdScheduleItemMergeValidateVo.ItemInfo();
|
||||
info.setItemId(item.getScheduleId());
|
||||
// 解析第一个源 schedule_id
|
||||
String sids = item.getScheduleDetailIds();
|
||||
Long firstSourceId = null;
|
||||
if (sids != null && !sids.isEmpty()) {
|
||||
String[] parts = sids.split(",");
|
||||
if (parts.length > 0) {
|
||||
try {
|
||||
firstSourceId = Long.valueOf(parts[0].trim());
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
}
|
||||
info.setSourceScheduleId(firstSourceId);
|
||||
info.setScheduleNo(item.getScheduleNo());
|
||||
info.setCustomerName(item.getCustomerName());
|
||||
info.setProductType(item.getProductType());
|
||||
info.setSpec(item.getSpec());
|
||||
info.setMaterial(item.getMaterial());
|
||||
info.setScheduleWeight(item.getScheduleWeight());
|
||||
info.setScheduleDetailIds(item.getScheduleDetailIds());
|
||||
// header 字段
|
||||
info.setBusinessUser(item.getBusinessUser());
|
||||
info.setBusinessPhone(item.getBusinessPhone());
|
||||
info.setDeliveryCycle(item.getDeliveryCycle());
|
||||
info.setUsePurpose(item.getUsePurpose());
|
||||
info.setThicknessTolerance(item.getThicknessTolerance());
|
||||
info.setWidthTolerance(item.getWidthTolerance());
|
||||
info.setSurfaceQuality(item.getSurfaceQuality());
|
||||
info.setSurfaceTreatment(item.getSurfaceTreatment());
|
||||
info.setInnerDiameter(item.getInnerDiameter());
|
||||
info.setOuterDiameter(item.getOuterDiameter());
|
||||
info.setPackReq(item.getPackReq());
|
||||
info.setCutEdgeReq(item.getCutEdgeReq());
|
||||
info.setSingleCoilWeight(item.getSingleCoilWeight());
|
||||
info.setWeightDeviation(item.getWeightDeviation());
|
||||
info.setOtherTechReq(item.getOtherTechReq());
|
||||
info.setPaymentDesc(item.getPaymentDesc());
|
||||
info.setRelContractNo(item.getRelContractNo());
|
||||
info.setTotalPlanWeight(item.getTotalPlanWeight());
|
||||
info.setOrderDate(item.getOrderDate());
|
||||
itemInfos.add(info);
|
||||
totalWeight = totalWeight.add(item.getScheduleWeight() != null ? item.getScheduleWeight() : BigDecimal.ZERO);
|
||||
}
|
||||
|
||||
// 6. 构建汇总
|
||||
SchProdScheduleItemMergeValidateVo.MergedSummary summary = new SchProdScheduleItemMergeValidateVo.MergedSummary();
|
||||
summary.setItemCount(items.size());
|
||||
summary.setTotalScheduleWeight(totalWeight);
|
||||
|
||||
// 7. 组装返回
|
||||
SchProdScheduleItemMergeValidateVo vo = new SchProdScheduleItemMergeValidateVo();
|
||||
vo.setConsistent(allConsistent);
|
||||
vo.setDiffs(diffs);
|
||||
vo.setItems(itemInfos);
|
||||
vo.setSummary(summary);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射获取字段值(用于动态字段比较 — SchProdSchedule header)
|
||||
*/
|
||||
private Object getFieldValue(SchProdSchedule header, String fieldName) {
|
||||
try {
|
||||
java.lang.reflect.Field field = SchProdSchedule.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(header);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反射获取字段值(用于动态字段比较 — SchProdScheduleItem 明细)
|
||||
*/
|
||||
private Object getItemFieldValue(SchProdScheduleItem item, String fieldName) {
|
||||
try {
|
||||
java.lang.reflect.Field field = SchProdScheduleItem.class.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(item);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,8 @@ public class SchProdScheduleServiceImpl implements ISchProdScheduleService {
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getOtherTechReq()), SchProdSchedule::getOtherTechReq, bo.getOtherTechReq());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getPaymentDesc()), SchProdSchedule::getPaymentDesc, bo.getPaymentDesc());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getReturnReason()), SchProdSchedule::getReturnReason, bo.getReturnReason());
|
||||
// 根据创建时间倒叙排序
|
||||
lqw.orderByDesc(SchProdSchedule::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user