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中间库可视化配置-----------
}

View File

@@ -167,21 +167,22 @@ spring:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 中间库连接已改为「第三方配置 → 上辅机中间库」页面维护,此处保留示例供参考
#update-begin---author:geh ---date:2026-06-02 for【MES上辅机】新增 SQL Server 中间表数据源MES_ShareDB-----------
sqlserver_mcs:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
# loginTimeout/connectTimeout花生壳映射不稳定时延长 TCP/登录等待(单位:秒 / 毫秒)
url: jdbc:sqlserver://1lo04860wn636.vicp.fun:31601;DatabaseName=MES_ShareDB;encrypt=false;trustServerCertificate=true;SelectMethod=cursor;loginTimeout=120;connectTimeout=120000;
username: sa
password: 123456
druid:
initial-size: 0
min-idle: 0
max-wait: 120000
connect-timeout: 120000
connection-error-retry-attempts: 10
time-between-connect-error-millis: 3000
break-after-acquire-failure: false
# sqlserver_mcs:
# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
# # loginTimeout/connectTimeout花生壳映射不稳定时延长 TCP/登录等待(单位:秒 / 毫秒)
# url: jdbc:sqlserver://1lo04860wn636.vicp.fun:31601;DatabaseName=MES_ShareDB;encrypt=false;trustServerCertificate=true;SelectMethod=cursor;loginTimeout=120;connectTimeout=120000;
# username: sa
# password: 123456
# druid:
# initial-size: 0
# min-idle: 0
# max-wait: 120000
# connect-timeout: 120000
# connection-error-retry-attempts: 10
# time-between-connect-error-millis: 3000
# break-after-acquire-failure: false
#update-end---author:geh ---date:2026-06-02 for【MES上辅机】新增 SQL Server 中间表数据源MES_ShareDB-----------
# # shardingjdbc数据源
# sharding-db:

View File

@@ -0,0 +1,36 @@
-- MES上辅机SQL Server 中间库可视化配置替代 application.yml 手工改连接
CREATE TABLE IF NOT EXISTS `mes_xsl_mcs_db_config` (
`id` varchar(36) NOT NULL COMMENT '主键',
`tenant_id` int DEFAULT 0 COMMENT '租户ID',
`server_host` varchar(200) NOT NULL COMMENT '服务器地址IP或域名可含端口',
`server_port` int DEFAULT 1433 COMMENT '端口',
`db_name` varchar(100) NOT NULL DEFAULT 'MES_ShareDB' COMMENT '数据库名',
`db_username` varchar(100) NOT NULL COMMENT '用户名',
`db_password` varchar(500) DEFAULT NULL COMMENT '密码AES加密',
`read_enabled` tinyint(1) DEFAULT 1 COMMENT '读取开关(0-,1-) MCSMES方向',
`write_enabled` tinyint(1) DEFAULT 1 COMMENT '写入开关(0-,1-) MESMCS方向',
`status` tinyint(1) DEFAULT 0 COMMENT '启用状态(0-,1-)',
`login_timeout` int DEFAULT 120 COMMENT '登录超时()',
`connect_timeout` int DEFAULT 120000 COMMENT '连接超时(毫秒)',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_mcs_db_tenant` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES上辅机SQL Server中间库配置';
-- 菜单MES上辅机数据 -> 中间库连接配置
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`)
SELECT '1900000000000000848', '1900000000000000830', '中间库连接配置', '/third/app?tab=mcs', 'system/appconfig/ThirdAppConfigList', 1, NULL, NULL, 1, NULL, '0', 0.50, 0, 'ant-design:database-outlined', 1, 1, 0, 0, 'SQL Server中间库连接与读写开关', 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000848');
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
SELECT REPLACE(UUID(), '-', ''), r.id, '1900000000000000848', NULL, NOW(), '127.0.0.1'
FROM `sys_role` r
WHERE r.`role_code` = 'admin'
AND NOT EXISTS (
SELECT 1 FROM `sys_role_permission` rp
WHERE rp.`role_id` = r.id AND rp.`permission_id` = '1900000000000000848'
);

View File

