This commit is contained in:
2026-06-16 15:35:57 +08:00
18 changed files with 1264 additions and 32 deletions

View File

@@ -1035,6 +1035,22 @@ jeecgboot-vue3/src/views/xslmes/mesXslEquipPartMapping/MesXslEquipPartMappingLis
jeecgboot-vue3/src/views/xslmes/mesXslEquipPartMapping/MesXslEquipPartMapping.data.ts
jeecgboot-vue3/src/views/xslmes/mesXslEquipPartMapping/MesXslEquipPartMapping.api.ts
-- author:GHT---date:20260616--for: 【MES上辅机】SQL Server中间库可视化配置第三方配置页+读写开关+热刷新数据源) ---
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/flyway/sql/mysql/V3.9.2_150__mes_xsl_mcs_db_config.sql
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/entity/MesXslMcsDbConfig.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/mapper/MesXslMcsDbConfigMapper.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/IMesXslMcsDbConfigService.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/service/impl/MesXslMcsDbConfigServiceImpl.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/datasource/McsDataSourceManager.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/datasource/McsDataSourceGuardAspect.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/config/McsDataSourceInitializer.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/mcs/controller/MesXslMcsDbConfigController.java
jeecgboot-vue3/src/views/system/appconfig/ThirdAppConfigList.vue
jeecgboot-vue3/src/views/system/appconfig/ThirdAppMcsDbConfigForm.vue
jeecgboot-vue3/src/views/system/appconfig/McsDbConfigModal.vue
jeecgboot-vue3/src/views/system/appconfig/McsDbConfig.data.ts
jeecgboot-vue3/src/views/system/appconfig/McsDbConfig.api.ts
-- author:jiangxh---date:20250603--for: 【MES】设备/质量管理主数据删除前引用校验(统一点检配置等下游阻断) ---
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/IMesXslDeleteReferenceService.java
jeecg-boot/jeecg-boot-module/jeecg-module-xslmes/src/main/java/org/jeecg/modules/xslmes/service/impl/MesXslDeleteReferenceServiceImpl.java

View File

