This commit is contained in:
geht
2026-07-30 17:02:10 +08:00
352 changed files with 43647 additions and 385 deletions

View File

@@ -1258,7 +1258,111 @@ JeecgUniapp-master/src/pages/rawMaterialInspect/rawMaterialInspect.vue
JeecgUniapp-master/src/pages/rawMaterialInspect/inspectResult.vue
JeecgUniapp-master/src/common/work.ts
-- author:GHT---date:20260730--for: <20><>XSLMES-20260730-DING-PULL<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̨<EFBFBD><EFBFBD><EFBFBD>ֶ<EFBFBD><EFBFBD><EFBFBD>ȡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܼ<EFBFBD><EFBFBD><EFBFBD> ---
-- author:jiangxh---date:20260728--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD>Ϊͬһ<CDAC><EFBFBD><E8B1B8>Ψһ ---
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslMixerConditionService.java
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixerConditionServiceImpl.java
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/controller/MesXslMixerConditionController.java
jeecgboot-vue3/src/views/xslmes/mesXslMixerCondition/MesXslMixerCondition.api.ts
jeecgboot-vue3/src/views/xslmes/mesXslMixerCondition/MesXslMixerCondition.data.ts
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><CFB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>%/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λС<CEBB><D0A1> ---
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/MesXslFormulaSpec.data.ts
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/components/MesXslFormulaSpecModal.vue
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϵ<EFBFBD><CFB5><33><CEBB>ĸ<EFBFBD><C4B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><33><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>2λ ---
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD>λС<CEBB><D0A1> ---
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingSpecModal.vue
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD>ϲ<EFBFBD>ѯ<EFBFBD><D1AF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F><><EFBFBD>ϼ<EFBFBD>ʾ<EFBFBD><CABE>ѡ<EFBFBD>ϸ<EFBFBD>Ϊģ<CEAA><C4A3><EFBFBD><EFBFBD>ѯ ---
jeecgboot-vue3/src/views/mes/material/materialQuery.util.ts
jeecgboot-vue3/src/views/mes/material/MesMixerMaterialList.vue
jeecgboot-vue3/src/views/mes/material/MesMaterialList.vue
jeecgboot-vue3/src/views/mes/material/modules/MesMixerMaterialSelectModal.vue
jeecgboot-vue3/src/views/mes/material/modules/MesMaterialSelectModal.vue
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>/<2F><><EFBFBD>ϡ<EFBFBD><CFA1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>У<E0BCAD><D0A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ψһ ---
jeecgboot-vue3/src/views/mes/material/MesMixerMaterial.api.ts
jeecgboot-vue3/src/views/mes/material/MesMixerMaterial.data.ts
jeecgboot-vue3/src/views/mes/material/MesMaterial.api.ts
jeecgboot-vue3/src/views/mes/material/MesMaterial.data.ts
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϡ<EFBFBD><CFA1><EFBFBD><EFBFBD><EFBFBD>Ĭ<EFBFBD>ϱ<EFBFBD><CFB1><EFBFBD><EFBFBD><EFBFBD>365<36><35> ---
jeecgboot-vue3/src/views/mes/material/MesMixerMaterial.data.ts
yy-admin-master/YY.Admin/ViewModels/MixerMaterial/MixerMaterialEditDialogViewModel.cs
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD><CFB8><EFBFBD><EFBFBD>%/<2F><><EFBFBD>ͳһ<CDB3><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λС<CEBB><D0A1> ---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_171__mes_xsl_formula_spec_line_decimal3.sql
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/MesXslFormulaSpec.data.ts
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/components/MesXslFormulaSpecModal.vue
-- author:jiangxh---date:20260729--for: <20><>WebSocket<65><74>pongTimeout 1s<31><73>10s <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
jeecgboot-vue3/src/hooks/web/useWebSocket.ts
-- author:jiangxh---date:20260729--for: <20><>WebSocket<65><74><EFBFBD>޸<EFBFBD><DEB8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Redis<69>ذ<EFBFBD><D8B0><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/message/websocket/WebSocket.java
jeecgboot-vue3/src/hooks/web/useWebSocket.ts
-- author:jiangxh---date:20260729--for: <20><>HTTP<54><50>Ĭ<EFBFBD>ϳ<EFBFBD>ʱ10s<30><73>60s<30><73><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD>120s ---
jeecgboot-vue3/src/utils/http/axios/index.ts
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/MesXslFormulaSpec.api.ts
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD>ɡ<EFBFBD>B/F<><46><EFBFBD>ϸ<EFBFBD>Ϊ<EFBFBD><CEAA><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>ƥ<EFBFBD><EFBFBD><E4A3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>ά<EFBFBD><CEAC> ---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
-- author:jiangxh---date:20260729--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD>ɡ<EFBFBD><C9A1>ս<EFBFBD><D5BD><EFBFBD>ϸĸ<CFB8><C4B8><EFBFBD>д<EFBFBD><D0B4><EFBFBD><EBBDBA><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
-- author:jiangxh---date:20260729--for: <20><>ѡ<EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>/<2F><><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE>ѡ<EFBFBD><D1A1><EFBFBD><EFBFBD>ȥ<EFBFBD><C8A5><EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD><CFB1> ---
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/components/MesXslMixingMaterialSelectModal.vue
jeecgboot-vue3/src/views/mes/material/modules/MesMaterialSelectModal.vue
jeecgboot-vue3/src/views/mes/material/materialQuery.util.ts
-- author:jiangxh---date:20260730--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD>ɡ<EFBFBD><C9A1>б<EFBFBD><D0B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɻ<EFBFBD><C9BB><EFBFBD>ʾ<EFBFBD><CABE>Ԥ<EFBFBD><D4A4>ʱ<EFBFBD><CAB1>ƥ<EFBFBD><EFBFBD><E4BDBA><EFBFBD>б<EFBFBD> ---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
jeecgboot-vue3/src/views/xslmes/mesXslFormulaSpec/components/MesXslFormulaGenerateMixingModal.vue
-- author:jiangxh---date:20260730--for: <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD>ɡ<EFBFBD>B/Fƥ<46><C6A5><EFBFBD>Ž<EFBFBD>ǰ׺+<2B><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƴSA01<30>Ȱ汾<C8B0><E6B1BE>׺ ---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
-- author:jiangxh---date:20260730--for: <20><><EFBFBD><EFBFBD><EFBFBD>ϡ<EFBFBD>ȥ<EFBFBD><C8A5><EFBFBD><EFBFBD><EFBFBD>ϱ<EFBFBD><CFB1>ҵ<EFBFBD><D2B5><EFBFBD>ֶΣ<D6B6>ƥ<EFBFBD><C6A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ---
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/entity/MesMaterial.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/controller/MesMaterialController.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/service/IMesMaterialService.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/service/impl/MesMaterialServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
jeecgboot-vue3/src/views/mes/material/MesMaterial.data.ts
jeecgboot-vue3/src/views/mes/material/MesMaterial.api.ts
jeecgboot-vue3/src/views/mes/material/modules/MesMaterialSelectModal.vue
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
-- author:jiangxh---date:20260730--for: <20><><EFBFBD><EFBFBD><EFBFBD>ϡ<EFBFBD><CFA1>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>ȥ<EFBFBD><C8A5><EFBFBD><EFBFBD>ŸĶ<C5B8> ---
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/entity/MesMaterial.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/controller/MesMaterialController.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/service/IMesMaterialService.java
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/service/impl/MesMaterialServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslFormulaSpecServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslMixingSpecServiceImpl.java
jeecgboot-vue3/src/views/mes/material/MesMaterial.data.ts
jeecgboot-vue3/src/views/mes/material/MesMaterial.api.ts
jeecgboot-vue3/src/views/mes/material/modules/MesMaterialSelectModal.vue
jeecgboot-vue3/src/views/xslmes/mesXslMixingSpec/MesXslMixingSpec.data.ts
-- author:jiangxh---date:20260730--for: <20><><EFBFBD><EFBFBD><EFBFBD>ϡ<EFBFBD><CFA1><EFBFBD><EFBFBD><EFBFBD>б<EFBFBD>/<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>أ<EFBFBD><D8A3><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EBBDBA><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB> ---
jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/mes/material/controller/MesMaterialController.java
jeecgboot-vue3/src/views/mes/material/MesMaterial.data.ts
-- author:GHT---date:20260730--for: <20><>XSLMES-20260730-DING-PULL<4C><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̨<EFBFBD><CCA8><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǿ<EFBFBD><C7BF><EFBFBD><EFBFBD><EFBFBD>ܼ<EFBFBD><DCBC><EFBFBD> ---
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/approval/integration/engine/IntegrationForceReplayHolder.java
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/approval/integration/engine/IntegrationOrchestrator.java
jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/dingtalk/stream/DingApprovalManualPullService.java
@@ -1267,4 +1371,4 @@ jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/app
jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_164__mes_xsl_approval_record_pull_ding_result.sql
jeecgboot-vue3/src/views/xslmes/approval/mesXslApprovalRecord/MesXslApprovalRecordList.vue
jeecgboot-vue3/src/views/xslmes/approval/mesXslApprovalRecord/MesXslApprovalRecord.api.ts
-- author:GHT---date:20260730--for: <20><>XSLMES-20260730-DING-PULL<4C><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̨<EFBFBD><CCA8><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǿ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܼ<EFBFBD><EFBFBD><EFBFBD> ---
-- author:GHT---date:20260730--for: <20><>XSLMES-20260730-DING-PULL<4C><4C><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̨<EFBFBD><CCA8><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǿ<EFBFBD><C7BF><EFBFBD><EFBFBD><EFBFBD>ܼ<EFBFBD><DCBC><EFBFBD> ---

View File

