/*
* Copyright (c) 京北方信息技术股份有限公司 Corporation 2023 . All rights reserved.
*
*/
package net.northking.cctp.project.api.env.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.constants.BaseDefineDependencyConstants;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.entity.Environment;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.exception.InvalidException;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.utils.JSONUtil;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.utils.SpringUtil;
import com.hzbank.testteam.autotest.dependencies.baseDefineDependency.vo.ResponseVO;
import com.hzbank.testteam.autotest.dependencies.monsterDependency.dto.DictItem;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoDatabase;
import net.northking.cctp.common.cache.EntityNameCache;
import net.northking.cctp.common.cache.EntityNameCatalog;
import net.northking.cctp.common.db.BasicService;
import net.northking.cctp.common.db.Pagination;
import net.northking.cctp.common.db.annotation.DatabaseReadOnly;
import net.northking.cctp.common.db.annotation.DatabaseWriteOnly;
import net.northking.cctp.common.db.excel.AbstractExcelService;
import net.northking.cctp.common.db.excel.ExcelHeader;
import net.northking.cctp.common.exception.PlatformRuntimeException;
import net.northking.cctp.common.http.QueryByPage;
import net.northking.cctp.common.http.QueryOrderBy;
import net.northking.cctp.common.http.ResultWrapper;
import net.northking.cctp.common.security.authentication.NKSecurityContext;
import net.northking.cctp.common.util.UUIDUtil;
import net.northking.cctp.platform.db.entity.CltDictItem;
import net.northking.cctp.project.api.pub.dto.PubDto;
import net.northking.cctp.common.feign.ApiAtsEnviromentFeign;
import net.northking.cctp.project.bus.feign.PlatformFeign;
import net.northking.cctp.project.constants.ColumnTypeConstants;
import net.northking.cctp.project.constants.ProjectAppError;
import net.northking.cctp.project.constants.ProjectConstants;
import net.northking.cctp.project.db.entity.CpProjectEnvDs;
import net.northking.cctp.project.db.service.CpProjectEnvDsService;
import net.northking.cctp.project.dto.env.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.*;
import java.util.Date;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 系统环境数据源信息服务实现类
 *
 * @author maven-cctp-plugin
 * @since 1.0
 */

@Service
@ConditionalOnMissingBean(name = "CpProjectEnvDsApiServiceImpl")
public class CpProjectEnvDsApiServiceImpl extends AbstractExcelService<CpProjectEnvDs> implements CpProjectEnvDsApiService
{
  /**
   * 日志
   */
  private final static Logger logger = LoggerFactory.getLogger(CpProjectEnvDsApiServiceImpl.class);

  @Autowired
  private CpProjectEnvDsService cpProjectEnvDsService;


  @Autowired
  private RedisTemplate<String, Object> redisTemplate;

  @Autowired
  private EntityNameCache entityNameCache;

  @Autowired
  private PlatformFeign platformFeign;

  @Autowired
  private ApiAtsEnviromentFeign apiAtsEnviromentFeign;

  //= Excel 导入导出相关代码 start ==================//
  @Override
  public BasicService<CpProjectEnvDs> getService()
  {
    return this.cpProjectEnvDsService;
  }

  @Override
  public ExcelHeader[] excelHeaders()
  {
    ExcelHeader id = new ExcelHeader("主键", "id", String.class);
    ExcelHeader tenantId = new ExcelHeader("租户id", "tenantId", String.class);
    ExcelHeader projectId = new ExcelHeader("系统id", "projectId", String.class);
    ExcelHeader envId = new ExcelHeader("环境id", "envId", String.class);
    ExcelHeader dsType = new ExcelHeader("数据源类型 1-mysql，2-Oracle，3-MongoDB，4-达梦，5-OceanBase，6-db2", "dsType", Boolean.class);
    ExcelHeader connName = new ExcelHeader("连接名称", "connName", String.class);
    ExcelHeader connHost = new ExcelHeader("主机", "connHost", String.class);
    ExcelHeader connPort = new ExcelHeader("端口", "connPort", Integer.class);
    ExcelHeader connDatabase = new ExcelHeader("数据库", "connDatabase", String.class);
    ExcelHeader connUsername = new ExcelHeader("用户名", "connUsername", String.class);
    ExcelHeader connPassword = new ExcelHeader("密码", "connPassword", String.class);
    ExcelHeader memo = new ExcelHeader("描述", "memo", String.class);
    ExcelHeader createdBy = new ExcelHeader("创建人", "createdBy", String.class);
    ExcelHeader createdTime = new ExcelHeader("创建时间", "createdTime", Date.class);
    ExcelHeader updatedBy = new ExcelHeader("修改人", "updatedBy", String.class);
    ExcelHeader updatedTime = new ExcelHeader("修改时间", "updatedTime", Date.class);
    ExcelHeader deleted = new ExcelHeader("是否删除", "deleted", Boolean.class);
    return new ExcelHeader[]{};
  }

