diff --git a/pom.xml b/pom.xml
index fa73ca3f..a886068c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -308,7 +308,7 @@
com.clickhouse
clickhouse-jdbc
- 0.6.0
+ 0.8.5
all
diff --git a/src/main/java/com/ycwl/basic/clickhouse/mapper/ClickHouseStatsMapper.java b/src/main/java/com/ycwl/basic/clickhouse/mapper/ClickHouseStatsMapper.java
deleted file mode 100644
index 8f44506f..00000000
--- a/src/main/java/com/ycwl/basic/clickhouse/mapper/ClickHouseStatsMapper.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.ycwl.basic.clickhouse.mapper;
-
-import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
-import org.apache.ibatis.annotations.Mapper;
-import org.apache.ibatis.annotations.Param;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * ClickHouse 统计数据 Mapper
- * 用于查询 t_stats 和 t_stats_record 表
- */
-@Mapper
-public interface ClickHouseStatsMapper {
-
- /**
- * 统计预览视频人数
- */
- Integer countPreviewVideoOfMember(CommonQueryReq query);
-
- /**
- * 统计扫码访问人数
- */
- Integer countScanCodeOfMember(CommonQueryReq query);
-
- /**
- * 统计推送订阅人数
- */
- Integer countPushOfMember(CommonQueryReq query);
-
- /**
- * 统计上传头像人数
- */
- Integer countUploadFaceOfMember(CommonQueryReq query);
-
- /**
- * 统计生成视频人数
- * 注意:需要关联 MySQL 中的 task 表,此处只返回 face_id 列表
- */
- List listFaceIdsWithUpload(CommonQueryReq query);
-
- /**
- * 统计总访问人数
- */
- Integer countTotalVisitorOfMember(CommonQueryReq query);
-
- /**
- * 统计预览视频条数
- */
- Integer countPreviewOfVideo(CommonQueryReq query);
-
- /**
- * 获取用户分销员 ID 列表
- */
- List getBrokerIdListForUser(@Param("memberId") Long memberId,
- @Param("startTime") Date startTime,
- @Param("endTime") Date endTime);
-
- /**
- * 获取用户最近进入类型
- */
- Long getUserRecentEnterType(@Param("memberId") Long memberId,
- @Param("endTime") Date endTime);
-
- /**
- * 获取用户项目 ID 列表
- */
- List getProjectIdListForUser(@Param("memberId") Long memberId,
- @Param("startTime") Date startTime,
- @Param("endTime") Date endTime);
-
- /**
- * 统计分销员扫码次数
- */
- Integer countBrokerScanCount(@Param("brokerId") Long brokerId);
-
- /**
- * 按日期统计分销员扫码数据(用于 BrokerRecordMapper.getDailySummaryByBrokerId)
- */
- List> getDailyScanStats(@Param("brokerId") Long brokerId,
- @Param("startTime") Date startTime,
- @Param("endTime") Date endTime);
-
- /**
- * 按小时统计扫码人数
- */
- List> scanCodeMemberChartByHour(CommonQueryReq query);
-
- /**
- * 按日期统计扫码人数
- */
- List> scanCodeMemberChartByDate(CommonQueryReq query);
-}
diff --git a/src/main/java/com/ycwl/basic/clickhouse/service/impl/ClickHouseStatsQueryServiceImpl.java b/src/main/java/com/ycwl/basic/clickhouse/service/impl/ClickHouseStatsQueryServiceImpl.java
index 99ec986e..7c5a2b48 100644
--- a/src/main/java/com/ycwl/basic/clickhouse/service/impl/ClickHouseStatsQueryServiceImpl.java
+++ b/src/main/java/com/ycwl/basic/clickhouse/service/impl/ClickHouseStatsQueryServiceImpl.java
@@ -1,15 +1,17 @@
package com.ycwl.basic.clickhouse.service.impl;
-import com.ycwl.basic.clickhouse.mapper.ClickHouseStatsMapper;
import com.ycwl.basic.clickhouse.service.StatsQueryService;
import com.ycwl.basic.mapper.TaskMapper;
import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
-import java.util.Collections;
+import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -17,102 +19,367 @@ import java.util.List;
/**
* ClickHouse 统计数据查询服务实现
* 当 clickhouse.enabled=true 时启用
+ *
+ * 注意:ClickHouse JDBC 驱动 0.6.x 对参数绑定支持有问题,
+ * 因此使用字符串格式化方式构建 SQL(参数均为内部生成的数值或日期,无 SQL 注入风险)
*/
@Slf4j
@Service
@ConditionalOnProperty(prefix = "clickhouse", name = "enabled", havingValue = "true")
public class ClickHouseStatsQueryServiceImpl implements StatsQueryService {
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+ private static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
@Autowired
- private ClickHouseStatsMapper clickHouseStatsMapper;
+ @Qualifier("clickHouseJdbcTemplate")
+ private NamedParameterJdbcTemplate namedJdbcTemplate;
+
+ private JdbcTemplate jdbcTemplate;
@Autowired
private TaskMapper taskMapper;
+ private JdbcTemplate getJdbcTemplate() {
+ if (jdbcTemplate == null) {
+ jdbcTemplate = namedJdbcTemplate.getJdbcTemplate();
+ }
+ return jdbcTemplate;
+ }
+
+ /**
+ * 格式化日期时间为 ClickHouse 可识别的字符串
+ */
+ private String formatDateTime(Date date) {
+ return date != null ? "'" + DATETIME_FORMAT.format(date) + "'" : null;
+ }
+
+ /**
+ * 格式化日期为 ClickHouse 可识别的字符串
+ */
+ private String formatDate(Date date) {
+ return date != null ? "'" + DATE_FORMAT.format(date) + "'" : null;
+ }
+
@Override
public Integer countPreviewVideoOfMember(CommonQueryReq query) {
- return clickHouseStatsMapper.countPreviewVideoOfMember(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ifNull(count(1), 0) AS count FROM ( ");
+ sql.append(" SELECT 1 FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(" ) ");
+ sql.append(" AND r.action = 'LOAD' ");
+ sql.append(" AND r.identifier = 'pages/videoSynthesis/buy' ");
+ sql.append(" AND JSONExtractString(r.params, 'share') = '' ");
+ if (query.getStartTime() != null) {
+ sql.append(" AND r.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append(" AND r.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+ sql.append(" GROUP BY s.member_id ");
+ sql.append(")");
+
+ return getJdbcTemplate().queryForObject(sql.toString(), Integer.class);
}
@Override
public Integer countScanCodeOfMember(CommonQueryReq query) {
- return clickHouseStatsMapper.countScanCodeOfMember(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ifNull(count(1), 0) AS count FROM ( ");
+ sql.append(" SELECT 1 FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(" ) ");
+ sql.append(" AND r.action = 'LAUNCH' ");
+ sql.append(" AND JSONExtractInt(r.params, 'scene') IN (1047, 1048, 1049) ");
+ if (query.getStartTime() != null) {
+ sql.append(" AND s.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append(" AND s.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+ sql.append(" GROUP BY s.member_id ");
+ sql.append(")");
+
+ return getJdbcTemplate().queryForObject(sql.toString(), Integer.class);
}
@Override
public Integer countPushOfMember(CommonQueryReq query) {
- return clickHouseStatsMapper.countPushOfMember(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ifNull(count(1), 0) AS count FROM ( ");
+ sql.append(" SELECT 1 FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(" ) ");
+ sql.append(" AND r.action = 'PERM_REQ' ");
+ sql.append(" AND r.identifier = 'NOTIFY' ");
+ if (query.getStartTime() != null) {
+ sql.append(" AND r.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append(" AND r.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+ sql.append(" GROUP BY s.member_id ");
+ sql.append(")");
+
+ return getJdbcTemplate().queryForObject(sql.toString(), Integer.class);
}
@Override
public Integer countUploadFaceOfMember(CommonQueryReq query) {
- return clickHouseStatsMapper.countUploadFaceOfMember(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ifNull(count(1), 0) AS count FROM ( ");
+ sql.append(" SELECT 1 FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(" ) ");
+ sql.append(" AND r.action = 'FACE_UPLOAD' ");
+ if (query.getStartTime() != null) {
+ sql.append(" AND s.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append(" AND s.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+ sql.append(" GROUP BY s.member_id ");
+ sql.append(")");
+
+ return getJdbcTemplate().queryForObject(sql.toString(), Integer.class);
+ }
+
+ private List listFaceIdsWithUpload(CommonQueryReq query) {
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT DISTINCT r.identifier FROM t_stats_record r ");
+ sql.append("INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append("WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(") ");
+ sql.append("AND r.action = 'FACE_UPLOAD' ");
+ if (query.getStartTime() != null) {
+ sql.append("AND s.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append("AND s.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+
+ return getJdbcTemplate().queryForList(sql.toString(), String.class);
}
@Override
public Integer countCompleteVideoOfMember(CommonQueryReq query) {
- // 从 ClickHouse 获取 face_id 列表,然后在 MySQL 中查询完成的任务
- List faceIds = clickHouseStatsMapper.listFaceIdsWithUpload(query);
+ List faceIds = listFaceIdsWithUpload(query);
if (faceIds == null || faceIds.isEmpty()) {
return 0;
}
- // 在 MySQL 中统计已完成任务的用户数
return taskMapper.countCompletedTaskMembersByFaceIds(faceIds);
}
@Override
public Integer countCompleteOfVideo(CommonQueryReq query) {
- // 从 ClickHouse 获取 face_id 列表,然后在 MySQL 中查询完成的任务数
- List faceIds = clickHouseStatsMapper.listFaceIdsWithUpload(query);
+ List faceIds = listFaceIdsWithUpload(query);
if (faceIds == null || faceIds.isEmpty()) {
return 0;
}
- // 在 MySQL 中统计已完成的任务数
return taskMapper.countCompletedTasksByFaceIds(faceIds);
}
@Override
public Integer countTotalVisitorOfMember(CommonQueryReq query) {
- return clickHouseStatsMapper.countTotalVisitorOfMember(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ifNull(count(1), 0) AS count FROM ( ");
+ sql.append(" SELECT 1 FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(" ) ");
+ sql.append(" AND r.action = 'LAUNCH' ");
+ if (query.getStartTime() != null) {
+ sql.append(" AND s.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append(" AND s.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+ sql.append(" GROUP BY s.member_id ");
+ sql.append(")");
+
+ return getJdbcTemplate().queryForObject(sql.toString(), Integer.class);
}
@Override
public Integer countPreviewOfVideo(CommonQueryReq query) {
- return clickHouseStatsMapper.countPreviewOfVideo(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ifNull(count(1), 0) AS count FROM ( ");
+ sql.append(" SELECT 1 FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' AND identifier = '").append(query.getScenicId()).append("' ");
+ sql.append(" ) ");
+ sql.append(" AND r.action = 'LOAD' ");
+ sql.append(" AND r.identifier = 'pages/videoSynthesis/buy' ");
+ sql.append(" AND JSONExtractString(r.params, 'id') != '' ");
+ sql.append(" AND JSONExtractString(r.params, 'share') = '' ");
+ if (query.getStartTime() != null) {
+ sql.append(" AND s.create_time >= ").append(formatDateTime(query.getStartTime())).append(" ");
+ }
+ if (query.getEndTime() != null) {
+ sql.append(" AND s.create_time <= ").append(formatDateTime(query.getEndTime())).append(" ");
+ }
+ sql.append(" GROUP BY JSONExtractString(r.params, 'id') ");
+ sql.append(")");
+
+ return getJdbcTemplate().queryForObject(sql.toString(), Integer.class);
}
@Override
public List getBrokerIdListForUser(Long memberId, Date startTime, Date endTime) {
- return clickHouseStatsMapper.getBrokerIdListForUser(memberId, startTime, endTime);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT toInt64(sub.identifier) AS identifier FROM ( ");
+ sql.append(" SELECT identifier, max(r.create_time) AS createTime ");
+ sql.append(" FROM t_stats_record r ");
+ sql.append(" INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append(" WHERE r.action = 'CODE_SCAN' ");
+ sql.append(" AND s.member_id = ").append(memberId).append(" ");
+ if (startTime != null) {
+ sql.append(" AND r.create_time >= ").append(formatDateTime(startTime)).append(" ");
+ }
+ if (endTime != null) {
+ sql.append(" AND r.create_time <= ").append(formatDateTime(endTime)).append(" ");
+ }
+ sql.append(" GROUP BY identifier ");
+ sql.append(") sub ORDER BY sub.createTime DESC");
+
+ return getJdbcTemplate().queryForList(sql.toString(), Long.class);
}
@Override
public Long getUserRecentEnterType(Long memberId, Date endTime) {
- return clickHouseStatsMapper.getUserRecentEnterType(memberId, endTime);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT JSONExtractInt(r.params, 'scene') AS scene ");
+ sql.append("FROM t_stats_record r ");
+ sql.append("INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append("WHERE r.action = 'LAUNCH' ");
+ sql.append(" AND s.member_id = ").append(memberId).append(" ");
+ if (endTime != null) {
+ sql.append(" AND r.create_time <= ").append(formatDateTime(endTime)).append(" ");
+ }
+ sql.append("ORDER BY r.create_time DESC LIMIT 1");
+
+ try {
+ return getJdbcTemplate().queryForObject(sql.toString(), Long.class);
+ } catch (Exception e) {
+ return null;
+ }
}
@Override
public List getProjectIdListForUser(Long memberId, Date startTime, Date endTime) {
- return clickHouseStatsMapper.getProjectIdListForUser(memberId, startTime, endTime);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT toInt64(r.identifier) AS identifier ");
+ sql.append("FROM t_stats_record r ");
+ sql.append("INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append("WHERE s.member_id = ").append(memberId).append(" ");
+ sql.append(" AND r.action = 'ENTER_PROJECT' ");
+ sql.append(" AND r.create_time < ").append(formatDateTime(endTime)).append(" ");
+ sql.append(" AND r.create_time > ").append(formatDateTime(startTime)).append(" ");
+ sql.append("ORDER BY r.create_time DESC LIMIT 1");
+
+ return getJdbcTemplate().queryForList(sql.toString(), Long.class);
}
@Override
public Integer countBrokerScanCount(Long brokerId) {
- return clickHouseStatsMapper.countBrokerScanCount(brokerId);
+ String sql = "SELECT count(1) AS count FROM t_stats_record " +
+ "WHERE action = 'CODE_SCAN' AND identifier = '" + brokerId + "'";
+
+ return getJdbcTemplate().queryForObject(sql, Integer.class);
}
@Override
public List> getDailyScanStats(Long brokerId, Date startTime, Date endTime) {
- return clickHouseStatsMapper.getDailyScanStats(brokerId, startTime, endTime);
+ String startDateStr = DATE_FORMAT.format(startTime);
+ String endDateStr = DATE_FORMAT.format(endTime);
+
+ String sql = "SELECT toDate(create_time) AS date, count(DISTINCT id) AS scanCount " +
+ "FROM t_stats_record " +
+ "WHERE action = 'CODE_SCAN' " +
+ " AND identifier = '" + brokerId + "' " +
+ " AND toDate(create_time) BETWEEN '" + startDateStr + "' AND '" + endDateStr + "' " +
+ "GROUP BY toDate(create_time)";
+
+ return getJdbcTemplate().query(sql, (rs, rowNum) -> {
+ HashMap map = new HashMap<>();
+ map.put("date", rs.getDate("date"));
+ map.put("scanCount", rs.getLong("scanCount"));
+ return map;
+ });
}
@Override
public List> scanCodeMemberChartByHour(CommonQueryReq query) {
- return clickHouseStatsMapper.scanCodeMemberChartByHour(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT formatDateTime(s.create_time, '%m-%d %H') AS t, ");
+ sql.append(" count(DISTINCT s.member_id) AS count ");
+ sql.append("FROM t_stats_record r ");
+ sql.append("INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append("WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' ");
+ if (query.getScenicId() != null) {
+ sql.append(" AND identifier = '").append(query.getScenicId()).append("' ");
+ }
+ sql.append(") ");
+ sql.append("AND r.action = 'LAUNCH' ");
+ sql.append("AND JSONExtractInt(r.params, 'scene') IN (1047, 1048, 1049) ");
+ sql.append("AND s.create_time BETWEEN ").append(formatDateTime(query.getStartTime()));
+ sql.append(" AND ").append(formatDateTime(query.getEndTime())).append(" ");
+ sql.append("GROUP BY formatDateTime(s.create_time, '%m-%d %H')");
+
+ return getJdbcTemplate().query(sql.toString(), (rs, rowNum) -> {
+ HashMap map = new HashMap<>();
+ map.put("t", rs.getString("t"));
+ map.put("count", rs.getString("count"));
+ return map;
+ });
}
@Override
public List> scanCodeMemberChartByDate(CommonQueryReq query) {
- return clickHouseStatsMapper.scanCodeMemberChartByDate(query);
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT formatDateTime(s.create_time, '%m-%d') AS t, ");
+ sql.append(" count(DISTINCT s.member_id) AS count ");
+ sql.append("FROM t_stats_record r ");
+ sql.append("INNER JOIN t_stats s ON r.trace_id = s.trace_id ");
+ sql.append("WHERE r.trace_id IN ( ");
+ sql.append(" SELECT trace_id FROM t_stats_record ");
+ sql.append(" WHERE action = 'ENTER_SCENIC' ");
+ if (query.getScenicId() != null) {
+ sql.append(" AND identifier = '").append(query.getScenicId()).append("' ");
+ }
+ sql.append(") ");
+ sql.append("AND r.action = 'LAUNCH' ");
+ sql.append("AND JSONExtractInt(r.params, 'scene') IN (1047, 1048, 1049) ");
+ sql.append("AND s.create_time BETWEEN ").append(formatDateTime(query.getStartTime()));
+ sql.append(" AND ").append(formatDateTime(query.getEndTime())).append(" ");
+ sql.append("GROUP BY formatDateTime(s.create_time, '%m-%d')");
+
+ return getJdbcTemplate().query(sql.toString(), (rs, rowNum) -> {
+ HashMap map = new HashMap<>();
+ map.put("t", rs.getString("t"));
+ map.put("count", rs.getString("count"));
+ return map;
+ });
}
}
diff --git a/src/main/java/com/ycwl/basic/config/ClickHouseDataSourceConfig.java b/src/main/java/com/ycwl/basic/config/ClickHouseDataSourceConfig.java
index f6084b21..6eba3b9b 100644
--- a/src/main/java/com/ycwl/basic/config/ClickHouseDataSourceConfig.java
+++ b/src/main/java/com/ycwl/basic/config/ClickHouseDataSourceConfig.java
@@ -1,56 +1,37 @@
package com.ycwl.basic.config;
import com.zaxxer.hikari.HikariDataSource;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.mybatis.spring.SqlSessionFactoryBean;
-import org.mybatis.spring.SqlSessionTemplate;
-import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import javax.sql.DataSource;
/**
* ClickHouse 数据源配置
* 用于 t_stats 和 t_stats_record 表的查询
+ *
+ * 使用 NamedParameterJdbcTemplate 而非 MyBatis,以避免干扰 MyBatis-Plus 的自动配置
*/
@Configuration
@ConditionalOnProperty(prefix = "clickhouse", name = "enabled", havingValue = "true")
-@MapperScan(
- basePackages = "com.ycwl.basic.clickhouse.mapper",
- sqlSessionFactoryRef = "clickHouseSqlSessionFactory"
-)
public class ClickHouseDataSourceConfig {
+ /**
+ * ClickHouse 数据源(非 Primary)
+ */
@Bean(name = "clickHouseDataSource")
@ConfigurationProperties(prefix = "clickhouse.datasource")
public DataSource clickHouseDataSource() {
return new HikariDataSource();
}
- @Bean(name = "clickHouseSqlSessionFactory")
- public SqlSessionFactory clickHouseSqlSessionFactory(
- @Qualifier("clickHouseDataSource") DataSource dataSource) throws Exception {
- SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
- factoryBean.setDataSource(dataSource);
- factoryBean.setMapperLocations(
- new PathMatchingResourcePatternResolver()
- .getResources("classpath:mapper/clickhouse/*.xml")
- );
- // 配置 MyBatis 设置
- org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
- configuration.setMapUnderscoreToCamelCase(true);
- factoryBean.setConfiguration(configuration);
- return factoryBean.getObject();
- }
-
- @Bean(name = "clickHouseSqlSessionTemplate")
- public SqlSessionTemplate clickHouseSqlSessionTemplate(
- @Qualifier("clickHouseSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
- return new SqlSessionTemplate(sqlSessionFactory);
+ @Bean(name = "clickHouseJdbcTemplate")
+ public NamedParameterJdbcTemplate clickHouseJdbcTemplate(
+ @Qualifier("clickHouseDataSource") DataSource dataSource) {
+ return new NamedParameterJdbcTemplate(dataSource);
}
}
diff --git a/src/main/java/com/ycwl/basic/config/MySqlPrimaryDataSourceConfig.java b/src/main/java/com/ycwl/basic/config/MySqlPrimaryDataSourceConfig.java
new file mode 100644
index 00000000..49d4f56f
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/config/MySqlPrimaryDataSourceConfig.java
@@ -0,0 +1,41 @@
+package com.ycwl.basic.config;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.sql.DataSource;
+
+/**
+ * MySQL 主数据源配置
+ *
+ * 当 ClickHouse 启用时,需要显式配置 MySQL 数据源并标记为 @Primary,
+ * 以确保 MyBatis-Plus 和其他组件使用正确的数据源
+ */
+@Configuration
+@ConditionalOnProperty(prefix = "clickhouse", name = "enabled", havingValue = "true")
+public class MySqlPrimaryDataSourceConfig {
+
+ /**
+ * MySQL 数据源属性
+ */
+ @Primary
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DataSourceProperties mysqlDataSourceProperties() {
+ return new DataSourceProperties();
+ }
+
+ /**
+ * MySQL 主数据源
+ * 使用 @Primary 确保这是默认数据源
+ */
+ @Primary
+ @Bean(name = "dataSource")
+ public DataSource mysqlDataSource(DataSourceProperties properties) {
+ return properties.initializeDataSourceBuilder().build();
+ }
+}
diff --git a/src/main/resources/mapper/clickhouse/ClickHouseStatsMapper.xml b/src/main/resources/mapper/clickhouse/ClickHouseStatsMapper.xml
deleted file mode 100644
index 43edd275..00000000
--- a/src/main/resources/mapper/clickhouse/ClickHouseStatsMapper.xml
+++ /dev/null
@@ -1,269 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-