@@ -133,24 +133,18 @@ public class ApprovalCallbackDispatcher {
DING_LOG_TAG, callbackName, ctx.getAction(), ctx.getBizTable(), ctx.getBizDataId());
}
try {
switch (ctx.getAction()) {
case NODE_APPROVED:
cb.onNodeApproved(ctx);
break;
case APPROVED:
cb.onApproved(ctx);
break;
case REJECTED:
cb.onRejected(ctx);
break;
//update-begin---author:GHT ---date:2026-06-08 for【风险修复-R5】分发撤销回调-----------
case CANCELLED:
cb.onCancelled(ctx);
break;
//update-end---author:GHT ---date:2026-06-08 for【风险修复-R5】分发撤销回调-----------
default:
break;
//update-begin---author:cursor ---date:20260615 for【XSLMES-20260615-A05】审批回调分发改用if-else避免调试热更新缺失switch合成类-----------
ApprovalCallbackContext.Action action = ctx.getAction();
if (action == ApprovalCallbackContext.Action.NODE_APPROVED) {
cb.onNodeApproved(ctx);
} else if (action == ApprovalCallbackContext.Action.APPROVED) {
cb.onApproved(ctx);
} else if (action == ApprovalCallbackContext.Action.REJECTED) {
cb.onRejected(ctx);
} else if (action == ApprovalCallbackContext.Action.CANCELLED) {
cb.onCancelled(ctx);
}
//update-end---author:cursor ---date:20260615 for【XSLMES-20260615-A05】审批回调分发改用if-else避免调试热更新缺失switch合成类-----------
if (dingTalk) {
log.info("{} 业务回调完成 {} action={} bizTable={} bizDataId={}",
DING_LOG_TAG, callbackName, ctx.getAction(), ctx.getBizTable(), ctx.getBizDataId());

View File

@@ -0,0 +1,48 @@
package org.jeecg.modules.xslmes.mcs.config;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
import org.jeecg.modules.xslmes.mcs.service.IMesXslMcsDbConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 启动时加载中间库配置,覆盖 application.yml 中的 sqlserver_mcs 连接
*/
@Slf4j
@Component
@Order(100)
public class McsDataSourceInitializer implements ApplicationRunner {
@Autowired
private IMesXslMcsDbConfigService mcsDbConfigService;
@Autowired
private McsDataSourceManager mcsDataSourceManager;
//update-begin---author:GHT ---date:20260616 for【MES上辅机】启动时加载中间库可视化配置-----------
@Override
public void run(ApplicationArguments args) {
try {
MesXslMcsDbConfig config = mcsDbConfigService.getOne(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesXslMcsDbConfig>()
.eq(MesXslMcsDbConfig::getTenantId, 0)
.orderByDesc(MesXslMcsDbConfig::getUpdateTime)
.last("LIMIT 1"),
false);
if (config != null) {
mcsDataSourceManager.applyConfig(config);
log.info("[MCS中间库] 已加载数据库中的中间库配置(含读写开关)");
} else {
log.info("[MCS中间库] 未找到已启用的数据库配置,继续使用 application.yml 中的 sqlserver_mcs");
}
} catch (Exception e) {
log.warn("[MCS中间库] 启动加载配置失败,将使用 yml 默认连接: {}", e.getMessage());
}
}
//update-end---author:GHT ---date:20260616 for【MES上辅机】启动时加载中间库可视化配置-----------
}

View File

@@ -0,0 +1,64 @@
package org.jeecg.modules.xslmes.mcs.config;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 注册 MCS 中间库 HTTP 拦截器(读取/写入开关在接口层生效)
*/
@Configuration
public class McsWebMvcConfig implements WebMvcConfigurer {
@Autowired
private McsDataSourceManager mcsDataSourceManager;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(mcsReadWriteInterceptor())
.addPathPatterns("/xslmes/mcs/**")
.excludePathPatterns("/xslmes/mcs/dbConfig/**");
}
//update-begin---author:GHT ---date:20260616 for【MES上辅机】HTTP层拦截中间库读写请求-----------
private HandlerInterceptor mcsReadWriteInterceptor() {
return new HandlerInterceptor() {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!mcsDataSourceManager.hasSavedConfig()) {
return true;
}
String uri = request.getRequestURI();
if (uri.contains("/mcs/dbConfig")) {
return true;
}
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
if (!mcsDataSourceManager.isReadEnabled()) {
throw new JeecgBootException("中间库读取已关闭,请在「中间库连接配置」中开启读取开关并保存");
}
if (isWriteRequest(request, uri) && !mcsDataSourceManager.isWriteEnabled()) {
throw new JeecgBootException("中间库写入已关闭,请在「中间库连接配置」中开启写入开关并保存");
}
return true;
}
private boolean isWriteRequest(HttpServletRequest request, String uri) {
String method = request.getMethod();
boolean mutating = "POST".equalsIgnoreCase(method)
|| "PUT".equalsIgnoreCase(method)
|| "DELETE".equalsIgnoreCase(method)
|| "PATCH".equalsIgnoreCase(method);
return mutating && uri.toLowerCase().contains("mestomcs");
}
};
}
//update-end---author:GHT ---date:20260616 for【MES上辅机】HTTP层拦截中间库读写请求-----------
}

View File

