feat: 实现产需单按工序步骤生成排产明细功能
1. 重构接收产需单接口,支持按配置工序步骤生成明细 2. 新增工艺、工艺步骤CRUD接口与管理页面 3. 新增工序选择组件 4. 优化产需单页面,增加历史记录功能 5. 为排产明细添加工序步骤名称展示
This commit is contained in:
@@ -2,6 +2,8 @@ package com.klp.flow.domain.bo;
|
||||
|
||||
import lombok.Data;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 产需单接收请求对象
|
||||
@@ -18,4 +20,55 @@ public class SchProdScheduleItemReceiveBo {
|
||||
@NotNull(message = "排产单主表ID不能为空")
|
||||
private Long scheduleId;
|
||||
|
||||
/**
|
||||
* 明细工序配置列表
|
||||
*/
|
||||
@NotEmpty(message = "明细工序配置不能为空")
|
||||
private List<DetailProcessConfig> detailProcessList;
|
||||
|
||||
/**
|
||||
* 明细工序配置
|
||||
*/
|
||||
@Data
|
||||
public static class DetailProcessConfig {
|
||||
/**
|
||||
* 产需单明细ID
|
||||
*/
|
||||
@NotNull(message = "明细ID不能为空")
|
||||
private Long scheduleDetailId;
|
||||
|
||||
/**
|
||||
* 工序ID
|
||||
*/
|
||||
@NotNull(message = "工序ID不能为空")
|
||||
private Long processId;
|
||||
|
||||
/**
|
||||
* 工序步骤列表
|
||||
*/
|
||||
@NotEmpty(message = "工序步骤不能为空")
|
||||
private List<StepConfig> stepList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工序步骤配置
|
||||
*/
|
||||
@Data
|
||||
public static class StepConfig {
|
||||
/**
|
||||
* 步骤ID
|
||||
*/
|
||||
@NotNull(message = "步骤ID不能为空")
|
||||
private Long stepId;
|
||||
|
||||
/**
|
||||
* 步骤顺序号
|
||||
*/
|
||||
private Long stepOrder;
|
||||
|
||||
/**
|
||||
* 步骤名称
|
||||
*/
|
||||
private String stepName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ package com.klp.flow.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.klp.common.annotation.ExcelDictFormat;
|
||||
import com.klp.common.convert.ExcelDictConvert;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* 工序定义主视图对象 sch_prod_process
|
||||
@@ -44,5 +47,10 @@ public class SchProdProcessVo {
|
||||
@ExcelProperty(value = "备注")
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 工艺步骤列表
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<SchProdProcessStepVo> stepList;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.klp.flow.domain.vo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
@@ -195,6 +196,12 @@ public class SchProdScheduleItemVo {
|
||||
@ExcelProperty(value = "工序ID")
|
||||
private Long actionId;
|
||||
|
||||
/**
|
||||
* 工序步骤名称
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String stepName;
|
||||
|
||||
/**
|
||||
* 规格 例:1.0X1250
|
||||
*/
|
||||
|
||||
@@ -11,13 +11,18 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.klp.flow.domain.bo.SchProdProcessBo;
|
||||
import com.klp.flow.domain.vo.SchProdProcessVo;
|
||||
import com.klp.flow.domain.vo.SchProdProcessStepVo;
|
||||
import com.klp.flow.domain.SchProdProcess;
|
||||
import com.klp.flow.domain.SchProdProcessStep;
|
||||
import com.klp.flow.mapper.SchProdProcessMapper;
|
||||
import com.klp.flow.mapper.SchProdProcessStepMapper;
|
||||
import com.klp.flow.service.ISchProdProcessService;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 工序定义主Service业务层处理
|
||||
@@ -30,13 +35,18 @@ import java.util.Collection;
|
||||
public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
||||
|
||||
private final SchProdProcessMapper baseMapper;
|
||||
private final SchProdProcessStepMapper stepMapper;
|
||||
|
||||
/**
|
||||
* 查询工序定义主
|
||||
*/
|
||||
@Override
|
||||
public SchProdProcessVo queryById(Long processId){
|
||||
return baseMapper.selectVoById(processId);
|
||||
SchProdProcessVo vo = baseMapper.selectVoById(processId);
|
||||
if (vo != null) {
|
||||
fillStepList(Collections.singletonList(vo));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,6 +56,9 @@ public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
||||
public TableDataInfo<SchProdProcessVo> queryPageList(SchProdProcessBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SchProdProcess> lqw = buildQueryWrapper(bo);
|
||||
Page<SchProdProcessVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
if (result.getRecords() != null && !result.getRecords().isEmpty()) {
|
||||
fillStepList(result.getRecords());
|
||||
}
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@@ -55,7 +68,34 @@ public class SchProdProcessServiceImpl implements ISchProdProcessService {
|
||||
@Override
|
||||
public List<SchProdProcessVo> queryList(SchProdProcessBo bo) {
|
||||
LambdaQueryWrapper<SchProdProcess> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
List<SchProdProcessVo> list = baseMapper.selectVoList(lqw);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
fillStepList(list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量填充工艺步骤列表
|
||||
*/
|
||||
private void fillStepList(List<SchProdProcessVo> voList) {
|
||||
List<Long> processIds = voList.stream()
|
||||
.map(SchProdProcessVo::getProcessId)
|
||||
.filter(id -> id != null)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (processIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LambdaQueryWrapper<SchProdProcessStep> stepQw = Wrappers.lambdaQuery();
|
||||
stepQw.in(SchProdProcessStep::getProcessId, processIds);
|
||||
stepQw.orderByAsc(SchProdProcessStep::getStepOrder);
|
||||
List<SchProdProcessStepVo> allSteps = stepMapper.selectVoList(stepQw);
|
||||
Map<Long, List<SchProdProcessStepVo>> stepMap = allSteps.stream()
|
||||
.collect(Collectors.groupingBy(SchProdProcessStepVo::getProcessId));
|
||||
for (SchProdProcessVo vo : voList) {
|
||||
vo.setStepList(stepMap.getOrDefault(vo.getProcessId(), Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SchProdProcess> buildQueryWrapper(SchProdProcessBo bo) {
|
||||
|
||||
@@ -18,15 +18,18 @@ 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.domain.SchProdProcessStep;
|
||||
import com.klp.flow.mapper.SchProdScheduleItemMapper;
|
||||
import com.klp.flow.mapper.SchProdScheduleMapper;
|
||||
import com.klp.flow.mapper.SchProdScheduleDetailMapper;
|
||||
import com.klp.flow.mapper.SchProdProcessStepMapper;
|
||||
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.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -46,6 +49,7 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
private final SchProdScheduleItemMapper baseMapper;
|
||||
private final SchProdScheduleMapper schProdScheduleMapper;
|
||||
private final SchProdScheduleDetailMapper schProdScheduleDetailMapper;
|
||||
private final SchProdProcessStepMapper stepMapper;
|
||||
|
||||
/**
|
||||
* 查询排产单主加明细可合并
|
||||
@@ -62,6 +66,9 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
public TableDataInfo<SchProdScheduleItemVo> queryPageList(SchProdScheduleItemBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SchProdScheduleItem> lqw = buildQueryWrapper(bo);
|
||||
Page<SchProdScheduleItemVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
if (result.getRecords() != null && !result.getRecords().isEmpty()) {
|
||||
fillStepName(result.getRecords());
|
||||
}
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@@ -71,7 +78,11 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
@Override
|
||||
public List<SchProdScheduleItemVo> queryList(SchProdScheduleItemBo bo) {
|
||||
LambdaQueryWrapper<SchProdScheduleItem> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
List<SchProdScheduleItemVo> list = baseMapper.selectVoList(lqw);
|
||||
if (list != null && !list.isEmpty()) {
|
||||
fillStepName(list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<SchProdScheduleItem> buildQueryWrapper(SchProdScheduleItemBo bo) {
|
||||
@@ -113,6 +124,30 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量填充工序步骤名称
|
||||
*/
|
||||
private void fillStepName(List<SchProdScheduleItemVo> voList) {
|
||||
List<Long> stepIds = voList.stream()
|
||||
.map(SchProdScheduleItemVo::getActionId)
|
||||
.filter(id -> id != null)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (stepIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LambdaQueryWrapper<SchProdProcessStep> stepQw = Wrappers.lambdaQuery();
|
||||
stepQw.in(SchProdProcessStep::getStepId, stepIds);
|
||||
List<SchProdProcessStep> steps = stepMapper.selectList(stepQw);
|
||||
Map<Long, String> stepNameMap = steps.stream()
|
||||
.collect(Collectors.toMap(SchProdProcessStep::getStepId, SchProdProcessStep::getStepName, (v1, v2) -> v1));
|
||||
for (SchProdScheduleItemVo vo : voList) {
|
||||
if (vo.getActionId() != null) {
|
||||
vo.setStepName(stepNameMap.get(vo.getActionId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增排产单主加明细可合并
|
||||
*/
|
||||
@@ -195,12 +230,13 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收产需单:从 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();
|
||||
List<SchProdScheduleItemReceiveBo.DetailProcessConfig> detailProcessList = receiveBo.getDetailProcessList();
|
||||
|
||||
// 1. 查询产需单主表
|
||||
SchProdSchedule header = schProdScheduleMapper.selectById(scheduleId);
|
||||
@@ -217,57 +253,74 @@ public class SchProdScheduleItemServiceImpl implements ISchProdScheduleItemServi
|
||||
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);
|
||||
// 3. 构建明细ID到工序配置的映射
|
||||
Map<Long, SchProdScheduleItemReceiveBo.DetailProcessConfig> processConfigMap = new HashMap<>();
|
||||
for (SchProdScheduleItemReceiveBo.DetailProcessConfig config : detailProcessList) {
|
||||
processConfigMap.put(config.getScheduleDetailId(), config);
|
||||
}
|
||||
|
||||
// 4. 批量插入
|
||||
// 4. 遍历每条 detail,根据工序步骤生成多条 SchProdScheduleItem
|
||||
List<SchProdScheduleItem> addList = new ArrayList<>();
|
||||
for (SchProdScheduleDetail detail : details) {
|
||||
SchProdScheduleItemReceiveBo.DetailProcessConfig processConfig = processConfigMap.get(detail.getScheduleDetailId());
|
||||
if (processConfig == null || processConfig.getStepList() == null || processConfig.getStepList().isEmpty()) {
|
||||
throw new RuntimeException("明细ID=" + detail.getScheduleDetailId() + " 未配置工序步骤");
|
||||
}
|
||||
|
||||
// 根据工序步骤数量生成多条排产单明细
|
||||
for (SchProdScheduleItemReceiveBo.StepConfig step : processConfig.getStepList()) {
|
||||
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为actionId
|
||||
item.setActionId(step.getStepId());
|
||||
|
||||
// 来源追溯(未合并,各存明细ID)
|
||||
item.setScheduleDetailIds(String.valueOf(detail.getScheduleDetailId()));
|
||||
|
||||
validEntityBeforeSave(item);
|
||||
addList.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 批量插入
|
||||
baseMapper.insertBatch(addList);
|
||||
|
||||
// 5. 更新产需单状态为 2(已下达)
|
||||
// 6. 更新产需单状态为 2(已下达)
|
||||
header.setScheduleStatus(2L);
|
||||
schProdScheduleMapper.updateById(header);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user