|
@@ -0,0 +1,346 @@
|
|
|
|
|
+package com.usky.issue.service.impl;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
+import com.usky.common.core.exception.BusinessException;
|
|
|
|
|
+import com.usky.common.security.utils.SecurityUtils;
|
|
|
|
|
+import com.usky.issue.domain.IssueCloudConfig;
|
|
|
|
|
+import com.usky.issue.domain.IssueSyncStatus;
|
|
|
|
|
+import com.usky.issue.domain.IssueSyncTask;
|
|
|
|
|
+import com.usky.issue.domain.IssueTriggerEvent;
|
|
|
|
|
+import com.usky.issue.mapper.IssueCloudConfigMapper;
|
|
|
|
|
+import com.usky.issue.mapper.IssueSyncStatusMapper;
|
|
|
|
|
+import com.usky.issue.mapper.IssueSyncTaskMapper;
|
|
|
|
|
+import com.usky.issue.mapper.IssueTriggerEventMapper;
|
|
|
|
|
+import com.usky.issue.service.CloudConfigService;
|
|
|
|
|
+import com.usky.issue.service.CloudOperationLogService;
|
|
|
|
|
+import com.usky.issue.service.client.CloudPlatformClient;
|
|
|
|
|
+import com.usky.issue.service.constant.CloudIntegrationConstants;
|
|
|
|
|
+import com.usky.issue.service.enums.SyncTypeEnum;
|
|
|
|
|
+import com.usky.issue.service.enums.TaskStatusEnum;
|
|
|
|
|
+import com.usky.issue.service.enums.TriggerEventCodeEnum;
|
|
|
|
|
+import com.usky.issue.service.support.CloudEntitySupport;
|
|
|
|
|
+import com.usky.issue.service.util.AesGcmCipher;
|
|
|
|
|
+import com.usky.issue.service.util.CredentialMaskUtil;
|
|
|
|
|
+import com.usky.issue.service.vo.CloudConfigResponse;
|
|
|
|
|
+import com.usky.issue.service.vo.CloudConfigSaveRequest;
|
|
|
|
|
+import com.usky.issue.service.vo.CloudConnectionTestRequest;
|
|
|
|
|
+import com.usky.issue.service.vo.CloudConnectionTestResponse;
|
|
|
|
|
+import com.usky.issue.service.vo.CloudConnectionTestResult;
|
|
|
|
|
+import com.usky.issue.service.vo.CloudDisableResponse;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.util.Arrays;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 云平台配置服务实现
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author fyc
|
|
|
|
|
+ * @date 2026-05-21
|
|
|
|
|
+ */
|
|
|
|
|
+@Service
|
|
|
|
|
+public class CloudConfigServiceImpl implements CloudConfigService {
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private IssueCloudConfigMapper configMapper;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private IssueSyncStatusMapper syncStatusMapper;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private IssueTriggerEventMapper triggerEventMapper;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private IssueSyncTaskMapper syncTaskMapper;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private AesGcmCipher aesGcmCipher;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CloudPlatformClient cloudPlatformClient;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private CloudOperationLogService operationLogService;
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public CloudConfigResponse getConfig() {
|
|
|
|
|
+ IssueCloudConfig config = findActiveConfig();
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return toResponse(config);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public CloudConfigResponse saveConfig(CloudConfigSaveRequest request) {
|
|
|
|
|
+ Integer tenantId = SecurityUtils.getTenantId();
|
|
|
|
|
+ String operator = CloudEntitySupport.resolveOperator();
|
|
|
|
|
+ String requestIp = CloudEntitySupport.resolveRequestIp();
|
|
|
|
|
+
|
|
|
|
|
+ if (!request.getTenantId().equals(tenantId)) {
|
|
|
|
|
+ throw new BusinessException("输入租户ID与登录租户不一致,请确认后重试!");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ IssueCloudConfig existing = findActiveConfig();
|
|
|
|
|
+ if (existing == null) {
|
|
|
|
|
+ IssueCloudConfig created = new IssueCloudConfig();
|
|
|
|
|
+ created.setTenantId(request.getTenantId());
|
|
|
|
|
+ created.setCredentialKey(aesGcmCipher.encrypt(request.getCredentialKey()));
|
|
|
|
|
+ created.setStatus(1);
|
|
|
|
|
+ created.setConnectionStatus(0);
|
|
|
|
|
+ created.setVersion(1);
|
|
|
|
|
+ CloudEntitySupport.fillOnInsert(created);
|
|
|
|
|
+ configMapper.insert(created);
|
|
|
|
|
+ initSyncStatusRows(created.getId(), created.getTenantId());
|
|
|
|
|
+ initTriggerEvents(created.getId(), created.getTenantId());
|
|
|
|
|
+ operationLogService.log(created.getId(), CloudIntegrationConstants.OPERATION_SAVE_CONFIG,
|
|
|
|
|
+ "新建配置 tenantId=" + request.getTenantId(), operator, requestIp);
|
|
|
|
|
+ return toResponse(created);
|
|
|
|
|
+ }
|
|
|
|
|
+ Integer version = existing.getVersion();
|
|
|
|
|
+ if (request.getVersion() != null && !request.getVersion().equals(version)) {
|
|
|
|
|
+ throw new BusinessException("配置版本冲突,请刷新后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ existing.setTenantId(request.getTenantId());
|
|
|
|
|
+ existing.setCredentialKey(aesGcmCipher.encrypt(request.getCredentialKey()));
|
|
|
|
|
+ existing.setStatus(1);
|
|
|
|
|
+ existing.setVersion(version + 1);
|
|
|
|
|
+ CloudEntitySupport.fillOnUpdate(existing);
|
|
|
|
|
+ int updated = configMapper.updateById(existing);
|
|
|
|
|
+ if (updated == 0) {
|
|
|
|
|
+ throw new BusinessException("配置版本冲突,请刷新后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ operationLogService.log(existing.getId(), CloudIntegrationConstants.OPERATION_SAVE_CONFIG,
|
|
|
|
|
+ "更新配置 tenantId=" + CredentialMaskUtil.mask(String.valueOf(request.getTenantId())), operator, requestIp);
|
|
|
|
|
+ return toResponse(configMapper.selectById(existing.getId()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public CloudConnectionTestResponse testConnection(CloudConnectionTestRequest request) {
|
|
|
|
|
+ String operator = CloudEntitySupport.resolveOperator();
|
|
|
|
|
+ String requestIp = CloudEntitySupport.resolveRequestIp();
|
|
|
|
|
+ Integer tenantId = request.getTenantId();
|
|
|
|
|
+ Integer loginTenantId = SecurityUtils.getTenantId();
|
|
|
|
|
+ if (loginTenantId != null && !tenantId.equals(loginTenantId)) {
|
|
|
|
|
+ throw new BusinessException("输入租户ID与登录租户不一致,请确认后重试!");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ IssueCloudConfig config = findConfigByTenantId(tenantId);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ throw new BusinessException("云平台配置不存在,请先保存配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getStatus() != null && config.getStatus() == 0) {
|
|
|
|
|
+ throw new BusinessException("云平台集成已禁用");
|
|
|
|
|
+ }
|
|
|
|
|
+ validateCredentialKey(config, request.getCredentialKey());
|
|
|
|
|
+
|
|
|
|
|
+ CloudConnectionTestResult result = cloudPlatformClient.testConnection(
|
|
|
|
|
+ request.getCloudAddress(), request.getTenantId(), request.getCredentialKey(),
|
|
|
|
|
+ SecurityUtils.getToken());
|
|
|
|
|
+
|
|
|
|
|
+ config.setConnectionStatus(result.isSuccess() ? 1 : 2);
|
|
|
|
|
+ config.setLastTestTime(LocalDateTime.now());
|
|
|
|
|
+ CloudEntitySupport.fillOnUpdate(config);
|
|
|
|
|
+ configMapper.updateById(config);
|
|
|
|
|
+ operationLogService.log(config.getId(), CloudIntegrationConstants.OPERATION_TEST_CONNECTION,
|
|
|
|
|
+ "连接测试" + (result.isSuccess() ? "成功" : "失败") + ":" + result.getMessage(), operator, requestIp);
|
|
|
|
|
+ return toConnectionTestResponse(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateCredentialKey(IssueCloudConfig config, String requestKey) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ String storedKey = aesGcmCipher.decrypt(config.getCredentialKey());
|
|
|
|
|
+ if (!storedKey.equals(requestKey)) {
|
|
|
|
|
+ throw new BusinessException("凭证密钥与已保存配置不一致");
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (BusinessException ex) {
|
|
|
|
|
+ throw ex;
|
|
|
|
|
+ } catch (Exception ex) {
|
|
|
|
|
+ throw new BusinessException("凭证密钥校验失败");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public CloudConnectionTestResponse validateTenantConnection(Integer tenantId) {
|
|
|
|
|
+ CloudConnectionTestResponse response = new CloudConnectionTestResponse();
|
|
|
|
|
+ if (tenantId == null) {
|
|
|
|
|
+ response.setSuccess(false);
|
|
|
|
|
+ response.setConnectionStatus(2);
|
|
|
|
|
+ response.setMessage("租户ID不能为空");
|
|
|
|
|
+ response.setNetworkReachable(true);
|
|
|
|
|
+ response.setAuthValid(false);
|
|
|
|
|
+ response.setTenantExists(false);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ IssueCloudConfig config = findConfigByTenantId(tenantId);
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ response.setSuccess(false);
|
|
|
|
|
+ response.setConnectionStatus(2);
|
|
|
|
|
+ response.setMessage(CloudIntegrationConstants.MSG_TENANT_CONFIG_MISSING);
|
|
|
|
|
+ response.setNetworkReachable(true);
|
|
|
|
|
+ response.setAuthValid(true);
|
|
|
|
|
+ response.setTenantExists(false);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getStatus() != null && config.getStatus() == 0) {
|
|
|
|
|
+ response.setSuccess(false);
|
|
|
|
|
+ response.setConnectionStatus(2);
|
|
|
|
|
+ response.setMessage("租户配置已禁用");
|
|
|
|
|
+ response.setNetworkReachable(true);
|
|
|
|
|
+ response.setAuthValid(true);
|
|
|
|
|
+ response.setTenantExists(false);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ response.setSuccess(true);
|
|
|
|
|
+ response.setConnectionStatus(1);
|
|
|
|
|
+ response.setMessage(CloudIntegrationConstants.MSG_CONNECTION_SUCCESS);
|
|
|
|
|
+ response.setNetworkReachable(true);
|
|
|
|
|
+ response.setAuthValid(true);
|
|
|
|
|
+ response.setTenantExists(true);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public CloudDisableResponse disable(Integer status) {
|
|
|
|
|
+ if (status == null || (status != 0 && status != 1)) {
|
|
|
|
|
+ throw new BusinessException("status 只能为 0(禁用)或 1(启用)");
|
|
|
|
|
+ }
|
|
|
|
|
+ String operator = CloudEntitySupport.resolveOperator();
|
|
|
|
|
+ String requestIp = CloudEntitySupport.resolveRequestIp();
|
|
|
|
|
+ IssueCloudConfig config = requireConfig();
|
|
|
|
|
+
|
|
|
|
|
+ int affected = 0;
|
|
|
|
|
+ config.setStatus(status);
|
|
|
|
|
+ CloudEntitySupport.fillOnUpdate(config);
|
|
|
|
|
+ configMapper.updateById(config);
|
|
|
|
|
+
|
|
|
|
|
+ if (status == 0) {
|
|
|
|
|
+ affected = cancelRunningTasks(config.getId());
|
|
|
|
|
+ operationLogService.log(config.getId(), CloudIntegrationConstants.OPERATION_DISABLE,
|
|
|
|
|
+ "禁用集成,终止任务数=" + affected, operator, requestIp);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ operationLogService.log(config.getId(), CloudIntegrationConstants.OPERATION_ENABLE,
|
|
|
|
|
+ "启用集成", operator, requestIp);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ CloudDisableResponse response = new CloudDisableResponse();
|
|
|
|
|
+ response.setDisabled(status == 0);
|
|
|
|
|
+ response.setAffectedTasks(affected);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private IssueCloudConfig requireConfig() {
|
|
|
|
|
+ IssueCloudConfig config = findActiveConfig();
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ throw new BusinessException("云平台配置不存在,请先保存配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ return config;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public IssueCloudConfig requireActiveConfig() {
|
|
|
|
|
+ IssueCloudConfig config = findActiveConfig();
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ throw new BusinessException("云平台配置不存在,请先保存配置");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getStatus() != null && config.getStatus() == 0) {
|
|
|
|
|
+ throw new BusinessException("云平台集成已禁用");
|
|
|
|
|
+ }
|
|
|
|
|
+ return config;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private IssueCloudConfig findActiveConfig() {
|
|
|
|
|
+ return findConfigByTenantId(SecurityUtils.getTenantId());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private IssueCloudConfig findConfigByTenantId(Integer tenantId) {
|
|
|
|
|
+ if (tenantId == null) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return configMapper.selectOne(new QueryWrapper<IssueCloudConfig>()
|
|
|
|
|
+ .eq("is_deleted", 0)
|
|
|
|
|
+ .eq("tenant_id", tenantId)
|
|
|
|
|
+ .orderByDesc("id")
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private CloudConnectionTestResponse toConnectionTestResponse(CloudConnectionTestResult result) {
|
|
|
|
|
+ CloudConnectionTestResponse response = new CloudConnectionTestResponse();
|
|
|
|
|
+ response.setSuccess(result.isSuccess());
|
|
|
|
|
+ response.setConnectionStatus(result.isSuccess() ? 1 : 2);
|
|
|
|
|
+ response.setMessage(result.getMessage());
|
|
|
|
|
+ response.setNetworkReachable(result.isNetworkReachable());
|
|
|
|
|
+ response.setAuthValid(result.isAuthValid());
|
|
|
|
|
+ response.setTenantExists(result.isTenantExists());
|
|
|
|
|
+ response.setCostTime(result.getCostTime());
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private int cancelRunningTasks(Long configId) {
|
|
|
|
|
+ List<IssueSyncTask> running = syncTaskMapper.selectList(new QueryWrapper<IssueSyncTask>()
|
|
|
|
|
+ .eq("config_id", configId)
|
|
|
|
|
+ .eq("is_deleted", 0)
|
|
|
|
|
+ .in("task_status",
|
|
|
|
|
+ TaskStatusEnum.PENDING.getCode(), TaskStatusEnum.RUNNING.getCode()));
|
|
|
|
|
+ for (IssueSyncTask task : running) {
|
|
|
|
|
+ task.setTaskStatus(TaskStatusEnum.CANCELLED.getCode());
|
|
|
|
|
+ task.setFinishTime(LocalDateTime.now());
|
|
|
|
|
+ task.setErrorSummary("集成已禁用,任务终止");
|
|
|
|
|
+ CloudEntitySupport.fillOnUpdate(task);
|
|
|
|
|
+ syncTaskMapper.updateById(task);
|
|
|
|
|
+ }
|
|
|
|
|
+ return running.size();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void initSyncStatusRows(Long configId, Integer tenantId) {
|
|
|
|
|
+ for (SyncTypeEnum type : SyncTypeEnum.values()) {
|
|
|
|
|
+ IssueSyncStatus row = new IssueSyncStatus();
|
|
|
|
|
+ row.setConfigId(configId);
|
|
|
|
|
+ row.setTenantId(tenantId);
|
|
|
|
|
+ row.setSyncType(type.getCode());
|
|
|
|
|
+ row.setTypeName(type.getLabel());
|
|
|
|
|
+ row.setTotalCount(0);
|
|
|
|
|
+ row.setSuccessCount(0);
|
|
|
|
|
+ row.setFailureCount(0);
|
|
|
|
|
+ row.setUnsyncedCount(0);
|
|
|
|
|
+ CloudEntitySupport.fillOnInsert(row);
|
|
|
|
|
+ syncStatusMapper.insert(row);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void initTriggerEvents(Long configId, Integer tenantId) {
|
|
|
|
|
+ Arrays.stream(TriggerEventCodeEnum.values()).forEach(code -> {
|
|
|
|
|
+ IssueTriggerEvent event = new IssueTriggerEvent();
|
|
|
|
|
+ event.setConfigId(configId);
|
|
|
|
|
+ event.setTenantId(tenantId);
|
|
|
|
|
+ event.setEventCode(code.getCode());
|
|
|
|
|
+ event.setEventName(code.getName());
|
|
|
|
|
+ event.setDescription(code.getDescription());
|
|
|
|
|
+ event.setEnabled(0);
|
|
|
|
|
+ CloudEntitySupport.fillOnInsert(event);
|
|
|
|
|
+ triggerEventMapper.insert(event);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private CloudConfigResponse toResponse(IssueCloudConfig config) {
|
|
|
|
|
+ CloudConfigResponse resp = new CloudConfigResponse();
|
|
|
|
|
+ resp.setId(config.getId());
|
|
|
|
|
+ resp.setTenantId(String.valueOf(config.getTenantId()));
|
|
|
|
|
+ try {
|
|
|
|
|
+ resp.setCredentialKey(aesGcmCipher.decrypt(config.getCredentialKey()));
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ resp.setCredentialKey(null);
|
|
|
|
|
+ }
|
|
|
|
|
+ resp.setStatus(config.getStatus());
|
|
|
|
|
+ resp.setConnectionStatus(config.getConnectionStatus());
|
|
|
|
|
+ resp.setVersion(config.getVersion());
|
|
|
|
|
+ resp.setLastTestTime(config.getLastTestTime());
|
|
|
|
|
+ resp.setUpdatedTime(config.getUpdatedTime());
|
|
|
|
|
+ resp.setUpdatedBy(config.getUpdatedBy());
|
|
|
|
|
+ resp.setCreatedTime(config.getCreatedTime());
|
|
|
|
|
+ resp.setCreatedBy(config.getCreatedBy());
|
|
|
|
|
+ return resp;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|