@@ -36,5 +36,11 @@
<artifactId>jeecg-module-print</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
<!-- 设备同步/桌面代理命令总线 -->
<dependency>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>jeecg-module-device-sync</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -130,6 +130,7 @@ public class MesXslAutoSmallMaterialDemandPlanController {
}
sql.append("GROUP BY ");
sql.append(statDateExpr).append(", ");
if (groupedByMachine) {
sql.append(machineExpr).append(", ");
}
@@ -160,14 +161,20 @@ public class MesXslAutoSmallMaterialDemandPlanController {
private String buildOrderBy(boolean groupedByMachine) {
if (groupedByMachine) {
return " ORDER BY machineName, rawMaterialName";
return " ORDER BY statDate DESC, machineName, rawMaterialName";
}
return " ORDER BY rawMaterialName";
return " ORDER BY statDate DESC, rawMaterialName";
}
private String buildRowId(MesXslSmallMaterialDemandPlanSummary row, boolean groupedByMachine, int seq) {
String machine = groupedByMachine ? StringUtils.defaultString(row.getMachineName()) : "ALL";
return machine + "_" + StringUtils.defaultString(row.getRawMaterialName()) + "_" + seq;
return StringUtils.defaultString(row.getStatDate())
+ "_"
+ machine
+ "_"
+ StringUtils.defaultString(row.getRawMaterialName())
+ "_"
+ seq;
}
private String trim(String value) {

View File

@@ -130,6 +130,7 @@ public class MesXslManualSmallMaterialDemandPlanController {
}
sql.append("GROUP BY ");
sql.append(statDateExpr).append(", ");
if (groupedByMachine) {
sql.append(machineExpr).append(", ");
}
@@ -160,14 +161,20 @@ public class MesXslManualSmallMaterialDemandPlanController {
private String buildOrderBy(boolean groupedByMachine) {
if (groupedByMachine) {
return " ORDER BY machineName, rawMaterialName";
return " ORDER BY statDate DESC, machineName, rawMaterialName";
}
return " ORDER BY rawMaterialName";
return " ORDER BY statDate DESC, rawMaterialName";
}
private String buildRowId(MesXslSmallMaterialDemandPlanSummary row, boolean groupedByMachine, int seq) {
String machine = groupedByMachine ? StringUtils.defaultString(row.getMachineName()) : "ALL";
return machine + "_" + StringUtils.defaultString(row.getRawMaterialName()) + "_" + seq;
return StringUtils.defaultString(row.getStatDate())
+ "_"
+ machine
+ "_"
+ StringUtils.defaultString(row.getRawMaterialName())
+ "_"
+ seq;
}
private String trim(String value) {

View File

@@ -0,0 +1,60 @@
package org.jeecg.modules.xslmes.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.xslmes.entity.MesXslMillAction;
import org.jeecg.modules.xslmes.service.IMesXslMillActionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
/**
* MES 开炼机动作接收表
*/
@Tag(name = "开炼机动作接收")
@RestController
@RequestMapping("/xslmes/mesXslMillAction")
public class MesXslMillActionController extends JeecgController<MesXslMillAction, IMesXslMillActionService> {
@Autowired private IMesXslMillActionService mesXslMillActionService;
@Operation(summary = "开炼机动作接收-分页列表查询")
@GetMapping("/list")
public Result<IPage<MesXslMillAction>> queryPageList(
MesXslMillAction model,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<MesXslMillAction> queryWrapper = QueryGenerator.initQueryWrapper(model, req.getParameterMap());
queryWrapper.orderByDesc("update_time");
IPage<MesXslMillAction> pageList = mesXslMillActionService.page(new Page<>(pageNo, pageSize), queryWrapper);
return Result.OK(pageList);
}
@Operation(summary = "开炼机动作接收-通过id查询")
@GetMapping("/queryById")
public Result<MesXslMillAction> queryById(@RequestParam(name = "id", required = true) String id) {
MesXslMillAction entity = mesXslMillActionService.getById(id);
if (entity == null) {
return Result.error("未找到对应数据");
}
return Result.OK(entity);
}
@RequiresPermissions("xslmes:mes_xsl_mill_action:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, MesXslMillAction model) {
return super.exportXls(request, model, MesXslMillAction.class, "开炼机动作接收");
}
}

View File

@@ -110,31 +110,43 @@ public class MesXslMixerConditionController
return Result.OK(entity);
}
@Operation(summary = "校验条件名称是否重复")
@Operation(summary = "校验条件名称是否重复(同一设备下)")
@GetMapping("/checkConditionName")
public Result<String> checkConditionName(
@RequestParam(name = "conditionName", required = true) String conditionName,
@RequestParam(name = "equipmentId", required = false) String equipmentId,
@RequestParam(name = "dataId", required = false) String dataId) {
if (oConvertUtils.isEmpty(conditionName) || conditionName.trim().isEmpty()) {
return Result.OK("该值可用!");
}
if (mesXslMixerConditionService.isConditionNameDuplicated(conditionName, dataId)) {
return Result.error("条件名称不能重复");
//update-begin---author:jiangxh ---date:20260728 for【密炼机条件】条件名称按设备唯一-----------
if (oConvertUtils.isEmpty(equipmentId) || equipmentId.trim().isEmpty()) {
return Result.OK("该值可用!");
}
if (mesXslMixerConditionService.isConditionNameDuplicated(conditionName, equipmentId, dataId)) {
return Result.error("同一设备下条件名称不能重复");
}
//update-end---author:jiangxh ---date:20260728 for【密炼机条件】条件名称按设备唯一-----------
return Result.OK("该值可用!");
}
@Operation(summary = "校验条件代码是否重复")
@Operation(summary = "校验条件代码是否重复(同一设备下)")
@GetMapping("/checkConditionCode")
public Result<String> checkConditionCode(
@RequestParam(name = "conditionCode", required = true) String conditionCode,
@RequestParam(name = "equipmentId", required = false) String equipmentId,
@RequestParam(name = "dataId", required = false) String dataId) {
if (oConvertUtils.isEmpty(conditionCode) || conditionCode.trim().isEmpty()) {
return Result.OK("该值可用!");
}
if (mesXslMixerConditionService.isConditionCodeDuplicated(conditionCode, dataId)) {
return Result.error("条件代码不能重复");
//update-begin---author:jiangxh ---date:20260728 for【密炼机条件】条件代码按设备唯一-----------
if (oConvertUtils.isEmpty(equipmentId) || equipmentId.trim().isEmpty()) {
return Result.OK("该值可用!");
}
if (mesXslMixerConditionService.isConditionCodeDuplicated(conditionCode, equipmentId, dataId)) {
return Result.error("同一设备下条件代码不能重复");
}
//update-end---author:jiangxh ---date:20260728 for【密炼机条件】条件代码按设备唯一-----------
return Result.OK("该值可用!");
}
@@ -153,16 +165,20 @@ public class MesXslMixerConditionController
return "条件名称不能为空";
}
model.setConditionName(model.getConditionName().trim());
if (mesXslMixerConditionService.isConditionNameDuplicated(model.getConditionName(), excludeId)) {
return "条件名称不能重复";
//update-begin---author:jiangxh ---date:20260728 for【密炼机条件】条件名称/代码按设备唯一-----------
if (mesXslMixerConditionService.isConditionNameDuplicated(
model.getConditionName(), model.getEquipmentId(), excludeId)) {
return "同一设备下条件名称不能重复";
}
if (oConvertUtils.isEmpty(model.getConditionCode()) || StringUtils.isBlank(model.getConditionCode())) {
return "条件代码不能为空";
}
model.setConditionCode(model.getConditionCode().trim());
if (mesXslMixerConditionService.isConditionCodeDuplicated(model.getConditionCode(), excludeId)) {
return "条件代码不能重复";
if (mesXslMixerConditionService.isConditionCodeDuplicated(
model.getConditionCode(), model.getEquipmentId(), excludeId)) {
return "同一设备下条件代码不能重复";
}
//update-end---author:jiangxh ---date:20260728 for【密炼机条件】条件名称/代码按设备唯一-----------
return null;
}
}

View File

@@ -95,9 +95,12 @@ public class MesXslRawMaterialDemandPlanController {
private String buildGroupedSql(
MesXslRawMaterialDemandPlanSummary query, boolean groupedByMachine, List<Object> params) {
String planDateExpr = "DATE(t.plan_date)";
boolean hasPlanDate = hasColumn("mes_xsl_raw_material_demand_plan", "plan_date");
boolean hasMaterialId = hasColumn("mes_xsl_raw_material_demand_plan", "material_id");
String planDateExpr = hasPlanDate ? "t.plan_date" : "DATE(t.create_time)";
String machineExpr = "COALESCE(NULLIF(TRIM(t.machine_name),''), '')";
String materialIdExpr = "COALESCE(NULLIF(TRIM(t.material_id),''), '')";
String materialIdExpr =
hasMaterialId ? "COALESCE(NULLIF(TRIM(t.material_id),''), '')" : "''";
String erpCodeExpr = "COALESCE(NULLIF(TRIM(t.erp_code),''), '')";
String rawMaterialExpr = "COALESCE(NULLIF(TRIM(t.raw_material_name),''), '')";
@@ -192,4 +195,12 @@ public class MesXslRawMaterialDemandPlanController {
private String trim(String value) {
return value == null ? "" : value.trim();
}
private boolean hasColumn(String tableName, String columnName) {
String sql =
"SELECT COUNT(1) FROM information_schema.COLUMNS "
+ "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?";
Long count = jdbcTemplate.queryForObject(sql, Long.class, tableName, columnName);
return count != null && count > 0;
}
}

View File

@@ -0,0 +1,113 @@
package org.jeecg.modules.xslmes.deviceagent;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.device.sync.agent.DeviceAgentCommandService;
import org.jeecg.modules.device.sync.agent.DeviceAgentResult;
import org.jeecg.modules.device.sync.agent.DeviceAgentSession;
import org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 桌面设备代理免密接口(注册/心跳/分块回传)。
*/
@Tag(name = "桌面设备代理")
@Slf4j
@RestController
@RequestMapping("/xslmes/deviceAgent/anon")
@RequiredArgsConstructor
public class MesXslDeviceAgentAnonController {
private final DeviceAgentSessionRegistry sessionRegistry;
private final DeviceAgentCommandService commandService;
@Operation(summary = "注册/心跳")
@PostMapping("/register")
public Result<Map<String, Object>> register(@RequestBody Map<String, Object> body) {
String deviceId = str(body, "deviceId");
if (StringUtils.isBlank(deviceId)) {
return Result.error("deviceId不能为空");
}
String hostName = str(body, "hostName");
Boolean dbConnected = bool(body.get("dbConnected"));
String dbMessage = str(body, "dbMessage");
sessionRegistry.registerOrHeartbeat(deviceId, hostName, dbConnected, dbMessage);
Map<String, Object> data = new HashMap<>();
data.put("deviceId", deviceId);
data.put("accepted", true);
return Result.OK(data);
}
@Operation(summary = "心跳")
@PostMapping("/heartbeat")
public Result<String> heartbeat(@RequestBody Map<String, Object> body) {
String deviceId = str(body, "deviceId");
if (StringUtils.isBlank(deviceId)) {
return Result.error("deviceId不能为空");
}
sessionRegistry.registerOrHeartbeat(deviceId, str(body, "hostName"), bool(body.get("dbConnected")), str(body, "dbMessage"));
return Result.OK("ok");
}
@Operation(summary = "命令结果分块回传(大数据量)")
@PostMapping("/resultChunk")
public Result<String> resultChunk(@RequestBody DeviceAgentResult result) {
if (result == null || StringUtils.isBlank(result.getRequestId())) {
return Result.error("requestId不能为空");
}
// 当前实现:整包完成;桌面可在单次 chunk 中带 lastChunk=true 直接完成
if (Boolean.TRUE.equals(result.getLastChunk()) || result.getChunkTotal() == null || result.getChunkTotal() <= 1) {
commandService.completeResult(result);
} else {
log.info("收到分块回传 requestId={} chunk={}/{}", result.getRequestId(), result.getChunkIndex(), result.getChunkTotal());
// 简化:仅最后一块完成;中间块忽略合并(桌面端大结果优先走单次 STOMP 或一次 REST 全量)
if (Boolean.TRUE.equals(result.getLastChunk())) {
commandService.completeResult(result);
}
}
return Result.OK("ok");
}
@Operation(summary = "在线代理列表")
@GetMapping("/online")
public Result<List<DeviceAgentSession>> online() {
return Result.OK(sessionRegistry.listOnline());
}
@Operation(summary = "查询代理状态")
@GetMapping("/status")
public Result<DeviceAgentSession> status(@RequestParam(name = "deviceId", required = false) String deviceId) {
return Result.OK(sessionRegistry.pickOnlineAgent(deviceId));
}
private static String str(Map<String, Object> body, String key) {
if (body == null || body.get(key) == null) {
return null;
}
String v = String.valueOf(body.get(key)).trim();
return v.isEmpty() || "null".equalsIgnoreCase(v) ? null : v;
}
private static Boolean bool(Object v) {
if (v == null) {
return null;
}
if (v instanceof Boolean) {
return (Boolean) v;
}
return Boolean.parseBoolean(String.valueOf(v));
}
}

View File

@@ -0,0 +1,86 @@
package org.jeecg.modules.xslmes.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
/**
* MES 开炼机动作接收表(采集自 MCSToMES_Act_Mill
*/
@Data
@TableName("mes_xsl_mill_action")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description = "MES开炼机动作接收表")
public class MesXslMillAction implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
private String id;
@Excel(name = "GUID", width = 36)
@Schema(description = "源表GUID")
private String guid;
@Excel(name = "动作地址", width = 10)
@Schema(description = "动作地址")
private Integer actAddr;
@Excel(name = "动作名称", width = 20)
@Schema(description = "动作名称")
private String actName;
@Excel(name = "动作名称(英)", width = 20)
@Schema(description = "动作名称(英)")
private String actNameEn;
@Excel(name = "动作备注", width = 15)
@Schema(description = "动作备注")
private String actMemo;
@Excel(name = "动作关联地址", width = 12)
@Schema(description = "动作关联地址")
private Integer actRepaddr;
@Excel(name = "写入时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "写入时间")
private Date writeTime;
@Excel(name = "读取时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "读取时间")
private Date readTime;
@Excel(name = "读写标识", width = 10)
@Schema(description = "读写标识")
private Integer rwFlag;
private Integer tenantId;
private String sysOrgCode;
private String createBy;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
private String updateBy;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
private Integer delFlag;
}

View File

@@ -0,0 +1,6 @@
package org.jeecg.modules.xslmes.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.xslmes.entity.MesXslMillAction;
public interface MesXslMillActionMapper extends BaseMapper<MesXslMillAction> {}

View File

@@ -0,0 +1,85 @@
package org.jeecg.modules.xslmes.mcs.access;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.device.sync.agent.DeviceAgentCommandService;
import org.jeecg.modules.device.sync.agent.DeviceAgentResult;
import org.jeecg.modules.device.sync.agent.DeviceAgentSession;
import org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 经桌面端代理访问设备库。
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DesktopAgentMcsDataAccess implements IMcsDataAccess {
private final DeviceAgentCommandService commandService;
private final DeviceAgentSessionRegistry sessionRegistry;
private final McsDataSourceManager mcsDataSourceManager;
@Override
public boolean isAgentMode() {
return true;
}
@Override
public String resolveAgentDeviceId() {
MesXslMcsDbConfig cfg = mcsDataSourceManager.getCachedConfig();
String preferred = cfg == null ? null : cfg.getAgentDeviceId();
DeviceAgentSession session = sessionRegistry.pickOnlineAgent(preferred);
if (session == null) {
throw new IllegalStateException("无在线桌面设备代理,无法访问设备库");
}
return session.getDeviceId();
}
@Override
public List<Map<String, Object>> query(String sql, List<Object> params) {
String deviceId = resolveAgentDeviceId();
DeviceAgentResult result = commandService.query(deviceId, sql, params, 60_000);
return result.getRows() == null ? List.of() : result.getRows();
}
@Override
public int execute(String sql, List<Object> params) {
String deviceId = resolveAgentDeviceId();
DeviceAgentResult result = commandService.write(deviceId, sql, params, 60_000);
return result.getAffected() == null ? 0 : result.getAffected();
}
@Override
public int pushRows(String table, String mode, List<String> keys, List<Map<String, Object>> rows) {
String deviceId = resolveAgentDeviceId();
Map<String, Object> payload = new HashMap<>();
payload.put("mode", StringUtils.defaultIfBlank(mode, "insert"));
if (keys != null) {
payload.put("keys", keys);
}
payload.put("rows", rows == null ? new ArrayList<>() : rows);
DeviceAgentResult result = commandService.syncPush(deviceId, table, payload, 60_000);
return result.getAffected() == null ? 0 : result.getAffected();
}
@Override
public boolean testConnection() {
try {
String deviceId = resolveAgentDeviceId();
DeviceAgentResult result = commandService.test(deviceId, 10_000);
return result != null && result.isSuccess();
} catch (Exception e) {
log.warn("桌面代理中间库测试失败: {}", e.getMessage());
return false;
}
}
}

View File

@@ -0,0 +1,159 @@
package org.jeecg.modules.xslmes.mcs.access;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 服务端直连 SQL Server调试/兼容保留)。
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class DirectJdbcMcsDataAccess implements IMcsDataAccess {
private final DataSource dataSource;
private final McsDataSourceManager mcsDataSourceManager;
@Override
public boolean isAgentMode() {
return false;
}
@Override
public String resolveAgentDeviceId() {
return null;
}
@Override
public List<Map<String, Object>> query(String sql, List<Object> params) {
JdbcTemplate jt = new JdbcTemplate(requireDs());
if (params == null || params.isEmpty()) {
return jt.queryForList(sql);
}
return jt.queryForList(sql, params.toArray());
}
@Override
public int execute(String sql, List<Object> params) {
JdbcTemplate jt = new JdbcTemplate(requireDs());
if (params == null || params.isEmpty()) {
return jt.update(sql);
}
return jt.update(sql, params.toArray());
}
@Override
public int pushRows(String table, String mode, List<String> keys, List<Map<String, Object>> rows) {
if (rows == null || rows.isEmpty()) {
return 0;
}
int affected = 0;
for (Map<String, Object> row : rows) {
if ("update".equalsIgnoreCase(mode) || "upsert".equalsIgnoreCase(mode)) {
int u = updateByKeys(table, keys, row);
if (u > 0) {
affected += u;
continue;
}
if ("update".equalsIgnoreCase(mode)) {
continue;
}
}
affected += insertRow(table, row);
}
return affected;
}
@Override
public boolean testConnection() {
try {
query("SELECT 1", null);
return true;
} catch (Exception e) {
log.warn("直连中间库测试失败: {}", e.getMessage());
return false;
}
}
private int insertRow(String table, Map<String, Object> row) {
List<String> cols = new ArrayList<>(row.keySet());
StringBuilder sql = new StringBuilder("INSERT INTO [").append(table).append("] (");
StringBuilder values = new StringBuilder(" VALUES (");
List<Object> params = new ArrayList<>();
for (int i = 0; i < cols.size(); i++) {
if (i > 0) {
sql.append(',');
values.append(',');
}
sql.append('[').append(cols.get(i)).append(']');
values.append('?');
params.add(row.get(cols.get(i)));
}
sql.append(')').append(values).append(')');
return execute(sql.toString(), params);
}
private int updateByKeys(String table, List<String> keys, Map<String, Object> row) {
if (keys == null || keys.isEmpty()) {
return 0;
}
List<String> setCols = new ArrayList<>();
for (String c : row.keySet()) {
boolean isKey = keys.stream().anyMatch(k -> k.equalsIgnoreCase(c));
if (!isKey) {
setCols.add(c);
}
}
if (setCols.isEmpty()) {
return 0;
}
StringBuilder sql = new StringBuilder("UPDATE [").append(table).append("] SET ");
List<Object> params = new ArrayList<>();
for (int i = 0; i < setCols.size(); i++) {
if (i > 0) {
sql.append(',');
}
sql.append('[').append(setCols.get(i)).append("]=?");
params.add(getIgnoreCase(row, setCols.get(i)));
}
sql.append(" WHERE ");
for (int i = 0; i < keys.size(); i++) {
if (i > 0) {
sql.append(" AND ");
}
sql.append('[').append(keys.get(i)).append("]=?");
params.add(getIgnoreCase(row, keys.get(i)));
}
return execute(sql.toString(), params);
}
private Object getIgnoreCase(Map<String, Object> row, String key) {
if (row.containsKey(key)) {
return row.get(key);
}
for (Map.Entry<String, Object> e : row.entrySet()) {
if (e.getKey() != null && e.getKey().equalsIgnoreCase(key)) {
return e.getValue();
}
}
return null;
}
private DataSource requireDs() {
DynamicRoutingDataSource routing = (DynamicRoutingDataSource) dataSource;
DataSource src = routing.getDataSources().get(McsDataSourceManager.DS_KEY);
if (src == null) {
throw new IllegalStateException("中间库数据源 " + McsDataSourceManager.DS_KEY + " 未注册");
}
return src;
}
}

View File

@@ -0,0 +1,31 @@
package org.jeecg.modules.xslmes.mcs.access;
import java.util.List;
import java.util.Map;
/**
* MCS 中间库访问抽象:直连 JDBC 或经桌面代理。
*/
public interface IMcsDataAccess {
boolean isAgentMode();
String resolveAgentDeviceId();
List<Map<String, Object>> query(String sql, List<Object> params);
int execute(String sql, List<Object> params);
/**
* 批量写入中间表EQ_SYNC_PUSH
*
* @param table 白名单表名
* @param mode insert / update / upsert
* @param keys 匹配键update/upsert
* @param rows 行数据
* @return 影响行数
*/
int pushRows(String table, String mode, List<String> keys, List<Map<String, Object>> rows);
boolean testConnection();
}

View File

@@ -0,0 +1,65 @@
package org.jeecg.modules.xslmes.mcs.access;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* 按配置在「桌面代理 / 直连」之间切换。
*/
@Service
@RequiredArgsConstructor
public class McsDataAccessFacade implements IMcsDataAccess {
public static final String MODE_DESKTOP_AGENT = "DESKTOP_AGENT";
public static final String MODE_DIRECT = "DIRECT";
private final McsDataSourceManager mcsDataSourceManager;
private final DesktopAgentMcsDataAccess desktopAgentMcsDataAccess;
private final DirectJdbcMcsDataAccess directJdbcMcsDataAccess;
public IMcsDataAccess current() {
return isAgentMode() ? desktopAgentMcsDataAccess : directJdbcMcsDataAccess;
}
@Override
public boolean isAgentMode() {
MesXslMcsDbConfig cfg = mcsDataSourceManager.getCachedConfig();
if (cfg == null) {
// 默认优先桌面代理
return true;
}
String mode = StringUtils.defaultIfBlank(cfg.getAccessMode(), MODE_DESKTOP_AGENT).trim().toUpperCase();
return MODE_DESKTOP_AGENT.equals(mode);
}
@Override
public String resolveAgentDeviceId() {
return current().resolveAgentDeviceId();
}
@Override
public List<Map<String, Object>> query(String sql, List<Object> params) {
return current().query(sql, params);
}
@Override
public int execute(String sql, List<Object> params) {
return current().execute(sql, params);
}
@Override
public int pushRows(String table, String mode, List<String> keys, List<Map<String, Object>> rows) {
return current().pushRows(table, mode, keys, rows);
}
@Override
public boolean testConnection() {
return current().testConnection();
}
}

View File

@@ -0,0 +1,30 @@
package org.jeecg.modules.xslmes.mcs.access;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* MES → MCS 经桌面代理下发EQ_SYNC_PUSH
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MesToMcsPushService {
private final McsDataAccessFacade mcsDataAccessFacade;
public int push(String table, String mode, List<String> keys, List<Map<String, Object>> rows) {
if (StringUtils.isBlank(table)) {
throw new IllegalArgumentException("table 不能为空");
}
int affected = mcsDataAccessFacade.pushRows(table, mode, keys, rows);
log.info("MES→MCS 下发完成 table={} mode={} rows={} affected={}",
table, mode, rows == null ? 0 : rows.size(), affected);
return affected;
}
}

View File

@@ -0,0 +1,290 @@
package org.jeecg.modules.xslmes.mcs.access.proxy;
import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class AgentProxyConnection implements Connection {
private final IMcsDataAccess dataAccess;
private boolean closed;
public AgentProxyConnection(IMcsDataAccess dataAccess) {
this.dataAccess = dataAccess;
}
@Override
public Statement createStatement() {
return new AgentProxyStatement(dataAccess, null);
}
@Override
public PreparedStatement prepareStatement(String sql) {
return new AgentProxyPreparedStatement(dataAccess, sql);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
throw new SQLException("CallableStatement not supported by agent proxy");
}
@Override
public String nativeSQL(String sql) {
return sql;
}
@Override
public void setAutoCommit(boolean autoCommit) {
}
@Override
public boolean getAutoCommit() {
return true;
}
@Override
public void commit() {
}
@Override
public void rollback() {
}
@Override
public void close() {
closed = true;
}
@Override
public boolean isClosed() {
return closed;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
throw new SQLException("DatabaseMetaData not supported by agent proxy");
}
@Override
public void setReadOnly(boolean readOnly) {
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public void setCatalog(String catalog) {
}
@Override
public String getCatalog() {
return null;
}
@Override
public void setTransactionIsolation(int level) {
}
@Override
public int getTransactionIsolation() {
return Connection.TRANSACTION_READ_COMMITTED;
}
@Override
public SQLWarning getWarnings() {
return null;
}
@Override
public void clearWarnings() {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) {
return createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) {
return prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
throw new SQLException("CallableStatement not supported");
}
@Override
public Map<String, Class<?>> getTypeMap() {
return Map.of();
}
@Override
public void setTypeMap(Map<String, Class<?>> map) {
}
@Override
public void setHoldability(int holdability) {
}
@Override
public int getHoldability() {
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
throw new SQLException("Savepoint not supported");
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
throw new SQLException("Savepoint not supported");
}
@Override
public void rollback(Savepoint savepoint) {
}
@Override
public void releaseSavepoint(Savepoint savepoint) {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) {
return createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) {
return prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
throw new SQLException("CallableStatement not supported");
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) {
return prepareStatement(sql);
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) {
return prepareStatement(sql);
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) {
return prepareStatement(sql);
}
@Override
public Clob createClob() throws SQLException {
throw new SQLException("not supported");
}
@Override
public Blob createBlob() throws SQLException {
throw new SQLException("not supported");
}
@Override
public NClob createNClob() throws SQLException {
throw new SQLException("not supported");
}
@Override
public SQLXML createSQLXML() throws SQLException {
throw new SQLException("not supported");
}
@Override
public boolean isValid(int timeout) {
return !closed;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public String getClientInfo(String name) {
return null;
}
@Override
public Properties getClientInfo() {
return new Properties();
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
throw new SQLException("not supported");
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setSchema(String schema) {
}
@Override
public String getSchema() {
return null;
}
@Override
public void abort(Executor executor) {
close();
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) {
}
@Override
public int getNetworkTimeout() {
return 0;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return iface.cast(this);
}
throw new SQLException("Not a wrapper");
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return iface.isInstance(this);
}
}

View File

@@ -0,0 +1,68 @@
package org.jeecg.modules.xslmes.mcs.access.proxy;
import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* 将 JDBC 调用转发到桌面设备代理的 DataSource供 @DS("sqlserver_mcs") 复用。
*/
public class AgentProxyDataSource implements DataSource {
private final IMcsDataAccess dataAccess;
public AgentProxyDataSource(IMcsDataAccess dataAccess) {
this.dataAccess = dataAccess;
}
@Override
public Connection getConnection() {
return new AgentProxyConnection(dataAccess);
}
@Override
public Connection getConnection(String username, String password) {
return getConnection();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
if (iface.isInstance(this)) {
return iface.cast(this);
}
throw new SQLException("Not a wrapper for " + iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return iface.isInstance(this);
}
@Override
public PrintWriter getLogWriter() {
return null;
}
@Override
public void setLogWriter(PrintWriter out) {
}
@Override
public void setLoginTimeout(int seconds) {
}
@Override
public int getLoginTimeout() {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
}

View File

@@ -0,0 +1,565 @@
package org.jeecg.modules.xslmes.mcs.access.proxy;
import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
public class AgentProxyPreparedStatement implements PreparedStatement {
private final IMcsDataAccess dataAccess;
private final String sql;
private final List<Object> params = new ArrayList<>();
private ResultSet currentRs;
private int updateCount = -1;
public AgentProxyPreparedStatement(IMcsDataAccess dataAccess, String sql) {
this.dataAccess = dataAccess;
this.sql = sql;
ensureCapacity(16);
}
private void ensureCapacity(int index1Based) {
while (params.size() < index1Based) {
params.add(null);
}
}
private void setParam(int parameterIndex, Object value) {
ensureCapacity(parameterIndex);
params.set(parameterIndex - 1, value);
}
@Override
public ResultSet executeQuery() throws SQLException {
try {
List<Map<String, Object>> rows = dataAccess.query(sql, new ArrayList<>(params));
currentRs = new MapListResultSet(rows);
updateCount = -1;
return currentRs;
} catch (Exception e) {
throw new SQLException(e.getMessage(), e);
}
}
@Override
public int executeUpdate() throws SQLException {
try {
updateCount = dataAccess.execute(sql, new ArrayList<>(params));
currentRs = null;
return updateCount;
} catch (Exception e) {
throw new SQLException(e.getMessage(), e);
}
}
@Override
public boolean execute() throws SQLException {
String trimmed = sql == null ? "" : sql.trim();
if (trimmed.regionMatches(true, 0, "SELECT", 0, 6)) {
executeQuery();
return true;
}
executeUpdate();
return false;
}
@Override
public void setNull(int parameterIndex, int sqlType) {
setParam(parameterIndex, null);
}
@Override
public void setBoolean(int parameterIndex, boolean x) {
setParam(parameterIndex, x);
}
@Override
public void setByte(int parameterIndex, byte x) {
setParam(parameterIndex, x);
}
@Override
public void setShort(int parameterIndex, short x) {
setParam(parameterIndex, x);
}
@Override
public void setInt(int parameterIndex, int x) {
setParam(parameterIndex, x);
}
@Override
public void setLong(int parameterIndex, long x) {
setParam(parameterIndex, x);
}
@Override
public void setFloat(int parameterIndex, float x) {
setParam(parameterIndex, x);
}
@Override
public void setDouble(int parameterIndex, double x) {
setParam(parameterIndex, x);
}
@Override
public void setBigDecimal(int parameterIndex, BigDecimal x) {
setParam(parameterIndex, x);
}
@Override
public void setString(int parameterIndex, String x) {
setParam(parameterIndex, x);
}
@Override
public void setBytes(int parameterIndex, byte[] x) {
setParam(parameterIndex, x);
}
@Override
public void setDate(int parameterIndex, Date x) {
setParam(parameterIndex, x);
}
@Override
public void setTime(int parameterIndex, Time x) {
setParam(parameterIndex, x);
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x) {
setParam(parameterIndex, x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
throw new SQLException("setAsciiStream not supported");
}
@Override
@Deprecated
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void clearParameters() {
params.clear();
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType) {
setParam(parameterIndex, x);
}
@Override
public void setObject(int parameterIndex, Object x) {
setParam(parameterIndex, x);
}
@Override
public void addBatch() {
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setRef(int parameterIndex, Ref x) {
setParam(parameterIndex, x);
}
@Override
public void setBlob(int parameterIndex, Blob x) {
setParam(parameterIndex, x);
}
@Override
public void setClob(int parameterIndex, Clob x) {
setParam(parameterIndex, x);
}
@Override
public void setArray(int parameterIndex, Array x) {
setParam(parameterIndex, x);
}
@Override
public ResultSetMetaData getMetaData() {
return currentRs == null ? null : safeMeta();
}
private ResultSetMetaData safeMeta() {
try {
return currentRs.getMetaData();
} catch (SQLException e) {
return null;
}
}
@Override
public void setDate(int parameterIndex, Date x, Calendar cal) {
setParam(parameterIndex, x);
}
@Override
public void setTime(int parameterIndex, Time x, Calendar cal) {
setParam(parameterIndex, x);
}
@Override
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) {
setParam(parameterIndex, x);
}
@Override
public void setNull(int parameterIndex, int sqlType, String typeName) {
setParam(parameterIndex, null);
}
@Override
public void setURL(int parameterIndex, URL x) {
setParam(parameterIndex, x);
}
@Override
public ParameterMetaData getParameterMetaData() throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setRowId(int parameterIndex, RowId x) {
setParam(parameterIndex, x);
}
@Override
public void setNString(int parameterIndex, String value) {
setParam(parameterIndex, value);
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setNClob(int parameterIndex, NClob value) {
setParam(parameterIndex, value);
}
@Override
public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setSQLXML(int parameterIndex, SQLXML xmlObject) {
setParam(parameterIndex, xmlObject);
}
@Override
public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) {
setParam(parameterIndex, x);
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setClob(int parameterIndex, Reader reader) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
throw new SQLException("not supported");
}
@Override
public void setNClob(int parameterIndex, Reader reader) throws SQLException {
throw new SQLException("not supported");
}
// Statement methods
@Override
public ResultSet executeQuery(String sql) throws SQLException {
throw new SQLException("use executeQuery()");
}
@Override
public int executeUpdate(String sql) throws SQLException {
throw new SQLException("use executeUpdate()");
}
@Override
public void close() {
currentRs = null;
}
@Override
public int getMaxFieldSize() {
return 0;
}
@Override
public void setMaxFieldSize(int max) {
}
@Override
public int getMaxRows() {
return 0;
}
@Override
public void setMaxRows(int max) {
}
@Override
public void setEscapeProcessing(boolean enable) {
}
@Override
public int getQueryTimeout() {
return 0;
}
@Override
public void setQueryTimeout(int seconds) {
}
@Override
public void cancel() {
}
@Override
public SQLWarning getWarnings() {
return null;
}
@Override
public void clearWarnings() {
}
@Override
public void setCursorName(String name) {
}
@Override
public boolean execute(String sql) throws SQLException {
throw new SQLException("use execute()");
}
@Override
public ResultSet getResultSet() {
return currentRs;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean getMoreResults() {
return false;
}
@Override
public void setFetchDirection(int direction) {
}
@Override
public int getFetchDirection() {
return ResultSet.FETCH_FORWARD;
}
@Override
public void setFetchSize(int rows) {
}
@Override
public int getFetchSize() {
return 0;
}
@Override
public int getResultSetConcurrency() {
return ResultSet.CONCUR_READ_ONLY;
}
@Override
public int getResultSetType() {
return ResultSet.TYPE_FORWARD_ONLY;
}
@Override
public void addBatch(String sql) {
}
@Override
public void clearBatch() {
}
@Override
public int[] executeBatch() {
return new int[0];
}
@Override
public Connection getConnection() {
return null;
}
@Override
public boolean getMoreResults(int current) {
return false;
}
@Override
public ResultSet getGeneratedKeys() {
return null;
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
throw new SQLException("not supported");
}
@Override
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
throw new SQLException("not supported");
}
@Override
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
throw new SQLException("not supported");
}
@Override
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
throw new SQLException("not supported");
}
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
throw new SQLException("not supported");
}
@Override
public boolean execute(String sql, String[] columnNames) throws SQLException {
throw new SQLException("not supported");
}
@Override
public int getResultSetHoldability() {
return 0;
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void setPoolable(boolean poolable) {
}
@Override
public boolean isPoolable() {
return false;
}
@Override
public void closeOnCompletion() {
}
@Override
public boolean isCloseOnCompletion() {
return false;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException("not a wrapper");
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
}

View File

@@ -0,0 +1,249 @@
package org.jeecg.modules.xslmes.mcs.access.proxy;
import org.jeecg.modules.xslmes.mcs.access.IMcsDataAccess;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class AgentProxyStatement implements Statement {
private final IMcsDataAccess dataAccess;
private ResultSet currentRs;
private int updateCount = -1;
public AgentProxyStatement(IMcsDataAccess dataAccess, String ignored) {
this.dataAccess = dataAccess;
}
@Override
public ResultSet executeQuery(String sql) throws SQLException {
try {
List<Map<String, Object>> rows = dataAccess.query(sql, new ArrayList<>());
currentRs = new MapListResultSet(rows);
updateCount = -1;
return currentRs;
} catch (Exception e) {
throw new SQLException(e.getMessage(), e);
}
}
@Override
public int executeUpdate(String sql) throws SQLException {
try {
updateCount = dataAccess.execute(sql, new ArrayList<>());
currentRs = null;
return updateCount;
} catch (Exception e) {
throw new SQLException(e.getMessage(), e);
}
}
@Override
public void close() {
currentRs = null;
}
@Override
public int getMaxFieldSize() {
return 0;
}
@Override
public void setMaxFieldSize(int max) {
}
@Override
public int getMaxRows() {
return 0;
}
@Override
public void setMaxRows(int max) {
}
@Override
public void setEscapeProcessing(boolean enable) {
}
@Override
public int getQueryTimeout() {
return 0;
}
@Override
public void setQueryTimeout(int seconds) {
}
@Override
public void cancel() {
}
@Override
public SQLWarning getWarnings() {
return null;
}
@Override
public void clearWarnings() {
}
@Override
public void setCursorName(String name) {
}
@Override
public boolean execute(String sql) throws SQLException {
String trimmed = sql == null ? "" : sql.trim();
if (trimmed.regionMatches(true, 0, "SELECT", 0, 6)) {
executeQuery(sql);
return true;
}
executeUpdate(sql);
return false;
}
@Override
public ResultSet getResultSet() {
return currentRs;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean getMoreResults() {
return false;
}
@Override
public void setFetchDirection(int direction) {
}
@Override
public int getFetchDirection() {
return ResultSet.FETCH_FORWARD;
}
@Override
public void setFetchSize(int rows) {
}
@Override
public int getFetchSize() {
return 0;
}
@Override
public int getResultSetConcurrency() {
return ResultSet.CONCUR_READ_ONLY;
}
@Override
public int getResultSetType() {
return ResultSet.TYPE_FORWARD_ONLY;
}
@Override
public void addBatch(String sql) {
}
@Override
public void clearBatch() {
}
@Override
public int[] executeBatch() {
return new int[0];
}
@Override
public Connection getConnection() {
return null;
}
@Override
public boolean getMoreResults(int current) {
return false;
}
@Override
public ResultSet getGeneratedKeys() {
return null;
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return executeUpdate(sql);
}
@Override
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
return executeUpdate(sql);
}
@Override
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
return executeUpdate(sql);
}
@Override
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
return execute(sql);
}
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return execute(sql);
}
@Override
public boolean execute(String sql, String[] columnNames) throws SQLException {
return execute(sql);
}
@Override
public int getResultSetHoldability() {
return 0;
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void setPoolable(boolean poolable) {
}
@Override
public boolean isPoolable() {
return false;
}
@Override
public void closeOnCompletion() {
}
@Override
public boolean isCloseOnCompletion() {
return false;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException("not a wrapper");
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
}

View File

@@ -0,0 +1,59 @@
package org.jeecg.modules.xslmes.mcs.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.xslmes.mcs.util.McsQueryHelper;
import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill;
import org.jeecg.modules.xslmes.mcs.service.IMcsToMesActMillService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* 开炼机动作(中间库 MCSToMES_Act_Mill
*/
@Tag(name = "开炼机动作(中间库)")
@RestController
@RequestMapping("/xslmes/mcs/mcsToMesActMill")
@Slf4j
public class McsToMesActMillController extends JeecgController<McsToMesActMill, IMcsToMesActMillService> {
@Autowired
private IMcsToMesActMillService mcsToMesActMillService;
@Operation(summary = "开炼机动作(中间库)-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<McsToMesActMill>> queryPageList(McsToMesActMill mcsToMesActMill,
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<McsToMesActMill> queryWrapper = McsQueryHelper.buildWrapper(mcsToMesActMill, req.getParameterMap());
Page<McsToMesActMill> page = new Page<>(pageNo, pageSize);
IPage<McsToMesActMill> pageList = mcsToMesActMillService.page(page, queryWrapper);
return Result.OK(pageList);
}
@Operation(summary = "开炼机动作(中间库)-通过id查询")
@GetMapping(value = "/queryById")
public Result<McsToMesActMill> queryById(@RequestParam(name = "id", required = true) String id) {
McsToMesActMill entity = mcsToMesActMillService.getById(id);
if (entity == null) {
return Result.error("未找到对应数据");
}
return Result.OK(entity);
}
@RequiresPermissions("xslmes:mcsToMesActMill:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, McsToMesActMill mcsToMesActMill) {
return super.exportXls(request, mcsToMesActMill, McsToMesActMill.class, "开炼机动作(中间库)");
}
}

View File

@@ -0,0 +1,41 @@
package org.jeecg.modules.xslmes.mcs.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.xslmes.mcs.access.MesToMcsPushService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* MES → MCS 经桌面代理下发。
*/
@Tag(name = "MES上辅机下发")
@RestController
@RequestMapping("/xslmes/mcs/push")
@RequiredArgsConstructor
public class MesToMcsPushController {
private final MesToMcsPushService mesToMcsPushService;
@Operation(summary = "经桌面代理写入中间表")
@PostMapping("/rows")
@SuppressWarnings("unchecked")
public Result<Map<String, Object>> pushRows(@RequestBody Map<String, Object> body) {
String table = body == null ? null : String.valueOf(body.getOrDefault("table", ""));
String mode = body == null ? "insert" : String.valueOf(body.getOrDefault("mode", "insert"));
List<String> keys = body == null ? null : (List<String>) body.get("keys");
List<Map<String, Object>> rows = body == null ? null : (List<Map<String, Object>>) body.get("rows");
int affected = mesToMcsPushService.push(table, mode, keys, rows);
Map<String, Object> data = new HashMap<>();
data.put("affected", affected);
return Result.OK(data);
}
}

View File

@@ -29,6 +29,9 @@ public class MesXslMcsDbConfigController {
@Autowired
private McsDataSourceManager mcsDataSourceManager;
@Autowired
private org.jeecg.modules.device.sync.agent.DeviceAgentSessionRegistry agentSessionRegistry;
private Integer resolveTenantId(Integer tenantId) {
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
if (tenantId == null) {
@@ -75,11 +78,21 @@ public class MesXslMcsDbConfigController {
data.put("dbConfigActive", mcsDataSourceManager.isDbConfigActive());
data.put("readEnabled", mcsDataSourceManager.isReadEnabled());
data.put("writeEnabled", mcsDataSourceManager.isWriteEnabled());
boolean agentMode = mcsDataSourceManager.isDesktopAgentMode();
data.put("accessMode", agentMode ? "DESKTOP_AGENT" : "DIRECT");
data.put("agentMode", agentMode);
if (cfg != null) {
data.put("serverHost", cfg.getServerHost());
data.put("serverPort", cfg.getServerPort());
data.put("dbName", cfg.getDbName());
data.put("agentDeviceId", cfg.getAgentDeviceId());
}
var agent = agentSessionRegistry.pickOnlineAgent(cfg == null ? null : cfg.getAgentDeviceId());
data.put("agentOnline", agent != null && agent.isOnline());
data.put("agentDbConnected", agent != null && agent.isDbConnected());
data.put("agentHostName", agent == null ? null : agent.getHostName());
data.put("agentDbMessage", agent == null ? null : agent.getDbMessage());
data.put("agentLastSeenAt", agent == null ? null : agent.getLastSeenAt());
return Result.OK(data);
}
}

View File

@@ -9,7 +9,11 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.util.dynamic.db.DataSourceCachePool;
import org.jeecg.modules.system.util.SecurityUtil;
import org.jeecg.modules.xslmes.mcs.access.DesktopAgentMcsDataAccess;
import org.jeecg.modules.xslmes.mcs.access.McsDataAccessFacade;
import org.jeecg.modules.xslmes.mcs.access.proxy.AgentProxyDataSource;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -37,6 +41,9 @@ public class McsDataSourceManager {
@Autowired
private DruidDataSourceCreator dataSourceCreator;
@Autowired
private ObjectProvider<DesktopAgentMcsDataAccess> desktopAgentMcsDataAccessProvider;
@Value("${spring.datasource.dynamic.datasource.sqlserver_mcs.url:}")
private String ymlUrl;
@@ -98,6 +105,17 @@ public class McsDataSourceManager {
}
if (Integer.valueOf(1).equals(config.getStatus())) {
try {
if (isDesktopAgentMode(config)) {
DesktopAgentMcsDataAccess agent = desktopAgentMcsDataAccessProvider.getIfAvailable();
if (agent == null) {
throw new IllegalStateException("桌面代理访问组件未就绪");
}
routing.addDataSource(DS_KEY, new AgentProxyDataSource(agent));
cachedConfig.set(config);
log.info("[MCS中间库] 已切换为桌面代理模式 sqlserver_mcs -> AgentProxy, deviceId={}",
config.getAgentDeviceId());
return;
}
String plainPassword = decryptPassword(config.getDbPassword());
DataSourceProperty property = buildDataSourceProperty(config, plainPassword);
DataSource mcsDs = dataSourceCreator.createDataSource(property);
@@ -117,6 +135,21 @@ public class McsDataSourceManager {
log.info("[MCS中间库] 连接未启用,读写开关仍按页面配置生效");
}
public boolean isDesktopAgentMode(MesXslMcsDbConfig config) {
MesXslMcsDbConfig cfg = config != null ? config : cachedConfig.get();
if (cfg == null) {
return true;
}
String mode = StringUtils.isBlank(cfg.getAccessMode())
? McsDataAccessFacade.MODE_DESKTOP_AGENT
: cfg.getAccessMode().trim().toUpperCase();
return McsDataAccessFacade.MODE_DESKTOP_AGENT.equals(mode);
}
public boolean isDesktopAgentMode() {
return isDesktopAgentMode(null);
}
private void closeAndRemoveDataSource(DynamicRoutingDataSource routing) {
if (!routing.getDataSources().containsKey(DS_KEY)) {
return;

View File

@@ -0,0 +1,76 @@
package org.jeecg.modules.xslmes.mcs.entity;
import java.io.Serializable;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 开炼机动作(中间库 MCSToMES_Act_Mill
*/
@Data
@TableName("MCSToMES_Act_Mill")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description = "开炼机动作(中间库)")
public class McsToMesActMill implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "GUID", type = IdType.INPUT)
@Schema(description = "GUID")
@Excel(name = "GUID", width = 36)
private String guid;
@Excel(name = "动作地址", width = 10)
@Schema(description = "动作地址")
@TableField("act_addr")
private Integer actAddr;
@Excel(name = "动作名称", width = 20)
@Schema(description = "动作名称")
@TableField("act_name")
private String actName;
@Excel(name = "动作名称(英)", width = 20)
@Schema(description = "动作名称(英)")
@TableField("act_name_en")
private String actNameEn;
@Excel(name = "动作备注", width = 15)
@Schema(description = "动作备注")
@TableField("act_memo")
private String actMemo;
@Excel(name = "动作关联地址", width = 12)
@Schema(description = "动作关联地址")
@TableField("act_Repaddr")
private Integer actRepaddr;
@Excel(name = "写入时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "写入时间")
@TableField("WriteTime")
private Date writeTime;
@Excel(name = "读取时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "读取时间")
@TableField("ReadTime")
private Date readTime;
@Excel(name = "读写标识", width = 10)
@Schema(description = "读写标识")
@TableField("RW_Flag")
private Integer rwFlag;
}

View File

@@ -66,6 +66,14 @@ public class MesXslMcsDbConfig implements Serializable {
@Schema(description = "备注")
private String remark;
//update-begin---author:cursor ---date:2026-07-20 for【设备库桥接】访问模式与绑定桌面代理-----------
@Schema(description = "访问模式DESKTOP_AGENT=桌面代理DIRECT=服务端直连")
private String accessMode;
@Schema(description = "绑定的桌面代理 deviceId")
private String agentDeviceId;
//update-end---author:cursor ---date:2026-07-20 for【设备库桥接】访问模式与绑定桌面代理-----------
private String createBy;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")

View File

@@ -0,0 +1,10 @@
package org.jeecg.modules.xslmes.mcs.mapper;
import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* 开炼机动作(中间库)
*/
public interface McsToMesActMillMapper extends BaseMapper<McsToMesActMill> {
}

View File

@@ -0,0 +1,10 @@
package org.jeecg.modules.xslmes.mcs.service;
import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* 开炼机动作(中间库)
*/
public interface IMcsToMesActMillService extends IService<McsToMesActMill> {
}

View File

@@ -0,0 +1,16 @@
package org.jeecg.modules.xslmes.mcs.service.impl;
import org.jeecg.modules.xslmes.mcs.entity.McsToMesActMill;
import org.jeecg.modules.xslmes.mcs.mapper.McsToMesActMillMapper;
import org.jeecg.modules.xslmes.mcs.service.IMcsToMesActMillService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.dynamic.datasource.annotation.DS;
/**
* 开炼机动作(中间库)
*/
@Service
@DS("sqlserver_mcs")
public class McsToMesActMillServiceImpl extends ServiceImpl<McsToMesActMillMapper, McsToMesActMill> implements IMcsToMesActMillService {
}

View File

@@ -30,6 +30,9 @@ public class MesXslMcsDbConfigServiceImpl extends ServiceImpl<MesXslMcsDbConfigM
@Autowired
private McsDataSourceManager mcsDataSourceManager;
@Autowired
private org.jeecg.modules.xslmes.mcs.access.McsDataAccessFacade mcsDataAccessFacade;
@Override
public MesXslMcsDbConfig getCurrentConfig(Integer tenantId) {
int tid = tenantId != null ? tenantId : 0;
@@ -61,10 +64,14 @@ public class MesXslMcsDbConfigServiceImpl extends ServiceImpl<MesXslMcsDbConfigM
if (config == null) {
return Result.error("配置不能为空");
}
if (StringUtils.isBlank(config.getServerHost())) {
if (StringUtils.isBlank(config.getAccessMode())) {
config.setAccessMode("DESKTOP_AGENT");
}
boolean agentMode = "DESKTOP_AGENT".equalsIgnoreCase(config.getAccessMode().trim());
if (!agentMode && StringUtils.isBlank(config.getServerHost())) {
return Result.error("服务器地址不能为空");
}
if (StringUtils.isBlank(config.getDbUsername())) {
if (!agentMode && StringUtils.isBlank(config.getDbUsername())) {
return Result.error("用户名不能为空");
}
if (config.getTenantId() == null) {
@@ -93,13 +100,20 @@ public class MesXslMcsDbConfigServiceImpl extends ServiceImpl<MesXslMcsDbConfigM
}
String plainPassword = resolvePlainPassword(config);
if (StringUtils.isBlank(plainPassword)) {
if (!agentMode && StringUtils.isBlank(plainPassword)) {
return Result.error("密码不能为空");
}
String err = mcsDataSourceManager.testConnection(config, plainPassword);
if (err != null && Integer.valueOf(1).equals(config.getStatus())) {
return Result.error("连接测试失败:" + err);
if (agentMode && StringUtils.isBlank(config.getServerHost())) {
config.setServerHost("desktop-agent");
}
if (agentMode && StringUtils.isBlank(config.getDbUsername())) {
config.setDbUsername("desktop-agent");
}
if (!agentMode) {
String err = mcsDataSourceManager.testConnection(config, plainPassword);
if (err != null && Integer.valueOf(1).equals(config.getStatus())) {
return Result.error("连接测试失败:" + err);
}
}
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
@@ -111,12 +125,16 @@ public class MesXslMcsDbConfigServiceImpl extends ServiceImpl<MesXslMcsDbConfigM
if (old == null) {
return Result.error("配置不存在");
}
config.setDbPassword(mcsDataSourceManager.encryptPassword(plainPassword));
if (StringUtils.isNotBlank(plainPassword)) {
config.setDbPassword(mcsDataSourceManager.encryptPassword(plainPassword));
} else {
config.setDbPassword(old.getDbPassword());
}
config.setUpdateBy(username);
config.setUpdateTime(now);
updateById(config);
} else {
config.setDbPassword(mcsDataSourceManager.encryptPassword(plainPassword));
config.setDbPassword(mcsDataSourceManager.encryptPassword(StringUtils.defaultString(plainPassword)));
config.setCreateBy(username);
config.setCreateTime(now);
config.setUpdateBy(username);
@@ -151,6 +169,14 @@ public class MesXslMcsDbConfigServiceImpl extends ServiceImpl<MesXslMcsDbConfigM
if (config == null) {
return Result.error("尚未配置中间库连接");
}
if (mcsDataSourceManager.isDesktopAgentMode(config)) {
try {
boolean ok = mcsDataAccessFacade.testConnection();
return ok ? Result.OK("桌面代理设备库连接成功") : Result.error("桌面代理设备库连接失败(代理离线或库不可达)");
} catch (Exception e) {
return Result.error("桌面代理探测失败:" + e.getMessage());
}
}
String plainPassword = mcsDataSourceManager.decryptPassword(config.getDbPassword());
String err = mcsDataSourceManager.testConnection(config, plainPassword);
if (err == null) {

View File

@@ -1,9 +1,9 @@
package org.jeecg.modules.xslmes.mcs.sync;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.xslmes.mcs.access.McsDataAccessFacade;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsSyncConfig;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsSyncField;
@@ -69,6 +69,9 @@ public class GenericMcsSyncEngine {
@Autowired
private org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager mcsDataSourceManager;
@Autowired
private McsDataAccessFacade mcsDataAccessFacade;
//update-begin---author:GHT ---date:20260617 for【MES上辅机】采集模式 全量/时间/增量-----------
public String sync(MesXslMcsSyncConfig cfg, List<MesXslMcsSyncField> fields) {
String sourceTable = trim(cfg.getSourceTable());
@@ -91,7 +94,6 @@ public class GenericMcsSyncEngine {
}
String mode = StringUtils.isBlank(cfg.getSyncMode()) ? MODE_FULL : cfg.getSyncMode().trim().toUpperCase();
JdbcTemplate sourceJt = new JdbcTemplate(getSourceDataSource());
JdbcTemplate targetJt = new JdbcTemplate(dataSource);
// 目标表标准字段探测 + 自动填充列
@@ -134,7 +136,7 @@ public class GenericMcsSyncEngine {
String predicate = flagPredicate(flagCol, cfg.getFlagCondition(), cfg.getFlagMatchValue());
String sql = "SELECT TOP " + limit + " " + colList(srcCols) + " FROM [" + sourceTable + "]"
+ " WHERE (" + predicate + ")";
rows = sourceJt.queryForList(sql);
rows = mcsDataAccessFacade.query(sql, null);
if (rows.isEmpty()) {
return "增量采集:无待采集数据";
}
@@ -149,10 +151,10 @@ public class GenericMcsSyncEngine {
sql.append(" AND [").append(incrCol).append("] < ?");
args.add(window[1]);
}
rows = sourceJt.queryForList(sql.toString(), args.toArray());
rows = mcsDataAccessFacade.query(sql.toString(), args);
} else {
// FULL
rows = sourceJt.queryForList("SELECT " + colList(srcCols) + " FROM [" + sourceTable + "]");
rows = mcsDataAccessFacade.query("SELECT " + colList(srcCols) + " FROM [" + sourceTable + "]", null);
}
if (rows.isEmpty()) {
@@ -209,7 +211,7 @@ public class GenericMcsSyncEngine {
// INCR(标记回写):对本批所有源行回写标记值,下轮不再采集
if (flagMode) {
String writeValue = StringUtils.isBlank(cfg.getFlagWriteValue()) ? FLAG_SYNCED : cfg.getFlagWriteValue();
int marked = writeBackFlag(sourceJt, sourceTable, flagCol, cfg.getFlagCondition(), cfg.getFlagMatchValue(), writeValue, keyMaps, rows);
int marked = writeBackFlag(sourceTable, flagCol, cfg.getFlagCondition(), cfg.getFlagMatchValue(), writeValue, keyMaps, rows);
return String.format("增量采集:新增%d更新%d未变%d回写标记%d源%d条",
ins, upd, unchanged, marked, rows.size());
}
@@ -252,14 +254,14 @@ public class GenericMcsSyncEngine {
* 避免误标在本轮 SELECT 之后才进入中间库、尚未采集的新数据;
* 并以采集条件谓词做守卫,避开本轮已被其他进程改动的行。</p>
*/
private int writeBackFlag(JdbcTemplate sourceJt, String sourceTable, String flagCol, String condition,
private int writeBackFlag(String sourceTable, String flagCol, String condition,
String matchValue, String writeValue, List<MesXslMcsSyncField> keyMaps, List<Map<String, Object>> rows) {
validateIdent(flagCol);
StringBuilder sql = new StringBuilder("UPDATE [").append(sourceTable).append("] SET [")
.append(flagCol).append("] = ? WHERE ");
sql.append(keyMaps.stream().map(k -> "[" + k.getSourceField() + "] = ?").collect(Collectors.joining(" AND ")));
sql.append(" AND (").append(flagPredicate(flagCol, condition, matchValue)).append(")");
List<Object[]> argsList = new ArrayList<>();
int total = 0;
Set<String> handled = new HashSet<>();
for (Map<String, Object> row : rows) {
Map<String, Object> rci = ci(row);
@@ -272,9 +274,9 @@ public class GenericMcsSyncEngine {
for (MesXslMcsSyncField k : keyMaps) {
args.add(rci.get(k.getSourceField()));
}
argsList.add(args.toArray());
total += mcsDataAccessFacade.execute(sql.toString(), args);
}
return batch(sourceJt, sql.toString(), argsList);
return total;
}
//update-end---author:GHT ---date:20260617 for【MES上辅机】增量匹配改为标记位回写-----------
@@ -502,15 +504,6 @@ public class GenericMcsSyncEngine {
return maps.stream().anyMatch(m -> m.getTargetField() != null && m.getTargetField().equalsIgnoreCase(targetCol));
}
private DataSource getSourceDataSource() {
DynamicRoutingDataSource routing = (DynamicRoutingDataSource) dataSource;
DataSource src = routing.getDataSources().get(McsDataSourceManager.DS_KEY);
if (src == null) {
throw new IllegalStateException("中间库数据源 " + McsDataSourceManager.DS_KEY + " 未注册");
}
return src;
}
private void validateIdent(String name) {
if (name == null || !IDENT.matcher(name).matches()) {
throw new IllegalArgumentException("非法的表名或字段名: " + name);

View File

@@ -0,0 +1,6 @@
package org.jeecg.modules.xslmes.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.xslmes.entity.MesXslMillAction;
public interface IMesXslMillActionService extends IService<MesXslMillAction> {}

View File

@@ -7,7 +7,9 @@ public interface IMesXslMixerConditionService extends IService<MesXslMixerCondit
void fillEquipmentName(MesXslMixerCondition model);
boolean isConditionNameDuplicated(String conditionName, String excludeId);
//update-begin---author:jiangxh ---date:20260728 for【密炼机条件】条件名称/代码按设备唯一-----------
boolean isConditionNameDuplicated(String conditionName, String equipmentId, String excludeId);
boolean isConditionCodeDuplicated(String conditionCode, String excludeId);
boolean isConditionCodeDuplicated(String conditionCode, String equipmentId, String excludeId);
//update-end---author:jiangxh ---date:20260728 for【密炼机条件】条件名称/代码按设备唯一-----------
}

View File

@@ -12,6 +12,7 @@ import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -300,6 +301,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
//update-begin---author:cursor ---date:20260521 for配合示方明细重量%按PHR/总PHR自动计算-----------
fillWeightPercent(line, weightBase);
//update-end---author:cursor ---date:20260521 for配合示方明细重量%按PHR/总PHR自动计算-----------
//update-begin---author:jiangxh ---date:20260729 for【配合示方】明细重量%/体积统一保留三位小数-----------
normalizeLineDecimals(line);
//update-end---author:jiangxh ---date:20260729 for【配合示方】明细重量%/体积统一保留三位小数-----------
if (line.getCreateTime() == null) {
line.setCreateTime(now);
}
@@ -339,7 +343,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
if (material == null || material.getSpecificGravity() == null || material.getSpecificGravity().compareTo(BigDecimal.ZERO) <= 0) {
return;
}
line.setVolume(line.getPhr().divide(material.getSpecificGravity(), 6, RoundingMode.HALF_UP));
//update-begin---author:jiangxh ---date:20260729 for【配合示方】明细体积保留三位小数-----------
line.setVolume(line.getPhr().divide(material.getSpecificGravity(), 3, RoundingMode.HALF_UP));
//update-end---author:jiangxh ---date:20260729 for【配合示方】明细体积保留三位小数-----------
}
private void fillWeightPercent(MesXslFormulaSpecLine line, BigDecimal totalPhr) {
@@ -347,10 +353,27 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
if (line.getWeightPercent() != null || line.getPhr() == null || totalPhr == null || totalPhr.compareTo(BigDecimal.ZERO) <= 0) {
return;
}
line.setWeightPercent(line.getPhr().multiply(BigDecimal.valueOf(100)).divide(totalPhr, 4, RoundingMode.HALF_UP));
//update-begin---author:jiangxh ---date:20260729 for【配合示方】明细重量%/体积保留三位小数-----------
line.setWeightPercent(line.getPhr().multiply(BigDecimal.valueOf(100)).divide(totalPhr, 3, RoundingMode.HALF_UP));
//update-end---author:jiangxh ---date:20260729 for【配合示方】明细重量%/体积保留三位小数-----------
//update-end---author:cursor ---date:20260521 for配合示方明细重量%按PHR/总PHR自动计算可手工覆盖-----------
}
//update-begin---author:jiangxh ---date:20260729 for【配合示方】明细重量%/体积统一保留三位小数-----------
/** 明细重量%、体积统一四舍五入到三位小数(含手工录入值) */
private void normalizeLineDecimals(MesXslFormulaSpecLine line) {
if (line == null) {
return;
}
if (line.getWeightPercent() != null) {
line.setWeightPercent(line.getWeightPercent().setScale(3, RoundingMode.HALF_UP));
}
if (line.getVolume() != null) {
line.setVolume(line.getVolume().setScale(3, RoundingMode.HALF_UP));
}
}
//update-end---author:jiangxh ---date:20260729 for【配合示方】明细重量%/体积统一保留三位小数-----------
//update-begin---author:cursor ---date:20260521 for【配合示方】保存后汇总A/Q胶比重写入数据库-----------
/** 根据明细重新汇总主表 TOTAL PHR、A胶TOTAL PHR、比重与人工配料标记 */
private void refreshMainSummary(String mainId) {
@@ -457,7 +480,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
if (material == null || material.getSpecificGravity() == null || material.getSpecificGravity().compareTo(BigDecimal.ZERO) <= 0) {
return null;
}
return line.getPhr().divide(material.getSpecificGravity(), 6, RoundingMode.HALF_UP);
//update-begin---author:jiangxh ---date:20260729 for【配合示方】明细体积保留三位小数-----------
return line.getPhr().divide(material.getSpecificGravity(), 3, RoundingMode.HALF_UP);
//update-end---author:jiangxh ---date:20260729 for【配合示方】明细体积保留三位小数-----------
}
//update-end---author:cursor ---date:20260521 for【配合示方】保存后汇总A/Q胶比重写入数据库-----------
@@ -540,7 +565,6 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
throw new IllegalArgumentException("请先维护混合段数");
}
String rubberName = resolveRubberName(main);
String formulaCodeSuffix = resolveFormulaCodeSuffix(main);
boolean hasQ = hasStepQ(main.getLineList());
List<MesXslFormulaMixingGeneratePreviewItemVO> items = new ArrayList<>();
int aIndex = 0;
@@ -551,16 +575,24 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
item.setStageIndex(i);
if (isQSegment) {
item.setStepType("Q");
item.setSpecCode(appendFormulaCodeSuffix("F" + rubberName, formulaCodeSuffix));
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】B/F匹配编号不拼版本后缀-----------
item.setSpecCode(buildBfRubberSpecCode("F", rubberName));
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】B/F匹配编号不拼版本后缀-----------
item.setASegmentIndex(null);
} else {
aIndex++;
item.setStepType("A");
item.setASegmentIndex(aIndex);
item.setSpecCode(appendFormulaCodeSuffix("B" + aIndex + rubberName, formulaCodeSuffix));
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】B/F匹配编号不拼版本后缀-----------
item.setSpecCode(buildBfRubberSpecCode("B" + aIndex, rubberName));
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】B/F匹配编号不拼版本后缀-----------
}
items.add(item);
}
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】列表点生成混炼示方时即匹配胶料列表-----------
// 点击「生成混炼示方」打开预览时即校验拼接示方编号是否已在胶料列表维护
assertRequiredRubberMaterialsExist(toGenerateRowsFromPreviewItems(items), main, main.getLineList());
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】列表点生成混炼示方时即匹配胶料列表-----------
MesXslFormulaMixingGeneratePreviewVO preview = new MesXslFormulaMixingGeneratePreviewVO();
preview.setFormulaSpecId(formulaSpecId);
preview.setRubberName(rubberName);
@@ -598,10 +630,10 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
int created = 0;
int totalStages = normalizeMixingStages(formula.getMixingStages());
Date today = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
//update-begin---author:cursor ---date:20260525 forXSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
Set<String> syncedRubberSpecCodes = new HashSet<>();
//update-begin---author:jiangxh ---date:20260729 for混炼示方生成】B/F胶料改为胶料列表匹配不存在则提示先维护-----------
Map<String, String> categoryIdCache = new HashMap<>();
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
assertRequiredRubberMaterialsExist(request.getRows(), formula, lines);
//update-end---author:jiangxh ---date:20260729 for【混炼示方生成】B/F胶料改为胶料列表匹配不存在则提示先维护-----------
for (MesXslFormulaMixingGenerateRowVO row : request.getRows()) {
if (row == null || oConvertUtils.isEmpty(row.getSpecCode()) || row.getStageIndex() == null) {
continue;
@@ -609,11 +641,6 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
if (CollectionUtils.isEmpty(row.getMachines())) {
throw new IllegalArgumentException("示方「" + row.getSpecCode() + "」请至少选择一个机台");
}
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
if (syncedRubberSpecCodes.add(row.getSpecCode().trim())) {
syncGeneratedRubberMixerMaterial(row, formula, compoundSpecificGravity, categoryIdCache);
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A48】生成混炼示方同步B/F段胶至密炼物料-----------
String materialStep = resolveMixingMaterialStep(row);
int mixingColumn = resolveMixingColumn(row, formula);
List<MesXslMixingSpecMaterial> materials = buildMixingMaterials(
@@ -647,11 +674,15 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A44】生成混炼示方时按设备有效体积计算填充体积-----------
mixing.setConvertFactor(BigDecimal.ONE);
boolean isFinalStage = "Q".equalsIgnoreCase(row.getStepType());
//update-begin---author:jiangxh ---date:20260729 for【混炼示方】母胶/终炼胶比重保留三位小数-----------
if (isFinalStage) {
mixing.setFinalRubberSg(compoundSpecificGravity);
} else {
mixing.setMotherRubberSg(formula.getARubberSg());
if (compoundSpecificGravity != null) {
mixing.setFinalRubberSg(compoundSpecificGravity.setScale(3, RoundingMode.HALF_UP));
}
} else if (formula.getARubberSg() != null) {
mixing.setMotherRubberSg(formula.getARubberSg().setScale(3, RoundingMode.HALF_UP));
}
//update-end---author:jiangxh ---date:20260729 for【混炼示方】母胶/终炼胶比重保留三位小数-----------
BigDecimal specificGravity = isFinalStage ? compoundSpecificGravity : formula.getARubberSg();
String machineLabel = StringUtils.defaultIfBlank(machine.getMachineName(), machine.getMachineId());
MesXslEquipmentLedger equipment = equipmentCache.computeIfAbsent(
@@ -766,15 +797,88 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
.divide(effectiveVolume, 6, RoundingMode.HALF_UP)
.multiply(BigDecimal.valueOf(100))
.multiply(factor)
.setScale(6, RoundingMode.HALF_UP);
//update-begin---author:jiangxh ---date:20260729 for【混炼示方】填充体积保留两位小数-----------
.setScale(2, RoundingMode.HALF_UP);
//update-end---author:jiangxh ---date:20260729 for【混炼示方】填充体积保留两位小数-----------
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A44】生成混炼示方时按设备有效体积计算填充体积-----------
//update-begin---author:cursor ---date:20260601 for【XSLMES-20260601-A62】生成混炼示方改为同步B/F段胶至胶料信息-----------
//update-begin---author:jiangxh ---date:20260729 for【混炼示方生成】B/F胶料改为胶料列表匹配不存在则提示先维护-----------
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】预览项转生成行供胶料匹配校验-----------
/** 预览行转为生成行结构,供胶料匹配校验复用 */
private List<MesXslFormulaMixingGenerateRowVO> toGenerateRowsFromPreviewItems(
List<MesXslFormulaMixingGeneratePreviewItemVO> items) {
List<MesXslFormulaMixingGenerateRowVO> rows = new ArrayList<>();
if (CollectionUtils.isEmpty(items)) {
return rows;
}
for (MesXslFormulaMixingGeneratePreviewItemVO item : items) {
if (item == null) {
continue;
}
MesXslFormulaMixingGenerateRowVO row = new MesXslFormulaMixingGenerateRowVO();
row.setSpecCode(item.getSpecCode());
row.setStageIndex(item.getStageIndex());
row.setASegmentIndex(item.getASegmentIndex());
row.setStepType(item.getStepType());
rows.add(row);
}
return rows;
}
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】预览项转生成行供胶料匹配校验-----------
/**
* 生成混炼示方时,将 B 段/F 段胶料同步写入「胶料信息」(mes_material)
* 除 ERP 编号留空、编码/名称为示方编号外,其余字段按配合示方所选原胶料信息赋值
* 生成前校验:按 B段序号/F + 胶料名 拼出的示方编号,必须已在胶料信息中存在;不再自动新建。
* 在列表点击「生成混炼示方」预览时即调用,确认生成时再次校验
*/
private void assertRequiredRubberMaterialsExist(
List<MesXslFormulaMixingGenerateRowVO> rows,
MesXslFormulaSpec formula,
List<MesXslFormulaSpecLine> lines) {
if (CollectionUtils.isEmpty(rows)) {
return;
}
LinkedHashSet<String> requiredCodes = new LinkedHashSet<>();
String rubberName = resolveRubberName(formula);
for (MesXslFormulaMixingGenerateRowVO row : rows) {
if (row == null || StringUtils.isBlank(row.getSpecCode())) {
continue;
}
requiredCodes.add(row.getSpecCode().trim());
String stepFilter = resolveMixingMaterialStep(row);
int mixingColumn = resolveMixingColumn(row, formula);
if ("Q".equalsIgnoreCase(stepFilter)) {
int lastBSegment = resolveLastBSegmentIndex(formula, lines);
if (lastBSegment > 0 && StringUtils.isNotBlank(rubberName)) {
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】母胶匹配不拼版本后缀-----------
requiredCodes.add(buildBfRubberSpecCode("B" + lastBSegment, rubberName));
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】母胶匹配不拼版本后缀-----------
}
} else if (mixingColumn > 1 && StringUtils.isNotBlank(rubberName)) {
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】母胶匹配不拼版本后缀-----------
requiredCodes.add(buildBfRubberSpecCode("B" + (mixingColumn - 1), rubberName));
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】母胶匹配不拼版本后缀-----------
}
}
List<String> missing = new ArrayList<>();
for (String code : requiredCodes) {
if (StringUtils.isBlank(code)) {
continue;
}
if (findRubberMaterialByCodeOrName(code.trim()) == null) {
missing.add(code.trim());
}
}
if (!missing.isEmpty()) {
throw new IllegalArgumentException("请先维护母胶和终胶信息:" + String.join("", missing));
}
}
/**
* @deprecated 已改为仅匹配胶料列表,保留方法避免历史调用编译缺口;生成流程不再自动写入胶料。
*/
@Deprecated
private void syncGeneratedRubberMixerMaterial(
MesXslFormulaMixingGenerateRowVO row,
MesXslFormulaSpec formula,
@@ -784,60 +888,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
return;
}
String specCode = row.getSpecCode().trim();
boolean isFinalStage = "Q".equalsIgnoreCase(resolveMixingMaterialStep(row));
String materialPhase = resolveGeneratedMaterialPhase(row);
MesMaterial sourceRubber = resolveSourceRubberMaterial(formula);
String categoryId = sourceRubber != null ? sourceRubber.getCategoryId() : null;
String rubberName = resolveRubberName(formula);
String generatedName = buildGeneratedRubberMaterialName(row, rubberName);
MesMaterial existing = findGeneratedRubberMaterialByCodeOrName(specCode, materialPhase);
Date now = new Date();
if (existing != null) {
copyGeneratedMaterialFieldsFromSource(existing, sourceRubber);
if (StringUtils.isNotBlank(categoryId)) {
existing.setCategoryId(categoryId);
}
if (StringUtils.isBlank(existing.getMaterialCode())) {
existing.setMaterialCode(specCode);
}
existing.setMaterialName(generatedName);
if (StringUtils.isBlank(existing.getAliasName()) && StringUtils.isNotBlank(rubberName)) {
existing.setAliasName(rubberName);
}
existing.setMaterialPhase(materialPhase);
existing.setEnableFlag(existing.getEnableFlag() == null ? 1 : existing.getEnableFlag());
existing.setUpdateTime(now);
mesMaterialService.updateById(existing);
log.info(
"[混炼示方生成] 更新胶料信息 id={}, code={}, phase={}, categoryId={}, isFinal={}",
existing.getId(),
specCode,
materialPhase,
categoryId,
isFinalStage);
return;
if (findRubberMaterialByCodeOrName(specCode) == null) {
throw new IllegalArgumentException("请先维护母胶和终胶信息:" + specCode);
}
MesMaterial material = new MesMaterial();
copyGeneratedMaterialFieldsFromSource(material, sourceRubber);
material.setMaterialCode(specCode);
material.setMaterialName(generatedName);
material.setAliasName(StringUtils.isNotBlank(rubberName) ? rubberName : null);
material.setCategoryId(categoryId);
material.setMaterialPhase(materialPhase);
material.setEnableFlag(1);
material.setIsSpecialRubber(0);
material.setDelFlag(CommonConstant.DEL_FLAG_0);
material.setCreateTime(now);
material.setUpdateTime(now);
mesMaterialService.save(material);
log.info(
"[混炼示方生成] 新增胶料信息 id={}, code={}, phase={}, categoryId={}, isFinal={}",
material.getId(),
specCode,
materialPhase,
categoryId,
isFinalStage);
}
private void copyGeneratedMaterialFieldsFromSource(MesMaterial target, MesMaterial source) {
@@ -867,6 +920,7 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
target.setLastErpSyncTime(source.getLastErpSyncTime());
target.setRemark(source.getRemark());
}
//update-end---author:jiangxh ---date:20260729 for【混炼示方生成】B/F胶料改为胶料列表匹配不存在则提示先维护-----------
private MesMaterial resolveSourceRubberMaterial(MesXslFormulaSpec formula) {
if (formula == null || StringUtils.isBlank(formula.getRubberMaterialId())) {
@@ -967,8 +1021,10 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
}
/**
* 配合示方胶料代号/示方编号中,胶料名称之后的后缀原样截取(如 SA01、SB01 等,不做固定拼接)。
* 配合示方胶料代号/示方编号中,胶料名称之后的后缀原样截取(如 SA01、SB01 等)。
* @deprecated 生成混炼示方匹配胶料已改为仅 B/F+胶料名,不再拼接版本后缀
*/
@Deprecated
private String resolveFormulaCodeSuffix(MesXslFormulaSpec main) {
if (main == null) {
return "";
@@ -995,6 +1051,10 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
return "";
}
/**
* @deprecated 生成混炼示方匹配胶料已改为仅 B/F+胶料名,请使用 {@link #buildBfRubberSpecCode(String, String)}
*/
@Deprecated
private String appendFormulaCodeSuffix(String baseSpecCode, String formulaCodeSuffix) {
if (StringUtils.isBlank(baseSpecCode)) {
return baseSpecCode;
@@ -1005,6 +1065,16 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
return baseSpecCode + formulaCodeSuffix;
}
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】B/F匹配编号仅前缀+胶料名-----------
/**
* 生成/匹配用示方编号仅「B{n}/F + 胶料名」,不拼接 SA01 等版本号。
* 例:胶料 XC001 三段 → B1XC001、B2XC001、FXC001
*/
private String buildBfRubberSpecCode(String phasePrefix, String rubberName) {
return StringUtils.defaultString(phasePrefix) + StringUtils.defaultString(rubberName).trim();
}
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】B/F匹配编号仅前缀+胶料名-----------
private boolean hasStepQ(List<MesXslFormulaSpecLine> lines) {
if (CollectionUtils.isEmpty(lines)) {
return false;
@@ -1071,10 +1141,11 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
if ("Q".equals(stepFilter)) {
//update-begin---author:cursor ---date:20260522 for【XSLMES-20260522-A38】F段加入最后一段B炼好胶料一条再加Q配合剂-----------
String rubberName = resolveRubberName(formula);
String codeSuffix = resolveFormulaCodeSuffix(formula);
int lastBSegment = resolveLastBSegmentIndex(formula, lines);
if (lastBSegment > 0) {
String motherSpecCode = appendFormulaCodeSuffix("B" + lastBSegment + rubberName, codeSuffix);
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】母胶编号不拼版本后缀-----------
String motherSpecCode = buildBfRubberSpecCode("B" + lastBSegment, rubberName);
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】母胶编号不拼版本后缀-----------
BigDecimal motherWeight = resolveStageCumulativeTotal(formula, lines, lastBSegment);
materials.add(
createMotherRubberMaterial(
@@ -1095,7 +1166,6 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
}
int bColumn = Math.max(stageIndex, 1);
String rubberName = resolveRubberName(formula);
String codeSuffix = resolveFormulaCodeSuffix(formula);
if (bColumn <= 1) {
appendBStageColumnAgents(lines, 1, materials, mixerCache, categoryNameCache, kindLookup);
reindexMaterialSortNo(materials);
@@ -1103,7 +1173,9 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
}
// B2/B3…上一段炼好胶料一条(如 B1+胶料名) + 本段列配合剂
int prevB = bColumn - 1;
String motherSpecCode = appendFormulaCodeSuffix("B" + prevB + rubberName, codeSuffix);
//update-begin---author:jiangxh ---date:20260730 for【混炼示方生成】母胶编号不拼版本后缀-----------
String motherSpecCode = buildBfRubberSpecCode("B" + prevB, rubberName);
//update-end---author:jiangxh ---date:20260730 for【混炼示方生成】母胶编号不拼版本后缀-----------
BigDecimal motherWeight = resolveStageCumulativeTotal(formula, lines, prevB);
materials.add(
createMotherRubberMaterial(
@@ -1205,7 +1277,7 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】上一段母胶明细行改绑胶料信息-----------
}
/** 上一段 B 段胶尚未写入胶料信息时,按配合示方胶料类别补建一条胶料信息 */
/** 上一段 B 段胶必须已在胶料信息中维护,不再自动补建 */
private void ensureMotherRubberMaterialSynced(
String motherSpecCode,
MesXslFormulaSpec formula,
@@ -1214,13 +1286,12 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
return;
}
String normalized = motherSpecCode.trim();
//update-begin---author:jiangxh ---date:20260729 for【混炼示方生成】上一段母胶改为胶料列表匹配不存在则提示-----------
if (findRubberMaterialByCodeOrName(normalized) != null) {
return;
}
MesXslFormulaMixingGenerateRowVO stub = new MesXslFormulaMixingGenerateRowVO();
stub.setSpecCode(normalized);
stub.setStepType("A");
syncGeneratedRubberMixerMaterial(stub, formula, null, categoryIdCache);
throw new IllegalArgumentException("请先维护母胶和终胶信息:" + normalized);
//update-end---author:jiangxh ---date:20260729 for【混炼示方生成】上一段母胶改为胶料列表匹配不存在则提示-----------
}
/** 按密炼物料主数据回填混炼示方明细行(名称/大小类/种类) */
@@ -1272,9 +1343,17 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
material.setMaterialMajor(null);
material.setMaterialMinor(categoryName);
material.setMixerMaterialId(rubber.getId());
material.setMaterialKind(
mesXslMixerMaterialKindCfgService.resolveMixingMaterialKind(
kindLookup, rubber.getCategoryId(), null, categoryName));
//update-begin---author:jiangxh ---date:20260729 for【混炼示方生成】母胶行种类按配置解析未命中则带入胶料兜底种类-----------
String kind = mesXslMixerMaterialKindCfgService.resolveMixingMaterialKind(
kindLookup, rubber.getCategoryId(), null, categoryName);
if (StringUtils.isBlank(kind) && kindLookup != null && StringUtils.isNotBlank(kindLookup.getRubberKindName())) {
kind = kindLookup.getRubberKindName().trim();
}
if (StringUtils.isBlank(kind)) {
kind = "胶料";
}
material.setMaterialKind(kind);
//update-end---author:jiangxh ---date:20260729 for【混炼示方生成】母胶行种类按配置解析未命中则带入胶料兜底种类-----------
}
/** 未查到胶料信息时,按配合示方胶料类别从配置表解析种类 */
@@ -1287,9 +1366,17 @@ public class MesXslFormulaSpecServiceImpl extends ServiceImpl<MesXslFormulaSpecM
String categoryName = resolveCategoryName(categoryId, categoryNameCache);
material.setMaterialMajor(null);
material.setMaterialMinor(categoryName);
material.setMaterialKind(
mesXslMixerMaterialKindCfgService.resolveMixingMaterialKind(
kindLookup, categoryId, null, categoryName));
//update-begin---author:jiangxh ---date:20260729 for【混炼示方生成】母胶兜底种类带入胶料种类-----------
String kind = mesXslMixerMaterialKindCfgService.resolveMixingMaterialKind(
kindLookup, categoryId, null, categoryName);
if (StringUtils.isBlank(kind) && kindLookup != null && StringUtils.isNotBlank(kindLookup.getRubberKindName())) {
kind = kindLookup.getRubberKindName().trim();
}
if (StringUtils.isBlank(kind)) {
kind = "胶料";
}
material.setMaterialKind(kind);
//update-end---author:jiangxh ---date:20260729 for【混炼示方生成】母胶兜底种类带入胶料种类-----------
}
//update-end---author:cursor ---date:20260601 for【XSLMES-20260601-A62】母胶明细行按胶料信息回填-----------
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A54】母炼胶明细行绑定已同步密炼物料主数据-----------

View File

@@ -0,0 +1,10 @@
package org.jeecg.modules.xslmes.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.xslmes.entity.MesXslMillAction;
import org.jeecg.modules.xslmes.mapper.MesXslMillActionMapper;
import org.jeecg.modules.xslmes.service.IMesXslMillActionService;
import org.springframework.stereotype.Service;
@Service
public class MesXslMillActionServiceImpl extends ServiceImpl<MesXslMillActionMapper, MesXslMillAction> implements IMesXslMillActionService {}

View File

@@ -18,13 +18,16 @@ public class MesXslMixerConditionServiceImpl
@Autowired private MesXslEquipmentLedgerMapper equipmentLedgerMapper;
//update-begin---author:jiangxh ---date:20260728 for【密炼机条件】条件名称/代码按设备唯一-----------
@Override
public boolean isConditionNameDuplicated(String conditionName, String excludeId) {
if (StringUtils.isBlank(conditionName)) {
public boolean isConditionNameDuplicated(String conditionName, String equipmentId, String excludeId) {
if (StringUtils.isBlank(conditionName) || StringUtils.isBlank(equipmentId)) {
return false;
}
LambdaQueryWrapper<MesXslMixerCondition> wrapper =
new LambdaQueryWrapper<MesXslMixerCondition>().eq(MesXslMixerCondition::getConditionName, conditionName.trim());
new LambdaQueryWrapper<MesXslMixerCondition>()
.eq(MesXslMixerCondition::getEquipmentId, equipmentId.trim())
.eq(MesXslMixerCondition::getConditionName, conditionName.trim());
if (StringUtils.isNotBlank(excludeId)) {
wrapper.ne(MesXslMixerCondition::getId, excludeId.trim());
}
@@ -32,17 +35,20 @@ public class MesXslMixerConditionServiceImpl
}
@Override
public boolean isConditionCodeDuplicated(String conditionCode, String excludeId) {
if (StringUtils.isBlank(conditionCode)) {
public boolean isConditionCodeDuplicated(String conditionCode, String equipmentId, String excludeId) {
if (StringUtils.isBlank(conditionCode) || StringUtils.isBlank(equipmentId)) {
return false;
}
LambdaQueryWrapper<MesXslMixerCondition> wrapper =
new LambdaQueryWrapper<MesXslMixerCondition>().eq(MesXslMixerCondition::getConditionCode, conditionCode.trim());
new LambdaQueryWrapper<MesXslMixerCondition>()
.eq(MesXslMixerCondition::getEquipmentId, equipmentId.trim())
.eq(MesXslMixerCondition::getConditionCode, conditionCode.trim());
if (StringUtils.isNotBlank(excludeId)) {
wrapper.ne(MesXslMixerCondition::getId, excludeId.trim());
}
return this.count(wrapper) > 0;
}
//update-end---author:jiangxh ---date:20260728 for【密炼机条件】条件名称/代码按设备唯一-----------
@Override
public void fillEquipmentName(MesXslMixerCondition model) {

View File

@@ -103,6 +103,8 @@ public class MesXslMixingProductionPlanServiceImpl
private void rebuildRawMaterialDemandPlan(List<MesXslMixingProductionPlan> savedPlans) {
jdbcTemplate.update("DELETE FROM mes_xsl_raw_material_demand_plan");
jdbcTemplate.update("DELETE FROM mes_xsl_auto_small_material_demand_plan");
jdbcTemplate.update("DELETE FROM mes_xsl_manual_small_material_demand_plan");
if (CollectionUtils.isEmpty(savedPlans)) {
return;
}
@@ -184,6 +186,8 @@ public class MesXslMixingProductionPlanServiceImpl
}
Map<String, RawDemandAggregate> aggregateMap = new HashMap<>();
Map<String, SmallDemandAggregate> autoAggregateMap = new HashMap<>();
Map<String, SmallDemandAggregate> manualAggregateMap = new HashMap<>();
for (MesXslMixingProductionPlan plan : validPlans) {
String specId = specIdByName.get(StringUtils.trimToEmpty(plan.getFormulaName()));
if (StringUtils.isBlank(specId)) {
@@ -241,19 +245,39 @@ public class MesXslMixingProductionPlanServiceImpl
StringUtils.defaultString(erpCode),
rawMaterialName));
aggregate.demandWeight = aggregate.demandWeight.add(demandWeight);
String smallKey =
String.join(
"|",
planDateKey,
StringUtils.defaultString(plan.getMachineId()),
StringUtils.defaultString(plan.getMachineName()),
rawMaterialName);
boolean isAutoKind = StringUtils.contains(StringUtils.trimToEmpty(detail.getMaterialKind()), "自动");
Map<String, SmallDemandAggregate> targetSmallMap = isAutoKind ? autoAggregateMap : manualAggregateMap;
SmallDemandAggregate smallAggregate =
targetSmallMap.computeIfAbsent(
smallKey,
k ->
new SmallDemandAggregate(
plan.getPlanDate(),
StringUtils.trimToNull(plan.getMachineId()),
StringUtils.trimToNull(plan.getMachineName()),
rawMaterialName));
smallAggregate.demandWeight = smallAggregate.demandWeight.add(demandWeight);
}
}
if (aggregateMap.isEmpty()) {
if (aggregateMap.isEmpty() && autoAggregateMap.isEmpty() && manualAggregateMap.isEmpty()) {
return;
}
List<Object[]> batchArgs = new ArrayList<>();
List<Object[]> rawBatchArgs = new ArrayList<>();
Date now = new Date();
for (RawDemandAggregate aggregate : aggregateMap.values()) {
if (aggregate.demandWeight.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
batchArgs.add(
rawBatchArgs.add(
new Object[] {
IdWorker.getIdStr(),
aggregate.machineId,
@@ -270,14 +294,53 @@ public class MesXslMixingProductionPlanServiceImpl
CommonConstant.DEL_FLAG_0
});
}
if (batchArgs.isEmpty()) {
return;
if (!rawBatchArgs.isEmpty()) {
jdbcTemplate.batchUpdate(
"INSERT INTO mes_xsl_raw_material_demand_plan "
+ "(id, machine_id, machine_name, material_id, erp_code, raw_material_name, demand_weight, standard_weight, actual_weight, plan_date, create_time, update_time, del_flag) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
rawBatchArgs);
}
jdbcTemplate.batchUpdate(
"INSERT INTO mes_xsl_raw_material_demand_plan "
+ "(id, machine_id, machine_name, material_id, erp_code, raw_material_name, demand_weight, standard_weight, actual_weight, plan_date, create_time, update_time, del_flag) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
batchArgs);
List<Object[]> autoBatchArgs = buildSmallDemandBatchArgs(autoAggregateMap, now);
if (!autoBatchArgs.isEmpty()) {
jdbcTemplate.batchUpdate(
"INSERT INTO mes_xsl_auto_small_material_demand_plan "
+ "(id, stat_date, machine_id, machine_name, raw_material_name, demand_weight, create_time, update_time, del_flag) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
autoBatchArgs);
}
List<Object[]> manualBatchArgs = buildSmallDemandBatchArgs(manualAggregateMap, now);
if (!manualBatchArgs.isEmpty()) {
jdbcTemplate.batchUpdate(
"INSERT INTO mes_xsl_manual_small_material_demand_plan "
+ "(id, stat_date, machine_id, machine_name, raw_material_name, demand_weight, create_time, update_time, del_flag) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
manualBatchArgs);
}
}
private List<Object[]> buildSmallDemandBatchArgs(
Map<String, SmallDemandAggregate> aggregateMap, Date now) {
List<Object[]> batchArgs = new ArrayList<>();
for (SmallDemandAggregate aggregate : aggregateMap.values()) {
if (aggregate.demandWeight.compareTo(BigDecimal.ZERO) <= 0) {
continue;
}
batchArgs.add(
new Object[] {
IdWorker.getIdStr(),
aggregate.statDate,
aggregate.machineId,
aggregate.machineName,
aggregate.rawMaterialName,
aggregate.demandWeight,
now,
now,
CommonConstant.DEL_FLAG_0
});
}
return batchArgs;
}
private static class RawDemandAggregate {
@@ -305,6 +368,22 @@ public class MesXslMixingProductionPlanServiceImpl
}
}
private static class SmallDemandAggregate {
private final Date statDate;
private final String machineId;
private final String machineName;
private final String rawMaterialName;
private BigDecimal demandWeight = BigDecimal.ZERO;
private SmallDemandAggregate(
Date statDate, String machineId, String machineName, String rawMaterialName) {
this.statDate = statDate;
this.machineId = machineId;
this.machineName = machineName;
this.rawMaterialName = rawMaterialName;
}
}
private boolean hasBusinessData(MesXslMixingProductionPlan row) {
return StringUtils.isNotBlank(row.getMachineId())
|| StringUtils.isNotBlank(row.getPlanId())