1476 lines
73 KiB
Java
1476 lines
73 KiB
Java
package com.ruoyi.system.controller;
|
||
|
||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||
import cn.dev33.satoken.annotation.SaMode;
|
||
import cn.hutool.json.JSONUtil;
|
||
import com.alibaba.excel.EasyExcel;
|
||
import com.alibaba.excel.ExcelWriter;
|
||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||
import com.ruoyi.common.annotation.Log;
|
||
import com.ruoyi.common.annotation.RepeatSubmit;
|
||
import com.ruoyi.common.config.DrawingFtpProperties;
|
||
import com.ruoyi.common.config.DingTalkProperties;
|
||
import com.ruoyi.common.core.controller.BaseController;
|
||
import com.ruoyi.common.core.domain.PageQuery;
|
||
import com.ruoyi.common.core.domain.R;
|
||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||
import com.ruoyi.common.core.page.TableDataInfo;
|
||
import com.ruoyi.common.core.validate.AddGroup;
|
||
import com.ruoyi.common.core.validate.EditGroup;
|
||
import com.ruoyi.common.dingding.util.DingUtil;
|
||
import com.ruoyi.common.enums.BusinessType;
|
||
import com.ruoyi.common.excel.DefaultExcelListener;
|
||
import com.ruoyi.common.excel.ExcelResult;
|
||
import com.ruoyi.common.exception.ServiceException;
|
||
import com.ruoyi.common.helper.LoginHelper;
|
||
import com.ruoyi.common.utils.FtpUtil;
|
||
import com.ruoyi.common.utils.KingdeeRouteDepartmentHelper;
|
||
import com.ruoyi.common.utils.JsyyOrderNo;
|
||
import com.ruoyi.common.utils.ProjectStatusLock;
|
||
import com.ruoyi.common.utils.StringUtils;
|
||
import com.ruoyi.common.utils.file.SmbUtil;
|
||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||
import com.ruoyi.system.constant.DingTodoSceneCodes;
|
||
import com.ruoyi.system.domain.*;
|
||
import com.ruoyi.system.domain.bo.ProcessRouteBo;
|
||
import com.ruoyi.system.domain.dto.*;
|
||
import com.ruoyi.system.domain.vo.PcRigidChainVo;
|
||
import com.ruoyi.system.domain.vo.PlannedProcessVo;
|
||
import com.ruoyi.system.domain.vo.ProcessRouteVo;
|
||
import com.ruoyi.system.domain.vo.ProductionOrderVo;
|
||
import com.ruoyi.system.jdmain.rouplan.Model;
|
||
import com.ruoyi.system.mapper.BomDetailsMapper;
|
||
import com.ruoyi.system.mapper.MaterialBomMapper;
|
||
import com.ruoyi.system.mapper.ProcessOrderProMapper;
|
||
import com.ruoyi.system.mapper.ProcessRouteMapper;
|
||
import com.ruoyi.system.runner.JdUtil;
|
||
import com.ruoyi.system.service.*;
|
||
import com.ruoyi.system.service.support.OperationPlanningWorkshopUpdateService;
|
||
import com.ruoyi.system.service.support.OperationReportWorkshopUpdateService;
|
||
import lombok.RequiredArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.beans.factory.annotation.Autowired;
|
||
import org.springframework.http.HttpStatus;
|
||
import org.springframework.http.MediaType;
|
||
import org.springframework.http.ResponseEntity;
|
||
import org.springframework.validation.annotation.Validated;
|
||
import org.springframework.web.bind.annotation.*;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
|
||
import javax.servlet.ServletOutputStream;
|
||
import javax.servlet.http.HttpServletResponse;
|
||
import javax.validation.constraints.NotEmpty;
|
||
import javax.validation.constraints.NotNull;
|
||
import java.io.*;
|
||
import java.math.BigDecimal;
|
||
import java.math.RoundingMode;
|
||
import java.net.URLEncoder;
|
||
import java.text.SimpleDateFormat;
|
||
import java.util.*;
|
||
import java.util.concurrent.CompletableFuture;
|
||
import java.util.stream.Collectors;
|
||
|
||
/**
|
||
* 工艺路线
|
||
*
|
||
* @date 2024-10-09
|
||
*/
|
||
@Validated
|
||
@RequiredArgsConstructor
|
||
@RestController
|
||
@RequestMapping("/system/route")
|
||
@Slf4j
|
||
public class ProcessRouteController extends BaseController {
|
||
private final ProcessRouteMapper baseMapper;
|
||
private final IProcessRouteService iProcessRouteService;
|
||
private final OperationPlanningWorkshopUpdateService operationPlanningWorkshopUpdateService;
|
||
private final OperationReportWorkshopUpdateService operationReportWorkshopUpdateService;
|
||
private final IBomDetailsService iBomDetailsService;
|
||
private final ProcessOrderProMapper proMapper;
|
||
private final DrawingFtpProperties drawingFtpProperties;
|
||
private final IImMaterialService materialService;
|
||
@Autowired
|
||
BomDetailsMapper bomDetailsMapper;
|
||
@Autowired
|
||
MaterialBomMapper materialBomMapper;
|
||
@Autowired
|
||
private DingTalkProperties dingTalkProperties;
|
||
@Autowired
|
||
private ISysOssService iSysOssService;
|
||
@Autowired
|
||
private IProcessOrderProService processOrderProService;
|
||
/**
|
||
* 计划表落盘 + 按 plan-user-id 推送(与 PLM 导出共用)
|
||
*/
|
||
private final ProcessPlanExcelPushService processPlanExcelPushService;
|
||
private final IDrawingRouteOperLogService drawingRouteOperLogService;
|
||
private final IDingTodoDispatchService dingTodoDispatchService;
|
||
private final DrawingFileQueryService drawingFileQueryService;
|
||
@Autowired
|
||
private ISysUserService userService;
|
||
|
||
@Autowired
|
||
private com.ruoyi.common.dingding.robot.RobotGroupService robotGroupService;
|
||
|
||
@Autowired
|
||
private com.ruoyi.common.dingding.media.MediaService mediaService;
|
||
|
||
private Long generateUniqueParentId(Long originalId) {
|
||
return originalId + 1000;
|
||
}
|
||
|
||
@Log(title = "查看BOM上传状态")
|
||
@SaCheckPermission("system:route:viewGetBomUploadStatus")
|
||
@PostMapping("/viewGetBomUploadStatus")
|
||
public R<String> viewGetBomUploadStatus(@RequestParam String rooteProdet) {
|
||
return iProcessRouteService.viewGetBomUploadStatus(rooteProdet);
|
||
}
|
||
|
||
/**
|
||
* 主动更新可用库存
|
||
*/
|
||
@SaCheckPermission("system:route:list")
|
||
@GetMapping("/list2")
|
||
public TableDataInfo<ProcessRouteVo> list2(ProcessRouteBo bo, PageQuery pageQuery) {
|
||
try {
|
||
// 查询工艺路线数据
|
||
TableDataInfo<ProcessRouteVo> processRouteVoTableDataInfo = iProcessRouteService.queryPageList2(bo, pageQuery);
|
||
// 用于存储父子关系的Map,key为"物料编码_物料名称",值为父级ProcessRouteVo
|
||
Map<String, ProcessRouteVo> routeMap = new HashMap<>();
|
||
// 存储顶级列表
|
||
List<ProcessRouteVo> topLevelList = new ArrayList<>();
|
||
// 获取查询返回的行数据
|
||
List<ProcessRouteVo> rows = processRouteVoTableDataInfo.getRows();
|
||
// 遍历所有工艺路线记录
|
||
for (ProcessRouteVo processRouteVo : rows) {
|
||
// 组合父级键值,物料编码 + 物料名称
|
||
String parentCodeAndName = String.format("%s_%s", processRouteVo.getMaterialCode(), processRouteVo.getMaterialName());
|
||
// 查找是否已存在父节点
|
||
ProcessRouteVo parent = routeMap.get(parentCodeAndName);
|
||
if (parent == null) {
|
||
// 如果父节点不存在,创建并初始化
|
||
parent = new ProcessRouteVo();
|
||
parent.setId(generateUniqueParentId(processRouteVo.getId()));
|
||
parent.setRouteDescription(processRouteVo.getRouteDescription());
|
||
// parent.setParentId((Long.valueOf(generateUniqueParentId(processRouteVo.getId()))));
|
||
parent.setMaterialCode(processRouteVo.getMaterialCode());
|
||
parent.setMaterialName(processRouteVo.getMaterialName());
|
||
parent.setMaterial(processRouteVo.getMaterial());
|
||
parent.setDiscWeight(processRouteVo.getDiscWeight());
|
||
parent.setFirstBatchQuantity(processRouteVo.getFirstBatchQuantity());
|
||
|
||
// 初始化子节点列表
|
||
parent.setChildren(new ArrayList<>());
|
||
// 将父节点放入Map中
|
||
routeMap.put(parentCodeAndName, parent);
|
||
// 将父节点加入顶级列表
|
||
topLevelList.add(parent);
|
||
}
|
||
// 创建子节点,并将其添加到父节点的子集
|
||
ProcessRouteVo child = createChildVo(processRouteVo, parent.getId(), parent.getMaterialCode(), parent.getMaterialName());
|
||
parent.getChildren().add(child);
|
||
}
|
||
// 设置返回的rows为顶级节点列表
|
||
processRouteVoTableDataInfo.setRows(topLevelList);
|
||
// 返回构建后的数据
|
||
return processRouteVoTableDataInfo;
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
return new TableDataInfo<>();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询工艺路线列表。项目令号「查看工艺」只分配 processlist 时也要能拉数。
|
||
*/
|
||
@SaCheckPermission(value = {"system:route:list", "system:orderPro:processlist"}, mode = SaMode.OR)
|
||
@GetMapping("/list")
|
||
public TableDataInfo<ProcessRouteVo> list(ProcessRouteBo bo, PageQuery pageQuery) {
|
||
try {
|
||
// 查询工艺路线数据
|
||
TableDataInfo<ProcessRouteVo> processRouteVoTableDataInfo = iProcessRouteService.queryPageList(bo, pageQuery);
|
||
// 用于存储父子关系的Map,key为"物料编码_物料名称",值为父级ProcessRouteVo
|
||
Map<String, ProcessRouteVo> routeMap = new HashMap<>();
|
||
// 存储顶级列表
|
||
List<ProcessRouteVo> topLevelList = new ArrayList<>();
|
||
// 获取查询返回的行数据
|
||
List<ProcessRouteVo> rows = processRouteVoTableDataInfo.getRows();
|
||
// 遍历所有工艺路线记录
|
||
for (ProcessRouteVo processRouteVo : rows) {
|
||
// 组合父级键值,物料编码 + 物料名称
|
||
String parentCodeAndName = String.format("%s_%s", processRouteVo.getMaterialCode(), processRouteVo.getMaterialName());
|
||
// 查找是否已存在父节点
|
||
ProcessRouteVo parent = routeMap.get(parentCodeAndName);
|
||
if (parent == null) {
|
||
// 如果父节点不存在,创建并初始化
|
||
parent = new ProcessRouteVo();
|
||
parent.setId(generateUniqueParentId(processRouteVo.getId()));
|
||
parent.setRouteDescription(processRouteVo.getRouteDescription());
|
||
// parent.setParentId((Long.valueOf(generateUniqueParentId(processRouteVo.getId()))));
|
||
parent.setMaterialCode(processRouteVo.getMaterialCode());
|
||
parent.setMaterialName(processRouteVo.getMaterialName());
|
||
parent.setMaterial(processRouteVo.getMaterial());
|
||
parent.setDiscWeight(processRouteVo.getDiscWeight());
|
||
parent.setBatchQuantity(processRouteVo.getBatchQuantity());
|
||
parent.setFirstBatchQuantity(processRouteVo.getFirstBatchQuantity());
|
||
|
||
// 初始化子节点列表
|
||
parent.setChildren(new ArrayList<>());
|
||
// 将父节点放入Map中
|
||
routeMap.put(parentCodeAndName, parent);
|
||
// 将父节点加入顶级列表
|
||
topLevelList.add(parent);
|
||
}
|
||
// 创建子节点,并将其添加到父节点的子集
|
||
ProcessRouteVo child = createChildVo(processRouteVo, parent.getId(), parent.getMaterialCode(), parent.getMaterialName());
|
||
parent.getChildren().add(child);
|
||
}
|
||
|
||
// 设置返回的rows为顶级节点列表
|
||
processRouteVoTableDataInfo.setRows(topLevelList);
|
||
|
||
// 返回构建后的数据
|
||
return processRouteVoTableDataInfo;
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
return new TableDataInfo<>();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 创建子节点的辅助方法
|
||
*
|
||
* @param processRouteVo 原始数据
|
||
* @return ProcessRouteVo 子节点
|
||
*/
|
||
private ProcessRouteVo createChildVo(ProcessRouteVo processRouteVo, Long parentId, String materialCode, String materialName) {
|
||
ProcessRouteVo child = new ProcessRouteVo();
|
||
child.setId(processRouteVo.getId());
|
||
child.setParentId(parentId);
|
||
child.setProcessNo(processRouteVo.getProcessNo());
|
||
child.setWorkCenter(processRouteVo.getWorkCenter());
|
||
child.setProcessName(processRouteVo.getProcessName());
|
||
child.setProcessDescription(processRouteVo.getProcessDescription());
|
||
child.setProcessControl(processRouteVo.getProcessControl());
|
||
child.setActivityDuration(processRouteVo.getActivityDuration());
|
||
child.setActivityUnit(processRouteVo.getActivityUnit());
|
||
child.setUnitQuantity(processRouteVo.getUnitQuantity());
|
||
child.setPlanStartTime(processRouteVo.getPlanStartTime());
|
||
child.setPlanEndTime(processRouteVo.getPlanEndTime());
|
||
child.setXuStartTime(processRouteVo.getXuStartTime());
|
||
child.setXuEndTime(processRouteVo.getXuEndTime());
|
||
return child;
|
||
}
|
||
|
||
/**
|
||
* 导出工艺路线列表
|
||
*/
|
||
@SaCheckPermission("system:route:export2")
|
||
@Log(title = "工艺路线", businessType = BusinessType.EXPORT)
|
||
@PostMapping("/export2")
|
||
public void export2(ProcessRouteBo bo, HttpServletResponse response) {
|
||
log.info("获取项目生产数据表:{}", bo);
|
||
List<ProcessRoute> list = iProcessRouteService.queryList(bo);
|
||
List<MaterialUsageDTO2> bomlist = JdUtil.getPRD_PPBOM(bo.getRouteDescription());
|
||
//过滤bomlist 只要009开头的标准件
|
||
bomlist = bomlist.stream().filter(bom -> bom.getMaterialCode().startsWith("009")).collect(Collectors.toList());
|
||
if (list.isEmpty() && bomlist.isEmpty()) {
|
||
throw new ServiceException("没有数据");
|
||
}
|
||
try {
|
||
// 设置响应头
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setCharacterEncoding("utf-8");
|
||
String fileName = URLEncoder.encode("工艺路线", "UTF-8").replaceAll("\\+", "%20");
|
||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||
ServletOutputStream os = response.getOutputStream();
|
||
|
||
// 创建ExcelWriter
|
||
try (com.alibaba.excel.ExcelWriter excelWriter = EasyExcel.write(os).autoCloseStream(false) // 防止自动关闭流
|
||
.build()) {
|
||
// 写入工艺路线sheet
|
||
if (!list.isEmpty()) {
|
||
WriteSheet writeSheet1 = EasyExcel.writerSheet(0, "工艺路线").head(ProcessRoute.class).build();
|
||
excelWriter.write(list, writeSheet1);
|
||
}
|
||
|
||
// 写入标准件清单sheet
|
||
if (!bomlist.isEmpty()) {
|
||
WriteSheet writeSheet2 = EasyExcel.writerSheet(1, "标准件清单").head(MaterialUsageDTO2.class).build();
|
||
excelWriter.write(bomlist, writeSheet2);
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
throw new RuntimeException("导出Excel异常: " + e.getMessage(), e);
|
||
}
|
||
}
|
||
|
||
@SaCheckPermission("system:route:export")
|
||
@Log(title = "工艺路线", businessType = BusinessType.EXPORT)
|
||
@PostMapping("/export")
|
||
public void export(ProcessRouteBo bo, HttpServletResponse response) {
|
||
log.info("获取项目生产数据表:{}", bo);
|
||
// 下载Excel模板
|
||
SmbUtil.downloadExcelFiles(bo.getRouteDescription());
|
||
// 构建Excel文件路径
|
||
String ExcelName = "D:\\file\\" + bo.getRouteDescription() + "汇总表.xlsx";
|
||
File file = new File(ExcelName);
|
||
if (!file.exists()) {
|
||
throw new ServiceException("文件不存在,请确认路径是否正确");
|
||
}
|
||
//读取file的所有的物料编码
|
||
try {
|
||
if (file.exists()) {
|
||
// 读取Excel的sheet6 从第三行开始 读取到第4列 无数据的跳过
|
||
DefaultExcelListener<ProcessRoute> listener = new DefaultExcelListener<>(true);
|
||
EasyExcel.read(ExcelName, ProcessRoute.class, listener).sheet(6).headRowNumber(3).doRead();
|
||
}
|
||
} catch (Exception e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
// 查询要写入的数据是否
|
||
List<ProcessRoute> list = iProcessRouteService.queryList(bo);
|
||
|
||
try {
|
||
// 设置响应头
|
||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||
response.setCharacterEncoding("utf-8");
|
||
String fileName = URLEncoder.encode("工艺路线", "UTF-8").replaceAll("\\+", "%20");
|
||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||
|
||
ServletOutputStream os = response.getOutputStream();
|
||
|
||
// 使用 EasyExcel 写入已有Excel的第七个sheet(index=6)
|
||
try (ExcelWriter excelWriter = EasyExcel.write(os).withTemplate(file) // 指定模板文件
|
||
.autoCloseStream(true).build()) {
|
||
|
||
// 写入到第七个 sheet(index=6),并命名为“工艺路线”
|
||
WriteSheet writeSheet = EasyExcel.writerSheet("已有工艺路线").head(ProcessRoute.class).needHead(true).build();
|
||
excelWriter.write(list, writeSheet);
|
||
}
|
||
|
||
os.flush();
|
||
} catch (Exception e) {
|
||
log.error("导出Excel异常", e);
|
||
throw new RuntimeException("导出Excel异常: " + e.getMessage(), e);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 获取工艺路线详细信息
|
||
*
|
||
* @param id 主键
|
||
*/
|
||
@SaCheckPermission("system:route:query")
|
||
@GetMapping("/{id}")
|
||
public R<ProcessRouteVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
|
||
return R.ok(iProcessRouteService.queryById(id));
|
||
}
|
||
|
||
/**
|
||
* 新增工艺路线
|
||
*/
|
||
@SaCheckPermission("system:route:add")
|
||
@Log(title = "工艺路线", businessType = BusinessType.INSERT)
|
||
@RepeatSubmit()
|
||
@PostMapping()
|
||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ProcessRouteBo bo) {
|
||
return toAjax(iProcessRouteService.insertByBo(bo));
|
||
}
|
||
|
||
/**
|
||
* 导出
|
||
* 修改工艺路线
|
||
*/
|
||
@SaCheckPermission("system:route:edit")
|
||
@Log(title = "工艺路线", businessType = BusinessType.UPDATE)
|
||
@RepeatSubmit()
|
||
@PutMapping()
|
||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ProcessRouteBo bo) {
|
||
return toAjax(iProcessRouteService.updateByBo(bo));
|
||
}
|
||
|
||
/**
|
||
* 删除工艺路线
|
||
*
|
||
* @param ids 主键串
|
||
*/
|
||
@SaCheckPermission("system:route:remove")
|
||
@Log(title = "工艺路线", businessType = BusinessType.DELETE)
|
||
@DeleteMapping("/{ids}")
|
||
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
|
||
return toAjax(iProcessRouteService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||
}
|
||
|
||
@Log(title = "生产工艺计划表导入", businessType = BusinessType.IMPORT)
|
||
@SaCheckPermission("system:route:importRoute")
|
||
@PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||
public R importData(@RequestParam("file") MultipartFile file,
|
||
@RequestParam(value = "productionOrderNo", required = false) String expectedOrderNo) throws Exception {
|
||
String originalFilename = file.getOriginalFilename();
|
||
|
||
log.info("读取文件名: " + originalFilename);
|
||
// 读取工艺部分sheet
|
||
ExcelResult<ProcessRouteVo> result = ExcelUtil.importExcelSheet6(file.getInputStream(), ProcessRouteVo.class, true);
|
||
List<ProcessRouteVo> list1 = result.getList();
|
||
// 读取总装部分sheet
|
||
ExcelResult<ProductionOrderVo> result1 = ExcelUtil.importExcelSheet1(file.getInputStream(), ProductionOrderVo.class, true);
|
||
List<ProductionOrderVo> list = result1.getList();
|
||
long started = System.currentTimeMillis();
|
||
if (list1 == null || list1.isEmpty()) {
|
||
recordRouteUpload(null, expectedOrderNo, originalFilename, 0,
|
||
IDrawingRouteOperLogService.STATUS_FAIL, "工艺生产计划表为空", started);
|
||
return R.fail("工艺生产计划表为空,请检查此表或将sheet移动至第七个!");
|
||
}
|
||
// 防呆:当前项目令号必须与文件内「生产令号」一致
|
||
String orderNoMismatch = validateImportOrderNo(expectedOrderNo, list1, list);
|
||
if (orderNoMismatch != null) {
|
||
ProcessOrderPro orderForMismatch = StringUtils.isNotBlank(expectedOrderNo)
|
||
? processOrderProService.selectByProjectNumber(expectedOrderNo.trim()) : null;
|
||
recordRouteUpload(orderForMismatch, expectedOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_FAIL, orderNoMismatch, started);
|
||
return R.fail(orderNoMismatch);
|
||
}
|
||
String resolvedOrderNo = firstNonBlankRouteDescription(list1);
|
||
if (StringUtils.isBlank(resolvedOrderNo) && StringUtils.isNotBlank(expectedOrderNo)) {
|
||
resolvedOrderNo = expectedOrderNo.trim();
|
||
}
|
||
// 后面异步推送会捕获令号,必须是 effectively final
|
||
final String productionOrderNo = resolvedOrderNo;
|
||
ProcessOrderPro orderForLock = processOrderProService.selectByProjectNumber(productionOrderNo);
|
||
if (orderForLock != null) {
|
||
String statusDeny = ProjectStatusLock.denyIfLocked(orderForLock.getBomStatus(), ProjectStatusLock.Action.UPLOAD_ROUTE);
|
||
if (statusDeny != null) {
|
||
recordRouteUpload(orderForLock, productionOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_FAIL, statusDeny, started);
|
||
return R.fail(statusDeny);
|
||
}
|
||
}
|
||
List<BomDetails> bomDetails = iBomDetailsService.selectByProjectNumber(productionOrderNo);
|
||
List<ProcessRoute> route = iProcessRouteService.selectByProjectNumber(productionOrderNo);
|
||
if (!bomDetails.isEmpty()) {
|
||
recordRouteUpload(orderForLock, productionOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_FAIL, "已导入过BOM,请先清空再上传", started);
|
||
return R.fail("项目 " + productionOrderNo + "已导入过BOM,请先清空再上传");
|
||
}
|
||
if (!route.isEmpty()) {
|
||
recordRouteUpload(orderForLock, productionOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_FAIL, "已导入过工艺,请先清空再上传", started);
|
||
return R.fail("项目 " + productionOrderNo + "已导入过工艺 ,请先清空再上传");
|
||
}
|
||
String workCenterError = validateImportWorkCenters(list1);
|
||
if (workCenterError != null) {
|
||
recordRouteUpload(orderForLock, productionOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_FAIL, "工段校验失败", started);
|
||
return R.fail("生产工艺计划表工段校验失败:\n" + workCenterError);
|
||
}
|
||
if (iProcessRouteService.saveData(list1, list)) {
|
||
recordRouteUpload(orderForLock, productionOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_SUCCESS, null, started);
|
||
// 计划表落到服务器固定目录(app.plan-excel.base-dir/令号/),保留不删
|
||
File savedPlanFile = null;
|
||
try {
|
||
File dir = new File(processPlanExcelPushService.getPlanExcelBaseDir(), productionOrderNo);
|
||
if (!dir.exists() && !dir.mkdirs()) {
|
||
log.warn("创建计划表目录失败: {}", dir.getAbsolutePath());
|
||
}
|
||
savedPlanFile = new File(dir, originalFilename);
|
||
file.transferTo(savedPlanFile);
|
||
log.info("上传的工艺计划表已落盘: {}", savedPlanFile.getAbsolutePath());
|
||
} catch (Exception e) {
|
||
log.error("保存上传的工艺计划表失败", e);
|
||
}
|
||
final File uploadFile = savedPlanFile;
|
||
|
||
// 获取当前登录用户昵称(需要在异步线程前获取)
|
||
String currentUserNickName = "未知用户";
|
||
try {
|
||
Long userId = LoginHelper.getUserId();
|
||
if (userId != null) {
|
||
SysUser user = userService.selectUserById(userId);
|
||
if (user != null && StringUtils.isNotEmpty(user.getNickName())) {
|
||
currentUserNickName = user.getNickName();
|
||
}
|
||
}
|
||
} catch (Exception e) {
|
||
log.warn("获取当前登录用户昵称失败", e);
|
||
}
|
||
|
||
String todoProjectName = orderForLock == null ? null : orderForLock.getProductionName();
|
||
Date todoProjectEndTime = orderForLock == null ? null : orderForLock.getProjectEndTime();
|
||
String planUserIds = dingTalkProperties.getPlanUserId();
|
||
// 异步:按 plan-user-id 发文件 + 发工艺审核卡片(落盘文件保留)
|
||
String finalNickName = currentUserNickName;
|
||
String operator = drawingRouteOperLogService.currentOperator();
|
||
String clientIp = drawingRouteOperLogService.currentClientIp();
|
||
Long projectId = orderForLock == null ? null : orderForLock.getId();
|
||
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
|
||
CompletableFuture.runAsync(() -> {
|
||
Thread.currentThread().setContextClassLoader(contextClassLoader);
|
||
long notifyStarted = System.currentTimeMillis();
|
||
ProcessPlanExcelPushService.FilePushResult filePush =
|
||
processPlanExcelPushService.sendToPlanUsers(uploadFile, originalFilename);
|
||
String todoError = dingTodoDispatchService.sendIfEnabled(
|
||
DingTodoSceneCodes.ROUTE_UPLOAD_PLAN,
|
||
productionOrderNo,
|
||
todoProjectName,
|
||
todoProjectEndTime,
|
||
uploadFile,
|
||
planUserIds);
|
||
if (StringUtils.isNotBlank(todoError) && filePush != null) {
|
||
filePush.setErrorMsg(StringUtils.isBlank(filePush.getErrorMsg())
|
||
? "待办: " + todoError
|
||
: filePush.getErrorMsg() + "; 待办: " + todoError);
|
||
if (filePush.getSuccessCount() > 0) {
|
||
filePush.setFailCount(filePush.getFailCount() + 1);
|
||
}
|
||
}
|
||
boolean cardOk = false;
|
||
String cardError = null;
|
||
String outTrackId = null;
|
||
try {
|
||
ProcessOrderPro order = processOrderProService.selectByProjectNumber(productionOrderNo);
|
||
if (order != null) {
|
||
// 获取 AccessToken
|
||
String accessToken = DingUtil.getAccessToken(dingTalkProperties.getAppKey(), dingTalkProperties.getAppSecret());
|
||
// 构造卡片数据
|
||
Map<String, String> cardDataMap = new HashMap<>();
|
||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||
cardDataMap.put("pro_number", StringUtils.defaultIfEmpty(order.getProductionOrderNo(), ""));
|
||
cardDataMap.put("createTime", order.getRouteUptime() != null ? sdf.format(new Date()) : "");
|
||
cardDataMap.put("title", finalNickName + "提交工艺审核");
|
||
cardDataMap.put("lastMessage", "请尽快处理");
|
||
cardDataMap.put("status", StringUtils.defaultIfEmpty("待审批", "待审批"));
|
||
|
||
outTrackId = "audit-card-" + System.currentTimeMillis();
|
||
// 调用发送给工艺负责人的方法 createAndDeliverCardToUser
|
||
DingUtil.createAndDeliverCardToUser(
|
||
accessToken,
|
||
dingTalkProperties.getRouteCardTemplateId(),
|
||
Collections.singletonList(dingTalkProperties.getRouteApprovalId()),
|
||
outTrackId,
|
||
cardDataMap,
|
||
dingTalkProperties.getCallbackRouteKey()
|
||
);
|
||
// 统一存入带 userId 后缀的完整 outTrackId,以便和钉钉回调保持一致
|
||
String fullOutTrackId = outTrackId + "_" + dingTalkProperties.getRouteApprovalId();
|
||
// 更新数据库中的AuditCardId
|
||
ProcessOrderPro update = new ProcessOrderPro();
|
||
update.setId(order.getId());
|
||
update.setUpdateBy("Dingding");
|
||
update.setAuditCardId(fullOutTrackId);
|
||
update.setBomStatus(2L);
|
||
// 更新审核状态为1(已审核/提交审核)
|
||
update.setRouteStatus(0L);
|
||
proMapper.updateById(update);
|
||
cardOk = true;
|
||
} else {
|
||
cardError = "项目不存在,未发送审核卡片";
|
||
}
|
||
} catch (Exception e) {
|
||
cardError = e.getMessage();
|
||
log.error("发送钉钉卡片异常", e);
|
||
}
|
||
recordRouteNotifyAfterUpload(productionOrderNo, projectId, originalFilename, finalNickName,
|
||
filePush, cardOk, cardError, outTrackId, operator, clientIp, notifyStarted);
|
||
});
|
||
return R.ok("上传物料成功");
|
||
} else {
|
||
recordRouteUpload(orderForLock, productionOrderNo, originalFilename, list1.size(),
|
||
IDrawingRouteOperLogService.STATUS_FAIL, "导入失败", started);
|
||
return R.fail("导入失败");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 防呆:页面所选项目令号须与 Excel「生产令号」一致。
|
||
* 工艺 sheet(routeDescription)与总装 sheet(productionOrderNo)都会检查。
|
||
*
|
||
* @return 错误提示;通过则 null
|
||
*/
|
||
private String validateImportOrderNo(String expectedOrderNo, List<ProcessRouteVo> routes,
|
||
List<ProductionOrderVo> products) {
|
||
LinkedHashSet<String> fileNos = new LinkedHashSet<>();
|
||
if (routes != null) {
|
||
for (ProcessRouteVo row : routes) {
|
||
if (row != null && StringUtils.isNotBlank(row.getRouteDescription())) {
|
||
fileNos.add(row.getRouteDescription().trim());
|
||
}
|
||
}
|
||
}
|
||
if (products != null) {
|
||
for (ProductionOrderVo row : products) {
|
||
if (row != null && StringUtils.isNotBlank(row.getProductionOrderNo())) {
|
||
fileNos.add(row.getProductionOrderNo().trim());
|
||
}
|
||
}
|
||
}
|
||
if (fileNos.isEmpty()) {
|
||
return "导入文件未读到生产令号,请检查工艺计划表「生产令号」列";
|
||
}
|
||
// 大小写不同仍视为同一令号,避免 Excel 大小写差异误报多个令号
|
||
String fileNo = null;
|
||
for (String no : fileNos) {
|
||
if (fileNo == null) {
|
||
fileNo = no;
|
||
} else if (!fileNo.equalsIgnoreCase(no)) {
|
||
return "导入文件中存在多个生产令号:" + String.join("、", fileNos) + ",请确认是否导错文件";
|
||
}
|
||
}
|
||
if (StringUtils.isBlank(expectedOrderNo)) {
|
||
return "请先选择要导入的项目令号";
|
||
}
|
||
String expected = expectedOrderNo.trim();
|
||
if (!expected.equalsIgnoreCase(fileNo)) {
|
||
return "导入令号不一致:当前项目为「" + expected + "」,文件中为「" + fileNo + "」,请确认后重新导入";
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 取工艺表中第一条非空生产令号。
|
||
*/
|
||
private static String firstNonBlankRouteDescription(List<ProcessRouteVo> routes) {
|
||
if (routes == null) {
|
||
return null;
|
||
}
|
||
for (ProcessRouteVo row : routes) {
|
||
if (row != null && StringUtils.isNotBlank(row.getRouteDescription())) {
|
||
return row.getRouteDescription().trim();
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private void recordRouteUpload(ProcessOrderPro order, String productionOrderNo, String fileName,
|
||
int lineCount, String status, String errorMsg, long started) {
|
||
Map<String, Object> summary = new LinkedHashMap<>();
|
||
summary.put("lineCount", lineCount);
|
||
summary.put("fileName", fileName);
|
||
drawingRouteOperLogService.record(IDrawingRouteOperLogService.BIZ_ROUTE_UPLOAD, row -> {
|
||
row.setChannel(IDrawingRouteOperLogService.CHANNEL_MANUAL);
|
||
row.setApiPath("/system/route/importData");
|
||
row.setProjectId(order == null ? null : order.getId());
|
||
row.setProductionOrderNo(productionOrderNo);
|
||
row.setFileName(fileName);
|
||
row.setStatus(status);
|
||
row.setErrorMsg(errorMsg);
|
||
row.setSummaryJson(JSONUtil.toJsonStr(summary));
|
||
row.setDurationMs(System.currentTimeMillis() - started);
|
||
});
|
||
}
|
||
|
||
private void recordRouteNotifyAfterUpload(String productionOrderNo, Long projectId, String fileName,
|
||
String nickName, ProcessPlanExcelPushService.FilePushResult filePush,
|
||
boolean cardOk, String cardError, String outTrackId,
|
||
String operator, String clientIp, long started) {
|
||
String fileStatus = filePush == null ? IDrawingRouteOperLogService.STATUS_FAIL : filePush.resolveStatus();
|
||
boolean fileOk = IDrawingRouteOperLogService.STATUS_SUCCESS.equals(fileStatus);
|
||
String status;
|
||
if (fileOk && cardOk) {
|
||
status = IDrawingRouteOperLogService.STATUS_SUCCESS;
|
||
} else if (!fileOk && !cardOk) {
|
||
status = IDrawingRouteOperLogService.STATUS_FAIL;
|
||
} else {
|
||
status = IDrawingRouteOperLogService.STATUS_PARTIAL;
|
||
}
|
||
StringBuilder error = new StringBuilder();
|
||
if (filePush != null && StringUtils.isNotBlank(filePush.getErrorMsg())) {
|
||
error.append("文件: ").append(filePush.getErrorMsg());
|
||
}
|
||
if (StringUtils.isNotBlank(cardError)) {
|
||
if (error.length() > 0) {
|
||
error.append("; ");
|
||
}
|
||
error.append("卡片: ").append(cardError);
|
||
}
|
||
Map<String, Object> summary = new LinkedHashMap<>();
|
||
summary.put("fileStatus", fileStatus);
|
||
summary.put("fileSuccessCount", filePush == null ? 0 : filePush.getSuccessCount());
|
||
summary.put("fileFailCount", filePush == null ? 0 : filePush.getFailCount());
|
||
summary.put("cardOk", cardOk);
|
||
summary.put("outTrackId", outTrackId);
|
||
summary.put("submitter", nickName);
|
||
String notifyUsers = joinNotifyUsers(
|
||
filePush == null ? null : filePush.getUserIds(),
|
||
dingTalkProperties.getRouteApprovalId());
|
||
drawingRouteOperLogService.record(IDrawingRouteOperLogService.BIZ_ROUTE_NOTIFY, row -> {
|
||
row.setChannel(IDrawingRouteOperLogService.CHANNEL_MANUAL);
|
||
row.setApiPath("/system/route/importData");
|
||
row.setProjectId(projectId);
|
||
row.setProductionOrderNo(productionOrderNo);
|
||
row.setNotifyType(IDrawingRouteOperLogService.NOTIFY_FILE_AND_CARD);
|
||
row.setNotifyUserIds(notifyUsers);
|
||
row.setMessageTitle(nickName + "提交工艺审核");
|
||
row.setFileName(fileName);
|
||
row.setStatus(status);
|
||
row.setErrorMsg(error.length() == 0 ? null : error.toString());
|
||
row.setSummaryJson(JSONUtil.toJsonStr(summary));
|
||
row.setOperator(operator);
|
||
row.setClientIp(clientIp);
|
||
row.setDurationMs(System.currentTimeMillis() - started);
|
||
});
|
||
}
|
||
|
||
private static String joinNotifyUsers(String... parts) {
|
||
StringBuilder sb = new StringBuilder();
|
||
if (parts == null) {
|
||
return null;
|
||
}
|
||
for (String part : parts) {
|
||
if (StringUtils.isBlank(part)) {
|
||
continue;
|
||
}
|
||
if (sb.length() > 0) {
|
||
sb.append(",");
|
||
}
|
||
sb.append(part.trim());
|
||
}
|
||
return sb.length() == 0 ? null : sb.toString();
|
||
}
|
||
|
||
/**
|
||
* 查询组焊件集合
|
||
*/
|
||
@Log(title = "查询组焊件集合")
|
||
@SaCheckPermission("system:route:pushGroupWeldments")
|
||
@PostMapping("/pushGroupWeldments")
|
||
public List<ProcessRoute> pushGroupWeldments(@RequestBody ProcessRoute processRoute) {
|
||
return iProcessRouteService.pushGroupWeldments(processRoute);
|
||
}
|
||
|
||
/**
|
||
* 查询需要单独生成BOm 的原材料集合
|
||
*/
|
||
@Log(title = "查询需要单独生成BOM的原材料集合")
|
||
@SaCheckPermission("system:route:getRawBom")
|
||
@PostMapping("/getRawBom")
|
||
public List<ProcessRoute> getRawBom(@RequestParam String rooteProdet) {
|
||
return iProcessRouteService.getRawBom(rooteProdet);
|
||
}
|
||
|
||
/**
|
||
* 查询工艺表中所有的需要做工艺的物料
|
||
*
|
||
* @return RequestBody List<ProcessRouteVo> routeVoList
|
||
*/
|
||
public List<ProcessRoute> getProcessRouteList(@RequestParam String rooteProdet) {
|
||
List<ProcessRoute> list = iProcessRouteService.pushRawMater(rooteProdet);
|
||
return list;
|
||
}
|
||
|
||
@Log(title = "查询工艺工序集合")
|
||
@SaCheckPermission("system:route:getProcessRoute")
|
||
@PostMapping("/getProcessRoute")
|
||
public List<ProcessRouteXuDTO> getProcessRoute(@RequestParam String rooteProdet) {
|
||
return iProcessRouteService.getProcessRoute(rooteProdet);
|
||
}
|
||
|
||
@Log(title = "推送工艺工序")
|
||
@SaCheckPermission("system:route:pushRouteBom")
|
||
@PostMapping("/pushRouteBom")
|
||
public R<ProcessRoutePushResultDTO> pushRouteBom(@RequestParam String rooteProdet, @RequestParam String groupName) {
|
||
String jsyyDeny = JsyyOrderNo.denyIfMatches(rooteProdet);
|
||
if (jsyyDeny != null) {
|
||
return R.fail(jsyyDeny);
|
||
}
|
||
ProcessOrderPro orderForLock = processOrderProService.selectByProjectNumber(rooteProdet);
|
||
if (orderForLock != null) {
|
||
String statusDeny = ProjectStatusLock.denyIfLocked(orderForLock.getBomStatus(), ProjectStatusLock.Action.PUSH_ROUTE);
|
||
if (statusDeny != null) {
|
||
return R.fail(statusDeny);
|
||
}
|
||
}
|
||
return iProcessRouteService.pushRouteBom(rooteProdet, groupName);
|
||
}
|
||
|
||
/**
|
||
* 按物料推送工艺:只推所选物料,不改项目整单状态。
|
||
*/
|
||
@Log(title = "按物料推送工艺工序")
|
||
@SaCheckPermission("system:route:pushRouteBom")
|
||
@PostMapping("/pushRouteBomByMaterial")
|
||
public R<ProcessRoutePushResultDTO> pushRouteBomByMaterial(@RequestParam String rooteProdet,
|
||
@RequestParam String groupName,
|
||
@RequestParam String materialCodes) {
|
||
String jsyyDeny = JsyyOrderNo.denyIfMatches(rooteProdet);
|
||
if (jsyyDeny != null) {
|
||
return R.fail(jsyyDeny);
|
||
}
|
||
ProcessOrderPro orderForLock = processOrderProService.selectByProjectNumber(rooteProdet);
|
||
if (orderForLock != null) {
|
||
String statusDeny = ProjectStatusLock.denyIfLocked(orderForLock.getBomStatus(), ProjectStatusLock.Action.PUSH_ROUTE);
|
||
if (statusDeny != null) {
|
||
return R.fail(statusDeny);
|
||
}
|
||
}
|
||
if (StringUtils.isBlank(materialCodes)) {
|
||
return R.fail("请选择要推送的物料");
|
||
}
|
||
List<String> codes = Arrays.stream(materialCodes.split("[,,;;\\s]+"))
|
||
.map(String::trim)
|
||
.filter(StringUtils::isNotBlank)
|
||
.distinct()
|
||
.collect(Collectors.toList());
|
||
return iProcessRouteService.pushRouteBomByMaterial(rooteProdet, groupName, codes);
|
||
}
|
||
|
||
@Log(title = "审核通过(发送钉钉通知,不推送金蝶)")
|
||
@SaCheckPermission("system:route:approveRoute")
|
||
@PostMapping("/approveRoute")
|
||
public R<Void> approveRoute(@RequestParam String rooteProdet) {
|
||
return processOrderProService.approveRoute(rooteProdet);
|
||
}
|
||
|
||
/**
|
||
* 查询项目列表
|
||
*/
|
||
@Log(title = "获取所有的项目令号")
|
||
@SaCheckPermission("system:route:getDistinctProjectCodes")
|
||
@GetMapping("/getDistinctProjectCodes")
|
||
public ResponseEntity<List<String>> getDistinctProjectCodes(String query) {
|
||
return ResponseEntity.ok(iProcessRouteService.getDistinctProjectCodes(query));
|
||
}
|
||
|
||
@Log(title = "获取工序列表")
|
||
@SaCheckPermission("system:route:getProcessInfoList")
|
||
@GetMapping("/getProcessInfoList")
|
||
public ResponseEntity<List<String>> getProcessInfoList(String query) {
|
||
return ResponseEntity.ok(iProcessRouteService.getProcessInfoList(query));
|
||
}
|
||
|
||
@Log(title = "根据生产令号获取生产编号==》获取计划编号")
|
||
@SaCheckPermission("system:route:getSelecPlanRouteList")
|
||
@PostMapping("/getSelecPlanRouteList")
|
||
public List<CombinedDTO> getSelecPlanRouteList(@RequestParam String rooteProdet) {
|
||
return iProcessRouteService.getSelecPlanRouteList(rooteProdet);
|
||
}
|
||
|
||
@Log(title = "生成工序计划单PDF")
|
||
@SaCheckPermission("system:route:generatePDFs")
|
||
@GetMapping("/generatePDFs")
|
||
public void generatePDFs(String rooteProdet,
|
||
@RequestParam(value = "materialCode", required = false) String materialCode,
|
||
HttpServletResponse response) throws IOException {
|
||
if (StringUtils.isBlank(rooteProdet)) {
|
||
throw new ServiceException("生产令号不能为空");
|
||
}
|
||
// 有物料编码:单份 PDF;无物料编码:令号下全部 PDF 压缩包
|
||
String filePath = iProcessRouteService.generatePDFs(rooteProdet, materialCode);
|
||
File outFile = new File(filePath);
|
||
if (!outFile.exists()) {
|
||
throw new FileNotFoundException("文件未找到: " + filePath);
|
||
}
|
||
if (outFile.length() <= 0) {
|
||
throw new IOException("文件为空: " + filePath);
|
||
}
|
||
boolean isPdf = filePath.toLowerCase().endsWith(".pdf");
|
||
boolean isZip = filePath.toLowerCase().endsWith(".zip");
|
||
if (!isPdf && !isZip) {
|
||
throw new IOException("未生成PDF或压缩包: " + filePath);
|
||
}
|
||
response.setContentType(isPdf ? "application/pdf" : "application/zip");
|
||
String encoded = URLEncoder.encode(outFile.getName(), "UTF-8").replaceAll("\\+", "%20");
|
||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encoded);
|
||
try (FileInputStream fis = new FileInputStream(outFile);
|
||
OutputStream outputStreams = response.getOutputStream()) {
|
||
byte[] buffer = new byte[1024];
|
||
int len;
|
||
while ((len = fis.read(buffer)) != -1) {
|
||
outputStreams.write(buffer, 0, len);
|
||
}
|
||
outputStreams.flush();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 下载 PLM 图纸/模型。后端校验项目来源后转发文件服务。
|
||
*/
|
||
@Log(title = "下载图纸模型", businessType = BusinessType.EXPORT)
|
||
@SaCheckPermission("system:route:downloadDrawingModel")
|
||
@GetMapping("/downloadDrawingModel")
|
||
public void downloadDrawingModel(@RequestParam String productionOrderNo,
|
||
@RequestParam String materialCode,
|
||
@RequestParam String fileType,
|
||
HttpServletResponse response) {
|
||
drawingFileQueryService.download(productionOrderNo, materialCode, fileType, response);
|
||
}
|
||
|
||
@Log(title = "更新计划时间")
|
||
@SaCheckPermission("system:route:updateProcessPlan")
|
||
@PostMapping("/updateProcessPlan")
|
||
public List<Model> updateProcessPlan(@RequestParam String rooteProdet) throws Exception {
|
||
return iProcessRouteService.updateProcessPlan(rooteProdet);
|
||
}
|
||
|
||
/**
|
||
* 按车间+物料默认条件拉取工序计划,回填工艺时间,保存时部门统一为 018(与 {@link OperationPlanningWorkshopUpdateService} 一致)。
|
||
*/
|
||
@Log(title = "按车间物料更新工序计划(默认条件)")
|
||
@SaCheckPermission("system:route:updateProcessPlan")
|
||
@PostMapping("/updateProcessPlanByWorkshop")
|
||
public R<List<Model>> updateProcessPlanByWorkshop() throws Exception {
|
||
return R.ok(operationPlanningWorkshopUpdateService.updateProcessPlanByWorkshopFilter());
|
||
}
|
||
|
||
/**
|
||
* 同上,BillQuery 过滤条件可自定义(部门 Compare/Value、物料 Compare/Value)。
|
||
*/
|
||
@Log(title = "按车间物料更新工序计划(自定义条件)")
|
||
@SaCheckPermission("system:route:updateProcessPlan")
|
||
@PostMapping("/updateProcessPlanByWorkshopCustom")
|
||
public R<List<Model>> updateProcessPlanByWorkshopCustom(
|
||
@RequestParam String departmentCompare,
|
||
@RequestParam String departmentNumber,
|
||
@RequestParam String productCompare,
|
||
@RequestParam String productFilterValue) throws Exception {
|
||
return R.ok(operationPlanningWorkshopUpdateService.updateProcessPlanByWorkshopFilter(
|
||
departmentCompare, departmentNumber, productCompare, productFilterValue));
|
||
}
|
||
|
||
@Log(title = "按车间物料更新工序汇报单(默认条件)")
|
||
@SaCheckPermission("system:route:updateProcessPlan")
|
||
@PostMapping("/updateOperationReportByWorkshop")
|
||
public R<List<Model>> updateOperationReportByWorkshop() throws Exception {
|
||
return R.ok(operationReportWorkshopUpdateService.updateOperationReportByWorkshopFilter());
|
||
}
|
||
|
||
@Log(title = "按车间物料更新工序汇报单(固定条件67/007/17/-MP)")
|
||
@SaCheckPermission("system:route:updateProcessPlan")
|
||
@PostMapping("/updateOperationReportByWorkshopCustom")
|
||
public R<List<Model>> updateOperationReportByWorkshopCustom() throws Exception {
|
||
return R.ok(operationReportWorkshopUpdateService.updateOperationReportByWorkshopFilter(
|
||
"67", "007", "17", "-MP"));
|
||
}
|
||
|
||
/**
|
||
* 更新生产订单的时间
|
||
*/
|
||
@Log(title = "更新生产订单时间")
|
||
@SaCheckPermission("system:route:updateProcesTime")
|
||
@PostMapping("/updateProcesTime")
|
||
public List<Model> updateProcesTime(@RequestParam String rooteProdet) throws Exception {
|
||
return iProcessRouteService.updateProcesTime(rooteProdet);
|
||
}
|
||
|
||
/**
|
||
* 保存工艺路线
|
||
*/
|
||
@SaCheckPermission("system:route:addRoute")
|
||
@Log(title = "保存工艺路线", businessType = BusinessType.INSERT)
|
||
@RepeatSubmit()
|
||
@PostMapping("/addRoute")
|
||
public R<Void> addRoute(@RequestBody List<ProcessRouteVo> list) {
|
||
|
||
return iProcessRouteService.addRoute(list);
|
||
}
|
||
|
||
@SaCheckPermission("system:route:deleteRoute")
|
||
@Log(title = "删除工艺路线", businessType = BusinessType.INSERT)
|
||
@RepeatSubmit()
|
||
@PostMapping("/deleteRoute")
|
||
public List<ProcessRoute> deleteRoute(@RequestBody ProcessRoute processRoute) {
|
||
return iProcessRouteService.deleteRoute(processRoute);
|
||
}
|
||
|
||
@Log(title = "获取材料bom列表")
|
||
@SaCheckPermission("system:route:getBomInfo")
|
||
@PostMapping("/getBomInfo")
|
||
public ResponseEntity<List<MaterialBom>> getProcessMaterialList(@RequestParam(value = "materialCode") String materialCode, @RequestParam(value = "materialName") String materialName, @RequestParam(value = "productionOrderNo") String productionOrderNo) {
|
||
return ResponseEntity.ok(iProcessRouteService.getProcessMaterialList(materialCode, materialName, productionOrderNo));
|
||
}
|
||
|
||
@Log(title = "获取金蝶列表")
|
||
@SaCheckPermission("system:route:getProcessRouteList")
|
||
@GetMapping("/getProcessRouteList")
|
||
public ResponseEntity<List<ProcessRouteJdDTO>> getProcessRouteList(@RequestParam(value = "materialCode") String materialCode, @RequestParam(value = "materialName") String materialName, @RequestParam(value = "productionOrderNo") String productionOrderNo) {
|
||
return ResponseEntity.ok(iProcessRouteService.getProcessRouteList(materialCode, materialName, productionOrderNo));
|
||
}
|
||
|
||
@Log(title = "导入时间", businessType = BusinessType.IMPORT)
|
||
@SaCheckPermission("system:route:importDataTime")
|
||
@PostMapping(value = "/importDataTime", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||
public R<Void> importDataTime(@RequestParam("file") MultipartFile file) throws Exception {
|
||
String originalFilename = file.getOriginalFilename();
|
||
log.info("读取文件名: " + originalFilename);
|
||
ExcelResult<ProcessRouteVo> result = ExcelUtil.importExcelSheet6(file.getInputStream(), ProcessRouteVo.class, true);
|
||
List<ProcessRouteVo> list = result.getList();
|
||
iProcessRouteService.importDataTime(list);
|
||
return R.ok("更新成功");
|
||
}
|
||
|
||
|
||
@Log(title = "导入时间", businessType = BusinessType.IMPORT)
|
||
@SaCheckPermission("system:route:importDataTime123")
|
||
@PostMapping(value = "/importDataTime123", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||
public R<Void> importDataTime123(@RequestParam("file") MultipartFile file) throws Exception {
|
||
DefaultExcelListener<PcRigidChainVo> listener = new DefaultExcelListener<>(true);
|
||
EasyExcel.read(file.getInputStream(), PcRigidChainVo.class, listener).excelType(ExcelTypeEnum.XLS).sheet(1).headRowNumber(4).doRead();
|
||
List<PcRigidChainVo> list1 = listener.getExcelResult().getList();
|
||
for (PcRigidChainVo pcRigidChainVO : list1) {
|
||
log.info("数据 ===============>: " + JSONUtil.toJsonStr(pcRigidChainVO));
|
||
}
|
||
return R.ok("更新成功");
|
||
}
|
||
|
||
|
||
@Log(title = "获取金蝶工艺路线")
|
||
@SaCheckPermission("system:route:getSelectProcessRout1e")
|
||
@PostMapping("/getSelectProcessRout1e")
|
||
public List<ProcessRouteSelectDTO> getSelectProcessRoute1(@RequestParam String materilCode) {
|
||
return iProcessRouteService.getSelectProcessRoute(materilCode);
|
||
}
|
||
|
||
private void processCombinedDTO(CombinedDTO combinedDTO, List<BomDetails> bomDetailsList, List<ProcessRoute> routeList, List<MaterialBom> materialBoms, String productionOrderNo) {
|
||
// 处理工序列表
|
||
combinedDTO.getProcesses().forEach(process -> {
|
||
ProcessRoute processRoute = createProcessRoute(process, combinedDTO, productionOrderNo);
|
||
routeList.add(processRoute);
|
||
});
|
||
// 处理物料清单
|
||
combinedDTO.getMaterialUsageDTOList().forEach(materialUsageDTO -> {
|
||
BomDetails bomDetails = createBomDetails(materialUsageDTO, combinedDTO, productionOrderNo);
|
||
bomDetailsList.add(bomDetails);
|
||
});
|
||
// 处理材料BOM
|
||
combinedDTO.getMaterialUsageDTOList().forEach(materialUsageDTO -> {
|
||
MaterialBom materialBom = creatBomMaterial(materialUsageDTO, combinedDTO, productionOrderNo);
|
||
materialBoms.add(materialBom);
|
||
});
|
||
|
||
}
|
||
|
||
private void processMaterAndRoute(JDMaterialAndRoute materialAndRoute, List<BomDetails> bomDetailsList, List<ProcessRoute> routeList, List<MaterialBom> materialBoms, String productionOrderNo) {
|
||
// 处理工序列表
|
||
materialAndRoute.getPlannedProcessVos().forEach(processRouteDTO -> {
|
||
ProcessRoute processRoute = createProcessRoute1(processRouteDTO, materialAndRoute, productionOrderNo);
|
||
routeList.add(processRoute);
|
||
});
|
||
// 处理物料清单
|
||
materialAndRoute.getMaterialUseDTOS().forEach(materialUsageDTO -> {
|
||
BomDetails bomDetails = createBomDetails1(materialUsageDTO, materialAndRoute, productionOrderNo);
|
||
bomDetailsList.add(bomDetails);
|
||
});
|
||
// 处理材料BOM
|
||
materialAndRoute.getMaterialUseDTOS().forEach(materialUsageDTO -> {
|
||
MaterialBom materialBom = creatBomMaterial1(materialUsageDTO, materialAndRoute, productionOrderNo);
|
||
materialBoms.add(materialBom);
|
||
});
|
||
|
||
}
|
||
|
||
private MaterialBom creatBomMaterial1(MaterialUseDTO materialUsageDTO, JDMaterialAndRoute materialAndRoute, String productionOrderNo) {
|
||
MaterialBom materialBom = new MaterialBom();
|
||
materialBom.setProjectNumber(productionOrderNo);
|
||
materialBom.setParentMaterialCode(materialAndRoute.getMaterialCode());
|
||
materialBom.setParentMaterialName(materialAndRoute.getMaterialName());
|
||
materialBom.setMaterialCode(materialUsageDTO.getMaterialCode());
|
||
materialBom.setMaterialName(materialUsageDTO.getMaterialName());
|
||
materialBom.setUnit(materialUsageDTO.getChildUnit());
|
||
materialBom.setMaterialType(materialUsageDTO.getCaizhi());
|
||
//保留四位小数
|
||
materialBom.setQuantity(String.valueOf(BigDecimal.valueOf(materialUsageDTO.getFenzi()).divide(new BigDecimal(materialUsageDTO.getFenmu()), 4, RoundingMode.HALF_UP)));
|
||
return materialBom;
|
||
}
|
||
|
||
private BomDetails createBomDetails1(MaterialUseDTO materialUsageDTO, JDMaterialAndRoute materialAndRoute, String productionOrderNo) {
|
||
BomDetails bomDetails = new BomDetails();
|
||
bomDetails.setTotalWeight(productionOrderNo);
|
||
bomDetails.setFNumber(materialAndRoute.getMaterialCode());
|
||
bomDetails.setFName(materialAndRoute.getMaterialName());
|
||
bomDetails.setPartNumber(materialUsageDTO.getMaterialCode());
|
||
bomDetails.setUnitWeight("是");
|
||
bomDetails.setWareHouse(materialUsageDTO.getChildUnit());
|
||
//子项分子
|
||
bomDetails.setQuantity(String.valueOf(materialUsageDTO.getFenzi()));
|
||
//子项分母
|
||
bomDetails.setDenominator(Double.valueOf(materialUsageDTO.getFenmu()));
|
||
bomDetails.setName(materialUsageDTO.getMaterialName());
|
||
bomDetails.setMaterial(materialUsageDTO.getCaizhi());
|
||
return bomDetails;
|
||
}
|
||
|
||
private ProcessRoute createProcessRoute1(ProcessRouteDTO processRouteDTO, JDMaterialAndRoute materialAndRoute, String productionOrderNo) {
|
||
|
||
ProcessRoute processRoute = new ProcessRoute();
|
||
// 设置路线描述为生产订单号
|
||
processRoute.setRouteDescription(productionOrderNo);
|
||
// 设置工序号
|
||
processRoute.setProcessNo(processRouteDTO != null ? processRouteDTO.getProcessNo() : null);
|
||
// 设置工序名称
|
||
processRoute.setProcessName(processRouteDTO != null ? processRouteDTO.getProcessName() : null);
|
||
|
||
if (materialAndRoute != null && StringUtils.isNotEmpty(materialAndRoute.getMaterialCode())) {
|
||
ImMaterial materialByCode = materialService.getMaterialByCode(materialAndRoute.getMaterialCode());
|
||
if (materialByCode != null && materialByCode.getSingleWeight() != null) {
|
||
processRoute.setDiscWeight(Double.valueOf(String.valueOf(materialByCode.getSingleWeight())));
|
||
} else {
|
||
processRoute.setDiscWeight(null);
|
||
}
|
||
} else {
|
||
processRoute.setDiscWeight(null);
|
||
}
|
||
|
||
processRoute.setMaterial(materialAndRoute != null ? materialAndRoute.getCaizhi() : null);
|
||
// 设置物料代码
|
||
processRoute.setMaterialCode(materialAndRoute != null ? materialAndRoute.getMaterialCode() : null);
|
||
processRoute.setMaterialName(materialAndRoute != null ? materialAndRoute.getMaterialName() : null);
|
||
|
||
processRoute.setWorkCenter(processRouteDTO != null ? processRouteDTO.getWorkCenter() : null);
|
||
// 基本时长
|
||
processRoute.setActivityDuration(processRouteDTO != null ? processRouteDTO.getActivityDuration() : null);
|
||
processRoute.setProcessDescription(processRouteDTO != null ? processRouteDTO.getProcessDescription() : null);
|
||
processRoute.setProcessControl(processRouteDTO != null ? processRouteDTO.getProcessControl() : null);
|
||
|
||
processRoute.setXuStartTime(null);
|
||
processRoute.setXuEndTime(null);
|
||
processRoute.setActivityUnit("分");
|
||
|
||
// 本批数量对应
|
||
processRoute.setBatchQuantity(materialAndRoute != null ? materialAndRoute.getBenpi() : null);
|
||
if (materialAndRoute != null && materialAndRoute.getDantai() != null) {
|
||
processRoute.setFirstBatchQuantity(Double.valueOf(materialAndRoute.getDantai()));
|
||
} else {
|
||
processRoute.setFirstBatchQuantity(null);
|
||
}
|
||
|
||
List<MaterialUseDTO> materialUsageDTOList = materialAndRoute != null ? materialAndRoute.getMaterialUseDTOS() : null;
|
||
if (materialUsageDTOList != null) {
|
||
for (MaterialUseDTO materialUsageDTO : materialUsageDTOList) {
|
||
if (materialUsageDTO != null) {
|
||
processRoute.setUnitQuantity(materialUsageDTO.getFenzi());
|
||
processRoute.setBomMaterial(materialUsageDTO.getCaizhi());
|
||
processRoute.setRawMaterialCode(materialUsageDTO.getMaterialCode());
|
||
processRoute.setRawMaterialName(materialUsageDTO.getMaterialName());
|
||
processRoute.setBomUnit(materialUsageDTO.getChildUnit());
|
||
if ("根".equals(materialUsageDTO.getChildUnit())) {
|
||
if (materialUsageDTO.getFenmu() != null) {
|
||
processRoute.setDiscUsage(String.valueOf(Double.valueOf(materialUsageDTO.getFenmu())));
|
||
} else {
|
||
processRoute.setDiscUsage(null);
|
||
}
|
||
} else {
|
||
if (materialUsageDTO.getFenzi() != null && materialUsageDTO.getFenmu() != null && materialUsageDTO.getFenmu() != 0) {
|
||
processRoute.setDiscUsage(String.valueOf(materialUsageDTO.getFenzi() / materialUsageDTO.getFenmu()));
|
||
} else {
|
||
processRoute.setDiscUsage(null);
|
||
}
|
||
}
|
||
processRoute.setMaterial(materialUsageDTO.getCaizhi());
|
||
} else {
|
||
processRoute.setUnitQuantity(null);
|
||
processRoute.setBomMaterial(null);
|
||
processRoute.setRawMaterialCode(null);
|
||
processRoute.setRawMaterialName(null);
|
||
processRoute.setBomUnit(null);
|
||
processRoute.setDiscUsage(null);
|
||
processRoute.setMaterial(null);
|
||
}
|
||
}
|
||
}
|
||
return processRoute;
|
||
}
|
||
|
||
@Log(title = "生成金蝶标准工艺")
|
||
@SaCheckPermission("system:route:getKindeeExcel")
|
||
@GetMapping("/getKindeeExcel")
|
||
public ResponseEntity<String> getKindeeExcel(String rooteProdet) throws IOException {
|
||
|
||
List<CombinedDTO> planRouteList = iProcessRouteService.getSelecPlanRouteList(rooteProdet);
|
||
List<BomDetails> bomDetailsList = new ArrayList<>();
|
||
List<ProcessRoute> routeList = new ArrayList<>();
|
||
List<MaterialBom> materialBoms = new ArrayList<>();
|
||
|
||
// 将数据写入excel 中
|
||
for (CombinedDTO combinedDTO : planRouteList) {
|
||
processCombinedDTO(combinedDTO, bomDetailsList, routeList, materialBoms, rooteProdet);
|
||
}
|
||
|
||
// 批量插入数据库
|
||
try {
|
||
bomDetailsMapper.insertBatch(bomDetailsList);
|
||
baseMapper.insertBatch(routeList);
|
||
materialBomMapper.insertBatch(materialBoms);
|
||
return ResponseEntity.ok("保存成功");
|
||
} catch (Exception e) {
|
||
// 处理异常并返回失败信息
|
||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("保存失败: " + e.getMessage());
|
||
}
|
||
}
|
||
|
||
private ProcessRoute createProcessRoute(PlannedProcessVo process, CombinedDTO combinedDTO, String productionOrderNo) {
|
||
ProcessRoute processRoute = new ProcessRoute();
|
||
// 设置路线描述为生产订单号
|
||
processRoute.setRouteDescription(productionOrderNo);
|
||
// 设置工序号
|
||
processRoute.setProcessNo(process.getFOperNumber());
|
||
// 设置工序名称
|
||
processRoute.setProcessName(process.getFProcessName());
|
||
String materialCode = combinedDTO.getMaterialCode();
|
||
try {
|
||
List<ProcessRoute> singleWeightList = iProcessRouteService.getSingleWeght(materialCode);
|
||
if (singleWeightList != null && !singleWeightList.isEmpty()) {
|
||
processRoute.setDiscWeight(singleWeightList.get(0).getDiscWeight());
|
||
}
|
||
} catch (Exception e) {
|
||
// 记录日志或采取其他措施
|
||
log.error("Error fetching single weight for material code: " + materialCode, e);
|
||
}
|
||
|
||
// 设置物料代码
|
||
processRoute.setMaterialCode(combinedDTO.getMaterialCode());
|
||
processRoute.setMaterialName(combinedDTO.getMaterialName());
|
||
Double fOperQty = process.getFOperQty();
|
||
long l = fOperQty.longValue();
|
||
processRoute.setWorkCenter(process.getFWorkCenterName());
|
||
// 基本时长
|
||
processRoute.setActivityDuration(process.getFActivity1BaseQty());
|
||
processRoute.setProcessDescription(process.getFOperDescription());
|
||
processRoute.setProcessControl(process.getFOptCtrlCodeIFName());
|
||
processRoute.setXuStartTime(process.getFSeqPlanStartTime());
|
||
processRoute.setXuEndTime(process.getFSeqPlanFinishTime());
|
||
processRoute.setActivityUnit("分");
|
||
// 本批数量对应
|
||
processRoute.setBatchQuantity(process.getFMOQty());
|
||
List<MaterialUsageDTO> materialUsageDTOList = combinedDTO.getMaterialUsageDTOList();
|
||
for (MaterialUsageDTO materialUsageDTO : materialUsageDTOList) {
|
||
processRoute.setUnitQuantity(materialUsageDTO.getFNumerator());
|
||
processRoute.setBomMaterial(materialUsageDTO.getChildMaterial());
|
||
processRoute.setRawMaterialCode(materialUsageDTO.getMaterialCode());
|
||
processRoute.setRawMaterialName(materialUsageDTO.getMaterialName());
|
||
processRoute.setBomUnit(materialUsageDTO.getUnit());
|
||
if (materialUsageDTO.getUnit().equals("根")) {
|
||
processRoute.setDiscUsage(String.valueOf(materialUsageDTO.getFDenominator()));
|
||
} else {
|
||
processRoute.setDiscUsage(String.valueOf(materialUsageDTO.getFNumerator() / materialUsageDTO.getFDenominator()));
|
||
}
|
||
|
||
processRoute.setMaterial(materialUsageDTO.getChildMaterial());
|
||
|
||
}
|
||
return processRoute;
|
||
}
|
||
|
||
private BomDetails createBomDetails(MaterialUsageDTO materialUsageDTO, CombinedDTO combinedDTO, String productionOrderNo) {
|
||
BomDetails bomDetails = new BomDetails();
|
||
bomDetails.setTotalWeight(productionOrderNo);
|
||
bomDetails.setFNumber(combinedDTO.getMaterialCode());
|
||
bomDetails.setFName(combinedDTO.getMaterialName());
|
||
bomDetails.setPartNumber(materialUsageDTO.getMaterialCode());
|
||
bomDetails.setUnitWeight("是");
|
||
bomDetails.setWareHouse(materialUsageDTO.getUnit());
|
||
//子项分子
|
||
bomDetails.setQuantity(String.valueOf(materialUsageDTO.getFNumerator()));
|
||
//子项分母
|
||
bomDetails.setDenominator(materialUsageDTO.getFDenominator());
|
||
bomDetails.setName(materialUsageDTO.getMaterialName());
|
||
if (materialUsageDTO.getItemType().equals("1")) {
|
||
bomDetails.setStats("外购");
|
||
} else {
|
||
bomDetails.setStats("自制");
|
||
}
|
||
|
||
bomDetails.setMaterial(materialUsageDTO.getChildMaterial());
|
||
return bomDetails;
|
||
}
|
||
|
||
private MaterialBom creatBomMaterial(MaterialUsageDTO materialUsageDTO, CombinedDTO combinedDTO, String productionOrderNo) {
|
||
MaterialBom materialBom = new MaterialBom();
|
||
materialBom.setProjectNumber(productionOrderNo);
|
||
materialBom.setParentMaterialCode(combinedDTO.getMaterialCode());
|
||
materialBom.setParentMaterialName(combinedDTO.getMaterialName());
|
||
materialBom.setMaterialCode(materialUsageDTO.getMaterialCode());
|
||
materialBom.setMaterialName(materialUsageDTO.getMaterialName());
|
||
materialBom.setUnit(materialUsageDTO.getUnit());
|
||
materialBom.setMaterialType(materialUsageDTO.getItemType());
|
||
materialBom.setQuantity(String.valueOf(BigDecimal.valueOf(materialUsageDTO.getFNumerator())));
|
||
|
||
return materialBom;
|
||
}
|
||
|
||
|
||
/**
|
||
* 导出工艺路线列表
|
||
*/
|
||
@SaCheckPermission("system:route:getAllRouteAndUse")
|
||
@Log(title = "获取全部工艺路线和物料清单", businessType = BusinessType.OTHER)
|
||
@PostMapping("/getAllRouteAndUse")
|
||
public void getAllRouteAndUse(@RequestParam("rooteProdet") String rooteProdet) {
|
||
log.info("获取项目生产数据表:{}", rooteProdet);
|
||
// 下载Excel模板 (切换为FTP方式)
|
||
// SmbUtil.downloadExcelFiles(rooteProdet);
|
||
|
||
String localDir = "D:\\file";
|
||
String remoteDir = "/2026/" + rooteProdet;
|
||
|
||
R<String> ftpResult = FtpUtil.downloadFtpDirectoryFiles(drawingFtpProperties, remoteDir, localDir);
|
||
if (R.isError(ftpResult)) {
|
||
log.error("FTP下载失败: {}", ftpResult.getMsg());
|
||
} else {
|
||
log.info("FTP下载结果: {}", ftpResult.getMsg());
|
||
}
|
||
|
||
// 构建Excel文件路径
|
||
String ExcelName = "D:\\file\\" + rooteProdet + "汇总表.xlsx";
|
||
File file = new File(ExcelName);
|
||
|
||
if (!file.exists()) {
|
||
throw new ServiceException("文件不存在,请确认路径是否正确");
|
||
}
|
||
//读取file的所有的物料编码
|
||
try {
|
||
|
||
if (file.exists()) {
|
||
// 读取Excel的sheet6 从第三行开始 读取到第4列 无数据的跳过
|
||
DefaultExcelListener<ProcessRouteGetDTO> listener = new DefaultExcelListener<>(true);
|
||
EasyExcel.read(ExcelName, ProcessRouteGetDTO.class, listener).sheet(6).headRowNumber(3).doRead();
|
||
List<ProcessRouteGetDTO> list = listener.getExcelResult().getList();
|
||
List<JDMaterialAndRoute> list1 = iProcessRouteService.getProcessRouteGD(list, rooteProdet);
|
||
|
||
List<BomDetails> bomDetailsList = new ArrayList<>();
|
||
List<ProcessRoute> routeList = new ArrayList<>();
|
||
List<MaterialBom> materialBoms = new ArrayList<>();
|
||
|
||
for (JDMaterialAndRoute materialAndRoute : list1) {
|
||
processMaterAndRoute(materialAndRoute, bomDetailsList, routeList, materialBoms, rooteProdet);
|
||
}
|
||
|
||
bomDetailsMapper.insertBatch(bomDetailsList);
|
||
baseMapper.insertBatch(routeList);
|
||
materialBomMapper.insertBatch(materialBoms);
|
||
}
|
||
} catch (Exception e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 更新生产订单仓库字段
|
||
*/
|
||
@SaCheckPermission("system:route:updateProductionOrders")
|
||
@Log(title = "更新生产订单仓库", businessType = BusinessType.OTHER)
|
||
@PostMapping("/updateProductionOrders")
|
||
public R updateProductionOrders(@RequestParam("rooteProdet") String rooteProdet, @RequestParam("cangKuNum") String cangKuNum) throws Exception {
|
||
//判断此项目是否有
|
||
List<String> list = iProcessRouteService.updateProductionOrders(rooteProdet, cangKuNum);
|
||
return R.ok(list);
|
||
}
|
||
|
||
/**
|
||
* 更新采购申请入库仓库仓库
|
||
*/
|
||
@SaCheckPermission("system:route:updateProductionOrders")
|
||
@Log(title = "更新采购申请单仓库", businessType = BusinessType.OTHER)
|
||
@PostMapping("/updateCgOrders")
|
||
public R updateCgOrders(@RequestParam("rooteProdet") String rooteProdet, @RequestParam("cangKuNum") String cangKuNum) throws Exception {
|
||
//判断此项目是否有
|
||
List<String> list = iProcessRouteService.updateCgOrders(rooteProdet, cangKuNum);
|
||
return R.ok(list);
|
||
}
|
||
|
||
/**
|
||
* 导入前校验「工作中心(加工车间)」:工段合法;
|
||
* 同一物料下机加(006)与装配(007)可混用,铆焊(018)不得与其他车间混用。
|
||
*/
|
||
private static String validateImportWorkCenters(List<ProcessRouteVo> list) {
|
||
if (list == null || list.isEmpty()) {
|
||
return null;
|
||
}
|
||
StringBuilder sb = new StringBuilder();
|
||
validateImportWorkCenterPerRow(list, sb);
|
||
validateImportMaterialSingleWorkshop(list, sb);
|
||
return sb.length() == 0 ? null : sb.toString();
|
||
}
|
||
|
||
/**
|
||
* 逐行:工段非空且在允许列表内
|
||
*/
|
||
private static void validateImportWorkCenterPerRow(List<ProcessRouteVo> list, StringBuilder sb) {
|
||
int seq = 0;
|
||
for (ProcessRouteVo vo : list) {
|
||
if (vo == null) {
|
||
continue;
|
||
}
|
||
Long processNo = vo.getProcessNo();
|
||
String workCenter = vo.getWorkCenter();
|
||
boolean hasProcess = processNo != null;
|
||
boolean hasWorkCenter = StringUtils.isNotBlank(workCenter);
|
||
if (!hasProcess && !hasWorkCenter) {
|
||
continue;
|
||
}
|
||
seq++;
|
||
if (!hasWorkCenter) {
|
||
appendWorkCenterError(sb, seq, processNo, vo.getMaterialCode(), "工作中心不能为空");
|
||
continue;
|
||
}
|
||
try {
|
||
String dept = KingdeeRouteDepartmentHelper.resolveDepartmentFNumber(workCenter);
|
||
log.debug("工段校验通过 第{}条工艺 processNo={} workCenter={} dept={}",
|
||
seq, processNo, KingdeeRouteDepartmentHelper.normalize(workCenter), dept);
|
||
} catch (ServiceException e) {
|
||
appendWorkCenterError(sb, seq, processNo, vo.getMaterialCode(), e.getMessage());
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 同一物料编码下:机加(006)与装配(007)允许混用;铆焊(018)不得与机加/装配混用。
|
||
*/
|
||
private static void validateImportMaterialSingleWorkshop(List<ProcessRouteVo> list, StringBuilder sb) {
|
||
Map<String, LinkedHashSet<String>> materialToDepts = new LinkedHashMap<>();
|
||
Map<String, LinkedHashSet<String>> materialToWorkCenters = new LinkedHashMap<>();
|
||
for (ProcessRouteVo vo : list) {
|
||
if (vo == null || vo.getProcessNo() == null || StringUtils.isBlank(vo.getWorkCenter())) {
|
||
continue;
|
||
}
|
||
String materialCode = StringUtils.trim(vo.getMaterialCode());
|
||
if (StringUtils.isBlank(materialCode)) {
|
||
continue;
|
||
}
|
||
String workCenter = KingdeeRouteDepartmentHelper.normalize(vo.getWorkCenter());
|
||
String dept;
|
||
try {
|
||
dept = KingdeeRouteDepartmentHelper.resolveDepartmentFNumber(workCenter);
|
||
} catch (ServiceException e) {
|
||
continue;
|
||
}
|
||
materialToDepts.computeIfAbsent(materialCode, k -> new LinkedHashSet<>()).add(dept);
|
||
materialToWorkCenters.computeIfAbsent(materialCode, k -> new LinkedHashSet<>()).add(workCenter);
|
||
}
|
||
for (Map.Entry<String, LinkedHashSet<String>> entry : materialToDepts.entrySet()) {
|
||
if (!KingdeeRouteDepartmentHelper.hasIncompatibleWorkshops(entry.getValue())) {
|
||
continue;
|
||
}
|
||
String materialCode = entry.getKey();
|
||
String deptDesc = entry.getValue().stream()
|
||
.map(d -> KingdeeRouteDepartmentHelper.departmentLabel(d) + "(" + d + ")")
|
||
.collect(Collectors.joining("、"));
|
||
String wcDesc = String.join("、", materialToWorkCenters.getOrDefault(materialCode, new LinkedHashSet<>()));
|
||
if (sb.length() > 0) {
|
||
sb.append('\n');
|
||
}
|
||
sb.append("物料 ").append(materialCode)
|
||
.append(" 的工艺路线存在不兼容的加工车间(").append(deptDesc)
|
||
.append("),涉及工段:").append(wcDesc)
|
||
.append(";机加与装配可同物料混用,铆焊车间不得与其他车间混用");
|
||
}
|
||
}
|
||
|
||
private static void appendWorkCenterError(StringBuilder sb, int seq, Long processNo, String materialCode, String msg) {
|
||
if (sb.length() > 0) {
|
||
sb.append('\n');
|
||
}
|
||
sb.append("第").append(seq).append("条工艺");
|
||
if (processNo != null) {
|
||
sb.append(" 工序号=").append(processNo);
|
||
}
|
||
if (StringUtils.isNotBlank(materialCode)) {
|
||
sb.append(" 物料=").append(materialCode);
|
||
}
|
||
sb.append(":").append(msg);
|
||
}
|
||
}
|