@@ -0,0 +1,85 @@
package org.jeecg.modules.xslmes.mcs.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
import org.jeecg.modules.xslmes.mcs.service.IMesXslMcsDbConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* MES上辅机 SQL Server 中间库连接配置
*/
@Tag(name = "MES上辅机中间库配置")
@RestController
@RequestMapping("/xslmes/mcs/dbConfig")
public class MesXslMcsDbConfigController {
@Autowired
private IMesXslMcsDbConfigService mcsDbConfigService;
@Autowired
private McsDataSourceManager mcsDataSourceManager;
private Integer resolveTenantId(Integer tenantId) {
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
if (tenantId == null) {
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), -1);
}
} else if (tenantId == null) {
tenantId = oConvertUtils.getInt(TenantContext.getTenant(), 0);
}
return tenantId;
}
@Operation(summary = "获取中间库配置")
@GetMapping("/get")
public Result<MesXslMcsDbConfig> getConfig(@RequestParam(name = "tenantId", required = false) Integer tenantId) {
return Result.OK(mcsDbConfigService.getCurrentConfig(resolveTenantId(tenantId)));
}
@Operation(summary = "保存中间库配置")
@PostMapping("/save")
public Result<String> saveConfig(@RequestBody MesXslMcsDbConfig config) {
if (config.getTenantId() == null) {
config.setTenantId(resolveTenantId(null));
}
return mcsDbConfigService.saveOrUpdateConfig(config);
}
@Operation(summary = "测试中间库连接")
@GetMapping("/testConnect")
public Result<String> testConnect(@RequestParam(name = "tenantId", required = false) Integer tenantId) {
return mcsDbConfigService.testConnection(resolveTenantId(tenantId));
}
@Operation(summary = "删除中间库配置")
@DeleteMapping("/delete")
public Result<String> deleteConfig(@RequestParam(name = "id") String id) {
return mcsDbConfigService.deleteConfig(id);
}
@Operation(summary = "获取中间库连接状态")
@GetMapping("/status")
public Result<Map<String, Object>> status() {
Map<String, Object> data = new LinkedHashMap<>();
MesXslMcsDbConfig cfg = mcsDataSourceManager.getCachedConfig();
data.put("dbConfigActive", mcsDataSourceManager.isDbConfigActive());
data.put("readEnabled", mcsDataSourceManager.isReadEnabled());
data.put("writeEnabled", mcsDataSourceManager.isWriteEnabled());
if (cfg != null) {
data.put("serverHost", cfg.getServerHost());
data.put("serverPort", cfg.getServerPort());
data.put("dbName", cfg.getDbName());
}
return Result.OK(data);
}
}

View File

@@ -0,0 +1,48 @@
package org.jeecg.modules.xslmes.mcs.datasource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* MCS 中间库读写开关守卫
* <ul>
* <li>读取开关拦截所有中间表查询McsToMes + MesToMcs 列表/详情等)</li>
* <li>写入开关:仅拦截 MES→MCS 方向的增删改</li>
* </ul>
*/
@Slf4j
@Aspect
@Component
@Order(1)
public class McsDataSourceGuardAspect {
@Autowired
private McsDataSourceManager mcsDataSourceManager;
//update-begin---author:GHT ---date:20260616 for【MES上辅机】读取开关拦截全部中间表查询-----------
@Before("(execution(* com.baomidou.mybatisplus.extension.service.IService+.*(..)) "
+ "&& (target(org.jeecg.modules.xslmes.mcs.service.impl.McsToMes*) "
+ "|| target(org.jeecg.modules.xslmes.mcs.service.impl.MesToMcs*)))")
public void guardRead(JoinPoint joinPoint) {
if (!mcsDataSourceManager.isReadEnabled()) {
throw new JeecgBootException("中间库读取已关闭,请在「中间库连接配置」中开启读取开关并保存");
}
}
@Before("execution(* org.jeecg.modules.xslmes.mcs.service.impl.MesToMcs*.save*(..)) || "
+ "execution(* org.jeecg.modules.xslmes.mcs.service.impl.MesToMcs*.update*(..)) || "
+ "execution(* org.jeecg.modules.xslmes.mcs.service.impl.MesToMcs*.remove*(..)) || "
+ "execution(* org.jeecg.modules.xslmes.mcs.service.impl.MesToMcs*.delete*(..))")
public void guardWrite(JoinPoint joinPoint) {
if (!mcsDataSourceManager.isWriteEnabled()) {
throw new JeecgBootException("中间库写入已关闭,请在「中间库连接配置」中开启写入开关并保存");
}
}
//update-end---author:GHT ---date:20260616 for【MES上辅机】读取开关拦截全部中间表查询-----------
}

