Merge branch 'main' of http://27.223.88.102:33000/chenx/qhmes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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上辅机】启动时加载中间库可视化配置-----------
|
||||
}
|
||||
@@ -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层拦截中间库读写请求-----------
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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上辅机】读取开关拦截全部中间表查询-----------
|
||||
}
|
||||
@@ -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中间库可视化配置-----------
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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> {
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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中间库可视化配置-----------
|
||||
}
|
||||
Reference in New Issue
Block a user