diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 8444dd5..c56e9d3 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -94,7 +94,7 @@
ruoyi-job
-
+
com.ruoyi
ruoyi-oss
@@ -161,6 +161,22 @@
1.39.0
compile
+
+ cn.dev33
+ sa-token-core
+ 1.39.0
+ compile
+
+
+ cn.dev33
+ sa-token-core
+ 1.39.0
+ compile
+
+
+ org.springframework
+ spring-context
+
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/CallbackController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/CallbackController.java
index 301cdb7..ff4565e 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/CallbackController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/CallbackController.java
@@ -9,6 +9,7 @@ import com.ruoyi.common.dingding.DingCallbackCrypto;
import com.ruoyi.common.dingding.robot.RobotGroupService;
import com.ruoyi.common.dingding.util.DingUtil;
import com.ruoyi.common.dingding.webhook.WebhookClient;
+import com.ruoyi.common.dingding.workflow.SchemeApplicationBpmsEventHandler;
import com.ruoyi.system.controller.KingdeeWorkCenterDataController;
import com.ruoyi.system.domain.ProcessOrderPro;
import com.ruoyi.system.mapper.ProcessOrderProMapper;
@@ -49,6 +50,9 @@ public class CallbackController {
@Autowired
private WebhookClient webhookClient; // 注入 WebhookClient
+ @Autowired
+ private SchemeApplicationBpmsEventHandler schemeApplicationBpmsEventHandler;
+
@PostMapping("/receive")
public Map callBack(@RequestBody(required = false) Map json) {
@@ -216,6 +220,8 @@ public class CallbackController {
eventType = "robot_message";
}
bizLogger.info("事件类型: {}", eventType);
+ //只处理「技术方案表」且「同意」场景
+ schemeApplicationBpmsEventHandler.handleEventPayload(eventType, eventJson.toJSONString());
// 4. 根据EventType分类处理
if ("check_url".equals(eventType)) {
bizLogger.info("测试回调url的正确性");
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/DingTalkController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/DingTalkController.java
index dc0251b..5d6124d 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/DingTalkController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/dingtalk/DingTalkController.java
@@ -2,12 +2,14 @@ package com.ruoyi.web.controller.dingtalk;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.dingding.relay.DingMessageRelayService;
import com.ruoyi.common.dingding.util.DingUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -18,6 +20,8 @@ import com.dingtalk.api.response.OapiV2UserGetResponse;
@RequestMapping("/ding/talk")
public class DingTalkController {
+ private final DingMessageRelayService dingMessageRelayService;
+
/**
* 查询用户详情
*/
@@ -136,4 +140,28 @@ public class DingTalkController {
return R.ok("发送成功", processQueryKey);
}
+
+ /**
+ * 示例:经消息中转平台发送 Markdown 单聊(需启动中转服务并配置 dingtalk.message-relay-base-url)
+ */
+ @GetMapping("/testRelayMarkdown")
+ public R testRelayMarkdown() {
+ StringBuilder markdownMsg = new StringBuilder();
+ markdownMsg.append("### 安全库存提醒\n\n")
+ .append("今日暂无安全库存预警数据。\n\n")
+ .append("**物料编号: 123321**\n")
+ .append("- 物料名称: 测试测试567\n")
+ .append("- 预计可用量: 200\n")
+ .append("- 即时库存: 100\n")
+ .append("- 最大库存: 50\n")
+ .append("---\n")
+ .append("**总数汇总**\n")
+ .append("- 今日安全库存预警数据总数: **50**\n");
+
+ String resp = dingMessageRelayService.sendMarkdownToUser(
+ "test测试",
+ markdownMsg.toString(),
+ Arrays.asList("史国臣", "田志阳"));
+ return R.ok("中转返回", resp);
+ }
}
diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml
index 7e86632..4f4f697 100644
--- a/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/ruoyi-admin/src/main/resources/application-dev.yml
@@ -49,8 +49,10 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
- #url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
- url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+ # 本机双实例:5.7 常用 3306,8.0 常用其它端口(示例 3307);内网库另见下行注释
+ #url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+ #url: jdbc:mysql://localhost:3307/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+ url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: root
# 从库数据源
@@ -58,11 +60,13 @@ spring:
lazy: true
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
- #url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
- url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
+ #url: jdbc:mysql://localhost:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+ #url: jdbc:mysql://localhost:3307/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+ url: jdbc:mysql://192.168.5.121:3306/item_retrieval_salve?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: root
sqlserver:
+ lazy: true
type: com.zaxxer.hikari.HikariDataSource
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://192.168.5.9:1433;databaseName=AIS20241010133631;encrypt=false;trustServerCertificate=true
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index f2d985f..840db38 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -30,7 +30,6 @@ server:
servlet:
# 应用的访问路径
context-path: /
- undertow:
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
max-http-post-size: -1
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
@@ -71,7 +70,7 @@ spring:
# 国际化资源文件路径
basename: i18n/messages
profiles:
- active: '@profiles.active@'
+ active: ${SPRING_PROFILES_ACTIVE:dev}
# 文件上传
servlet:
multipart:
@@ -99,6 +98,15 @@ spring:
# 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false
+plm:
+ pdf:
+ base-dir: D:/plm/pdf
+ separator: ""
+ auth:
+ app-key: plm
+ app-secret: change-me
+ username: plm_bot
+
# Sa-Token配置
sa-token:
# token名称 (同时也是cookie名称)
@@ -296,6 +304,8 @@ management:
logfile:
external-file: ./logs/sys-console.log
dingtalk:
+ # 钉钉消息中转服务地址(本服务 POST {base}/ding/send)
+ message-relay-base-url: http://192.168.5.121:8089
app-key: dingebfrpzqko25vo8w6
app-secret: M_hoza71hmEqsbbeXM-7MBg67EINqi9C6mKj4zWKQysXN-68GCYYc5DZoV0hXVvk
robot-code: dingebfrpzqko25vo8w6
@@ -310,3 +320,15 @@ dingtalk:
route-approval-id: 01201038164023402380
plan-user-id: 313514580637707245,192530665123549546
sync-k3-notify-user-ids: 313514580637707245,351225426738742993,192530665123549546
+ # 技术方案申请表 OA 模板 processCode(钉钉后台复制);留空则不处理 BPMS 事件
+ scheme-application-process-code: PROC-81A79B4D-F0EA-4353-9A91-68FD7E3BCEBB
+ # 整单审批结束且 result=agree 时拉取表单(推荐,与 event.json 顶层 result=agree 一致)
+ scheme-bpms-on-instance-agree: true
+ # 单审批任务结束且 result=agree 时拉取(会多次触发;任务节点可能出现 audit 等非 agree)
+ scheme-bpms-on-task-agree: false
+ # 日志中只输出这些表单控件名称(逗号分隔);留空则输出全部非空字段
+ scheme-application-log-field-names: 方案提交日期,是否收到方案,任务详情
+
+pdf:
+ # PDF字体(支持 classpath: 前缀)
+ font-path: classpath:font/arial unicode ms.ttf
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/DingTalkProperties.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/DingTalkProperties.java
index c169f62..5046ec5 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/config/DingTalkProperties.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/DingTalkProperties.java
@@ -78,6 +78,31 @@ public class DingTalkProperties {
*/
private String syncK3NotifyUserIds;
+ /**
+ * 消息中转服务根地址(如 http://host:8089,不要带尾斜杠也可),为空则不调用中转发送
+ */
+ private String messageRelayBaseUrl;
+
+ /**
+ * 技术方案申请表 OA 模板 processCode(如 PROC-xxx);为空则不处理 BPMS 审批事件
+ */
+ private String schemeApplicationProcessCode = "";
+
+ /**
+ * 是否在 bpms_instance_change 整单结束且 result=agree 时拉取审批详情(推荐)
+ */
+ private boolean schemeBpmsOnInstanceAgree = true;
+
+ /**
+ * 是否在 bpms_task_change 单任务结束且 result=agree 时拉取审批详情(会多次触发,默认关闭)
+ */
+ private boolean schemeBpmsOnTaskAgree = false;
+
+ /**
+ * 拉取详情后日志中输出的控件名称(逗号分隔);为空则输出全部非空表单字段
+ */
+ private String schemeApplicationLogFieldNames = "";
+
/**
* 回调路由 Key
*/
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/dingding/DingTalkStreamService.java b/ruoyi-common/src/main/java/com/ruoyi/common/dingding/DingTalkStreamService.java
index 1df5d51..465d90e 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/dingding/DingTalkStreamService.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/dingding/DingTalkStreamService.java
@@ -11,15 +11,19 @@ import org.springframework.stereotype.Component;
import shade.com.alibaba.fastjson2.JSONObject;
import javax.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
/**
* 钉钉 Stream 模式事件推送服务
*/
@Component
+@RequiredArgsConstructor
public class DingTalkStreamService {
private static final Logger log = LoggerFactory.getLogger(DingTalkStreamService.class);
+ private final com.ruoyi.common.dingding.workflow.SchemeApplicationBpmsEventHandler schemeApplicationBpmsEventHandler;
+
// 钉钉应用的 AppKey
private static final String CLIENT_ID = "dingebfrpzqko25vo8w6";
// 钉钉应用的 AppSecret
@@ -57,6 +61,7 @@ public class DingTalkStreamService {
log.info("BornTime: {}", bornTime);
log.info("BizData: {}", bizData != null ? bizData.toString() : "null");
+ schemeApplicationBpmsEventHandler.handleEventPayload(eventType, bizData != null ? bizData.toJSONString() : "{}");
//处理事件
process(bizData);
//消费成功
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/dingding/util/DingUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/dingding/util/DingUtil.java
index 2f730cd..204ebed 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/dingding/util/DingUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/dingding/util/DingUtil.java
@@ -934,7 +934,14 @@ public class DingUtil {
}
}
-
-
+ /**
+ * 使用 Token 初始化账号 Client(OA 审批 / workflow)
+ */
+ public static com.aliyun.dingtalkworkflow_1_0.Client createWorkflowClient() throws Exception {
+ com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
+ config.protocol = "https";
+ config.regionId = "central";
+ return new com.aliyun.dingtalkworkflow_1_0.Client(config);
+ }
}
diff --git a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
index 9efa0bb..a67dc08 100644
--- a/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
+++ b/ruoyi-extend/ruoyi-monitor-admin/src/main/resources/application.yml
@@ -4,7 +4,7 @@ spring:
application:
name: ruoyi-monitor-admin
profiles:
- active: @profiles.active@
+ active: ${SPRING_PROFILES_ACTIVE:dev}
logging:
config: classpath:logback-plus.xml
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml
index 78e390f..a77aa81 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application-dev.yml
@@ -9,12 +9,14 @@ spring.boot.admin.client:
username: ruoyi
password: 123456
---- # 数据库配置
+--- # 数据库配置(与 ruoyi-admin dev 同一实例;本机 5.7 多为 3306,MySQL8 多为 3307)
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ #url: jdbc:mysql://localhost:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+ url: jdbc:mysql://localhost:3307/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
+ #url: jdbc:mysql://192.168.5.121:3306/item_retrieval-evo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: root
hikari:
diff --git a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
index 6b53fbe..d9d438b 100644
--- a/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
+++ b/ruoyi-extend/ruoyi-xxl-job-admin/src/main/resources/application.yml
@@ -7,7 +7,7 @@ spring:
application:
name: ruoyi-xxl-job-admin
profiles:
- active: @profiles.active@
+ active: ${SPRING_PROFILES_ACTIVE:dev}
mvc:
servlet:
load-on-startup: 0
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java
index 0636a5c..a58068f 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/BomDetailsController.java
@@ -84,6 +84,8 @@ public class BomDetailsController extends BaseController {
private final IProcessOrderProService iProcessOrderProService;
+ private final IProductionOrderService iProductionOrderService;
+
private final ProcessOrderProMapper processOrderProMapper;
private final BomDetailsMapper bomDetailsMapper;
@@ -390,12 +392,17 @@ public class BomDetailsController extends BaseController {
if (bomDetails != null && !bomDetails.isEmpty()) {
//TODO 处理父级物料
ProcessOrderPro bo = iProcessOrderProService.selectByProjectNumber(totalWeight);
-
// 第一步:处理所有物料的保存
for (BomDetails material : bomDetails) {
// 获取工艺表中的非委外工时
Double fbWorkTime = iProcessRouteService.getFbWorkTime(material);
if (material.getPartNumber() != null && material.getName() != null && material.getUnitWeight().equals("否")) {
+ if (material.getPartNumber().startsWith("015") || material.getPartNumber().startsWith("009")) {
+ return R.fail("015和009为管控物料,请先申请");
+ }
+ if (material.getRemarks() != null && material.getRemarks().contains("甲供")) {
+ continue;
+ }
String state = determineState(material);
//判断bom类型是生产还是电气
if (material.getBomType().equals("0")) {
@@ -437,13 +444,13 @@ public class BomDetailsController extends BaseController {
if (needUpload) {
try {
// 物料清单保存方法,判断是电气还是生产
- BOMUploadResult bomUploadResult = FBloadBillOfMaterialsPreservation(bomDetails, bo,groupName);
+ BOMUploadResult bomUploadResult = FBloadBillOfMaterialsPreservation(bomDetails, bo, groupName);
if (bomUploadResult.isSuccess()) {
KindegeeLogDTO logDTO = new KindegeeLogDTO();
logDTO.setProjectCode(bo.getProductionOrderNo());
logDTO.setMaterialCode(fnumber); // 使用物料编码而不是单个物料的编码
logDTO.setCode("200");
- logDTO.setReason("成功上传"+"版本号:"+bomUploadResult.getNumber());
+ logDTO.setReason("成功上传" + "版本号:" + bomUploadResult.getNumber());
logDTOS.add(logDTO);
} else {
KindegeeLogDTO logDTO = new KindegeeLogDTO();
@@ -498,8 +505,8 @@ public class BomDetailsController extends BaseController {
}
//查询工艺路线数量
LambdaQueryWrapper wqroute = new LambdaQueryWrapper<>();
- wqroute.eq(ProcessRoute::getRouteDescription,totalWeight)
- .eq(ProcessRoute::getProcessNo,"10");
+ wqroute.eq(ProcessRoute::getRouteDescription, totalWeight)
+ .eq(ProcessRoute::getProcessNo, "10");
Long routeCount = routeMapper.selectCount(wqroute);
// 计算统计数据
int bomCount = fNumberCount + partdiagramCodeCount;
@@ -771,7 +778,7 @@ public class BomDetailsController extends BaseController {
}
// FBOM物料清单保存
- public BOMUploadResult FBloadBillOfMaterialsPreservation(List bomlist, ProcessOrderPro bo,String groupName) {
+ public BOMUploadResult FBloadBillOfMaterialsPreservation(List bomlist, ProcessOrderPro bo, String groupName) {
BomDetails bomDetails1 = bomlist.get(0);
int verification = isMaterialVerification(bomDetails1.getFNumber(), bomDetails1.getFName());
@@ -928,7 +935,20 @@ public class BomDetailsController extends BaseController {
JsonObject FSVRIAssistant = new JsonObject();
FSVRIAssistant.addProperty("FNumber", materialProperties.getMaterialAttributeId());
model.add("F_SVRI_Assistant", FSVRIAssistant);
-
+ // 单重:与 Excel 批量匹配逻辑一致(production_order.drawing_no → process_route.material_code);图号用物料编码 partNumber;库中未查到则为 null
+ Double dz = iProductionOrderService.resolveSingleWeightByMaterialCode(bomDetails.getPartNumber());
+ if (dz == null) {
+ if (bomDetails.getDanZhong() != null && bomDetails.getSingleWeghit() != null) {
+ dz = bomDetails.getSingleWeghit();
+ } else if (bomDetails.getDanZhong() != null) {
+ dz = bomDetails.getDanZhong();
+ } else if (bomDetails.getSingleWeghit() != null) {
+ dz = bomDetails.getSingleWeghit();
+ }
+ }
+ if (dz != null) {
+ model.addProperty("F_HBYT_DZ", dz);
+ }
// 创建FMaterialGroup对象,并加入Model
JsonObject fMaterialGroup = new JsonObject();
fMaterialGroup.addProperty("FNumber", "XM-100");
@@ -946,6 +966,9 @@ public class BomDetailsController extends BaseController {
// 创建FCategoryID对象,并加入SubHeadEntity
JsonObject fCategoryID = new JsonObject();
+ if(bomDetails.getFNumber().contains("-MP")){
+ fCategoryID.addProperty("FNumber", "007");
+ }
fCategoryID.addProperty("FNumber", "CHLB05_SYS");
subHeadEntity.add("FCategoryID", fCategoryID);
@@ -1094,7 +1117,7 @@ public class BomDetailsController extends BaseController {
/*
* FB父级物料保存接口
*/
- public static int loadFBMaterialPreservation(BomDetails bomDetail) {
+ public int loadFBMaterialPreservation(BomDetails bomDetail) {
K3CloudApi client = new K3CloudApi();
// 创建一个空的JsonObject
JsonObject json = new JsonObject();
@@ -1117,7 +1140,20 @@ public class BomDetailsController extends BaseController {
model.add("FMaterialGroup", fMaterialGroup);
model.addProperty("FIsHandleReserve", true);
-
+ // 单重:与 Excel 批量匹配逻辑一致(production_order.drawing_no → process_route.material_code);图号用物料编码 partNumber;库中未查到则为 null
+ Double dz = iProductionOrderService.resolveSingleWeightByMaterialCode(bomDetail.getPartNumber());
+ if (dz == null) {
+ if (bomDetail.getDanZhong() != null && bomDetail.getSingleWeghit() != null) {
+ dz = bomDetail.getSingleWeghit();
+ } else if (bomDetail.getDanZhong() != null) {
+ dz = bomDetail.getDanZhong();
+ } else if (bomDetail.getSingleWeghit() != null) {
+ dz = bomDetail.getSingleWeghit();
+ }
+ }
+ if (dz != null) {
+ model.addProperty("F_HBYT_DZ", dz);
+ }
// 创建SubHeadEntity对象,并加入Model
JsonObject subHeadEntity = new JsonObject();
model.add("SubHeadEntity", subHeadEntity);
@@ -1127,6 +1163,9 @@ public class BomDetailsController extends BaseController {
// 创建FCategoryID对象,并加入SubHeadEntity
JsonObject fCategoryID = new JsonObject();
+ if (bomDetail.getFNumber().contains("-MP")){
+ fCategoryID.addProperty("FNumber", "007");//
+ }
fCategoryID.addProperty("FNumber", "CHLB05_SYS");// 产成品
subHeadEntity.add("FCategoryID", fCategoryID);
@@ -1512,11 +1551,19 @@ public class BomDetailsController extends BaseController {
json.add("Model", model);
model.addProperty("FMATERIALID", 0);
- //单重
- if (!(bomDetails1.getDanZhong() == null) && !(bomDetails1.getSingleWeghit() == null)) {
- model.addProperty("F_HBYT_DZ", bomDetails1.getSingleWeghit());
- } else {
- model.addProperty("F_HBYT_DZ", bomDetails1.getDanZhong());
+ // 单重:与 Excel 批量匹配逻辑一致(production_order.drawing_no → process_route.material_code);图号用物料编码 partNumber;库中未查到则为 null
+ Double dz = iProductionOrderService.resolveSingleWeightByMaterialCode(bomDetails1.getPartNumber());
+ if (dz == null) {
+ if (bomDetails1.getDanZhong() != null && bomDetails1.getSingleWeghit() != null) {
+ dz = bomDetails1.getSingleWeghit();
+ } else if (bomDetails1.getDanZhong() != null) {
+ dz = bomDetails1.getDanZhong();
+ } else if (bomDetails1.getSingleWeghit() != null) {
+ dz = bomDetails1.getSingleWeghit();
+ }
+ }
+ if (dz != null) {
+ model.addProperty("F_HBYT_DZ", dz);
}
model.addProperty("FSpecification", bomDetails1.getRemarks());
model.addProperty("FNumber", bomDetails1.getPartNumber());
@@ -1544,9 +1591,9 @@ public class BomDetailsController extends BaseController {
subHeadEntity.addProperty("FErpClsID", states);
subHeadEntity.addProperty("FFeatureItem", "1");
-
+ //存货类别
JsonObject fCategoryID = new JsonObject();
- if (states.equals("2") || states.equals("3")) {
+ if (states.equals("2") || states.equals("3")||bomDetails1.getPartNumber().contains("-MP")) {
fCategoryID.addProperty("FNumber", "007");
} else {
fCategoryID.addProperty("FNumber", "006");
@@ -1834,7 +1881,7 @@ public class BomDetailsController extends BaseController {
// 创建SubHeadEntity对象,并加入Model
JsonObject subHeadEntity = new JsonObject();
model.add("SubHeadEntity", subHeadEntity);
- if (bomDetails1.getWareHouse().contains("伊特")){
+ if (bomDetails1.getWareHouse().contains("伊特")) {
subHeadEntity.addProperty("FErpClsID", "2");
}
subHeadEntity.addProperty("FErpClsID", "1");
@@ -2260,7 +2307,7 @@ public class BomDetailsController extends BaseController {
List list = new ArrayList<>();
if (rows != null) {
for (ElectricalMaterialBomVO bomVO : rows) {
- if(bomVO.getRemark().equals("研发已领用")|| bomVO.getRemark().contains("领用")){
+ if (bomVO.getRemark() != null && bomVO.getRemark().contains("领用")) {
continue;
}
BomDetails bomDetails = new BomDetails();
@@ -2271,6 +2318,9 @@ public class BomDetailsController extends BaseController {
bomDetails.setName(bomVO.getDrawingName());
bomDetails.setMaterial(bomVO.getModel());
bomDetails.setWareHouse(bomVO.getBrand());
+ if(bomVO.getBrand().contains("伊特")){
+ bomDetails.setStats("自制");
+ }
bomDetails.setStats("外购");
bomDetails.setQuantity(bomVO.getQuantity() == null ? null : bomVO.getQuantity().toString());
bomDetails.setRemarks(bomVO.getUnit());
@@ -2320,6 +2370,7 @@ public class BomDetailsController extends BaseController {
}
return materialsToAdd;
}
+
@Log(title = "查看BOM上传日志")
@SaCheckPermission("system:route:viewGetBomUploadStatus")
@PostMapping("/viewGetBomUploadStatus")
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java
index 408d1a8..fce4ed5 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/EleMaterialsController.java
@@ -13,9 +13,13 @@ import java.util.stream.Collectors;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kingdee.bos.webapi.entity.SuccessEntity;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.utils.JdUtils;
import com.ruoyi.system.domain.EleMaterials;
@@ -453,43 +457,103 @@ public class EleMaterialsController extends BaseController {
}
- @Log(title = "更新物料单重", businessType = BusinessType.IMPORT)
+ @Log(title = "更新物料单重", businessType = BusinessType.UPDATE)
@SaCheckPermission("system:route:updateDanzhong")
- @PostMapping(value = "/updateDanzhong", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
- public R updateDanzhong(@RequestParam("file") MultipartFile file) throws Exception {
- String originalFilename = file.getOriginalFilename();
- log.info("读取文件名: " + originalFilename);
- ExcelResult result = ExcelUtil.importExcelSheet1(file.getInputStream(), JdDanZhong.class, true);
- List list = result.getList();
+ @PostMapping("/updateDanzhong")
+ public R updateDanzhong() {
+ final int pageSize = 500;
+ int pageNum = 1;
+ long totalRows = 0;
+ long skippedDisabled = 0;
+ long skippedNoWeight = 0;
+ long submitted = 0;
+ int failedTasks = 0;
- // 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
+ List> futures = new ArrayList<>();
- list.forEach(vmi -> executor.submit(() -> {
- List entryID = JdUtil.getEntryID2(vmi.getMaterialCode());
- entryID.forEach(jdEntryVmi -> {
- int fmaterialid = jdEntryVmi.getFMATERIALID();
-
- try {
- log.info("=====================>开始更新 " + vmi.getMaterialCode() + " 的单重");
- JdUtil.updateDanzhong(fmaterialid,vmi.getDanzhong() );
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
- }));
-
- // 关闭线程池并等待所有任务完成
- executor.shutdown();
try {
- if (!executor.awaitTermination(60, TimeUnit.MINUTES)) {
- executor.shutdownNow(); // 超时后强制关闭
+ while (true) {
+ LambdaQueryWrapper qw = Wrappers.lambdaQuery();
+ qw.isNotNull(ImMaterial::getMaterialCode);
+ qw.ne(ImMaterial::getMaterialCode, "");
+ qw.and(w -> w.isNull(ImMaterial::getForbidStatus).or().ne(ImMaterial::getForbidStatus, "B"));
+ qw.orderByAsc(ImMaterial::getId);
+ Page page = materialMapper.selectPage(new Page<>(pageNum, pageSize), qw);
+ List rows = page.getRecords();
+ if (rows.isEmpty()) {
+ break;
+ }
+ for (ImMaterial m : rows) {
+ totalRows++;
+ if (m == null || StringUtils.isBlank(m.getMaterialCode())) {
+ continue;
+ }
+ if (StringUtils.isNotBlank(m.getForbidStatus()) && "B".equalsIgnoreCase(m.getForbidStatus().trim())) {
+ skippedDisabled++;
+ continue;
+ }
+ if (m.getSingleWeight() == null) {
+ skippedNoWeight++;
+ continue;
+ }
+ final String materialCode = m.getMaterialCode().trim();
+ final String danzhongStr = m.getSingleWeight().stripTrailingZeros().toPlainString();
+
+ futures.add(executor.submit(() -> {
+ List entryID = JdUtil.getEntryID2(materialCode);
+ if (entryID == null || entryID.isEmpty()) {
+ log.warn("金蝶未查到物料编码: {}", materialCode);
+ return null;
+ }
+ for (JdEntryVmi jdEntryVmi : entryID) {
+ int fmaterialid = jdEntryVmi.getFMATERIALID();
+ try {
+ log.info("=====================>开始更新 {} 的单重(来自 im_material)", materialCode);
+ JdUtil.updateDanzhong(fmaterialid, danzhongStr);
+ } catch (Exception e) {
+ log.error("更新单重失败 materialCode={}, fmaterialid={}", materialCode, fmaterialid, e);
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+ }));
+ submitted++;
+ }
+ if (rows.size() < pageSize) {
+ break;
+ }
+ pageNum++;
+ }
+
+ for (Future future : futures) {
+ try {
+ future.get();
+ } catch (ExecutionException e) {
+ failedTasks++;
+ log.error("更新物料单重任务异常: {}", e.getCause() != null ? e.getCause().getMessage() : e.getMessage(), e);
+ }
}
} catch (InterruptedException e) {
- executor.shutdownNow();
- Thread.currentThread().interrupt(); // 保留中断状态
+ Thread.currentThread().interrupt();
+ log.error("更新物料单重被中断", e);
+ return R.fail("任务被中断");
+ } finally {
+ executor.shutdown();
+ try {
+ if (!executor.awaitTermination(60, TimeUnit.MINUTES)) {
+ executor.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ executor.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
}
+ log.info("updateDanzhong 完成: 扫描行={}, 提交金蝶任务={}, 跳过禁用={}, 跳过无单重={}, 失败任务={}", totalRows, submitted, skippedDisabled, skippedNoWeight, failedTasks);
+ if (failedTasks > 0) {
+ return R.fail(String.format("部分失败: 失败任务数=%d,详见日志", failedTasks));
+ }
return R.ok("更新成功");
}
@@ -542,8 +606,6 @@ public class EleMaterialsController extends BaseController {
log.info("读取文件名: " + originalFilename);
ExcelResult result = ExcelUtil.importExcelSheet1(file.getInputStream(), JdDanZhong.class, true);
List list = result.getList();
-
-
return R.ok("更新成功");
}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java
index fbfb98d..6ce04ac 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/controller/ImMaterialController.java
@@ -21,17 +21,26 @@ import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.excel.ExcelResult;
import com.ruoyi.common.utils.JdUtils;
+import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.ImMaterial;
import com.ruoyi.system.domain.bo.ImMaterialBo;
+import com.ruoyi.system.domain.dto.buildcapacity.BuildCapacityDTO;
+import com.ruoyi.system.domain.dto.buildcapacity.CalcInfo;
+import com.ruoyi.system.domain.dto.buildcapacity.MaterialItem;
+import com.ruoyi.system.domain.dto.buildcapacity.StockInfo;
+import com.ruoyi.system.domain.dto.productionorder.ProductionOrderMaterialsDTO;
+import com.ruoyi.system.domain.vo.BuildCapacityMaterialExportVo;
import com.ruoyi.system.domain.vo.ImMaterialVo;
import com.ruoyi.system.mapper.ImMaterialMapper;
import com.ruoyi.system.service.IImMaterialService;
import com.ruoyi.system.service.IPartCostService;
import com.xxl.job.core.handler.annotation.XxlJob;
+import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -43,8 +52,15 @@ import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import static com.ruoyi.common.core.mapper.BaseMapperPlus.log;
@@ -72,6 +88,12 @@ public class ImMaterialController extends BaseController {
private final ImMaterialMapper imMaterialMapper;
private final IPartCostService iPartCostService;
+
+ @Value("${plm.pdf.base-dir:D:/plm/pdf}")
+ private String plmPdfBaseDir;
+
+ @Value("${plm.pdf.separator:}")
+ private String plmPdfSeparator;
/**
* 金蝶物料保存 → 自动推送到此接口
* SaIgnore 注解用于开放此接口,无需 token 验证
@@ -114,6 +136,426 @@ public class ImMaterialController extends BaseController {
}
}
+ @SaIgnore
+ @PostMapping(value = "/plm/pdf", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public R