View File

@@ -0,0 +1,229 @@
package org.jeecg.modules.xslmes.mcs.datasource;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DataSourceProperty;
import com.baomidou.dynamic.datasource.creator.druid.DruidDataSourceCreator;
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.entity.MesXslMcsDbConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.concurrent.atomic.AtomicReference;
/**
* MCS SQL Server 动态数据源管理器
* <p>配置保存后热刷新 sqlserver_mcs 数据源,无需改 application.yml</p>
*/
@Slf4j
@Component
public class McsDataSourceManager {
public static final String DS_KEY = "sqlserver_mcs";
private final AtomicReference<MesXslMcsDbConfig> cachedConfig = new AtomicReference<>();
@Autowired
private DataSource dataSource;
@Autowired
private DruidDataSourceCreator dataSourceCreator;
@Value("${spring.datasource.dynamic.datasource.sqlserver_mcs.url:}")
private String ymlUrl;
@Value("${spring.datasource.dynamic.datasource.sqlserver_mcs.username:}")
private String ymlUsername;
@Value("${spring.datasource.dynamic.datasource.sqlserver_mcs.password:}")
private String ymlPassword;
@Value("${spring.datasource.dynamic.datasource.sqlserver_mcs.driver-class-name:com.microsoft.sqlserver.jdbc.SQLServerDriver}")
private String ymlDriverClassName;
public MesXslMcsDbConfig getCachedConfig() {
return cachedConfig.get();
}
public boolean hasSavedConfig() {
return cachedConfig.get() != null;
}
public boolean isDbConfigActive() {
MesXslMcsDbConfig cfg = cachedConfig.get();
return cfg != null && Integer.valueOf(1).equals(cfg.getStatus());
}
public boolean isReadEnabled() {
MesXslMcsDbConfig cfg = cachedConfig.get();
if (cfg == null) {
return true;
}
return Integer.valueOf(1).equals(cfg.getReadEnabled());
}
public boolean isWriteEnabled() {
MesXslMcsDbConfig cfg = cachedConfig.get();
if (cfg == null) {
return true;
}
return Integer.valueOf(1).equals(cfg.getWriteEnabled());
}
//update-begin---author:GHT ---date:20260616 for【MES上辅机】SQL Server中间库可视化配置-----------
/**
* 将配置应用到动态数据源(启用时注册/更新,禁用时移除)
*/
public void applyConfig(MesXslMcsDbConfig config) {
cachedConfig.set(config);
DynamicRoutingDataSource routing = (DynamicRoutingDataSource) dataSource;
closeAndRemoveDataSource(routing);
DataSourceCachePool.removeCache(DS_KEY);
if (config == null) {
restoreYmlDataSource(routing);
log.info("[MCS中间库] 已清除页面配置");
return;
}
if (Integer.valueOf(1).equals(config.getStatus())) {
try {
String plainPassword = decryptPassword(config.getDbPassword());
DataSourceProperty property = buildDataSourceProperty(config, plainPassword);
DataSource mcsDs = dataSourceCreator.createDataSource(property);
routing.addDataSource(DS_KEY, mcsDs);
log.info("[MCS中间库] 动态数据源 {} 已热刷新(无需重启): {}:{}/{}", DS_KEY,
config.getServerHost(), config.getServerPort(), config.getDbName());
} catch (Exception e) {
cachedConfig.set(config);
log.error("[MCS中间库] 刷新动态数据源失败", e);
throw new RuntimeException("刷新中间库数据源失败:" + e.getMessage(), e);
}
return;
}
restoreYmlDataSource(routing);
log.info("[MCS中间库] 连接未启用,读写开关仍按页面配置生效");
}
private void closeAndRemoveDataSource(DynamicRoutingDataSource routing) {
if (!routing.getDataSources().containsKey(DS_KEY)) {
return;
}
DataSource old = routing.getDataSource(DS_KEY);
routing.removeDataSource(DS_KEY);
closeDataSourceQuietly(old);
}
private void closeDataSourceQuietly(DataSource ds) {
if (ds instanceof DruidDataSource druid && druid.isEnable()) {
try {
druid.close();
} catch (Exception e) {
log.warn("[MCS中间库] 关闭旧连接池失败: {}", e.getMessage());
}
}
}
/**
* 测试 JDBC 连通性(不写入动态数据源)
*/
public String testConnection(MesXslMcsDbConfig config, String plainPassword) {
if (config == null) {
return "配置为空";
}
String jdbcUrl = buildJdbcUrl(config);
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
try (Connection conn = DriverManager.getConnection(jdbcUrl, config.getDbUsername(), plainPassword)) {
if (conn.isValid(5)) {
return null;
}
return "连接无效";
}
} catch (Exception e) {
log.warn("[MCS中间库] 连接测试失败: {}", e.getMessage());
return e.getMessage();
}
}
public String buildJdbcUrl(MesXslMcsDbConfig config) {
int loginTimeout = config.getLoginTimeout() != null ? config.getLoginTimeout() : 120;
int connectTimeout = config.getConnectTimeout() != null ? config.getConnectTimeout() : 120000;
String host = resolveHost(config);
int port = config.getServerPort() != null ? config.getServerPort() : 1433;
String dbName = StringUtils.isNotBlank(config.getDbName()) ? config.getDbName() : "MES_ShareDB";
return "jdbc:sqlserver://" + host + ":" + port
+ ";DatabaseName=" + dbName
+ ";encrypt=false;trustServerCertificate=true;SelectMethod=cursor"
+ ";loginTimeout=" + loginTimeout
+ ";connectTimeout=" + connectTimeout + ";";
}
private DataSourceProperty buildDataSourceProperty(MesXslMcsDbConfig config, String plainPassword) {
DataSourceProperty property = new DataSourceProperty();
property.setPoolName(DS_KEY);
property.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
property.setUrl(buildJdbcUrl(config));
property.setUsername(config.getDbUsername());
property.setPassword(plainPassword);
return property;
}
private void restoreYmlDataSource(DynamicRoutingDataSource routing) {
if (StringUtils.isBlank(ymlUrl)) {
log.warn("[MCS中间库] yml 中未配置 sqlserver_mcs无法恢复默认数据源");
return;
}
try {
DataSourceProperty property = new DataSourceProperty();
property.setPoolName(DS_KEY);
property.setDriverClassName(ymlDriverClassName);
property.setUrl(ymlUrl);
property.setUsername(ymlUsername);
property.setPassword(ymlPassword);
routing.addDataSource(DS_KEY, dataSourceCreator.createDataSource(property));
} catch (Exception e) {
log.error("[MCS中间库] 恢复 yml 默认数据源失败", e);
}
}
private String resolveHost(MesXslMcsDbConfig config) {
String host = config.getServerHost() == null ? "" : config.getServerHost().trim();
if (host.contains(":")) {
String[] parts = host.split(":", 2);
host = parts[0];
if (config.getServerPort() == null || config.getServerPort() == 1433) {
try {
config.setServerPort(Integer.parseInt(parts[1]));
} catch (NumberFormatException ignored) {
// 端口解析失败时保留原端口
}
}
}
return host;
}
public String decryptPassword(String encrypted) {
if (StringUtils.isBlank(encrypted)) {
return "";
}
try {
return SecurityUtil.jiemi(encrypted);
} catch (Exception e) {
return encrypted;
}
}
public String encryptPassword(String plain) {
if (StringUtils.isBlank(plain)) {
return plain;
}
return SecurityUtil.jiami(plain);
}
//update-end---author:GHT ---date:20260616 for【MES上辅机】SQL Server中间库可视化配置-----------
}