@@ -0,0 +1,19 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
getConfig = '/xslmes/mcs/dbConfig/get',
saveConfig = '/xslmes/mcs/dbConfig/save',
testConnect = '/xslmes/mcs/dbConfig/testConnect',
deleteConfig = '/xslmes/mcs/dbConfig/delete',
status = '/xslmes/mcs/dbConfig/status',
}
export const getMcsDbConfig = (params?) => defHttp.get({ url: Api.getConfig, params });
export const saveMcsDbConfig = (params) => defHttp.post({ url: Api.saveConfig, params });
export const testMcsDbConnect = (params?) => defHttp.get({ url: Api.testConnect, params }, { isTransformResponse: false });
export const deleteMcsDbConfig = (params) => defHttp.delete({ url: Api.deleteConfig, params }, { joinParamsToUrl: true });
export const getMcsDbStatus = () => defHttp.get({ url: Api.status });

View File

@@ -0,0 +1,100 @@
import { FormSchema } from '/@/components/Form';
export const mcsDbConfigFormSchema: FormSchema[] = [
{ label: 'id', field: 'id', component: 'Input', show: false },
{ label: 'tenantId', field: 'tenantId', component: 'InputNumber', show: false },
{
label: '服务器地址',
field: 'serverHost',
component: 'Input',
required: true,
componentProps: { placeholder: 'IP 或域名,例如 192.168.1.10 或 xxx.vicp.fun' },
},
{
label: '端口',
field: 'serverPort',
component: 'InputNumber',
defaultValue: 1433,
required: true,
componentProps: { min: 1, max: 65535, style: { width: '100%' } },
},
{
label: '数据库名',
field: 'dbName',
component: 'Input',
defaultValue: 'MES_ShareDB',
required: true,
},
{
label: '用户名',
field: 'dbUsername',
component: 'Input',
required: true,
componentProps: { placeholder: '例如 sa' },
},
{
label: '密码',
field: 'dbPassword',
component: 'InputPassword',
componentProps: { placeholder: '编辑时留空表示不修改密码' },
},
{
label: '读取开关',
field: 'readEnabled',
component: 'Switch',
helpMessage: '关闭后所有中间表列表/详情查询将被拦截(含 MCS→MES 与 MES→MCS 方向)',
componentProps: {
checkedChildren: '开启',
checkedValue: 1,
unCheckedChildren: '关闭',
unCheckedValue: 0,
},
defaultValue: 1,
},
{
label: '写入开关',
field: 'writeEnabled',
component: 'Switch',
helpMessage: '关闭后 MES→MCS 方向的增删改将被拦截(不影响列表查询,查询由读取开关控制)',
componentProps: {
checkedChildren: '开启',
checkedValue: 1,
unCheckedChildren: '关闭',
unCheckedValue: 0,
},
defaultValue: 1,
},
{
label: '启用连接',
field: 'status',
component: 'Switch',
helpMessage: '开启后立即连接中间库并热刷新数据源,无需重启后端',
componentProps: {
checkedChildren: '启用',
checkedValue: 1,
unCheckedChildren: '停用',
unCheckedValue: 0,
},
defaultValue: 0,
},
{
label: '登录超时(秒)',
field: 'loginTimeout',
component: 'InputNumber',
defaultValue: 120,
componentProps: { min: 10, max: 600, style: { width: '100%' } },
},
{
label: '连接超时(毫秒)',
field: 'connectTimeout',
component: 'InputNumber',
defaultValue: 120000,
componentProps: { min: 5000, max: 600000, style: { width: '100%' } },
},
{
label: '备注',
field: 'remark',
component: 'InputTextArea',
componentProps: { rows: 2 },
},
];

View File

@@ -0,0 +1,62 @@
<template>
<BasicModal @register="registerModal" :width="720" title="上辅机中间库配置" @ok="handleSubmit">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { useForm, BasicForm } from '/@/components/Form';
import { mcsDbConfigFormSchema } from './McsDbConfig.data';
import { getMcsDbConfig, saveMcsDbConfig } from './McsDbConfig.api';
import { getTenantId } from '/@/utils/auth';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
name: 'McsDbConfigModal',
components: { BasicModal, BasicForm },
emits: ['success'],
setup(_props, { emit }) {
const { createMessage } = useMessage();
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
schemas: mcsDbConfigFormSchema,
showActionButtonGroup: false,
labelCol: { span: 24 },
wrapperCol: { span: 24 },
});
const [registerModal, { setModalProps, closeModal }] = useModalInner(async () => {
setModalProps({ confirmLoading: true });
await resetFields();
const tenantId = getTenantId();
const values = await getMcsDbConfig({ tenantId });
setModalProps({ confirmLoading: false });
if (values) {
await setFieldsValue({ ...values, dbPassword: '' });
} else {
await setFieldsValue({ tenantId, readEnabled: 1, writeEnabled: 1, status: 0, serverPort: 1433, dbName: 'MES_ShareDB' });
}
});
async function handleSubmit() {
const values = await validate();
if (!values.id && !values.dbPassword) {
createMessage.warning('请输入数据库密码');
return;
}
setModalProps({ confirmLoading: true });
try {
await saveMcsDbConfig(values);
createMessage.success('保存成功,已热刷新连接,无需重启后端');
emit('success');
closeModal();
} finally {
setModalProps({ confirmLoading: false });
}
}
return { registerForm, registerModal, handleSubmit };
},
});
</script>