  /**
   * Excel导入，入库之前的处理
   * @param records 从Excel读取到的记录
   * @return 待入库的记录
   */
  @Override
  protected List<CpProjectEnvDs> beforeInsertBatch(List<CpProjectEnvDs> records)
  {
    for (CpProjectEnvDs record : records)
    {
      record.setId(UUIDUtil.create32UUID());
    }
    return records;
  }

  /**
   * 写入Excel之前的处理<br>
   * 数据的特殊处理在此处加工处理
   *
   * @param record 来自数据库记录
   * @return 待写入Excel的记录
   */
  @Override
  protected CpProjectEnvDs beforeWriteExcel(CpProjectEnvDs record)
  {
    return record;
  }

  //= Excel 导入导出相关代码 end =======================//

  //= 增删改查 相关代码 start ==================//
  @Override
  @DatabaseReadOnly
  public Pagination<CpProjectEnvDsDetailDto> pagingQuery(QueryByPage<CpProjectEnvDsQueryDto> queryByPage)
  {
    List<QueryOrderBy> orderByList = new ArrayList<>();
    QueryOrderBy orderBy = new QueryOrderBy();
    orderBy.desc(ProjectConstants.CREATED_TIME);
    orderByList.add(orderBy);
    queryByPage.setOrderByList(orderByList);
    Pagination<CpProjectEnvDsDetailDto> pagingQuery = this.cpProjectEnvDsService.pagingQuery(queryByPage);
    if (pagingQuery.getRecords() == null || pagingQuery.getRecords().size() <= 0){
      return pagingQuery;
    }
    for (CpProjectEnvDsDetailDto record : pagingQuery.getRecords()) {
      String createdBy = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER, record.getCreatedBy());
      record.setCreatedBy(StringUtils.isBlank(createdBy)?record.getCreatedBy():createdBy);
      String updatedBy = entityNameCache.translateById(EntityNameCatalog.MONSTER_USER, record.getUpdatedBy());
      record.setUpdatedBy(StringUtils.isBlank(updatedBy)?record.getUpdatedBy():updatedBy);

    }
    return pagingQuery;
  }

  @Override
  @DatabaseWriteOnly
  @Transactional(rollbackFor = Exception.class)
  public String add(CpProjectEnvDsAddDto dto)
  {
    // 校验系统环境下是否存在同名数据源
    CpProjectEnvDs sameNameParams = new CpProjectEnvDs();
    sameNameParams.setConnName(dto.getConnName());
    sameNameParams.setProjectId(dto.getProjectId());
    sameNameParams.setEnvId(dto.getEnvId());
    List<CpProjectEnvDs> sameNameDsList = this.cpProjectEnvDsService.query(sameNameParams);
    if (sameNameDsList != null && sameNameDsList.size() > 0){
      throw new PlatformRuntimeException(ProjectAppError.PROJECT_ENV_DS_IS_EXISTS);
    }
    List<CpProjectEnvDs> envDsList = new ArrayList<>();
    String uuid = UUIDUtil.create32UUID();
    CpProjectEnvDs entity = new CpProjectEnvDs();
    BeanUtils.copyProperties(dto, entity);
    entity.setId(uuid);
    entity.setAdvancedConfig(JSON.toJSONString(dto.getAdvancedConfigMap()));
    entity.setCreatedBy(NKSecurityContext.getUserId());
    entity.setCreatedTime(new Date());
    entity.setUpdatedBy(NKSecurityContext.getUserId());
    entity.setUpdatedTime(new Date());
    envDsList.add(entity);

    ResponseVO<List<Environment>> responseVo = apiAtsEnviromentFeign.listByProject(Long.valueOf(dto.getProjectId()));
    if (responseVo == null || !responseVo.getCode().equals(BaseDefineDependencyConstants.SYSTEM_RESPONSE_CODE_SUCCESS)) {
      throw new InvalidException("服务间调用获取系统环境失败");
    }
    List<Environment> data = responseVo.getData();
    for (Environment vo : data) {
      if (vo.getId().toString().equals(dto.getEnvId())){
        continue;
      }
      // 同步该数据源信息至其他环境下
      CpProjectEnvDs envDs = new CpProjectEnvDs();
      envDs.setId(UUIDUtil.create32UUID());
      envDs.setTenantId(entity.getTenantId());
      envDs.setProjectId(entity.getProjectId());
      envDs.setEnvId(vo.getId().toString());
      envDs.setConnName(entity.getConnName());
      envDs.setCreatedBy(NKSecurityContext.getUserId());
      envDs.setCreatedTime(new Date());
      envDs.setUpdatedBy(NKSecurityContext.getUserId());
      envDs.setUpdatedTime(new Date());
      envDsList.add(envDs);
    }
    this.cpProjectEnvDsService.insertByBatch(envDsList);
    return uuid;
  }

  @Override
  @DatabaseWriteOnly
  public Integer updateByPK(CpProjectEnvDsUpdateDto dto) throws IllegalAccessException
  {
    // 修改数据源连接信息
    CpProjectEnvDs entity = new CpProjectEnvDs();
    BeanUtils.copyProperties(dto, entity);
    entity.setAdvancedConfig(JSON.toJSONString(dto.getAdvancedConfigMap()));
    entity.setUpdatedBy(NKSecurityContext.getUserId());
    entity.setUpdatedTime(new Date());
    return this.cpProjectEnvDsService.updateByPrimaryKey(entity);
  }

  @Override
  @DatabaseReadOnly
  public CpProjectEnvDsDetailDto findByPK(String uuid)
  {
    CpProjectEnvDs example = new CpProjectEnvDs();
    example.setId(uuid);
    List<CpProjectEnvDs> entitys = this.cpProjectEnvDsService.query(example);
    if (entitys.isEmpty())
    {
      return null;
    }

    CpProjectEnvDsDetailDto dto = new CpProjectEnvDsDetailDto();
    BeanUtils.copyProperties(entitys.get(0), dto);
    String advancedConfig = entitys.get(0).getAdvancedConfig();
    if (StringUtils.isNotBlank(advancedConfig)) {
      dto.setAdvancedConfigMap(JSON.parseObject(advancedConfig));
    }
    return dto;
  }

  @Override
  @DatabaseWriteOnly
  public Integer deleteByPK(String uuid)
  {
    // 删除数据源时，同时删除其他环境下相同名称的数据源
    CpProjectEnvDs envDs = this.cpProjectEnvDsService.findByPrimaryKey(uuid);
    if (envDs == null){
      throw new PlatformRuntimeException(ProjectAppError.PROJECT_ENV_DS_IS_NULL);
    }
    CpProjectEnvDs deleteParams = new CpProjectEnvDs();
    deleteParams.setProjectId(envDs.getProjectId());
    deleteParams.setConnName(envDs.getConnName());
    return this.cpProjectEnvDsService.deleteByExample(deleteParams);
  }


  @Override
  public Integer updateBatch(List<CpProjectEnvDsUpdateDto> batch) throws IllegalAccessException
  {
    int sum = 0;
    for (CpProjectEnvDsUpdateDto dto : batch)
    {
      sum = sum + this.updateByPK(dto);
    }
    return sum;
  }

  @Override
  public Integer deleteBatch(List<Object> batch)
  {
    return this.cpProjectEnvDsService.deleteByPrimaryKeys(batch);
  }

  @Override
  public List<CpProjectEnvDsSelectDto> findSelect(String projectId, String envId) {
    List<CpProjectEnvDsSelectDto> resultList = new ArrayList<>();
    CpProjectEnvDs params = new CpProjectEnvDs();
    params.setProjectId(projectId);
    params.setEnvId(envId);
    params.setTenantId(NKSecurityContext.getTenantId());
    List<CpProjectEnvDs> dsList = this.cpProjectEnvDsService.queryEnvDsByDto(params);
    if (dsList == null || dsList.size() <= 0){
      return resultList;
    }
    for (CpProjectEnvDs envDs : dsList) {
      CpProjectEnvDsSelectDto selectDto = new CpProjectEnvDsSelectDto();
      selectDto.setValue(envDs.getId());
      selectDto.setLabel(envDs.getConnName());
      selectDto.setDsType(envDs.getDsType());
      resultList.add(selectDto);
    }
    return resultList;
  }

  @Override
  public Boolean checkConnectionById(String uuid) {
    CpProjectEnvDs envDs = this.cpProjectEnvDsService.findByPrimaryKey(uuid);
    if (envDs == null){
      throw new PlatformRuntimeException(ProjectAppError.PROJECT_ENV_DS_IS_NULL);
    }
    if (envDs.getDsType() == null){
      throw new PlatformRuntimeException(ProjectAppError.DS_CONNECTION_PARAMS_IS_NULL);
    }
    CpProjectEnvDsConnDto connDto = new CpProjectEnvDsConnDto();
    BeanUtils.copyProperties(envDs, connDto);
    connDto.setAdvancedConfigMap(JSON.parseObject(envDs.getAdvancedConfig()));
    return this.checkConnection(connDto);
  }

  @Override
  public Boolean checkConnection(CpProjectEnvDsConnDto connDto) {
    // 数据源类型 0-MongoDB，1-mysql，2-Oracle，3-达梦，4-OceanBase，5-db2
    Integer dsType = connDto.getDsType();
    if (dsType == 0){
      logger.debug("连接mongodb数据库");
      try {
        MongoClient mongoClient = getMongoClient(connDto);
        mongoClient.listDatabases().first();
        logger.debug("连接成功");
        return true;
      }catch (Exception e){
        logger.error("连接mongodb异常", e);
        throw new PlatformRuntimeException(ProjectAppError.DS_CONNECTION_FAIL);
      }
    }else {
      Connection connection = null;
      try {
        connection = getDbConnection(connDto);
        if (connection == null){
          throw new PlatformRuntimeException(ProjectAppError.DB_CONN_FAIL);
        }
        logger.debug("连接成功");
        return true;
      }catch (Exception s) {
        logger.error("数据库连接异常", s);
        throw new PlatformRuntimeException(ProjectAppError.DS_CONNECTION_FAIL);
      }finally {
        if (connection != null){
          try {
            connection.close();
          }catch (Exception e){
            logger.error("数据库连接关闭异常", e);
          }
        }
      }
    }
  }

  /**
   * 获取mongodb客户端
   * @param connDto 连接参数
   * @return 结果集
   */
  private MongoClient getMongoClient(CpProjectEnvDsConnDto connDto){
    List<ServerAddress> addrs=new ArrayList<>();
    ServerAddress addr = new ServerAddress(connDto.getConnHost(), connDto.getConnPort());
    addrs.add(addr);
    List<MongoCredential> mcs=new ArrayList<>();
    MongoCredential mc = MongoCredential.createScramSha1Credential(connDto.getConnUsername(),
            connDto.getConnDatabase(), connDto.getConnPassword().toCharArray());
    mcs.add(mc);
    return new MongoClient(addrs, mcs);
  }


  /**
   * 获取数据库连接
   * @param connDto 连接参数
   * @return Connection
   */
  private Connection getDbConnection(CpProjectEnvDsConnDto connDto) throws Exception{
    logger.debug("获取数据库驱动");
    String driver = getDsDriver(connDto.getDsType() + "");
    logger.debug("驱动为：{}", driver);
    logger.debug("获取数据库连接URL模板信息");
    String url = getDsUrlTemp(connDto.getDsType() + "", connDto.getAdvancedConfigMap());
    logger.debug("URL模板为：{}", url);
    url = String.format(url, connDto.getConnHost(), connDto.getConnPort(), connDto.getConnDatabase());
    logger.debug("完整URL地址为：{}", url);
    Class.forName(driver);
    DriverManager.setLoginTimeout(40);
    if (connDto.getDsType() == 2){
      // oracle数据库url可使用sid或服务名连接，所以这里尝试两种连接方式
      try {
        return DriverManager.getConnection(url, connDto.getConnUsername(), connDto.getConnPassword());
      }catch (Exception e){
        logger.warn("oracle服务名方式连接失败, {}", e.getMessage());
      }
      url = url.replace("/", ":");
      logger.debug("替换为sid连接方式后的URL地址为：{}", url);
    }
    return DriverManager.getConnection(url, connDto.getConnUsername(), connDto.getConnPassword());
  }

  @Override
  public Map<String, Object> executeSql(CpProjectEnvDsExecuteDto executeDto) {
    CpProjectEnvDs projectEnvDs = this.cpProjectEnvDsService.findByPrimaryKey(executeDto);
    if (projectEnvDs == null){
      throw new PlatformRuntimeException(ProjectAppError.PROJECT_ENV_DS_IS_NULL);
    }
    CpProjectEnvDsConnDto connDto = new CpProjectEnvDsConnDto();
    BeanUtils.copyProperties(projectEnvDs, connDto);
    Map<String, Object> advancedConfigMap = JSON.parseObject(projectEnvDs.getAdvancedConfig());
    connDto.setAdvancedConfigMap(advancedConfigMap);
    return genExecute(connDto, executeDto.getSqlContent(), executeDto.getPageNo(), executeDto.getPageSize());
  }

  @Override
  public String executeModuleSql(CpProjectEnvDsModuleExecDto execDto) {
    if (StringUtils.isBlank(NKSecurityContext.getTenantId())){
      NKSecurityContext.setTenantId(execDto.getTenantId());
    }
    // 判断来源类型 1-静态数据源，2-动态数据源
    String sourceType = execDto.getSourceType();
    CpProjectEnvDsConnDto connDto = new CpProjectEnvDsConnDto();
    if (ProjectConstants.DS_CONFIG_SOURCE_TYPE_STATIC.equals(sourceType)){
      if (execDto.getType() == null || StringUtils.isBlank(execDto.getHost()) || execDto.getPort() == null ||
              StringUtils.isBlank(execDto.getDbName()) || StringUtils.isBlank(execDto.getUsername()) ||
              StringUtils.isBlank(execDto.getPassword())){
        throw new PlatformRuntimeException(ProjectAppError.DS_CONNECTION_PARAMS_IS_NULL);
      }
      connDto.setDsType(execDto.getType());
      connDto.setConnName(execDto.getName());
      connDto.setConnHost(execDto.getHost());
      connDto.setConnPort(execDto.getPort());
      connDto.setConnDatabase(execDto.getDbName());
      connDto.setConnUsername(execDto.getUsername());
      connDto.setConnPassword(execDto.getPassword());
      connDto.setAdvancedConfigMap(execDto.getAdvancedConfigMap());
    }else if (ProjectConstants.DS_CONFIG_SOURCE_TYPE_DYNAMIC.equals(sourceType)){
      CpProjectEnvDs params = new CpProjectEnvDs();
      params.setProjectId(execDto.getProjectId());
      params.setEnvId(execDto.getEnvId());
      params.setConnName(execDto.getName());
      List<CpProjectEnvDs> envDsList = this.cpProjectEnvDsService.query(params);
      if (envDsList == null || envDsList.size() <= 0){
        throw new PlatformRuntimeException(ProjectAppError.PROJECT_ENV_DS_IS_NULL);
      }
      CpProjectEnvDs envDs = envDsList.get(0);
      BeanUtils.copyProperties(envDs, connDto);
    }else{
      throw new PlatformRuntimeException(ProjectAppError.DS_CONFIG_SOURCE_EXCEPTION, sourceType);
    }

    // pageNo为0是不进行分页
    Map<String, Object> resultMap = this.genExecute(connDto, execDto.getSqlContent(), 0, 5000);
    if (resultMap == null || resultMap.size() <= 0){
      return "";
    }
    return resultMap.get(ProjectConstants.MAP_KEY_RESULT) == null
            ? "" : JSONObject.toJSONString(resultMap.get(ProjectConstants.MAP_KEY_RESULT));
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void syncProjectEnvDs(String projectId, List<String> envIds) {
    // 删除不在修改的环境数据源信息
    CpProjectEnvDsDeleteDto deleteDto = new CpProjectEnvDsDeleteDto();
    deleteDto.setTenantId(NKSecurityContext.getTenantId());
    deleteDto.setProjectId(projectId);
    deleteDto.setEnvIds(envIds);
    cpProjectEnvDsService.deleteByProjectIdNoInEnvIds(deleteDto);

    // 查询剩余的环境数据源信息
    CpProjectEnvDs params = new CpProjectEnvDs();
    params.setTenantId(NKSecurityContext.getTenantId());
    params.setProjectId(projectId);
    List<CpProjectEnvDs> envDsList = cpProjectEnvDsService.query(params);
    if (envDsList != null && envDsList.size() > 0){
      List<String> oldEnvIds = envDsList.stream().map(CpProjectEnvDs::getEnvId).collect(Collectors.toList());
      List<String> diffEnvIds = new ArrayList<>(envIds);
      diffEnvIds.removeAll(oldEnvIds);
      if (diffEnvIds.size() > 0){
        CpProjectEnvDs cpProjectEnvDs = envDsList.get(0);
        List<CpProjectEnvDs> dsBatchList = new ArrayList<>();
        for (String envId : diffEnvIds) {
          CpProjectEnvDs newEnvDs = new CpProjectEnvDs();
          newEnvDs.setId(UUIDUtil.create32UUID());
          newEnvDs.setTenantId(NKSecurityContext.getTenantId());
          newEnvDs.setProjectId(projectId);
          newEnvDs.setEnvId(envId);
          newEnvDs.setConnName(cpProjectEnvDs.getConnName());
          newEnvDs.setCreatedBy(NKSecurityContext.getUserId());
          newEnvDs.setCreatedTime(new Date());
          newEnvDs.setUpdatedBy(NKSecurityContext.getUserId());
          newEnvDs.setUpdatedTime(new Date());
          dsBatchList.add(newEnvDs);
        }
        cpProjectEnvDsService.insertByBatch(dsBatchList);
      }
    }
  }

  /**
   * 通用执行
   * @param connDto 连接信息
   * @param sqlContent sql
   * @param pageNo 页码
   * @param pageSize 页数
   * @return 结果集
   */
  private Map<String, Object> genExecute(CpProjectEnvDsConnDto connDto, String sqlContent, int pageNo, int pageSize){
    // 2023/9/20 判断连接信息
    if (connDto.getDsType() == null || StringUtils.isBlank(connDto.getConnHost()) || connDto.getConnPort() == null
            || StringUtils.isBlank(connDto.getConnDatabase()) || StringUtils.isBlank(connDto.getConnUsername())
            || StringUtils.isBlank(connDto.getConnPassword())){
      throw new PlatformRuntimeException(ProjectAppError.DS_CONNECTION_PARAMS_IS_NULL);
    }
    // mongodb数据查询
    if (connDto.getDsType() == 0){
      try {
        return handleMongoResult(connDto, sqlContent, pageNo, pageSize);
      }catch (Exception e){
        logger.error("MongoDB 执行失败", e);
        throw new PlatformRuntimeException(ProjectAppError.DB_EXECUTE_SQL_FAIL, e.getMessage());
      }
    }

    Connection connection = null;
    try{
      connection = getDbConnection(connDto);
      if (connection == null){
        throw new PlatformRuntimeException(ProjectAppError.DB_CONN_FAIL);
      }
      logger.debug("判断是否是存储过程");
      if (sqlContent.contains(ProjectConstants.SQL_CONTENT_PROCEDURE)){
        logger.debug("存储过程使用 CallableStatement 执行");
        CallableStatement callableStatement = connection.prepareCall(sqlContent);
        return this.handleResultSet(connDto,callableStatement, pageNo, pageSize);
      }else{
        logger.debug("普通sql使用 PreparedStatement 执行");
        PreparedStatement statement = connection.prepareStatement(sqlContent);
        statement.setMaxRows(100); // 限制最大返回100条
        return this.handleResultSet(connDto,statement, pageNo, pageSize);
      }
    }catch (Exception e){
      logger.error("执行SQL出现异常", e);
      throw new PlatformRuntimeException(ProjectAppError.DB_EXECUTE_SQL_FAIL, e.getMessage());
    }finally {
      if (connection != null) {
        try {
          logger.debug("关闭数据库连接");
          connection.close();
        } catch (Exception e) {
          logger.error("Connection close exception", e);
        }
      }
    }
  }

  /**
   * 处理mongodb查询的结果集
   * @param connDto 连接信息
   * @param sqlContent sql
   * @param pageNo 页码
   * @param pageSize 页数
   * @return 结果集
   */
  private Map<String, Object> handleMongoResult(CpProjectEnvDsConnDto connDto, String sqlContent, int pageNo, int pageSize){
    Map<String, Object> resultMap = new HashMap<>();
    MongoClient mongoClient = getMongoClient(connDto);
    MongoDatabase database = mongoClient.getDatabase(connDto.getConnDatabase());
    Document search = Document.parse(sqlContent);
    Document document = database.runCommand(search);
    if (document == null || document.size() == 0 || document.get(ProjectConstants.DOCUMENT_CURSOR) == null){
      return resultMap;
    }
    Document cursor = document.get(ProjectConstants.DOCUMENT_CURSOR, Document.class);
    List firstBatch = cursor.get(ProjectConstants.CURSOR_FIRST_BATCH, List.class);
    if (firstBatch.size() <= 0){
      return resultMap;
    }
    Set<String> headers = new LinkedHashSet<>();
    Map map = (Map) firstBatch.get(0);
    map.remove(ProjectConstants.REMOVE_ITEM);
    for (Object obj : map.keySet()) {
      headers.add(obj.toString());
    }
    mongoClient.close();

    logger.debug("结果分页处理");
    if (pageNo == 0){
      resultMap.put(ProjectConstants.MAP_KEY_RESULT, firstBatch);
      resultMap.put(ProjectConstants.MAP_KEY_TOTAL, firstBatch.size());
    }else {
      resultMap = pageByResults(firstBatch, pageNo, pageSize);
    }
    if (headers.size() > 0){
      resultMap.put(ProjectConstants.RESULT_HEADERS, headers);
    }
    return resultMap;
  }

  /**
   * 处理数据库结果集
   * @param statement 声明
   * @param pageNo 页码
   * @param pageSize 页数
   * @return 结果集
   * @throws Exception 异常
   */
  public Map<String, Object> handleResultSet(CpProjectEnvDsConnDto connDto,PreparedStatement statement, int pageNo, int pageSize) throws Exception{
    Map<String, Object> resultMap = new HashMap<>();
    Set<String> headers = new LinkedHashSet<>();
    List<JSONObject> resultList = new ArrayList<>();
    ResultSet resultSet = null;
    try {
      // true-读，false-写
      boolean isSelect = statement.execute();
      if (!isSelect) {
        logger.debug("写操作，返回更新数量");
        int updateCount = statement.getUpdateCount();
        resultMap.put(ProjectConstants.MAP_KEY_RESULT, updateCount);
        return resultMap;
      }
      resultSet = statement.executeQuery();
      // 数据处理
      // 键名
      ResultSetMetaData metaData = resultSet.getMetaData();
      // 键数量
      int columnCount = metaData.getColumnCount();
      while (resultSet.next()) {
        JSONObject temp = new JSONObject(new LinkedHashMap<>());
        for (int i = 1; i <= columnCount; i++) {
          //判断数据库类型是否为ORACLE 和 字段类型为 NUMERIC
          if (connDto.getDsType().equals(ColumnTypeConstants.ORACLE)
                  && (metaData.getColumnType(i) == ColumnTypeConstants.NUMERIC
                  || metaData.getColumnType(i) == ColumnTypeConstants.DOUBLE)) {
            temp.put(metaData.getColumnName(i), resultSet.getBigDecimal(i));
          } else {
            temp.put(metaData.getColumnName(i), resultSet.getString(i));
          }
          headers.add(metaData.getColumnName(i));
        }
        resultList.add(temp);
      }
    }finally {
      logger.debug("关闭数据库资源");
      if (resultSet != null){
        try {
          resultSet.close();
        }catch (Exception e){
          logger.error("ResultSet close exception", e);
        }
      }
      if (statement != null){
        try {
          statement.close();
        }catch (Exception e){
          logger.error("Statement close exception", e);
        }
      }
    }
    logger.debug("结果分页处理");
    if (pageNo == 0){
      resultMap.put(ProjectConstants.MAP_KEY_RESULT, resultList);
      resultMap.put(ProjectConstants.MAP_KEY_TOTAL, resultList.size());
    }else {
      resultMap = pageByResults(resultList, pageNo, pageSize);
    }
    if (headers.size() > 0){
      resultMap.put(ProjectConstants.RESULT_HEADERS, headers);
    }
    return resultMap;
  }

  /**
   * 分页处理
   * @param results 结果数据
   * @param pageNo 页码
   * @param pageSize 页数
   * @return 结果集
   */
  private Map<String, Object> pageByResults(List<?> results, int pageNo, int pageSize) {
    Map<String, Object> map = new HashMap<>();
    if (CollectionUtils.isNotEmpty(results)) {
      int total = results.size();
      int pageCount;
      List<?> subList;

      int m = total % pageSize;
      if (m > 0) {
        pageCount = total / pageSize + 1;
      } else {
        pageCount = total / pageSize;
      }
      if (m == 0) {
        subList = results.subList((pageNo - 1) * pageSize, pageSize * pageNo);
      } else {
        if (pageNo == pageCount) {
          subList = results.subList((pageNo - 1) * pageSize, total);
        } else {
          subList = results.subList((pageNo - 1) * pageSize, pageSize * pageNo);
        }
      }
      map.put(ProjectConstants.MAP_KEY_RESULT, subList);
      map.put(ProjectConstants.MAP_KEY_TOTAL, total);
    }
    return map;
  }

  /**
   * 根据数据源类型获取驱动
   *
   * @param dsType 数据源类型
   * @return 驱动信息
   */
  private String getDsDriver(String dsType) {
    Map<String, Object> dsDriverMap = null;
    logger.debug("获取缓存中字典数据库驱动数据 tenant_id =>: " + NKSecurityContext.getTenantId());
    String dsDriverKey = ProjectConstants.DICT_PREFIX + ProjectConstants.DS_DRIVER_TYPE_DICT;
    List<DictItem> dictItemList = null;
    try {
      RedissonClient redissonClient = SpringUtil.getBean(RedissonClient.class);
      RBucket<List<DictItem>> bucket = redissonClient.getBucket(dsDriverKey);
      dictItemList = bucket.get();
    } catch (Exception e) {
      logger.info("从redis获取数据源失败" + e);
    }

    List<CltDictItem> dsDriverDictItemList = null;
    if (dictItemList == null || dictItemList.size() <= 0) {
      logger.debug("缓存中不存在，调用接口查询");
      PubDto pubDto = new PubDto();
      List<String> codeList = new ArrayList<>();
      codeList.add(ProjectConstants.DS_DRIVER_TYPE_DICT);
      pubDto.setResultList(codeList);
      pubDto.setTenantId(NKSecurityContext.getTenantId());
      ResultWrapper<Map<String, Object>> codeResult = platformFeign.getPubDictByCode(pubDto);
      if (codeResult != null && codeResult.isSuccess() && codeResult.getData() != null && !codeResult.getData().isEmpty()) {
        logger.debug("接口中数据库驱动数据数据：" + JSON.toJSONString(codeResult.getData()));
        Object o = codeResult.getData().get(ProjectConstants.DS_DRIVER_TYPE_DICT);
        if (o != null) {
          dsDriverDictItemList = JSONUtil.convertValue(o, new TypeReference<List<CltDictItem>>() {
          });
        }
        logger.debug("字典中数据库驱动数据数量为：" + dsDriverDictItemList.size());
        if (CollectionUtils.isNotEmpty(dsDriverDictItemList)) {
          dsDriverMap = dsDriverDictItemList.stream().collect(
                  Collectors.toMap(CltDictItem::getItemCode, CltDictItem::getItemName));
        }
      } else {
        throw new PlatformRuntimeException(ProjectAppError.CACHE_DS_DRIVER_IS_NULL);
      }
    } else {
      dsDriverMap = dictItemList.stream().collect(
              Collectors.toMap(DictItem::getCode, DictItem::getName));
    }

    if (dsDriverMap == null || !dsDriverMap.containsKey(dsType + "")) {
      logger.error("缓存字典中不存在该类型[{}]的数据库驱动信息", dsType);
      throw new PlatformRuntimeException(ProjectAppError.DS_DRIVER_IS_NULL);
    }
    return dsDriverMap.get(dsType).toString();
  }

  /**
   * 根据数据源类型和高级属性组装URL模板
   *
   * @param dsType            数据源类型
   * @param advancedConfigMap 高级属性
   * @return URL模板
   */
  private String getDsUrlTemp(String dsType, Map<String, Object> advancedConfigMap) {
    Map<String, Object> dsUrlTempMap = null;
    logger.debug("获取缓存中字典数据库URL模板数据");
    String dsUrlTempKey = ProjectConstants.DICT_PREFIX + ProjectConstants.DS_URL_TEMP_DICT;
    List<DictItem> dictItemList = null;
    try {
      RedissonClient redissonClient = SpringUtil.getBean(RedissonClient.class);
      RBucket<List<DictItem>> bucket = redissonClient.getBucket(dsUrlTempKey);
      dictItemList = bucket.get();
    } catch (Exception e) {
      logger.info("从redis获取数据源失败" + e);
    }
    List<CltDictItem> dsUrlTempDictItemList = null;
    if (CollectionUtils.isEmpty(dictItemList)) {
      logger.debug("缓存中不存在，调用接口查询");
      PubDto pubDto = new PubDto();
      List<String> codeList = new ArrayList<>();
      codeList.add(ProjectConstants.DS_URL_TEMP_DICT);
      pubDto.setResultList(codeList);
      pubDto.setTenantId(NKSecurityContext.getTenantId());
      ResultWrapper<Map<String, Object>> codeResult = platformFeign.getPubDictByCode(pubDto);
      if (codeResult != null && codeResult.isSuccess() && codeResult.getData() != null && !codeResult.getData().isEmpty()) {
        logger.debug("接口中数据库URL模板数据数量为：" + codeResult.getData());
        Object o = codeResult.getData().get(ProjectConstants.DS_URL_TEMP_DICT);
        if (o != null) {
          dsUrlTempDictItemList = JSONUtil.convertValue(o, new TypeReference<List<CltDictItem>>() {
          });
          logger.debug("字典中数据库URL模板数据数量为：" + dsUrlTempDictItemList);
          if (CollectionUtils.isNotEmpty(dsUrlTempDictItemList)) {

            dsUrlTempMap = dsUrlTempDictItemList.stream().collect(
                    Collectors.toMap(CltDictItem::getItemCode, CltDictItem::getItemName));
          }
        }
      } else {
        throw new PlatformRuntimeException(ProjectAppError.CACHE_DS_URL_TEMP_IS_NULL);
      }
    } else {
      dsUrlTempMap = dictItemList.stream().collect(
              Collectors.toMap(DictItem::getCode, DictItem::getName));
    }
    if (dsUrlTempMap == null || !dsUrlTempMap.containsKey(dsType)) {
      logger.error("缓存字典中不存在该类型[{}]的数据库URL模板信息", dsType);
      throw new PlatformRuntimeException(ProjectAppError.DS_URL_TEMP_IS_NULL);
    }
    String dsUrlTemp = dsUrlTempMap.get(dsType).toString();
    StringBuilder sb = new StringBuilder();
    if (advancedConfigMap != null && advancedConfigMap.size() > 0) {
      logger.debug("URL模板添加高级属性");
      advancedConfigMap.forEach((key, value) -> {
        if (key != null && value != null) {
          sb.append(key).append("=").append(value).append("&");
        }
      });
    }
    if (sb.length() > 0) {
      sb.deleteCharAt(sb.length() - 1);
      if (dsUrlTemp.contains("?")) {
        dsUrlTemp = dsUrlTemp + "&" + sb.toString();
      } else {
        dsUrlTemp = dsUrlTemp + "?" + sb.toString();
      }
    }
    return dsUrlTemp;
  }

}