View File

@@ -0,0 +1,80 @@
package org.jeecg.modules.xslmes.mcs.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 com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* MES上辅机 SQL Server 中间库配置
*/
@Data
@TableName("mes_xsl_mcs_db_config")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description = "MES上辅机SQL Server中间库配置")
public class MesXslMcsDbConfig implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private String id;
@Schema(description = "租户ID")
private Integer tenantId;
@Schema(description = "服务器地址")
private String serverHost;
@Schema(description = "端口")
private Integer serverPort;
@Schema(description = "数据库名")
private String dbName;
@Schema(description = "用户名")
private String dbUsername;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@Schema(description = "密码")
private String dbPassword;
@Schema(description = "读取开关(0-关,1-开)")
private Integer readEnabled;
@Schema(description = "写入开关(0-关,1-开)")
private Integer writeEnabled;
@Schema(description = "启用状态(0-关,1-开)")
private Integer status;
@Schema(description = "登录超时(秒)")
private Integer loginTimeout;
@Schema(description = "连接超时(毫秒)")
private Integer connectTimeout;
@Schema(description = "备注")
private String remark;
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;
}

View File

@@ -0,0 +1,10 @@
package org.jeecg.modules.xslmes.mcs.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
/**
* MES上辅机 SQL Server 中间库配置 Mapper
*/
public interface MesXslMcsDbConfigMapper extends BaseMapper<MesXslMcsDbConfig> {
}

