完善 MCS 桌面代理与开炼机动作同步,修复原料相关菜单权限,桌面端新增上辅机 MES 菜单。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-27 15:56:05 +08:00
parent 7a9c19e4f3
commit 442a4c8ae2
113 changed files with 10169 additions and 91 deletions

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

@@ -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

@@ -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

@@ -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())