采购订单列表推送至企业微信更新20250916

This commit is contained in:
tzy 2025-09-26 14:51:02 +08:00
parent d5a75728b9
commit 6abb92c321
28 changed files with 1095 additions and 267 deletions

View File

@ -7,10 +7,12 @@ import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ResourceUtils;
import java.io.*;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
@ -22,27 +24,29 @@ import java.util.Map;
public class ExcelTemplateProc {
/**
* @param templateFileName
* @param exportFilePathAndName
* @param staticDataMap
* @param dynamicDataMappingList
* @return void
* @description: 根据模版导出Excel入口
* @author susu
* @date 2024/2/20
* 根据模版导出Excel入口
* @param templateFileName 模板文件路径可以是classpath路径 本地绝对路径
* @param exportFilePathAndName 导出文件完整路径
* @param staticDataMap 静态数据
* @param dynamicDataMappingList 动态数据
*/
public static void doExportExcelByTemplateProc(String templateFileName, String exportFilePathAndName,
public static void doExportExcelByTemplateProc(String templateFileName,
String exportFilePathAndName,
Map<String, Object> staticDataMap,
List<DynamicDataMapping> dynamicDataMappingList) throws IOException {
/**
* 1. 从resources下加载模板并替换
* 使用 ResourceUtils 加载文件
*/
File file = ResourceUtils.getFile("classpath:"+templateFileName);
InputStream inputStream = new FileInputStream(file);
InputStream inputStream;
if (templateFileName.startsWith("/") || templateFileName.contains(":")) {
// 绝对路径 (Windows: D:/... Linux: /opt/...)
inputStream = Files.newInputStream(new File(templateFileName).toPath());
} else {
// classpath 路径
ClassPathResource resource = new ClassPathResource(templateFileName);
inputStream = resource.getInputStream();
}
Workbook workbook = dealAllSheetsByTemplate(inputStream, staticDataMap, dynamicDataMappingList);
// 2. 保存到本地
saveExportFile(workbook, exportFilePathAndName);
}
@ -87,15 +91,15 @@ public class ExcelTemplateProc {
Map<String, Object> staticDataMap,
List<DynamicDataMapping> dynamicDataMappingList) throws IOException {
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
// 处理所有sheet页
for (int sheetIndex = 0; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) {
XSSFSheet sheet = workbook.getSheetAt(sheetIndex);
// 按模板处理sheet页
dealSheetDataByTemplate(sheet, staticDataMap, dynamicDataMappingList);
}
return workbook;
}
@ -134,7 +138,7 @@ public class ExcelTemplateProc {
if (row == null) {
continue; // 跳过空行
}
// 添加调试信息检查每一行是否有静态数据占位符
if (row.getFirstCellNum() != -1 && row.getLastCellNum() != -1) {
for (int j = row.getFirstCellNum(); j < row.getLastCellNum(); j++) {
@ -147,10 +151,10 @@ public class ExcelTemplateProc {
}
}
}
// 先处理静态数据再处理动态数据
dealTemplateDataRow(row, null, staticDataMap);
DynamicDataMapping dynamicDataMapping = getDynamicRowDataByMatch(row, dynamicDataMappingList);
if (dynamicDataMapping != null) {
i = getTemplateLastRowIndexAfterDealTemplate(sheet, i, dynamicDataMapping);
@ -177,21 +181,21 @@ public class ExcelTemplateProc {
if (dataMap == null || row == null) {
return;
}
int firstCellNum = row.getFirstCellNum();
int lastCellNum = row.getLastCellNum();
// 检查是否有有效的单元格范围
if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) {
return;
}
// 添加调试信息显示静态数据处理
if (dataPrefix == null || dataPrefix.isEmpty()) {
System.out.println("开始处理静态数据行: " + row.getRowNum() + ", dataMap=" + dataMap.toString());
}
for (int i = firstCellNum; i < lastCellNum; i++) {
XSSFCell cell = row.getCell(i);
if (cell != null) {
@ -224,16 +228,16 @@ public class ExcelTemplateProc {
if (StringUtils.isEmpty(cellValue)) {
return;
}
boolean flag = false;
dataPrefix = StringUtils.isEmpty(dataPrefix) ? "" : (dataPrefix + ".");
for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
// 循环所有因为可能一行有多个占位符
String cellTemplateStr = "{{" + dataPrefix + entry.getKey() + "}}";
if (cellValue.contains(cellTemplateStr)) {
// 替换模版中单元格的数据
String replacementValue = entry.getValue() == null ? "" : entry.getValue().toString();
@ -267,29 +271,29 @@ public class ExcelTemplateProc {
if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) {
return null;
}
// 检查行是否为空
if (row == null) {
return null;
}
int firstCellNum = row.getFirstCellNum();
int lastCellNum = row.getLastCellNum();
// 检查是否有有效的单元格范围
if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) {
return null;
}
for (int j = firstCellNum; j < lastCellNum; j++) {
XSSFCell cell = row.getCell(j);
if (cell == null) {
continue;
}
String value = cell.getStringCellValue();
if (value != null) {
for (DynamicDataMapping dynamicData : dynamicDataMappingList) {
if (value.startsWith("{{" + dynamicData.getDataId() + ".")) {
return dynamicData;
@ -361,20 +365,20 @@ public class ExcelTemplateProc {
if (row == null) {
return false;
}
int firstCellNum = row.getFirstCellNum();
int lastCellNum = row.getLastCellNum();
if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) {
return false;
}
for (int j = firstCellNum; j < lastCellNum; j++) {
XSSFCell cell = row.getCell(j);
if (cell == null) {
continue;
}
String value = cell.getStringCellValue();
if (value != null && value.contains("{{") && value.contains("}}")) {
return true;
@ -390,24 +394,24 @@ public class ExcelTemplateProc {
if (dynamicDataMappingList == null || dynamicDataMappingList.size() < 1) {
return false;
}
if (row == null) {
return false;
}
int firstCellNum = row.getFirstCellNum();
int lastCellNum = row.getLastCellNum();
if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) {
return false;
}
for (int j = firstCellNum; j < lastCellNum; j++) {
XSSFCell cell = row.getCell(j);
if (cell == null) {
continue;
}
String value = cell.getStringCellValue();
if (value != null) {
for (DynamicDataMapping dynamicData : dynamicDataMappingList) {
@ -427,20 +431,20 @@ public class ExcelTemplateProc {
if (row == null) {
return;
}
int firstCellNum = row.getFirstCellNum();
int lastCellNum = row.getLastCellNum();
if (firstCellNum == -1 || lastCellNum == -1 || firstCellNum >= lastCellNum) {
return;
}
for (int j = firstCellNum; j < lastCellNum; j++) {
XSSFCell cell = row.getCell(j);
if (cell == null) {
continue;
}
String value = cell.getStringCellValue();
if (value != null && value.contains("{{") && value.contains("}}")) {
// 清理动态数据占位符保留静态内容

View File

@ -0,0 +1,26 @@
package com.ruoyi.common.result;
import lombok.Data;
@Data
public class BOMUploadResult {
private boolean success; // 是否成功
private String id; // 金蝶内部ID
private String number; // 单据编号
private String errorMessage; // 错误信息失败时才有
public static BOMUploadResult success(String id, String number) {
BOMUploadResult result = new BOMUploadResult();
result.setSuccess(true);
result.setId(id);
result.setNumber(number);
return result;
}
public static BOMUploadResult fail(String errorMessage) {
BOMUploadResult result = new BOMUploadResult();
result.setSuccess(false);
result.setErrorMessage(errorMessage);
return result;
}
}

View File

@ -0,0 +1,179 @@
package com.ruoyi.common.utils;
import java.util.Comparator;
/**
* 版本号排序比较器
* 支持类似 30AD.1, 30AD.2, 30AD.01.1, 30AD.01.2, 30AD.01A.1, 30AD.01A.2 的排序
*
* 排序规则
* 1. 先按主要部分排序 30AD
* 2. 再按数字部分排序 1, 2, 01, 10
* 3. 最后按字母部分排序 A, B
*
* @author system
*/
public class VersionComparator implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
if (s1 == null && s2 == null) {
return 0;
}
if (s1 == null) {
return -1;
}
if (s2 == null) {
return 1;
}
// 按点分割字符串
String[] parts1 = s1.split("\\.");
String[] parts2 = s2.split("\\.");
int maxLength = Math.max(parts1.length, parts2.length);
for (int i = 0; i < maxLength; i++) {
String part1 = i < parts1.length ? parts1[i] : "";
String part2 = i < parts2.length ? parts2[i] : "";
int result = comparePart(part1, part2);
if (result != 0) {
return result;
}
}
return 0;
}
/**
* 比较单个部分
* 支持数字和字母混合的比较
*/
private int comparePart(String part1, String part2) {
if (part1.equals(part2)) {
return 0;
}
// 如果其中一个为空空的小于非空的
if (part1.isEmpty()) {
return -1;
}
if (part2.isEmpty()) {
return 1;
}
// 特殊处理如果都是纯数字按数值比较
Integer num1 = parseNumber(part1);
Integer num2 = parseNumber(part2);
if (num1 != null && num2 != null) {
return num1.compareTo(num2);
}
// 分离数字和字母部分
String[] segments1 = splitNumberAndLetter(part1);
String[] segments2 = splitNumberAndLetter(part2);
// 比较每个段
int maxSegments = Math.max(segments1.length, segments2.length);
for (int i = 0; i < maxSegments; i++) {
String seg1 = i < segments1.length ? segments1[i] : "";
String seg2 = i < segments2.length ? segments2[i] : "";
int result = compareSegment(seg1, seg2);
if (result != 0) {
return result;
}
}
return 0;
}
/**
* 将字符串分离为数字和字母段
* 例如: "01A" -> ["01", "A"]
* "2" -> ["2"]
* "A" -> ["A"]
* "01FV" -> ["01", "FV"]
*/
private String[] splitNumberAndLetter(String str) {
if (str == null || str.isEmpty()) {
return new String[]{""};
}
StringBuilder result = new StringBuilder();
boolean lastWasDigit = false;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
boolean isDigit = Character.isDigit(c);
if (i > 0 && isDigit != lastWasDigit) {
result.append("|");
}
result.append(c);
lastWasDigit = isDigit;
}
return result.toString().split("\\|");
}
/**
* 比较单个段
*/
private int compareSegment(String seg1, String seg2) {
if (seg1.equals(seg2)) {
return 0;
}
if (seg1.isEmpty()) {
return -1;
}
if (seg2.isEmpty()) {
return 1;
}
// 尝试解析为数字
Integer num1 = parseNumber(seg1);
Integer num2 = parseNumber(seg2);
// 如果都是数字按数字比较
if (num1 != null && num2 != null) {
return num1.compareTo(num2);
}
// 如果一个是数字一个是字符串数字优先
if (num1 != null && num2 == null) {
return -1;
}
if (num1 == null && num2 != null) {
return 1;
}
// 如果都是字符串按字符串比较忽略大小写
return seg1.compareToIgnoreCase(seg2);
}
/**
* 尝试将字符串解析为数字
* 如果字符串只包含数字则返回对应的Integer
* 否则返回null
*/
private Integer parseNumber(String str) {
if (str == null || str.isEmpty()) {
return null;
}
try {
// 检查是否只包含数字
if (str.matches("\\d+")) {
return Integer.parseInt(str);
}
} catch (NumberFormatException e) {
// 忽略异常返回null
}
return null;
}
}

View File

@ -3,6 +3,9 @@ package com.ruoyi.system.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
@ -18,16 +21,20 @@ 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.enums.BusinessType;
import com.ruoyi.common.result.BOMUploadResult;
import com.ruoyi.common.utils.JdUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.BomDetails;
import com.ruoyi.system.domain.FigureSave;
import com.ruoyi.system.domain.MaterialProperties;
import com.ruoyi.system.domain.ProcessOrderPro;
import com.ruoyi.system.domain.bo.BomDetailsBo;
import com.ruoyi.system.domain.bo.ProcessOrderProBo;
import com.ruoyi.system.domain.dto.JdValidateBomDTO;
import com.ruoyi.system.domain.dto.JdChildDTO;
import com.ruoyi.system.domain.dto.KindegeeLogDTO;
import com.ruoyi.system.domain.vo.BomDetailsVo;
import com.ruoyi.system.domain.vo.ElectricalMaterialBomVO;
import com.ruoyi.system.mapper.FigureSaveMapper;
import com.ruoyi.system.mapper.ProcessOrderProMapper;
import com.ruoyi.system.runner.JdUtil;
@ -49,6 +56,7 @@ import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import static org.aspectj.bridge.MessageUtil.fail;
@ -354,12 +362,13 @@ public class BomDetailsController extends BaseController {
@PostMapping("/updateFBMaterial")
public R updateFBMaterial(@RequestBody List<Map<String, String>> bomDetailParams) {
List<BomDetails> bomDetailsList = new ArrayList<>();
Set<String> processedMaterials = new HashSet<>(); // 用于跟踪已处理的物料编码
List<KindegeeLogDTO> logDTOS = new ArrayList<>();
List<String> failedMaterials = new ArrayList<>(); // 用于跟踪处理失败的物料
String totalWeight = "";
// 遍历前端传来的数据
for (Map<String, String> param : bomDetailParams) {
String fnumber = param.get("fnumber"); // 物料编码
String fname = param.get("fname"); // 物料编码
totalWeight = param.get("totalWeight"); // 生产令号
// 根据物料编码和生产令号查询
@ -367,15 +376,9 @@ public class BomDetailsController extends BaseController {
log.info("处理物料编码: {}, 生产令号: {}", fnumber, totalWeight);
if (bomDetails != null && !bomDetails.isEmpty()) {
//TODO 处理父级物料
ProcessOrderProBo bo = iProcessOrderProService.selectByProjectNumber(totalWeight);
ProcessOrderPro bo = iProcessOrderProService.selectByProjectNumber(totalWeight);
List<FigureSave> list = iFigureSaveService.selectByProCode(totalWeight);
if (list.size() > 0) {
for (FigureSave figureSave : list) {
String figureNumber = figureSave.getFigureNumber();
List<BomDetails> bomDetails1 = iBomDetailsService.selectByFNumberAndTotalWeight(figureNumber, totalWeight);
}
}
// 第一步处理所有物料的保存
for (BomDetails material : bomDetails) {
// 获取工艺表中的非委外工时
Double fbWorkTime = iProcessRouteService.getFbWorkTime(material);
@ -383,7 +386,6 @@ public class BomDetailsController extends BaseController {
if (material.getPartNumber() != null && material.getName() != null && material.getUnitWeight().equals("")) {
String state = determineState(material);
log.info("开始新增不存在的物料 ==> 物料图号: {}, 物料名称: {}", material.getPartNumber(), material.getName());
try {
int result = loadMaterialPreservation(material, state, fbWorkTime);
if (result == 1) {
@ -405,69 +407,179 @@ public class BomDetailsController extends BaseController {
}
}
// 保存物料清单之前进行BOM校验
if (validateBOM(fnumber, bomDetails)) {
failedMaterials.add(fnumber);
// 第二步BOM校验和上传在循环外部
boolean needUpload = !validateBOM(fnumber, bomDetails);
if (needUpload) {
try {
// 物料清单保存方法
BOMUploadResult bomUploadResult = FBloadBillOfMaterialsPreservation(bomDetails, bo);
if (bomUploadResult.isSuccess()) {
KindegeeLogDTO logDTO = new KindegeeLogDTO();
logDTO.setProjectCode(bo.getProductionOrderNo());
logDTO.setMaterialCode(fnumber); // 使用物料编码而不是单个物料的编码
logDTO.setMaterialName(fname);
logDTO.setCode(bomUploadResult.getNumber());
logDTO.setReason(bomUploadResult.getNumber()+"OK");
logDTOS.add(logDTO);
} else {
KindegeeLogDTO logDTO = new KindegeeLogDTO();
logDTO.setProjectCode(bo.getProductionOrderNo());
logDTO.setMaterialCode(fnumber);
logDTO.setMaterialName(fname);
logDTO.setCode(bomUploadResult.getNumber());
logDTO.setReason(bomUploadResult.getErrorMessage());
logDTOS.add(logDTO);
}
bomDetailsList.addAll(bomDetails);
} catch (Exception e) {
log.error("保存BOM失败, 物料编码: {}, 错误: {}", fnumber, e.getMessage());
failedMaterials.add(fnumber);
}
} else {
KindegeeLogDTO logDTO = new KindegeeLogDTO();
logDTO.setProjectCode(bo.getProductionOrderNo());
logDTO.setMaterialCode(fnumber);
logDTO.setMaterialName(fname);
logDTO.setCode("200");
logDTO.setReason("BOM已存在且一致");
logDTOS.add(logDTO);
log.info("BOM已存在且一致物料编码: {},跳过保存", fnumber);
}
// 物料清单保存方法
FBloadBillOfMaterialsPreservation(bomDetails, bo);
bomDetailsList.addAll(bomDetails);
}
}
//更新项目进度
ProcessOrderProBo processOrderProBo = iProcessOrderProService.selectByProjectNumber(totalWeight);
ProcessOrderPro processOrderProBo = iProcessOrderProService.selectByProjectNumber(totalWeight);
processOrderProBo.setDrawingType( JSONUtil.toJsonStr(logDTOS));
processOrderProBo.setBomStatus(2L);
processOrderProMapper.selectById(processOrderProBo);
processOrderProMapper.updateById(processOrderProBo);
// 返回处理结果
return R.ok("成功", bomDetailsList);
}
private boolean validateBOM(String fnumber, List<BomDetails> bomDetails) {
List<JdValidateBomDTO> JDBomList = JdUtil.getSelectBomList(fnumber);
// 1. 判断BOM是否为空
if (JDBomList == null || JDBomList.isEmpty()) {
log.error("未在金蝶中找到相同的BOM,需要上传,物料编码: {}", fnumber);
return false; // BOM为空,需要上传
log.error("未在金蝶中找到相同的BOM需要上传,物料编码: {}", fnumber);
return false;
}
for (JdValidateBomDTO jdBom : JDBomList) {
// 3. 检查子项数量是否一致
if (jdBom.getChilds().size() != bomDetails.size()) {
continue; // 数量不一致,跳过这个BOM继续检查下一个
// 子项排序 PartNumber -> Name -> PartdiagramCode -> PartdiagramName 保证顺序唯一
bomDetails.sort(Comparator.comparing(BomDetails::getPartNumber, Comparator.nullsFirst(String::compareTo))
.thenComparing(BomDetails::getName, Comparator.nullsFirst(String::compareTo))
.thenComparing(BomDetails::getPartdiagramCode, Comparator.nullsFirst(String::compareTo))
.thenComparing(BomDetails::getPartdiagramName, Comparator.nullsFirst(String::compareTo)));
log.info("开始验证BOM - 物料编码: {}, 金蝶BOM数量: {}, 输入BOM子项数量: {}",
fnumber, JDBomList.size(), bomDetails.size());
for (int bomIndex = 0; bomIndex < JDBomList.size(); bomIndex++) {
JdValidateBomDTO jdBom = JDBomList.get(bomIndex);
List<JdChildDTO> childs = jdBom.getChilds();
log.debug("正在比较第{}个金蝶BOM - 子项数量: {}", bomIndex + 1, childs.size());
childs.sort(Comparator.comparing(JdChildDTO::getPartNumber, Comparator.nullsFirst(String::compareTo))
.thenComparing(JdChildDTO::getName, Comparator.nullsFirst(String::compareTo))
.thenComparing(JdChildDTO::getPartdiagramCode, Comparator.nullsFirst(String::compareTo))
.thenComparing(JdChildDTO::getPartdiagramName, Comparator.nullsFirst(String::compareTo)));
if (childs.size() != bomDetails.size()) {
log.warn("BOM子项数量不一致 - 物料编码: {}, 金蝶BOM子项数量: {}, 输入BOM子项数量: {}",
fnumber, childs.size(), bomDetails.size());
continue; // 数量不一致跳过当前金蝶BOM
}
// 4. 比较每个子项的内容
boolean isMatch = true;
for (int i = 0; i < jdBom.getChilds().size(); i++) {
JdChildDTO jdChild = jdBom.getChilds().get(i);
List<String> mismatchDetails = new ArrayList<>();
for (int i = 0; i < childs.size(); i++) {
JdChildDTO jdChild = childs.get(i);
BomDetails inputBomDetail = bomDetails.get(i);
// 比较物料编码和名称
if (!jdChild.getPartNumber().equals(inputBomDetail.getPartNumber()) || !jdChild.getName().equals(inputBomDetail.getName())) {
isMatch = false;
break;
List<String> itemMismatches = new ArrayList<>();
// 字符串比较null/空格统一处理
if (!equalsStr(jdChild.getPartNumber(), inputBomDetail.getPartNumber())) {
itemMismatches.add(String.format("PartNumber [金蝶:'%s' vs 输入:'%s']",
jdChild.getPartNumber(), inputBomDetail.getPartNumber()));
}
// 比较分子和分母
if (!jdChild.getDenominator().equals(inputBomDetail.getDenominator()) || !jdChild.getQuantity().equals(inputBomDetail.getQuantity())) {
/* if (!equalsStr(jdChild.getName(), inputBomDetail.getName())) {
itemMismatches.add(String.format("Name [金蝶:'%s' vs 输入:'%s']",
jdChild.getName(), inputBomDetail.getName()));
}
*/
if (!equalsStr(jdChild.getPartdiagramCode(), inputBomDetail.getPartdiagramCode())) {
itemMismatches.add(String.format("PartdiagramCode [金蝶:'%s' vs 输入:'%s']",
jdChild.getPartdiagramCode(), inputBomDetail.getPartdiagramCode()));
}
/* if (!equalsStr(jdChild.getPartdiagramName(), inputBomDetail.getPartdiagramName())) {
itemMismatches.add(String.format("PartdiagramName [金蝶:'%s' vs 输入:'%s']",
jdChild.getPartdiagramName(), inputBomDetail.getPartdiagramName()));
}
*/
// 数值比较BigDecimal + stripTrailingZeros
BigDecimal jdQuantity = new BigDecimal(String.valueOf(jdChild.getQuantity())).stripTrailingZeros();
BigDecimal inputQuantity = new BigDecimal(String.valueOf(inputBomDetail.getQuantity())).stripTrailingZeros();
BigDecimal jdDenominator = new BigDecimal(String.valueOf(jdChild.getDenominator())).stripTrailingZeros();
BigDecimal inputDenominator = new BigDecimal(String.valueOf(inputBomDetail.getDenominator())).stripTrailingZeros();
if (jdQuantity.compareTo(inputQuantity) != 0) {
itemMismatches.add(String.format("Quantity [金蝶:%s vs 输入:%s]",
jdQuantity, inputQuantity));
}
if (jdDenominator.compareTo(inputDenominator) != 0) {
itemMismatches.add(String.format("Denominator [金蝶:%s vs 输入:%s]",
jdDenominator, inputDenominator));
}
// 如果有不匹配的字段记录详细信息
if (!itemMismatches.isEmpty()) {
String mismatchInfo = String.format("第%d项不匹配: %s", i + 1, String.join(", ", itemMismatches));
mismatchDetails.add(mismatchInfo);
isMatch = false;
break;
}
// 调试日志
log.debug("对比第{}项 -> PartNumber [{} vs {}], Name [{} vs {}], Code [{} vs {}], DiagramName [{} vs {}], Quantity [{} vs {}], Denominator [{} vs {}]",
i + 1,
jdChild.getPartNumber(), inputBomDetail.getPartNumber(),
jdChild.getName(), inputBomDetail.getName(),
jdChild.getPartdiagramCode(), inputBomDetail.getPartdiagramCode(),
jdChild.getPartdiagramName(), inputBomDetail.getPartdiagramName(),
jdQuantity, inputQuantity,
jdDenominator, inputDenominator);
}
// 如果不匹配打印详细的不一致信息
if (!isMatch) {
log.warn("BOM不匹配详情 - 物料编码: {}", fnumber);
log.warn("金蝶BOM子项数量: {}, 输入BOM子项数量: {}", childs.size(), bomDetails.size());
for (String mismatch : mismatchDetails) {
log.warn(" {}", mismatch);
}
}
if (isMatch) {
// 找到匹配的BOM无需上传
log.info("BOM完全相同物料编码: {},无需上传", fnumber);
return true;
return true; // 找到匹配BOM
}
}
// 所有BOM都不匹配需要上传
log.info("BOM不存在或不一致物料编码: {},需要上传", fnumber);
return false;
return false; // 所有BOM都不匹配
}
// null/空格统一处理方法
private boolean equalsStr(String a, String b) {
String s1 = (a == null || a.trim().isEmpty()) ? "" : a.trim();
String s2 = (b == null || b.trim().isEmpty()) ? "" : b.trim();
return s1.equals(s2);
}
@ -589,7 +701,7 @@ public class BomDetailsController extends BaseController {
}
// FBOM物料清单保存
public void FBloadBillOfMaterialsPreservation(List<BomDetails> bomlist, ProcessOrderProBo bo) {
public BOMUploadResult FBloadBillOfMaterialsPreservation(List<BomDetails> bomlist, ProcessOrderPro bo) {
BomDetails bomDetails1 = bomlist.get(0);
int verification = isMaterialVerification(bomDetails1.getFNumber(), bomDetails1.getFName());
@ -648,7 +760,7 @@ public class BomDetailsController extends BaseController {
List<JsonObject> fTreeEntityList = new ArrayList<>();
for (BomDetails details : bomlist) {
if (bomlist.isEmpty()) {
return;
return null;
}
// 创建FTreeEntity对象并加入FTreeEntity数组
JsonObject fTreeEntityItem = new JsonObject();
@ -673,7 +785,6 @@ public class BomDetailsController extends BaseController {
}
fTreeEntityItem.addProperty("FSupplyType", supplyType);
}
fTreeEntityItem.addProperty("FMATERIALTYPE", "1");
// 创建FMATERIALIDCHILD对象并加入FTreeEntity
JsonObject fMaterialIdChild = new JsonObject();
@ -708,30 +819,23 @@ public class BomDetailsController extends BaseController {
fTreeEntityItem.add("FOWNERID", FOWNERID);
FOWNERID.addProperty("FNumber", "GYS_070");
}
fTreeEntityList.add(fTreeEntityItem);
}
String jsonData = json.toString();
log.info("打印json:" + jsonData);
try {
// 业务对象标识
String formId = "ENG_BOM";
// 调用接口
String resultJson = client.save(formId, jsonData);
// 用于记录结果
Gson gson = new Gson();
// 对返回结果进行解析和校验
RepoRet repoRet = gson.fromJson(resultJson, RepoRet.class);
if (repoRet.getResult().getResponseStatus().isIsSuccess()) {
log.debug("物料清单bom 保存成功===================>" + "图号:" + bomDetails1.getFNumber());
String resultJson = new K3CloudApi().save("ENG_BOM", json.toString());
// 直接用 Jackson 解析
BOMUploadResult result = parseK3Response(resultJson);
if (result.isSuccess()) {
log.info("✅ BOM保存成功, 图号={}", bomDetails1.getFNumber());
} else {
log.error("物料清单bom 保存失败===================>" + "图号:" + bomDetails1.getFNumber());
log.error("❌ BOM保存失败, 图号={}, 错误={}", bomDetails1.getFNumber(), result.getErrorMessage());
}
return result;
} catch (Exception e) {
R.fail(e.getMessage());
log.error("保存BOM异常图号={}, 错误={}", bomDetails1.getFNumber(), e.getMessage(), e);
return BOMUploadResult.fail("保存BOM异常: " + e.getMessage());
}
// 输出生成的Json
}
/*
@ -2031,5 +2135,51 @@ public class BomDetailsController extends BaseController {
}
return 1;
}
public BOMUploadResult parseK3Response(String jsonResponse) {
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonResponse);
JsonNode result = root.path("Result");
JsonNode responseStatus = result.path("ResponseStatus");
boolean isSuccess = responseStatus.path("IsSuccess").asBoolean(false);
if (isSuccess) {
String id = result.path("Id").asText();
String number = result.path("Number").asText();
return BOMUploadResult.success(id, number);
} else {
JsonNode errors = responseStatus.path("Errors");
String errorMsg = (errors != null && errors.isArray() && !errors.isEmpty())
? errors.toString()
: "未知错误";
return BOMUploadResult.fail(errorMsg);
}
} catch (Exception e) {
return BOMUploadResult.fail("解析返回信息失败: " + e.getMessage());
}
}
/**
* 导入BOM
* daorubaon
*
* @param file 导入文件
*/
@Log(title = "明细导入", businessType = BusinessType.IMPORT)
@SaCheckPermission("system:details:import")
@PostMapping(value = "/importData21", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Void> importData21(@RequestPart("file") MultipartFile file) throws Exception {
List<ElectricalMaterialBomVO> electricalMaterialBomVOS = ExcelUtil.importExcel(file.getInputStream(), ElectricalMaterialBomVO.class);
for (ElectricalMaterialBomVO bomVO : electricalMaterialBomVOS) {
BomDetails bomDetails = new BomDetails();
bomDetails.setTotalWeight(bomVO.getProductionOrderNo());
bomDetails.setFNumber(bomVO.getDrawingNo());
bomDetails.setFName(bomVO.getDrawingName());
bomDetails.setPartdiagramCode(bomVO.getParentDrawingNo());
bomDetails.setPartdiagramName(bomVO.getParentPart());
}
return null;
}
}

View File

@ -527,5 +527,17 @@ public class EleMaterialsController extends BaseController {
return R.ok("更新成功");
}
@Log(title = "更新物料单重", businessType = BusinessType.IMPORT)
@SaCheckPermission("system:materials:importEleBom")
@PostMapping(value = "/importEleBom", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Void> importEleBom(@RequestParam("file") MultipartFile file) throws Exception {
String originalFilename = file.getOriginalFilename();
log.info("读取文件名: " + originalFilename);
ExcelResult<JdDanZhong> result = ExcelUtil.importExcelSheet1(file.getInputStream(), JdDanZhong.class, true);
List<JdDanZhong> list = result.getList();
return R.ok("更新成功");
}
}

View File

@ -270,7 +270,7 @@ public class ImMaterialController extends BaseController {
@XxlJob("updateMaterials")
public Boolean updateMaterials() throws Exception {
Date date = new Date();
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<ImMaterial> imMaterials = updateJdMaterial(sdf.format(date));
Boolean result = iImMaterialService.updateByFMid(imMaterials);
@ -284,14 +284,14 @@ public class ImMaterialController extends BaseController {
json.addProperty("FormId", "BD_MATERIAL");
json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus");
JsonArray filterString = new JsonArray();
JsonObject filterObject = new JsonObject();
/*JsonObject filterObject = new JsonObject();
filterObject.addProperty("FieldName", "FForbidStatus"); // 使用传入的 fieldName
filterObject.addProperty("Compare", "105");
filterObject.addProperty("Value", "A");
filterObject.addProperty("Left", "");
filterObject.addProperty("Right", "");
filterObject.addProperty("Logic", 0);
filterString.add(filterObject);
filterString.add(filterObject);*/
JsonObject filterObject1 = new JsonObject();
filterObject1.addProperty("FieldName", "FCreateDate"); // 使用传入的 fieldName
filterObject1.addProperty("Compare", "93");
@ -301,7 +301,7 @@ public class ImMaterialController extends BaseController {
filterObject1.addProperty("Logic", 1);
filterString.add(filterObject1);
JsonObject filterObject2 = new JsonObject();
filterObject2.addProperty("FieldName", "FCreateDate"); // 使用传入的 fieldName
filterObject2.addProperty("FieldName", "FModifyDate"); // 使用传入的 fieldName
filterObject2.addProperty("Compare", "93");
filterObject2.addProperty("Value", date);
filterObject2.addProperty("Left", "");
@ -356,7 +356,7 @@ public class ImMaterialController extends BaseController {
//请求参数要求为json字符串
JsonObject json = new JsonObject();
json.addProperty("FormId", "BD_MATERIAL");
json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus,FIsVmiBusiness");
json.addProperty("FieldKeys", "FMATERIALID,FNumber,FName,F_HBYT_PP,FCategoryID.FNumber,F_HBYT_DZ,F_SVRI_Assistant.FNumber,FErpClsID,FBaseUnitId.FName,FModifyDate,FForbidStatus,FIsVmiBusiness");
JsonArray filterString = new JsonArray();
log.debug("构建查询条件...");
@ -407,6 +407,7 @@ public class ImMaterialController extends BaseController {
* 保存所有的物料编码
* @return List<ImMaterial>
*/
@XxlJob("insertJDMaterial")
@GetMapping("/insertJDMaterial")
public List<ImMaterial> insertJDMaterial() throws Exception {
List<ImMaterial> materialList = loadMaterial();

View File

@ -476,28 +476,28 @@ public class KingdeeWorkCenterDataController extends BaseController {
// 转入
long transInQty = item.getLong("FTransInQty");
//转出小于转入数量
if (transOutQty<transInQty){
// 转换为实体对象
KingdeeWorkCenterDataBo data = new KingdeeWorkCenterDataBo();
data.setWorkCenter(workCenter);
data.setMoBillNo(item.getString("MoBillNo"));
data.setMoOrderNo(item.getString("MoOrderNo"));
data.setMaterialNumber(item.getString("FMaterialNumber"));
data.setMaterialName(item.getString("FMaterialName"));
data.setOperQty(item.getLong("FOperQty"));
data.setTransInQty(transInQty);
data.setTransOutQty(transOutQty);
data.setOperNumber(item.getString("FOperNumber"));
data.setProcessName(item.getString("FProcessName"));
data.setOperPlanStartTime(item.getString("FOperPlanStartTime2"));
data.setOperPlanFinishTime(planFinishTime);
data.setDelayDays(item.getString("FDelayDays"));
kingdeeWorkCenterDataVos.add(data);
Boolean b = iKingdeeWorkCenterDataService.insertByBo(data);
if (!b) {
return R.fail("保存工段数据失败");
if (transOutQty < transInQty) {
// 转换为实体对象
KingdeeWorkCenterDataBo data = new KingdeeWorkCenterDataBo();
data.setWorkCenter(workCenter);
data.setMoBillNo(item.getString("MoBillNo"));
data.setMoOrderNo(item.getString("MoOrderNo"));
data.setMaterialNumber(item.getString("FMaterialNumber"));
data.setMaterialName(item.getString("FMaterialName"));
data.setOperQty(item.getLong("FOperQty"));
data.setTransInQty(transInQty);
data.setTransOutQty(transOutQty);
data.setOperNumber(item.getString("FOperNumber"));
data.setProcessName(item.getString("FProcessName"));
data.setOperPlanStartTime(item.getString("FOperPlanStartTime2"));
data.setOperPlanFinishTime(planFinishTime);
data.setDelayDays(item.getString("FDelayDays"));
kingdeeWorkCenterDataVos.add(data);
Boolean b = iKingdeeWorkCenterDataService.insertByBo(data);
if (!b) {
return R.fail("保存工段数据失败");
}
}
}
}
}
}
@ -677,7 +677,7 @@ public class KingdeeWorkCenterDataController extends BaseController {
if (groupCodes.contains(materialCode)) {
// 将物料添加到对应组的Map中
groupMaterials.computeIfAbsent(groupType, k -> new HashMap<>())
.put(materialCode, stock);
.put(materialCode, stock);
break;
}
}
@ -706,9 +706,9 @@ public class KingdeeWorkCenterDataController extends BaseController {
// 查找对应的安全库存配置
SafetyStock matchingSafetyStock = safetyStocks.stream()
.filter(s -> s.getMaterialCode().equals(materialCode))
.findFirst()
.orElse(null);
.filter(s -> s.getMaterialCode().equals(materialCode))
.findFirst()
.orElse(null);
if (matchingSafetyStock == null) {
log.warn("物料 {} 未找到对应的安全库存配置", materialCode);
@ -744,14 +744,14 @@ public class KingdeeWorkCenterDataController extends BaseController {
int result = baseMapper.insert(newStock);
if (result > 0) {
log.info("成功补充物料到数据库: 物料编码={}, 组={}, 使用模板物料={}",
materialCode, groupType, template.getMaterialCode());
materialCode, groupType, template.getMaterialCode());
allStocks.add(newStock);
} else {
log.error("补充物料到数据库失败: 物料编码={}, 组={}", materialCode, groupType);
}
} catch (Exception e) {
log.error("补充物料到数据库失败: 物料编码={}, 组={}, 错误信息={}",
materialCode, groupType, e.getMessage(), e);
materialCode, groupType, e.getMessage(), e);
}
}
}
@ -763,6 +763,7 @@ public class KingdeeWorkCenterDataController extends BaseController {
log.info("物料处理完成,最终物料总数: {}", allStocks.size());
return allStocks;
}
@Log(title = "金蝶安全库存数据", businessType = BusinessType.OTHER)
@XxlJob("getKuCunTo40SB")
public R<Void> getKuCunTo40SB() {
@ -775,13 +776,13 @@ public class KingdeeWorkCenterDataController extends BaseController {
String materialCode = "40SB/L";
// 获取即时库存
double inventoryQty = Optional.ofNullable(JdUtil.getKuCun(materialCode))
.map(list ->list.stream()
.map(list -> list.stream()
.mapToDouble(JDInventoryDTO::getFBaseQty)
.sum())
.orElse(0.0);
// 查询子项物料的未领料量
double fNoPickedQty = Optional.ofNullable(JdUtil.getWeiLingliao(materialCode))
.map(list ->list.stream()
double fNoPickedQty = Optional.ofNullable(JdUtil.getWeiLingliao(materialCode))
.map(list -> list.stream()
.mapToDouble(JDProductionDTO::getFNoPickedQty)
.sum())
.orElse(0.0);
@ -818,9 +819,8 @@ public class KingdeeWorkCenterDataController extends BaseController {
.append("> **创建时间:** ").append(DateUtil.formatDateTime(new Date())).append("\n");
String part = markdownMsg.toString();
wxRobotUtil.sendMarkdownMsgToWeChatGroup(part, robotId);
String part = markdownMsg.toString();
wxRobotUtil.sendMarkdownMsgToWeChatGroup(part, robotId);
return R.ok();
}
@ -1011,10 +1011,10 @@ public class KingdeeWorkCenterDataController extends BaseController {
for (PurchaseRequestExcelDTO allOrder : allOrders) {
String productionOrderNo = allOrder.getFUCHNText();
String deliveryDate = allOrder.getFArrivalDate();
if (allOrder.getFCloseStatus().equals("A")){
if (allOrder.getFCloseStatus().equals("A")) {
allOrder.setFCloseStatus("未关闭");
}
if (allOrder.getFDocumentStatus().equals("C")){
if (allOrder.getFDocumentStatus().equals("C")) {
allOrder.setFDocumentStatus("已审核");
}
@ -1048,6 +1048,7 @@ public class KingdeeWorkCenterDataController extends BaseController {
log.info("过滤完成,剩余数量: {}", filteredOrders.size());
return filteredOrders;
}
@Log(title = "采购订单和采购申请单临期数据")
@XxlJob("getPurchaseOrder2")
@PostMapping("/getPurchaseOrder2")

View File

@ -119,7 +119,7 @@ public class PcRigidChainController extends BaseController {
}
@SaCheckPermission("system:pcRigidChain:importVariableData')")
@SaCheckPermission("system:pcRigidChain:importVariableData")
@PostMapping("/importVariableData")
public R<List<PcRigidChain>> importVariableData(@RequestParam("file") MultipartFile file) throws Exception {
if (file == null || file.isEmpty()) {
@ -131,8 +131,10 @@ public class PcRigidChainController extends BaseController {
if (originalFilename == null || originalFilename.isEmpty()) {
return R.fail("文件名不能为空");
}
String type = "";
log.info("开始处理文件: {}, 文件大小: {} bytes", originalFilename, fileBytes.length);
String type = "";
if (originalFilename.contains("30D")) {
type = "30D";
} else if (originalFilename.contains("30S")) {
@ -153,28 +155,45 @@ public class PcRigidChainController extends BaseController {
type = "125R";
}
log.info("检测到文件类型: {}", type);
//每个产品都会有 ,,+
List<String> AxialDirection = Arrays.asList("R", "L", "L+R");
List<String> axialDirection = Arrays.asList("R", "L", "L+R");
ExcelReaderBuilder read = EasyExcel.read(new ByteArrayInputStream(fileBytes));
List<PcRigidChain> pcRigidChainsToUpdate = null;
for (ReadSheet readSheet : read.build().excelExecutor().sheetList()) {
List<PcRigidChain> pcRigidChainsToUpdate = new ArrayList<>();
List<ReadSheet> sheetList = read.build().excelExecutor().sheetList();
log.info("Excel文件包含 {} 个工作表", sheetList.size());
for (ReadSheet readSheet : sheetList) {
log.info("正在处理工作表: {} (第{}页)", readSheet.getSheetName(), readSheet.getSheetNo() + 1);
DefaultExcelListener<PcRigidChainVo> excelListener = new DefaultExcelListener<>(true);
EasyExcel.read(new ByteArrayInputStream(fileBytes), PcRigidChainVo.class, excelListener)
.excelType(ExcelTypeEnum.XLS)
.sheet(readSheet.getSheetNo())
.headRowNumber(3)
.headRowNumber(4)
.doRead();
List<PcRigidChainVo> list = excelListener.getExcelResult().getList();
pcRigidChainsToUpdate = new ArrayList<>();
log.info("工作表 {} 解析完成,共读取 {} 行数据", readSheet.getSheetName(), list.size());
// 批量查询数据库中的记录
log.debug("开始查询数据库中类型为 {} 的记录", type);
List<PcRigidChain> chains = iPcRigidChainService.selectPcRigidChainByType(type);
log.info("数据库中共找到 {} 条类型为 {} 的记录", chains.size(), type);
Map<String, PcRigidChain> chainMap = chains.stream()
.collect(Collectors.toMap(c -> c.getTypeName() + "_" + c.getAxialDirection(), c -> c));
log.debug("数据库记录映射表构建完成,共 {} 个键值对", chainMap.size());
for (String s : AxialDirection) {
int processedCount = 0;
int matchedCount = 0;
for (String s : axialDirection) {
log.debug("开始处理轴向方向: {}", s);
for (PcRigidChainVo pcRigidChainVO : list) {
processedCount++;
Long vOne = pcRigidChainVO.getVOne();
String sheetName = readSheet.getSheetName();
String box = "";
@ -187,40 +206,139 @@ public class PcRigidChainController extends BaseController {
}
String typeName = type + "/" + s + "-" + vOne + "/" + box;
String searchKey = typeName + "_" + s;
log.debug("此物料的完整图号:=====================>{}", typeName);
log.debug("处理记录 {}: 完整图号={}, 搜索键={}", processedCount, typeName, searchKey);
// 从缓存中查找
PcRigidChain dbChain = chainMap.get(typeName + "_" + s);
PcRigidChain dbChain = chainMap.get(searchKey);
if (dbChain != null) {
matchedCount++;
log.info("找到匹配记录: {}", typeName);
// 记录修改前的关键字段值
log.info("修改前字段值:");
log.info(" - ID: {}", dbChain.getId());
log.info(" - Journey: {} -> {}", dbChain.getJourney(), vOne);
log.info(" - TypeName: {} -> {}", dbChain.getTypeName(), typeName);
log.info(" - Type: {} -> {}", dbChain.getType(), type);
log.info(" - Box: {} -> {}", dbChain.getBox(), box);
log.info(" - AxialDirection: {} -> {}", dbChain.getAxialDirection(), s);
// 记录Excel中的关键字段值
log.info("Excel数据关键字段:");
log.info(" - VOne: {}", pcRigidChainVO.getVOne());
log.info(" - VTwo: {}", pcRigidChainVO.getVTwo());
log.info(" - VThree: {}", pcRigidChainVO.getVThree());
log.info(" - LOne: {}", pcRigidChainVO.getLOne());
log.info(" - LTwo: {}", pcRigidChainVO.getLTwo());
log.info(" - LThree: {}", pcRigidChainVO.getLThree());
log.info(" - SumWeight: {}", pcRigidChainVO.getSumWeight());
log.info(" - ChainWeight: {}", pcRigidChainVO.getChainWeight());
log.info(" - DynamicLoad: {}", pcRigidChainVO.getDynamicLoad());
log.info(" - DeadLoad: {}", pcRigidChainVO.getDeadLoad());
log.info(" - Speed: {}", pcRigidChainVO.getSpeed());
log.info(" - Efficiency: {}", pcRigidChainVO.getEfficiency());
log.info(" - ChainPitch: {}", pcRigidChainVO.getChainPitch());
log.info(" - PitchRadius: {}", pcRigidChainVO.getPitchRadius());
log.info(" - MinimumAltitude: {}", pcRigidChainVO.getMinimumAltitude());
log.info(" - SingleMeterChainWeight: {}", pcRigidChainVO.getSingleMeterChainWeight());
log.info(" - DrivingBoxWeight: {}", pcRigidChainVO.getDrivingBoxWeight());
log.info(" - ChainBoxWeight: {}", pcRigidChainVO.getChainBoxWeight());
log.info(" - Univalence: {}", pcRigidChainVO.getUnivalence());
// 执行字段复制和更新
BeanUtil.copyProperties(pcRigidChainVO, dbChain, "id");
dbChain.setJourney(vOne);
dbChain.setTypeName(typeName);
dbChain.setType(type);
dbChain.setBox(box);
dbChain.setAxialDirection(s);
// dbChain.setUpdateBy(SecurityUtils.getUsername());
dbChain.setCreateTime(new Date());
dbChain.setUpdateTime(new Date());
// 记录修改后的关键字段值
log.info("修改后字段值:");
log.info(" - Journey: {}", dbChain.getJourney());
log.info(" - TypeName: {}", dbChain.getTypeName());
log.info(" - Type: {}", dbChain.getType());
log.info(" - Box: {}", dbChain.getBox());
log.info(" - AxialDirection: {}", dbChain.getAxialDirection());
log.info(" - LOne: {}", dbChain.getLOne());
log.info(" - LTwo: {}", dbChain.getLTwo());
log.info(" - LThree: {}", dbChain.getLThree());
log.info(" - SumWeight: {}", dbChain.getSumWeight());
log.info(" - ChainWeight: {}", dbChain.getChainWeight());
log.info(" - DynamicLoad: {}", dbChain.getDynamicLoad());
log.info(" - DeadLoad: {}", dbChain.getDeadLoad());
log.info(" - Speed: {}", dbChain.getSpeed());
log.info(" - Efficiency: {}", dbChain.getEfficiency());
log.info(" - ChainPitch: {}", dbChain.getChainPitch());
log.info(" - PitchRadius: {}", dbChain.getPitchRadius());
log.info(" - MinimumAltitude: {}", dbChain.getMinimumAltitude());
log.info(" - SingleMeterChainWeight: {}", dbChain.getSingleMeterChainWeight());
log.info(" - DrivingBoxWeight: {}", dbChain.getDrivingBoxWeight());
log.info(" - ChainBoxWeight: {}", dbChain.getChainBoxWeight());
log.info(" - Univalence: {}", dbChain.getUnivalence());
log.info(" - CreateTime: {}", dbChain.getCreateTime());
log.info(" - UpdateTime: {}", dbChain.getUpdateTime());
pcRigidChainsToUpdate.add(dbChain);
}
}
}
if (!pcRigidChainsToUpdate.isEmpty()) {
for (PcRigidChain pcRigidChain : pcRigidChainsToUpdate) {
int i = pcRigidChainMapper.updateById(pcRigidChain);
if (i > 0) {
log.debug("物料:{}更新成功!!", pcRigidChain.getTypeName());
log.info("物料: {} 已添加到更新列表", typeName);
} else {
log.debug("物料:{}更新失败!!", pcRigidChain.getTypeName());
log.warn("未找到对应的数据库记录: {}", searchKey);
}
}
} else {
return R.fail("没有找到要更新的数据");
}
log.info("工作表 {} 处理完成 - 处理记录数: {}, 匹配成功数: {}",
readSheet.getSheetName(), processedCount, matchedCount);
}
if (!pcRigidChainsToUpdate.isEmpty()) {
log.info("开始批量更新数据库,共 {} 条记录", pcRigidChainsToUpdate.size());
int successCount = 0;
int failCount = 0;
for (PcRigidChain pcRigidChain : pcRigidChainsToUpdate) {
log.info("正在更新物料: {}, ID: {}", pcRigidChain.getTypeName(), pcRigidChain.getId());
// 记录更新前的关键字段值
log.info("更新前数据库字段值:");
log.info(" - Journey: {}", pcRigidChain.getJourney());
log.info(" - LOne: {}", pcRigidChain.getLOne());
log.info(" - LTwo: {}", pcRigidChain.getLTwo());
log.info(" - LThree: {}", pcRigidChain.getLThree());
log.info(" - SumWeight: {}", pcRigidChain.getSumWeight());
log.info(" - ChainWeight: {}", pcRigidChain.getChainWeight());
log.info(" - DynamicLoad: {}", pcRigidChain.getDynamicLoad());
log.info(" - DeadLoad: {}", pcRigidChain.getDeadLoad());
log.info(" - Speed: {}", pcRigidChain.getSpeed());
log.info(" - Efficiency: {}", pcRigidChain.getEfficiency());
log.info(" - ChainPitch: {}", pcRigidChain.getChainPitch());
log.info(" - PitchRadius: {}", pcRigidChain.getPitchRadius());
log.info(" - MinimumAltitude: {}", pcRigidChain.getMinimumAltitude());
log.info(" - SingleMeterChainWeight: {}", pcRigidChain.getSingleMeterChainWeight());
log.info(" - DrivingBoxWeight: {}", pcRigidChain.getDrivingBoxWeight());
log.info(" - ChainBoxWeight: {}", pcRigidChain.getChainBoxWeight());
log.info(" - Univalence: {}", pcRigidChain.getUnivalence());
int i = pcRigidChainMapper.updateById(pcRigidChain);
if (i > 0) {
successCount++;
log.info("物料:{} 更新成功!影响行数: {}", pcRigidChain.getTypeName(), i);
} else {
failCount++;
log.warn("物料:{} 更新失败!影响行数: {}", pcRigidChain.getTypeName(), i);
}
}
log.info("批量更新完成 - 成功: {} 条, 失败: {} 条", successCount, failCount);
} else {
log.warn("没有找到要更新的数据");
return R.fail("没有找到要更新的数据");
}
log.info("Excel导入处理完成共处理 {} 条记录", pcRigidChainsToUpdate.size());
return R.ok("导入成功", pcRigidChainsToUpdate);
}
}

View File

@ -3,12 +3,14 @@ package com.ruoyi.system.controller;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import com.alibaba.excel.EasyExcel;
import com.ruoyi.common.excel.DefaultExcelListener;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.VersionComparator;
import com.ruoyi.common.utils.file.SmbUtil;
import com.ruoyi.common.poi.ExcelTemplateProc;
import com.ruoyi.common.poi.DynamicDataMapping;
@ -60,16 +62,13 @@ import org.springframework.web.multipart.MultipartFile;
@Slf4j
@RequestMapping("/system/orderPro")
public class ProcessOrderProController extends BaseController {
private final IProcessOrderProService iProcessOrderProService;
@Autowired
private final ProductionOrderServiceImpl productionOrderService;
private final IMrpResultCheckService iMrpResultCheckService;
private final IBomDetailsService iBomDetailsService;
private final ProcessOrderProMapper processOrderProMapper;
private final IImMaterialService imMaterialService;
private final ISafetyStockService iSafetyStockService;
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy年MM月dd日");
/**
* 查询项目令号列表
*/
@ -344,7 +343,8 @@ public class ProcessOrderProController extends BaseController {
public List<OverdueProjectVo> getOverdueProjects() {
return iProcessOrderProService.getOverdueProjects();
}
@SaCheckPermission("system:orderPro:geMRPResults")
@Log(title = "获取MRP复核结果", businessType = BusinessType.OTHER)
@PostMapping("/getMRPResults/{id}")
public R<List<MrpResultCheck>> geMRPResults(@PathVariable Long id) {
return iMrpResultCheckService.getMRPResults(id);
@ -371,7 +371,7 @@ public class ProcessOrderProController extends BaseController {
// 1. 读取第一个sheet的数据list - 使用POI直接读取以保留空格
List<ProductionOrderVo> allDataList = readExcelWithPOI(excelName);
List<ProcessRoute> routeList = readExcelPOIRoute(excelName);
// 2. 读取原始表数据
List<BomDataVO> rawDataList = readRawDataTable(rawDataFile);
@ -391,7 +391,7 @@ public class ProcessOrderProController extends BaseController {
if (materialCode != null) {
String drawingNo = item.getDrawingNo();
String drawingName = item.getDrawingName();
if (drawingNo != null && drawingName != null) {
if (drawingName != null) {
ImMaterial material = imMaterialService.selectByCodeAndName(drawingNo, drawingName);
if (material != null) {
//判断是否是VMI物料
@ -406,7 +406,7 @@ public class ProcessOrderProController extends BaseController {
existingVmi.setQuantity(currentQuantity + itemQuantity);
Integer currentBatchQuantity = existingVmi.getBatchQuantity() != null ? existingVmi.getBatchQuantity() : 0;
Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.valueOf(item.getBatchQuantity()) : 0;
Integer itemBatchQuantity = item.getBatchQuantity() != null ? Integer.parseInt(item.getBatchQuantity()) : 0;
existingVmi.setBatchQuantity(currentBatchQuantity + itemBatchQuantity);
found = true;
break;
@ -431,15 +431,44 @@ public class ProcessOrderProController extends BaseController {
|| materialCode.startsWith("009001") || materialCode.startsWith("009081")
|| (remark != null && remark.contains("外购"))) {
// 过滤安全库存如果属于安全库存则进入工艺数据列表
Boolean isSafeStock = iSafetyStockService.isSafeCode(materialCode);
Boolean isSafeStock = iSafetyStockService.isSafeCode(materialCode.trim());
if (isSafeStock) {
// 属于安全库存添加到工艺数据列表
processDataList.add(item);
continue; // 已分类跳过后续检查
} else {
// 不属于安全库存添加到电气外包列表
ElecOutDataVO elecData = convertToElecOutDataVO(item);
elecOutList.add(elecData);
// 不属于安全库存检查是否已存在相同的DrawingNo
boolean found = false;
for (ElecOutDataVO existingElec : elecOutList) {
if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingElec.getDrawingNo())) {
// 将数量和批次数量相加
Double currentQuantity = existingElec.getQuantity() != null ? existingElec.getQuantity() : 0.0;
Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0;
Double newQuantity = currentQuantity + itemQuantity;
existingElec.setQuantity(newQuantity);
// 批次数量相加String类型
String currentBatchQuantity = existingElec.getBatchQuantity() != null ? (existingElec.getBatchQuantity()).toString() : "0";
String itemBatchQuantity = item.getBatchQuantity() != null ? item.getBatchQuantity() : "0";
try {
Integer currentBatch = Integer.valueOf(currentBatchQuantity);
Integer itemBatch = Integer.valueOf(itemBatchQuantity);
String newBatchQuantity = String.valueOf(currentBatch + itemBatch);
existingElec.setBatchQuantity(Integer.valueOf(newBatchQuantity));
} catch (NumberFormatException e) {
// 如果转换失败保持原值
existingElec.setBatchQuantity(Integer.valueOf(currentBatchQuantity));
}
found = true;
break;
}
}
// 如果没有找到相同的DrawingNo则添加新的电气外包数据
if (!found) {
ElecOutDataVO elecData = convertToElecOutDataVO(item);
elecOutList.add(elecData);
}
continue; // 已分类跳过后续检查
}
}
@ -463,7 +492,7 @@ public class ProcessOrderProController extends BaseController {
// 检查是否已存在相同的DrawingNo
boolean found = false;
for (ProductionOrderVo existingProcess : processDataList) {
if (materialCode != null && materialCode.equals(existingProcess.getDrawingNo())) {
if (item.getDrawingNo() != null && item.getDrawingNo().equals(existingProcess.getDrawingNo())) {
// 将数量和批次数量相加
Double currentQuantity = existingProcess.getQuantity() != null ? existingProcess.getQuantity() : 0.0;
Double itemQuantity = item.getQuantity() != null ? item.getQuantity() : 0.0;
@ -491,12 +520,12 @@ public class ProcessOrderProController extends BaseController {
if (!found) {
processDataList.add(item);
}
List<ProcessRouteExcelDTO> excelDTOList = iProcessOrderProService.getRouteAndBomDetail( processDataList,orderPro);
}
// 使用Excel模板文件
String templatePath = "EXCEL模板/生产及工艺计划模版.xlsx";
String templatePath = "D:/java/excel-template/生产及工艺计划模版.xlsx";
String outputPath = "D:/file/" + orderPro.getProductionOrderNo() + "生产及工艺计划表.xlsx";
// 准备模板数据
@ -504,9 +533,29 @@ public class ProcessOrderProController extends BaseController {
staticDataMap.put("productionOrderNo", orderPro.getProductionOrderNo());
staticDataMap.put("productionName", orderPro.getProductionName());
// 添加静态数据调试信息
log.info("静态数据: {}", staticDataMap);
//获取工艺数据信息
List<ProcessRouteExcelDTO> excelDTOList = iProcessOrderProService.getRouteAndBomDetail(routeList,processDataList,orderPro);
excelDTOList.sort(Comparator.comparing(
ProcessRouteExcelDTO::getMaterial,
Comparator.nullsLast((m1, m2) -> {
// 总装部件优先
boolean isTotal1 = "总装部件".equals(m1);
boolean isTotal2 = "总装部件".equals(m2);
if (isTotal1 && !isTotal2) return -1;
if (!isTotal1 && isTotal2) return 1;
if (isTotal1 && isTotal2) return 0;
// 其他材质按字母顺序排序
if (m1 == null && m2 == null) return 0;
if (m1 == null) return 1;
if (m2 == null) return -1;
return m1.compareTo(m2);
})
).thenComparing(
ProcessRouteExcelDTO::getMaterialCode,
Comparator.nullsLast(new VersionComparator())
));
// 准备动态数据映射
List<DynamicDataMapping> dynamicDataMappingList = new ArrayList<>();
@ -551,6 +600,11 @@ public class ProcessOrderProController extends BaseController {
List<Map<String, Object>> evoDataList = convertEVOProductsDataToMapList(evoProductsList);
dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("EVOProductsDataVO", evoDataList));
}
// 添加伊特产品数据
if (!excelDTOList.isEmpty()) {
List<Map<String, Object>> evoRouteDataList = convertRouteDataToMapList(excelDTOList);
dynamicDataMappingList.addAll(DynamicDataMapping.createOneDataList("ProcessRouteExcelDTO", evoRouteDataList));
}
// 使用模板导出Excel
ExcelTemplateProc.doExportExcelByTemplateProc(templatePath, outputPath, staticDataMap, dynamicDataMappingList);
@ -584,6 +638,44 @@ public class ProcessOrderProController extends BaseController {
}
}
private List<ProcessRoute> readExcelPOIRoute(String excelName) {
List<ProcessRoute> resultList = new ArrayList<>();
try (FileInputStream fis = new FileInputStream(excelName);
XSSFWorkbook workbook = new XSSFWorkbook(fis)) {
XSSFSheet sheet = workbook.getSheetAt(6); // 读取第一个sheet
// 从第3行开始读取headRowNumber=2所以从第3行开始
for (int rowIndex = 2; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
XSSFRow row = sheet.getRow(rowIndex);
if (row == null) {
continue;
}
ProcessRoute vo = new ProcessRoute();
// 根据列索引读取数据保留原始空格
vo.setMaterialCode(getCellValueAsString(row.getCell(0))); // 图号
vo.setMaterialName(getCellValueAsString(row.getCell(1))); // 名称
vo.setMaterial(getCellValueAsString(row.getCell(2))); // 数量
vo.setDiscWeight(getCellValueAsDouble(row.getCell(3))); // 单重
vo.setBatchQuantity(getCellValueAsLong(row.getCell(18))); // 批次数量
vo.setUnitQuantity(getCellValueAsDouble(row.getCell(17))); // 批次数量
resultList.add(vo);
}
log.info("使用POI读取Excel成功共{}条记录", resultList.size());
} catch (Exception e) {
log.error("使用POI读取Excel失败", e);
throw new ServiceException("读取Excel文件失败: " + e.getMessage());
}
return resultList;
}
/**
* 读取RawDataTable数据
*/
@ -850,7 +942,7 @@ public class ProcessOrderProController extends BaseController {
elecData.setIndex(null); // ProductionOrder没有index字段
elecData.setDrawingNo(item.getDrawingNo());
elecData.setName(item.getDrawingName());
elecData.setQuantity(item.getQuantity() != null ? item.getQuantity().intValue() : null);
elecData.setQuantity(Double.valueOf(item.getQuantity() != null ? item.getQuantity().intValue() : null));
elecData.setMaterial(item.getMaterial());
elecData.setUnitWeight(item.getSingleWeight() != null ? BigDecimal.valueOf(item.getSingleWeight()) : null);
elecData.setTotalWeight(item.getTotalWeight() != null ? BigDecimal.valueOf(item.getTotalWeight()) : null);
@ -984,14 +1076,16 @@ public class ProcessOrderProController extends BaseController {
}
return mapList;
}
private String formatDate(Date date) {
return date == null ? "" : DATE_FORMAT.format(date);
}
/**
* 转换工艺VO为Map列表用于模板
*/
private List<Map<String, Object>> convertRouteDataToMapList(List<ProcessRouteVo> routeDataList) {
private List<Map<String, Object>> convertRouteDataToMapList(List<ProcessRouteExcelDTO> routeDataList) {
List<Map<String, Object>> mapList = new ArrayList<>();
int index = 1;
for (ProcessRouteVo item : routeDataList) {
for (ProcessRouteExcelDTO item : routeDataList) {
Map<String, Object> map = new HashMap<>();
map.put("index", index);
map.put("routeDescription", item.getRouteDescription()); // 生产令号
@ -1015,10 +1109,10 @@ public class ProcessOrderProController extends BaseController {
map.put("unitQuantity", item.getUnitQuantity()); // 单台数量
map.put("batchQuantity", item.getBatchQuantity()); // 本批数量
map.put("firstBatchQuantity", item.getFirstBatchQuantity()); // 首批数量
map.put("planStartTime", item.getPlanStartTime()); // 计划开始时间
map.put("planEndTime", item.getPlanEndTime()); // 计划结束时间
map.put("xuStartTime", item.getXuStartTime()); // 序开始时间
map.put("xuEndTime", item.getXuEndTime()); // 序结束时间
map.put("planStartTime", formatDate(item.getPlanStartTime()));
map.put("planEndTime", formatDate(item.getPlanEndTime()));
map.put("xuStartTime", formatDate(item.getXuStartTime()));
map.put("xuEndTime", formatDate(item.getXuEndTime()));
mapList.add(map);
index++;
}

View File

@ -27,6 +27,7 @@ import com.ruoyi.system.domain.vo.*;
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.IBomDetailsService;
@ -70,6 +71,7 @@ public class ProcessRouteController extends BaseController {
@Autowired
MaterialBomMapper materialBomMapper;
private final IBomDetailsService iBomDetailsService;
private final ProcessOrderProMapper proMapper;
private Long generateUniqueParentId(Long originalId) {
return originalId + 1000;
}
@ -393,7 +395,7 @@ public class ProcessRouteController extends BaseController {
if (!route.isEmpty()){
return R.fail("项目 "+productionOrderNo+"已导入过工艺 ,请先清空再上传");
}
if (iProcessRouteService.saveData(result.getList(), list)) {
if (iProcessRouteService.saveData(list1, list)) {
return R.ok("上传物料成功");
} else {
return R.fail("导入失败");
@ -475,8 +477,9 @@ public class ProcessRouteController extends BaseController {
public void generatePDFs(String rooteProdet, HttpServletResponse response) throws IOException {
// 调用服务层方法生成 ZIP 文件并获取其路径
String zipFilePath = iProcessRouteService.generatePDFs(rooteProdet);
System.out.println("ZIP 文件路径: " + zipFilePath);
ProcessOrderPro processOrderPro = proMapper.selectByProjectNumber(rooteProdet);
processOrderPro.setBomStatus(5L);
proMapper.updateById(processOrderPro);
// 读取文件为字节数组
File zipFile = new File(zipFilePath);
if (!zipFile.exists()) {

View File

@ -36,16 +36,19 @@ public class BomDetails extends BaseEntity {
*/
@JsonProperty("F_HBYT_BJMC")
private String partdiagramName;
/**
* 父级物料编码
*/
@JsonProperty("FMATERIALID.FNumber")
private String fNumber;
/**
* 父级物料名称
*/
@JsonProperty("FITEMNAME")
private String fName;
/**
* 子项物料单位
*/
@ -57,11 +60,13 @@ public class BomDetails extends BaseEntity {
*/
@JsonProperty("FMATERIALIDCHILD.FNumber")
private String partNumber;
/**
*子项物料名称
*/
@JsonProperty("FCHILDITEMNAME")
private String name;
/**
*属性
*/

View File

@ -86,7 +86,8 @@ public class ImMaterial extends BaseEntity {
/**
* 搜索次数
*/
private Long searchCount;
@JsonProperty("F_HBYT_PP")
private String searchCount;
/**
* 金蝶修改时间当日
*/

View File

@ -0,0 +1,12 @@
package com.ruoyi.system.domain.dto;
import lombok.Data;
@Data
public class KindegeeLogDTO {
private String projectCode;
private String materialCode;
private String materialName;
private String code;
private String reason;
}

View File

@ -6,8 +6,6 @@ import java.util.Date;
@Data
public class ProcessRouteExcelDTO {
private Long id;
/**
@ -35,7 +33,7 @@ public class ProcessRouteExcelDTO {
/**
* 单重KG
*/
private Double discWeight;
private String discWeight;
/**
* 材料BOM物料编码
*/
@ -59,7 +57,7 @@ public class ProcessRouteExcelDTO {
/**
* 材料BOM用量
*/
private Double discUsage;
private String discUsage;
/**
* 材料BOM单位
*/
@ -103,13 +101,13 @@ public class ProcessRouteExcelDTO {
/**
* 单台数量
*/
private Long unitQuantity;
private String unitQuantity;
/**
* 本批数量
*/
private Long batchQuantity;
private String batchQuantity;
/**
* 首批数量

View File

@ -73,7 +73,7 @@ public class EleMaterialsVo {
/**
* 物料值
*/
@ExcelProperty(value = "工时")
@ExcelProperty(value = "工时")
private String materialValue;

View File

@ -21,7 +21,7 @@ public class ElecOutDataVO {
private String name;
/** 数量 */
private Integer quantity;
private Double quantity;
/** 材料 */
private String material;

View File

@ -0,0 +1,93 @@
package com.ruoyi.system.domain.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
* 电气物料BOM
* 2025-09-25
*/
@Data
public class ElectricalMaterialBomVO {
/**
* ID
*/
private Long id;
/**
* 生产令号
*/
@ExcelProperty(value = "生产令号")
private String productionOrderNo;
/**
* 生产图号
*/
@ExcelProperty(value = "物料编码")
private String drawingNo;
/**
* 生产名称
*/
@ExcelProperty(value = "物料名称")
private String drawingName;
/**
* 型号
*/
@ExcelProperty(value = "型号")
private String Model;
/**
* 品牌
*/
@ExcelProperty(value = "品牌")
private Double Brand;
/**
* 单台数量
*/
@ExcelProperty(value = "单台数量")
private Double quantity;
/**
* 批次数量
*/
@ExcelProperty(value = "批次数量")
private String batchQuantity;
/**
* 单位
*/
@ExcelProperty(value = "单位")
private Double unit;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
/**
* 部件名称
*/
@ExcelProperty(value = "上级部件")
private String parentPart;
/**
* 部件图号
*/
@ExcelProperty(value = "上级部件图号")
private String parentDrawingNo;
/**
* 主产品图号
*/
@ExcelProperty(value = "主产品图号")
private String mainProducts;
/**
* 主产品名称
*/
@ExcelProperty(value = "主产品名称")
private String mainProductsName;
}

View File

@ -52,7 +52,7 @@ public class PcRigidChainVo extends BaseEntity
private String box;
/** 行程mm */
@ExcelProperty( "{V1}")
//@ExcelProperty( "{V1}")
private Long journey;
/** 标记号 */

View File

@ -1528,7 +1528,6 @@ public class JdUtil {
needUpdateFields.add("FMATERIALID");
needUpdateFields.add("FStandHourUnitId");
// 创建 NeedReturnFields 数组
JsonArray needReturnFields = new JsonArray();
needReturnFields.add("SubHeadEntity5_FEntryId");
needReturnFields.add("FStdLaborProcessTime");

View File

@ -58,7 +58,7 @@ public interface IProcessOrderProService {
List<ProcessRoute> selectProList(ProcessOrderProBo bo);
ProcessOrderProBo selectByProjectNumber(String routeDescription);
ProcessOrderPro selectByProjectNumber(String routeDescription);
void batchUpdateProjectTimeRanges();
List<ProcessOrderPro> selectByProjectNumbers(Set<String> routeDescSet);
@ -78,5 +78,5 @@ public interface IProcessOrderProService {
List<OverdueProjectVo> getOverdueProjects();
List<ProcessRouteExcelDTO> getRouteAndBomDetail(List<ProductionOrderVo> processDataList,ProcessOrderPro orderPro);
List<ProcessRouteExcelDTO> getRouteAndBomDetail(List<ProcessRoute> routlist,List<ProductionOrderVo> processDataList,ProcessOrderPro orderPro);
}

View File

@ -726,7 +726,7 @@ public class ImMaterialServiceImpl implements IImMaterialService {
*/
@Override
public Boolean insertJDMaterial(List<ImMaterial> materialList) {
if (materialList.size()<0 || materialList.isEmpty()){
if (materialList.isEmpty()){
return false;
}
return baseMapper.insertBatch(materialList);

View File

@ -37,11 +37,9 @@ import java.util.*;
public class MrpResultCheckServiceImpl implements IMrpResultCheckService {
private final MrpResultCheckMapper baseMapper;
private final FigureSaveMapper figureSaveMapper;
private final ProcessOrderProMapper processOrderProMapper;
private final IMaterialBomService iMaterialBomService;
private final IProductionOrderService iProductionOrderService;
private final IBomDetailsService iBomDetailsService;
private final IProcessRouteService iProcessRouteService;
/**
* 查询MRP运算结果复查
@ -281,7 +279,8 @@ public class MrpResultCheckServiceImpl implements IMrpResultCheckService {
baseMapper.insert(vo);
}
processOrderPro.setBomStatus(4L);
processOrderProMapper.updateById(processOrderPro);
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfReader;
@ -28,13 +29,14 @@ import com.ruoyi.system.domain.vo.ProductionOrderVo;
import com.ruoyi.system.listener.FileToZip;
import com.ruoyi.system.listener.SmbUtils;
import com.ruoyi.system.mapper.FigureSaveMapper;
import com.ruoyi.system.mapper.MaterialBomMapper;
import com.ruoyi.system.mapper.ProcessRouteMapper;
import com.ruoyi.system.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.system.domain.bo.ProcessOrderProBo;
@ -138,6 +140,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
lqw.eq(bo.getProjectEndTime() != null, ProcessOrderPro::getProjectEndTime, bo.getProjectEndTime());
lqw.eq(StringUtils.isNotBlank(bo.getUnit()), ProcessOrderPro::getUnit, bo.getUnit());
lqw.eq(bo.getQuantity() != null, ProcessOrderPro::getQuantity, bo.getQuantity());
lqw.eq(bo.getBomStatus() != null, ProcessOrderPro::getBomStatus, bo.getBomStatus());
lqw.eq(bo.getIsEnterpriseStandard() != null, ProcessOrderPro::getIsEnterpriseStandard, bo.getIsEnterpriseStandard());
lqw.eq(bo.getDrawingPath() != null, ProcessOrderPro::getDrawingPath, bo.getDrawingPath());
//按照创建时间排序最新的在顶端
@ -245,18 +248,10 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
* @return
*/
@Override
public ProcessOrderProBo selectByProjectNumber(String routeDescription) {
public ProcessOrderPro selectByProjectNumber(String routeDescription) {
LambdaQueryWrapper<ProcessOrderPro> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ProcessOrderPro::getProductionOrderNo, routeDescription);
ProcessOrderPro processOrderPro = baseMapper.selectOne(wrapper);
ProcessOrderProBo processOrderProBo = new ProcessOrderProBo();
// 添加null检查
if (processOrderPro != null) {
BeanUtils.copyProperties(processOrderPro, processOrderProBo);
}
return processOrderProBo;
return baseMapper.selectOne(wrapper);
}
@ -288,7 +283,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
// 更新至计划模块中
if (earliestStartTime != null && latestEndTime != null) {
ProcessOrderProBo processOrderPro = selectByProjectNumber(projectNumber);
ProcessOrderPro processOrderPro = selectByProjectNumber(projectNumber);
if (processOrderPro != null) {
processOrderPro.setPlanStartTime(earliestStartTime);
@ -359,7 +354,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
pwProductionBill.setProduct(productInfos);
// 6. 调用API
return callDrawingApi(pwProductionBill,orderPro);
return callDrawingApi(pwProductionBill, orderPro);
}
@ -533,7 +528,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
datainfo.setG20(String.valueOf(rigidChain.getGTwenty()));
}
private String callDrawingApi(PwProductionBill pwProductionBill,ProcessOrderProBo orderPro) {
private String callDrawingApi(PwProductionBill pwProductionBill, ProcessOrderProBo orderPro) {
try {
String drawingDate = JSONObject.toJSONString(pwProductionBill);
log.info("请求出图报文=====>{}", drawingDate);
@ -557,7 +552,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
// 判断完成条件processstate=4 表示完成
if ("idle".equals(currentStatus) && processState == 4) {
ProcessOrderPro processOrderPro = new ProcessOrderPro();
BeanUtils.copyProperties(orderPro,processOrderPro);
BeanUtils.copyProperties(orderPro, processOrderPro);
processOrderPro.setBomStatus(1L);//
baseMapper.updateById(processOrderPro);
@ -1072,7 +1067,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
OverdueProjectVo projectVo = new OverdueProjectVo();
projectVo.setProjectCode(processOrderPro.getProductionName());
// projectVo.setDesigner(processOrderPro.getProgramDesigner());
// projectVo.setDesigner(processOrderPro.getProgramDesigner());
projectVo.setStartDate(processOrderPro.getPlanStartTime().toString());
projectVo.setPlanDate(processOrderPro.getPlanEndTime().toString());
@ -1106,6 +1101,7 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
/**
* 递归删除目录及其内容
*
* @param directory 要删除的目录
* @throws IOException 删除失败时抛出异常
*/
@ -1128,21 +1124,170 @@ public class ProcessOrderProServiceImpl implements IProcessOrderProService {
}
}
}
/**
* 将工艺部分 材料BOM部分 合并成ProcessRouteExcelDTO
*
* @return
*/
@Override
public List<ProcessRouteExcelDTO> getRouteAndBomDetail(List<ProductionOrderVo> processDataList,ProcessOrderPro orderPro) {
String proRoot = orderPro.getProductionOrderNo();
//工艺部分
List<ProcessRoute> processRoutes = iProcessRouteService.selectByProjectNumber(proRoot);
List<MaterialBom> materialBoms = materialBomService.selectByProCode(proRoot);
for (ProductionOrderVo productionOrderVo : processDataList) {
ProcessRouteExcelDTO processRouteExcelDTO = new ProcessRouteExcelDTO();
public List<ProcessRouteExcelDTO> getRouteAndBomDetail(List<ProcessRoute> routlist,List<ProductionOrderVo> processDataList, ProcessOrderPro orderPro) {
String proRoot = orderPro.getProductionOrderNo();
// 查出所有工艺路线和BOM
List<ProcessRoute> allRoutes = iProcessRouteService.selectByProjectNumber(proRoot);
List<MaterialBom> allBoms = materialBomService.selectByProCode(proRoot);
// 建立 routlist Map 方便查找
Map<String, ProcessRoute> routeMap1 = routlist.stream()
.filter(r -> r.getMaterialCode() != null)
.collect(Collectors.toMap(ProcessRoute::getMaterialCode, r -> r));
// 合并数据
for (ProcessRoute route : allRoutes) {
ProcessRoute source = routeMap1.get(route.getMaterialCode());
if (source != null) {
copyPropertiesIgnoreNull(source, route); // routlist 的非空字段补充 allRoutes
}
}
List<ProcessRouteExcelDTO> resultList = new ArrayList<>();
// 规范化 key
Function<String, String> normalize = s -> s == null ? "" : s.trim().toUpperCase();
// materialCode 分组工艺路线
Map<String, List<ProcessRoute>> routeMap = allRoutes.stream()
.collect(Collectors.groupingBy(r -> normalize.apply(r.getMaterialCode())));
// parentMaterialCode 分组BOM
Map<String, List<MaterialBom>> bomMap = allBoms.stream()
.collect(Collectors.groupingBy(b -> normalize.apply(b.getParentMaterialCode())));
// 把订单数据做成 Map
Map<String, ProductionOrderVo> orderMap = processDataList.stream()
.collect(Collectors.toMap(vo -> normalize.apply(vo.getDrawingNo()), vo -> vo, (a, b) -> a));
// 合并 material keys
LinkedHashSet<String> materialKeys = new LinkedHashSet<>();
materialKeys.addAll(routeMap.keySet());
orderMap.keySet().stream()
.filter(key -> !materialKeys.contains(key))
.forEach(materialKeys::add);
for (String key : materialKeys) {
List<ProcessRoute> processRoutes = routeMap.getOrDefault(key, Collections.emptyList());
List<MaterialBom> materialBoms = bomMap.getOrDefault(key, Collections.emptyList());
ProductionOrderVo orderVo = orderMap.get(key);
// 优先从 processRoutes 取值其次才用 orderVo最后才用 BOM key
String originalMaterialCode = !processRoutes.isEmpty()
? processRoutes.get(0).getMaterialCode().trim()
: (orderVo != null
? (orderVo.getDrawingNo() != null ? orderVo.getDrawingNo().trim() : null)
: (!materialBoms.isEmpty()
? (materialBoms.get(0).getParentMaterialCode() != null ? materialBoms.get(0).getParentMaterialCode().trim() : null)
: key));
String originalMaterialName = !processRoutes.isEmpty()
? (processRoutes.get(0).getMaterialName() != null ? processRoutes.get(0).getMaterialName().trim() : null)
: (orderVo != null ? (orderVo.getDrawingName() != null ? orderVo.getDrawingName().trim() : null) : null);
String originalMaterial = !processRoutes.isEmpty()
? (processRoutes.get(0).getMaterial() != null ? processRoutes.get(0).getMaterial().trim() : null)
: (orderVo != null ? (orderVo.getMaterial() != null ? orderVo.getMaterial().trim() : null) : null);
Double originalUnitWeight = !processRoutes.isEmpty() ? processRoutes.get(0).getDiscWeight() : (orderVo != null ? orderVo.getSingleWeight() : null);
// 🔹 新增两个数量字段
Double unitQuantity = !processRoutes.isEmpty()
? processRoutes.get(0).getUnitQuantity()
: (orderVo != null ? orderVo.getQuantity() : null);
Double batchQuantity = !processRoutes.isEmpty()
? (processRoutes.get(0).getBatchQuantity() != null ? processRoutes.get(0).getBatchQuantity() : 0)
: (orderVo != null
? (orderVo.getBatchQuantity() != null
? Double.valueOf(orderVo.getBatchQuantity())
: null)
: null);
int maxSize = Math.max(processRoutes.size(), materialBoms.size());
if (maxSize == 0 && orderVo != null) maxSize = 1;
for (int i = 0; i < maxSize; i++) {
ProcessRouteExcelDTO dto = new ProcessRouteExcelDTO();
// 补充名称/材质/单重字段
dto.setMaterialCode(originalMaterialCode);
dto.setMaterialName(originalMaterialName);
dto.setMaterial(originalMaterial);
dto.setDiscWeight(String.valueOf(originalUnitWeight));
dto.setUnitQuantity(String.valueOf(unitQuantity));
dto.setBatchQuantity(String.valueOf(batchQuantity));
// 订单数量信息
if (orderVo != null) {
dto.setRouteDescription(orderVo.getProductionOrderNo());
dto.setUnitQuantity(String.valueOf(orderVo.getQuantity()));
dto.setBatchQuantity(orderVo.getBatchQuantity());
}
// 工艺部分
if (i < processRoutes.size()) {
ProcessRoute route = processRoutes.get(i);
dto.setProcessNo(route.getProcessNo());
dto.setProcessName(route.getProcessName());
dto.setWorkCenter(route.getWorkCenter());
dto.setProcessDescription(route.getProcessDescription());
dto.setProcessControl(route.getProcessControl());
dto.setActivityDuration(route.getActivityDuration());
dto.setActivityUnit(route.getActivityUnit());
dto.setXuStartTime(route.getXuStartTime());
dto.setXuEndTime(route.getXuEndTime());
}
// BOM 部分
if (i < materialBoms.size()) {
MaterialBom bom = materialBoms.get(i);
dto.setRawMaterialCode(bom.getMaterialCode());
dto.setRawMaterialName(bom.getMaterialName());
dto.setBomMaterial(bom.getMaterialType());
dto.setBomUnit(bom.getUnit());
// 单位转换如果单位为mm则数值乘以1000
String quantity = bom.getQuantity();
if ("mm".equals(bom.getUnit()) && quantity != null) {
try {
BigDecimal originalValue = new BigDecimal(quantity);
BigDecimal convertedValue = originalValue.multiply(new BigDecimal("1000"));
dto.setDiscUsage(convertedValue.toString());
} catch (NumberFormatException e) {
// 如果转换失败使用原值
dto.setDiscUsage(quantity);
}
} else {
dto.setDiscUsage(quantity);
}
}
resultList.add(dto);
}
}
return Collections.emptyList();
return resultList;
}
public static String[] getNullPropertyNames(Object source) {
BeanWrapper src = new BeanWrapperImpl(source);
java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();
Set<String> emptyNames = new HashSet<>();
for (java.beans.PropertyDescriptor pd : pds) {
Object srcValue = src.getPropertyValue(pd.getName());
if (srcValue == null) emptyNames.add(pd.getName());
}
return emptyNames.toArray(new String[0]);
}
public static void copyPropertiesIgnoreNull(Object src, Object target) {
BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
}
}

View File

@ -569,19 +569,7 @@ public class ProcessRouteServiceImpl implements IProcessRouteService {
saveBomDetails(bomDetailsVos);
List<ProcessRoute> routeArrayList = new ArrayList<>();
Map<String, Double> inventoryCache = new HashMap<>();
// 批量查询所有需要的库存信息
/* for (ProcessRoute processRoute : processRoutes) {
String materialCode = processRoute.getMaterialCode();
if (!inventoryCache.containsKey(materialCode)) {
try {
Double kucun = JdUtil.getKeyong(materialCode);
inventoryCache.put(materialCode, kucun);
} catch (Exception e) {
log.error("查询库存失败: {}", materialCode, e);
inventoryCache.put(materialCode, 0.0);
}
}
}*/
boolean allEmpty = processRoutes.stream().allMatch(route -> route.getProcessNo() == null && route.getProcessName() == null);
//获取工艺中所有的时间 挑选出最早的时间和最晚的时间
@ -622,8 +610,7 @@ public class ProcessRouteServiceImpl implements IProcessRouteService {
for (ProcessRoute processRoute : processRoutes) {
String materialCode = processRoute.getMaterialCode();
Double kucun = inventoryCache.get(materialCode);
boolean isDuplicateWelding = "组焊件".equals(processRoute.getMaterial())
&& duplicateWeldingMaterials.contains(materialCode);
boolean isDuplicateWelding = "组焊件".equals(processRoute.getMaterial()) && duplicateWeldingMaterials.contains(materialCode);
if (isDuplicateWelding && processedWeldingMaterials.contains(materialCode)) {
continue;
@ -663,7 +650,6 @@ public class ProcessRouteServiceImpl implements IProcessRouteService {
defaultRoute.setProcessControl(null);
defaultRoute.setActivityDuration(null);
defaultRoute.setActivityUnit("");
defaultRoute.setFirstBatchQuantity(kucun);
defaultRoute.setCreateTime(new Date());
defaultRoute.setUpdateTime(new Date());
routeArrayList.add(defaultRoute);
@ -696,11 +682,14 @@ public class ProcessRouteServiceImpl implements IProcessRouteService {
}
} else {
for (ProcessRoute processRoute : processRoutes) {
if (processRoute.getProcessNo() != null || processRoute.getProcessName() != null) {
if (processRoute.getProcessNo() != null
||processRoute.getWorkCenter()!= null
||processRoute.getProcessName() != null
||processRoute.getMaterial().equals("总装部件")) {
processRoute.setActivityUnit("");
processRoute.setCreateTime(new Date());
processRoute.setUpdateTime(new Date());
processRoute.setFirstBatchQuantity(processRoute.getFirstBatchQuantity());
processRoute.setBatchQuantity(processRoute.getBatchQuantity());
processRoute.setUnitQuantity(processRoute.getUnitQuantity());
routeArrayList.add(processRoute);
}

View File

@ -158,9 +158,8 @@ public class SafetyStockServiceImpl implements ISafetyStockService {
public Boolean isSafeCode(String materialCode) {
LambdaQueryWrapper<SafetyStock> safetyStockLambdaQueryWrapper = new LambdaQueryWrapper<>();
safetyStockLambdaQueryWrapper.eq(SafetyStock::getMaterialCode,materialCode);
SafetyStock safetyStock = baseMapper.selectOne(safetyStockLambdaQueryWrapper);
return safetyStock != null;
List<SafetyStock> safetyStocks = baseMapper.selectList(safetyStockLambdaQueryWrapper);
return !safetyStocks.isEmpty();
}
/**

View File

@ -79,7 +79,7 @@ public class WorkProcedureServiceImpl implements IWorkProcedureService {
parentTask.setParent(null);
// 获取工艺订单信息
ProcessOrderProBo processOrderProBo = iProcessOrderProService.selectByProjectNumber(processRoute.getRouteDescription());
ProcessOrderPro processOrderProBo = iProcessOrderProService.selectByProjectNumber(processRoute.getRouteDescription());
Date start = processOrderProBo.getPlanStartTime();
Date end = processOrderProBo.getPlanEndTime();