View File

@@ -0,0 +1,31 @@
package org.jeecg.modules.xslmes.mcs.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
/**
* MES上辅机 SQL Server 中间库配置 Service
*/
public interface IMesXslMcsDbConfigService extends IService<MesXslMcsDbConfig> {
/**
* 获取当前租户配置(无则返回 null
*/
MesXslMcsDbConfig getCurrentConfig(Integer tenantId);
/**
* 保存或更新配置并刷新动态数据源
*/
Result<String> saveOrUpdateConfig(MesXslMcsDbConfig config);
/**
* 测试数据库连接
*/
Result<String> testConnection(Integer tenantId);
/**
* 删除配置并移除动态数据源
*/
Result<String> deleteConfig(String id);
}

View File

@@ -0,0 +1,186 @@
package org.jeecg.modules.xslmes.mcs.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.xslmes.mcs.datasource.McsDataSourceManager;
import org.jeecg.modules.xslmes.mcs.entity.MesXslMcsDbConfig;
import org.jeecg.modules.xslmes.mcs.mapper.MesXslMcsDbConfigMapper;
import org.jeecg.modules.xslmes.mcs.service.IMesXslMcsDbConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.Date;
/**
* MES上辅机 SQL Server 中间库配置 Service 实现
*/
@Slf4j
@Service
public class MesXslMcsDbConfigServiceImpl extends ServiceImpl<MesXslMcsDbConfigMapper, MesXslMcsDbConfig>
implements IMesXslMcsDbConfigService {
@Autowired
private McsDataSourceManager mcsDataSourceManager;
@Override
public MesXslMcsDbConfig getCurrentConfig(Integer tenantId) {
int tid = tenantId != null ? tenantId : 0;
LambdaQueryWrapper<MesXslMcsDbConfig> qw = new LambdaQueryWrapper<>();
qw.eq(MesXslMcsDbConfig::getTenantId, tid);
qw.orderByDesc(MesXslMcsDbConfig::getUpdateTime);
qw.last("LIMIT 1");
MesXslMcsDbConfig config = getOne(qw, false);
if (config != null) {
config.setDbPassword(null);
}
return config;
}
//update-begin---author:GHT ---date:20260616 for【MES上辅机】SQL Server中间库可视化配置-----------
@Override
@Transactional(rollbackFor = Exception.class)
public Result<String> saveOrUpdateConfig(MesXslMcsDbConfig config) {
if (config == null) {
return Result.error("配置不能为空");
}
if (StringUtils.isBlank(config.getServerHost())) {
return Result.error("服务器地址不能为空");
}
if (StringUtils.isBlank(config.getDbUsername())) {
return Result.error("用户名不能为空");
}
if (config.getTenantId() == null) {
config.setTenantId(0);
}
if (config.getServerPort() == null) {
config.setServerPort(1433);
}
if (StringUtils.isBlank(config.getDbName())) {
config.setDbName("MES_ShareDB");
}
if (config.getReadEnabled() == null) {
config.setReadEnabled(1);
}
if (config.getWriteEnabled() == null) {
config.setWriteEnabled(1);
}
if (config.getStatus() == null) {
config.setStatus(0);
}
if (config.getLoginTimeout() == null) {
config.setLoginTimeout(120);
}
if (config.getConnectTimeout() == null) {
config.setConnectTimeout(120000);
}
String plainPassword = resolvePlainPassword(config);
if (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);
}
LoginUser user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
String username = user != null ? user.getUsername() : "system";
Date now = new Date();
if (StringUtils.isNotBlank(config.getId())) {
MesXslMcsDbConfig old = getById(config.getId());
if (old == null) {
return Result.error("配置不存在");
}
config.setDbPassword(mcsDataSourceManager.encryptPassword(plainPassword));
config.setUpdateBy(username);
config.setUpdateTime(now);
updateById(config);
} else {
config.setDbPassword(mcsDataSourceManager.encryptPassword(plainPassword));
config.setCreateBy(username);
config.setCreateTime(now);
config.setUpdateBy(username);
config.setUpdateTime(now);
save(config);
}
MesXslMcsDbConfig applied = getById(config.getId());
scheduleApplyAfterCommit(applied);
return Result.OK("保存成功,中间库连接已热刷新,无需重启后端");
}
/**
* 事务提交后再刷新数据源,避免库未落盘就切换连接
*/
private void scheduleApplyAfterCommit(MesXslMcsDbConfig applied) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
mcsDataSourceManager.applyConfig(applied);
}
});
} else {
mcsDataSourceManager.applyConfig(applied);
}
}
@Override
public Result<String> testConnection(Integer tenantId) {
MesXslMcsDbConfig config = getOneByTenant(tenantId);
if (config == null) {
return Result.error("尚未配置中间库连接");
}
String plainPassword = mcsDataSourceManager.decryptPassword(config.getDbPassword());
String err = mcsDataSourceManager.testConnection(config, plainPassword);
if (err == null) {
return Result.OK("连接成功");
}
return Result.error("连接失败:" + err);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Result<String> deleteConfig(String id) {
MesXslMcsDbConfig config = getById(id);
if (config == null) {
return Result.error("配置不存在");
}
removeById(id);
mcsDataSourceManager.applyConfig(null);
return Result.OK("已删除配置,中间库连接已断开(无需重启后端)");
}
private MesXslMcsDbConfig getOneByTenant(Integer tenantId) {
int tid = tenantId != null ? tenantId : 0;
LambdaQueryWrapper<MesXslMcsDbConfig> qw = new LambdaQueryWrapper<>();
qw.eq(MesXslMcsDbConfig::getTenantId, tid);
qw.orderByDesc(MesXslMcsDbConfig::getUpdateTime);
qw.last("LIMIT 1");
return getOne(qw, false);
}
private String resolvePlainPassword(MesXslMcsDbConfig config) {
if (StringUtils.isNotBlank(config.getDbPassword())) {
return config.getDbPassword();
}
if (StringUtils.isNotBlank(config.getId())) {
MesXslMcsDbConfig old = getById(config.getId());
if (old != null) {
return mcsDataSourceManager.decryptPassword(old.getDbPassword());
}
}
return null;
}
//update-end---author:GHT ---date:20260616 for【MES上辅机】SQL Server中间库可视化配置-----------
}