View File

@@ -5,6 +5,7 @@
<li :class="activeKey === 'ding' ? 'active' : ''" @click="dingLiClick('ding')"><a>钉钉集成</a></li>
<li :class="activeKey === 'wechat' ? 'active' : ''" @click="dingLiClick('wechat')"><a>企业微信集成</a></li>
<li :class="activeKey === 'kingdee' ? 'active' : ''" @click="dingLiClick('kingdee')"><a>金蝶集成</a></li>
<li :class="activeKey === 'mcs' ? 'active' : ''" @click="dingLiClick('mcs')"><a>上辅机中间库</a></li>
</ul>
</div>
<div v-show="activeKey === 'ding'" class="base-collapse">
@@ -16,14 +17,19 @@
<div v-show="activeKey === 'kingdee'" class="base-collapse">
<ThirdAppKingdeeConfigForm />
</div>
<div v-show="activeKey === 'mcs'" class="base-collapse">
<ThirdAppMcsDbConfigForm />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { defineComponent, onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import ThirdAppDingTalkConfigForm from './ThirdAppDingTalkConfigForm.vue';
import ThirdAppWeEnterpriseConfigForm from './ThirdAppWeEnterpriseConfigForm.vue';
import ThirdAppKingdeeConfigForm from './ThirdAppKingdeeConfigForm.vue';
import ThirdAppMcsDbConfigForm from './ThirdAppMcsDbConfigForm.vue';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
@@ -32,13 +38,22 @@
ThirdAppDingTalkConfigForm,
ThirdAppWeEnterpriseConfigForm,
ThirdAppKingdeeConfigForm,
ThirdAppMcsDbConfigForm,
},
setup() {
const { prefixCls } = useDesign('j-dd-container');
const route = useRoute();
//选中的key
const activeKey = ref<string>('ding');
onMounted(() => {
const tab = route.query.tab as string;
if (tab === 'mcs') {
activeKey.value = 'mcs';
}
});
/**
* tab点击事件
* @param key

View File

@@ -0,0 +1,208 @@
<template>
<div class="base-collapse">
<div class="header"> 上辅机中间库 </div>
<a-collapse expand-icon-position="right" :bordered="false">
<a-collapse-panel key="1">
<template #header>
<div style="font-size: 16px"> 1.配置说明</div>
</template>
<div class="base-desc">
在此配置 SQL Server 中间库MES_ShareDB连接信息<strong>保存后立即生效无需重启后端</strong>也无需修改 application.yml
读取开关控制 MCSMES 方向数据查询写入开关控制 MESMCS 方向数据写入
</div>
</a-collapse-panel>
</a-collapse>
<div class="sync-padding">
<a-collapse expand-icon-position="right" :bordered="false">
<a-collapse-panel key="2">
<template #header>
<div style="width: 100%; justify-content: space-between; display: flex">
<div style="font-size: 16px"> 2.连接信息及开关</div>
</div>
</template>
<a-alert v-if="connStatus" type="info" show-icon style="margin-bottom: 12px" message="当前运行状态" :description="connStatus" />
<div class="flex-flow">
<div class="base-title">服务器</div>
<div class="base-message">
<a-input :value="displayHost" readonly />
</div>
</div>
<div class="flex-flow">
<div class="base-title">数据库</div>
<div class="base-message">
<a-input :value="configData.dbName || '-'" readonly />
</div>
</div>
<div class="flex-flow">
<div class="base-title">用户名</div>
<div class="base-message">
<a-input :value="configData.dbUsername || '-'" readonly />
</div>
</div>
<div class="flex-flow">
<div class="base-title">读取开关</div>
<div class="base-message" style="display: flex; align-items: center; height: 50px">
<a-tag :color="runtimeStatus.readEnabled ? 'green' : 'default'">{{ runtimeStatus.readEnabled ? '已开启' : '已关闭' }}</a-tag>
</div>
</div>
<div class="flex-flow">
<div class="base-title">写入开关</div>
<div class="base-message" style="display: flex; align-items: center; height: 50px">
<a-tag :color="runtimeStatus.writeEnabled ? 'green' : 'default'">{{ runtimeStatus.writeEnabled ? '已开启' : '已关闭' }}</a-tag>
</div>
</div>
<div class="flex-flow">
<div class="base-title">连接状态</div>
<div class="base-message" style="display: flex; align-items: center; height: 50px">
<a-tag :color="runtimeStatus.dbConfigActive ? 'green' : 'orange'">{{ runtimeStatus.dbConfigActive ? '已启用数据库配置' : '使用 yml 默认配置' }}</a-tag>
</div>
</div>
<div style="margin-top: 20px; width: 100%; text-align: right">
<a-button type="primary" @click="editClick">编辑配置</a-button>
<a-button @click="testClick" style="margin-left: 10px">连接测试</a-button>
<a-button v-if="configData.id" @click="deleteClick" danger style="margin-left: 10px">删除配置</a-button>
</div>
</a-collapse-panel>
</a-collapse>
</div>
</div>
<McsDbConfigModal @register="registerModal" @success="reload" />
</template>
<script lang="ts">
import { computed, defineComponent, onMounted, ref } from 'vue';
import { Modal } from 'ant-design-vue';
import { useModal } from '/@/components/Modal';
import { useMessage } from '/@/hooks/web/useMessage';
import { getTenantId } from '/@/utils/auth';
import McsDbConfigModal from './McsDbConfigModal.vue';
import { deleteMcsDbConfig, getMcsDbConfig, getMcsDbStatus, testMcsDbConnect } from './McsDbConfig.api';
export default defineComponent({
name: 'ThirdAppMcsDbConfigForm',
components: { McsDbConfigModal },
setup() {
const { createMessage } = useMessage();
const configData = ref<any>({});
const runtimeStatus = ref<any>({ dbConfigActive: false, readEnabled: true, writeEnabled: true });
const [registerModal, { openModal }] = useModal();
const displayHost = computed(() => {
if (!configData.value.serverHost) return '-';
const port = configData.value.serverPort || 1433;
return `${configData.value.serverHost}:${port}`;
});
const connStatus = computed(() => {
const s = runtimeStatus.value;
if (!configData.value.id) {
return '尚未保存数据库配置,当前使用 application.yml 中的 sqlserver_mcs 默认连接。';
}
return `读取:${s.readEnabled ? '开' : '关'} | 写入:${s.writeEnabled ? '开' : '关'} | 连接:${s.dbConfigActive ? '已启用数据库配置' : '使用 yml 默认配置'}`;
});
async function reload() {
const tenantId = getTenantId();
configData.value = (await getMcsDbConfig({ tenantId })) || {};
runtimeStatus.value = (await getMcsDbStatus()) || {};
}
function editClick() {
openModal(true, {});
}
async function testClick() {
const tenantId = getTenantId();
const res = await testMcsDbConnect({ tenantId });
if (res.success) {
createMessage.success(res.message || '连接成功');
} else {
createMessage.warning(res.message || '连接失败');
}
}
function deleteClick() {
Modal.confirm({
title: '删除配置',
content: '删除后将断开数据库中的中间库配置,系统回退使用 application.yml 默认连接。确认删除?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
await deleteMcsDbConfig({ id: configData.value.id });
createMessage.success('已删除');
await reload();
},
});
}
onMounted(reload);
return {
configData,
runtimeStatus,
displayHost,
connStatus,
registerModal,
editClick,
testClick,
deleteClick,
reload,
};
},
});
</script>
<style lang="less" scoped>
.header {
align-items: center;
box-sizing: border-box;
display: flex;
height: 50px;
justify-content: space-between;
font-weight: 700;
font-size: 18px;
color: @text-color;
}
.flex-flow {
display: flex;
min-height: 0;
}
.sync-padding {
padding: 12px 0 16px;
color: @text-color;
}
.base-collapse {
margin-top: 20px;
padding: 0 24px;
font-size: 20px;
.base-desc {
font-size: 14px;
}
.base-title {
width: 100px;
text-align: left;
height: 50px;
line-height: 50px;
}
.base-message {
width: 100%;
height: 50px;
line-height: 50px;
}
:deep(.ant-collapse-header) {
padding: 12px 0 16px;
}
:deep(.ant-collapse-content-box) {
padding-left: 0;
}
}
</style>