You've already forked FrameTour-BE
feat(database): 迁移统计数据查询到ClickHouse
- 添加ClickHouse数据源配置和相关依赖 - 实现ClickHouse统计查询服务和MySQL兜底方案 - 新增扫码统计、订单统计等数据查询接口 - 重构分销员数据统计逻辑,整合MySQL和ClickHouse数据源 - 更新应用配置文件以支持ClickHouse启用开关 - 修改分布式任务统计以支持跨库查询场景
This commit is contained in:
8
pom.xml
8
pom.xml
@@ -303,6 +303,14 @@
|
|||||||
<artifactId>poi-ooxml</artifactId>
|
<artifactId>poi-ooxml</artifactId>
|
||||||
<version>5.4.0</version>
|
<version>5.4.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- ClickHouse JDBC Driver -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.clickhouse</groupId>
|
||||||
|
<artifactId>clickhouse-jdbc</artifactId>
|
||||||
|
<version>0.6.0</version>
|
||||||
|
<classifier>all</classifier>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
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<String> listFaceIdsWithUpload(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计总访问人数
|
||||||
|
*/
|
||||||
|
Integer countTotalVisitorOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计预览视频条数
|
||||||
|
*/
|
||||||
|
Integer countPreviewOfVideo(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户分销员 ID 列表
|
||||||
|
*/
|
||||||
|
List<Long> 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<Long> getProjectIdListForUser(@Param("memberId") Long memberId,
|
||||||
|
@Param("startTime") Date startTime,
|
||||||
|
@Param("endTime") Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计分销员扫码次数
|
||||||
|
*/
|
||||||
|
Integer countBrokerScanCount(@Param("brokerId") Long brokerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期统计分销员扫码数据(用于 BrokerRecordMapper.getDailySummaryByBrokerId)
|
||||||
|
*/
|
||||||
|
List<HashMap<String, Object>> getDailyScanStats(@Param("brokerId") Long brokerId,
|
||||||
|
@Param("startTime") Date startTime,
|
||||||
|
@Param("endTime") Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按小时统计扫码人数
|
||||||
|
*/
|
||||||
|
List<HashMap<String, String>> scanCodeMemberChartByHour(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期统计扫码人数
|
||||||
|
*/
|
||||||
|
List<HashMap<String, String>> scanCodeMemberChartByDate(CommonQueryReq query);
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package com.ycwl.basic.clickhouse.service;
|
||||||
|
|
||||||
|
import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计数据查询服务接口
|
||||||
|
* 用于抽象 t_stats 和 t_stats_record 表的查询
|
||||||
|
* 支持 MySQL 和 ClickHouse 两种实现
|
||||||
|
*/
|
||||||
|
public interface StatsQueryService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计预览视频人数
|
||||||
|
*/
|
||||||
|
Integer countPreviewVideoOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计扫码访问人数
|
||||||
|
*/
|
||||||
|
Integer countScanCodeOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计推送订阅人数
|
||||||
|
*/
|
||||||
|
Integer countPushOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计上传头像人数
|
||||||
|
*/
|
||||||
|
Integer countUploadFaceOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计生成视频人数
|
||||||
|
*/
|
||||||
|
Integer countCompleteVideoOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计生成视频条数
|
||||||
|
*/
|
||||||
|
Integer countCompleteOfVideo(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计总访问人数
|
||||||
|
*/
|
||||||
|
Integer countTotalVisitorOfMember(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计预览视频条数
|
||||||
|
*/
|
||||||
|
Integer countPreviewOfVideo(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户分销员 ID 列表
|
||||||
|
*/
|
||||||
|
List<Long> getBrokerIdListForUser(Long memberId, Date startTime, Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户最近进入类型
|
||||||
|
*/
|
||||||
|
Long getUserRecentEnterType(Long memberId, Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户项目 ID 列表
|
||||||
|
*/
|
||||||
|
List<Long> getProjectIdListForUser(Long memberId, Date startTime, Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计分销员扫码次数
|
||||||
|
*/
|
||||||
|
Integer countBrokerScanCount(Long brokerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期统计分销员扫码数据
|
||||||
|
*/
|
||||||
|
List<HashMap<String, Object>> getDailyScanStats(Long brokerId, Date startTime, Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按小时统计扫码人数
|
||||||
|
*/
|
||||||
|
List<HashMap<String, String>> scanCodeMemberChartByHour(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期统计扫码人数
|
||||||
|
*/
|
||||||
|
List<HashMap<String, String>> scanCodeMemberChartByDate(CommonQueryReq query);
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
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.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClickHouse 统计数据查询服务实现
|
||||||
|
* 当 clickhouse.enabled=true 时启用
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@ConditionalOnProperty(prefix = "clickhouse", name = "enabled", havingValue = "true")
|
||||||
|
public class ClickHouseStatsQueryServiceImpl implements StatsQueryService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ClickHouseStatsMapper clickHouseStatsMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TaskMapper taskMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countPreviewVideoOfMember(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.countPreviewVideoOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countScanCodeOfMember(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.countScanCodeOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countPushOfMember(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.countPushOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countUploadFaceOfMember(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.countUploadFaceOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countCompleteVideoOfMember(CommonQueryReq query) {
|
||||||
|
// 从 ClickHouse 获取 face_id 列表,然后在 MySQL 中查询完成的任务
|
||||||
|
List<String> faceIds = clickHouseStatsMapper.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<String> faceIds = clickHouseStatsMapper.listFaceIdsWithUpload(query);
|
||||||
|
if (faceIds == null || faceIds.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// 在 MySQL 中统计已完成的任务数
|
||||||
|
return taskMapper.countCompletedTasksByFaceIds(faceIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countTotalVisitorOfMember(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.countTotalVisitorOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countPreviewOfVideo(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.countPreviewOfVideo(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getBrokerIdListForUser(Long memberId, Date startTime, Date endTime) {
|
||||||
|
return clickHouseStatsMapper.getBrokerIdListForUser(memberId, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getUserRecentEnterType(Long memberId, Date endTime) {
|
||||||
|
return clickHouseStatsMapper.getUserRecentEnterType(memberId, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getProjectIdListForUser(Long memberId, Date startTime, Date endTime) {
|
||||||
|
return clickHouseStatsMapper.getProjectIdListForUser(memberId, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countBrokerScanCount(Long brokerId) {
|
||||||
|
return clickHouseStatsMapper.countBrokerScanCount(brokerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HashMap<String, Object>> getDailyScanStats(Long brokerId, Date startTime, Date endTime) {
|
||||||
|
return clickHouseStatsMapper.getDailyScanStats(brokerId, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HashMap<String, String>> scanCodeMemberChartByHour(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.scanCodeMemberChartByHour(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HashMap<String, String>> scanCodeMemberChartByDate(CommonQueryReq query) {
|
||||||
|
return clickHouseStatsMapper.scanCodeMemberChartByDate(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package com.ycwl.basic.clickhouse.service.impl;
|
||||||
|
|
||||||
|
import com.ycwl.basic.clickhouse.service.StatsQueryService;
|
||||||
|
import com.ycwl.basic.mapper.StatisticsMapper;
|
||||||
|
import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MySQL 统计数据查询服务实现
|
||||||
|
* 当 clickhouse.enabled 未启用时使用此实现(兜底)
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@ConditionalOnProperty(prefix = "clickhouse", name = "enabled", havingValue = "false", matchIfMissing = true)
|
||||||
|
public class MySqlStatsQueryServiceImpl implements StatsQueryService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StatisticsMapper statisticsMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countPreviewVideoOfMember(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countPreviewVideoOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countScanCodeOfMember(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countScanCodeOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countPushOfMember(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countPushOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countUploadFaceOfMember(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countUploadFaceOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countCompleteVideoOfMember(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countCompleteVideoOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countCompleteOfVideo(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countCompleteOfVideo(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countTotalVisitorOfMember(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countTotalVisitorOfMember(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countPreviewOfVideo(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.countPreviewOfVideo(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getBrokerIdListForUser(Long memberId, Date startTime, Date endTime) {
|
||||||
|
return statisticsMapper.getBrokerIdListForUser(memberId, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getUserRecentEnterType(Long memberId, Date endTime) {
|
||||||
|
return statisticsMapper.getUserRecentEnterType(memberId, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getProjectIdListForUser(Long memberId, Date startTime, Date endTime) {
|
||||||
|
return statisticsMapper.getProjectIdListForUser(memberId, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer countBrokerScanCount(Long brokerId) {
|
||||||
|
return statisticsMapper.countBrokerScanCount(brokerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HashMap<String, Object>> getDailyScanStats(Long brokerId, Date startTime, Date endTime) {
|
||||||
|
return statisticsMapper.getDailyScanStats(brokerId, startTime, endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HashMap<String, String>> scanCodeMemberChartByHour(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.scanCodeMemberChartByHour(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<HashMap<String, String>> scanCodeMemberChartByDate(CommonQueryReq query) {
|
||||||
|
return statisticsMapper.scanCodeMemberChartByDate(query);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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 javax.sql.DataSource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ClickHouse 数据源配置
|
||||||
|
* 用于 t_stats 和 t_stats_record 表的查询
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnProperty(prefix = "clickhouse", name = "enabled", havingValue = "true")
|
||||||
|
@MapperScan(
|
||||||
|
basePackages = "com.ycwl.basic.clickhouse.mapper",
|
||||||
|
sqlSessionFactoryRef = "clickHouseSqlSessionFactory"
|
||||||
|
)
|
||||||
|
public class ClickHouseDataSourceConfig {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,10 @@ import com.ycwl.basic.model.pc.broker.resp.BrokerRecordRespVO;
|
|||||||
import com.ycwl.basic.model.pc.broker.resp.DailySummaryRespVO;
|
import com.ycwl.basic.model.pc.broker.resp.DailySummaryRespVO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,4 +31,11 @@ public interface BrokerRecordMapper {
|
|||||||
int update(BrokerRecord brokerRecord);
|
int update(BrokerRecord brokerRecord);
|
||||||
|
|
||||||
List<DailySummaryRespVO> getDailySummaryByBrokerId(Long brokerId, Date startTime, Date endTime);
|
List<DailySummaryRespVO> getDailySummaryByBrokerId(Long brokerId, Date startTime, Date endTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期统计分销员订单数据(不含扫码统计,已迁移到 ClickHouse)
|
||||||
|
*/
|
||||||
|
List<HashMap<String, Object>> getDailyOrderStats(@Param("brokerId") Long brokerId,
|
||||||
|
@Param("startTime") Date startTime,
|
||||||
|
@Param("endTime") Date endTime);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import com.ycwl.basic.model.mobile.statistic.req.CommonQueryReq;
|
|||||||
import com.ycwl.basic.model.mobile.statistic.req.StatisticsRecordAddReq;
|
import com.ycwl.basic.model.mobile.statistic.req.StatisticsRecordAddReq;
|
||||||
import com.ycwl.basic.model.mobile.statistic.resp.AppStatisticsFunnelVO;
|
import com.ycwl.basic.model.mobile.statistic.resp.AppStatisticsFunnelVO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -108,6 +109,18 @@ public interface StatisticsMapper {
|
|||||||
|
|
||||||
List<HashMap<String, String>> scanCodeMemberChartByDate(CommonQueryReq query);
|
List<HashMap<String, String>> scanCodeMemberChartByDate(CommonQueryReq query);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计分销员扫码次数
|
||||||
|
*/
|
||||||
|
Integer countBrokerScanCount(Long brokerId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按日期统计分销员扫码数据
|
||||||
|
*/
|
||||||
|
List<HashMap<String, Object>> getDailyScanStats(@Param("brokerId") Long brokerId,
|
||||||
|
@Param("startTime") Date startTime,
|
||||||
|
@Param("endTime") Date endTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统计订单数量和金额(包含推送订单和现场订单)
|
* 统计订单数量和金额(包含推送订单和现场订单)
|
||||||
* @param query
|
* @param query
|
||||||
|
|||||||
@@ -59,4 +59,16 @@ public interface TaskMapper {
|
|||||||
List<TaskEntity> selectAllFailed();
|
List<TaskEntity> selectAllFailed();
|
||||||
|
|
||||||
TaskEntity listLastFaceTemplateTask(Long faceId, Long templateId);
|
TaskEntity listLastFaceTemplateTask(Long faceId, Long templateId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 face_id 列表统计已完成任务的用户数
|
||||||
|
* 用于 ClickHouse 迁移后的跨库统计
|
||||||
|
*/
|
||||||
|
Integer countCompletedTaskMembersByFaceIds(@Param("faceIds") List<String> faceIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 face_id 列表统计已完成任务数
|
||||||
|
* 用于 ClickHouse 迁移后的跨库统计
|
||||||
|
*/
|
||||||
|
Integer countCompletedTasksByFaceIds(@Param("faceIds") List<String> faceIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.ycwl.basic.service.mobile.impl;
|
|||||||
import cn.hutool.core.date.DateField;
|
import cn.hutool.core.date.DateField;
|
||||||
import cn.hutool.core.date.DateUnit;
|
import cn.hutool.core.date.DateUnit;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import com.ycwl.basic.clickhouse.service.StatsQueryService;
|
||||||
import com.ycwl.basic.utils.JacksonUtil;
|
import com.ycwl.basic.utils.JacksonUtil;
|
||||||
import com.ycwl.basic.enums.StatisticEnum;
|
import com.ycwl.basic.enums.StatisticEnum;
|
||||||
import com.ycwl.basic.mapper.StatisticsMapper;
|
import com.ycwl.basic.mapper.StatisticsMapper;
|
||||||
@@ -41,6 +42,9 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private StatisticsMapper statisticsMapper;
|
private StatisticsMapper statisticsMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StatsQueryService statsQueryService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付订单金额、预览_支付转化率、扫码_付费用户转化率
|
* 支付订单金额、预览_支付转化率、扫码_付费用户转化率
|
||||||
@@ -210,19 +214,19 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
|
|||||||
// Integer cameraShotOfMemberNum=statisticsMapper.countCameraShotOfMember(query);
|
// Integer cameraShotOfMemberNum=statisticsMapper.countCameraShotOfMember(query);
|
||||||
//扫码访问人数
|
//扫码访问人数
|
||||||
// 扫小程序码或景区码进入访问的用户数,包括授权用户(使用OpenID进行精准统计)和未授权用户(使用 UUID统计访问)。但当用户授权时,获取OpenID并与UUID关联,删除本地UUID,避免重复记录。
|
// 扫小程序码或景区码进入访问的用户数,包括授权用户(使用OpenID进行精准统计)和未授权用户(使用 UUID统计访问)。但当用户授权时,获取OpenID并与UUID关联,删除本地UUID,避免重复记录。
|
||||||
Integer scanCodeVisitorOfMemberNum=statisticsMapper.countScanCodeOfMember(query);
|
Integer scanCodeVisitorOfMemberNum=statsQueryService.countScanCodeOfMember(query);
|
||||||
//上传头像(人脸)人数
|
//上传头像(人脸)人数
|
||||||
// 上传了人脸的用户数(包括本地临时ID和获取到OpenID的,同一设备微信获取到OpenID要覆盖掉之前生成的临时ID),上传多张人脸都只算一个人。
|
// 上传了人脸的用户数(包括本地临时ID和获取到OpenID的,同一设备微信获取到OpenID要覆盖掉之前生成的临时ID),上传多张人脸都只算一个人。
|
||||||
Integer uploadFaceOfMemberNum=statisticsMapper.countUploadFaceOfMember(query);
|
Integer uploadFaceOfMemberNum=statsQueryService.countUploadFaceOfMember(query);
|
||||||
//推送订阅人数
|
//推送订阅人数
|
||||||
// 只要点了允许通知,哪怕只勾选1条订阅都算
|
// 只要点了允许通知,哪怕只勾选1条订阅都算
|
||||||
Integer pushOfMemberNum =statisticsMapper.countPushOfMember(query);
|
Integer pushOfMemberNum =statsQueryService.countPushOfMember(query);
|
||||||
//生成视频人数
|
//生成视频人数
|
||||||
// 生成过Vlog视频的用户ID数,要注意屏蔽掉以前没有片段也能生成的情况
|
// 生成过Vlog视频的用户ID数,要注意屏蔽掉以前没有片段也能生成的情况
|
||||||
Integer completeVideoOfMemberNum =statisticsMapper.countCompleteVideoOfMember(query);
|
Integer completeVideoOfMemberNum =statsQueryService.countCompleteVideoOfMember(query);
|
||||||
//预览视频人数
|
//预览视频人数
|
||||||
// 购买前播放了5秒的视频条数。
|
// 购买前播放了5秒的视频条数。
|
||||||
Integer previewVideoOfMemberNum =statisticsMapper.countPreviewVideoOfMember(query);
|
Integer previewVideoOfMemberNum =statsQueryService.countPreviewVideoOfMember(query);
|
||||||
if (previewVideoOfMemberNum==null){
|
if (previewVideoOfMemberNum==null){
|
||||||
previewVideoOfMemberNum=0;
|
previewVideoOfMemberNum=0;
|
||||||
}
|
}
|
||||||
@@ -233,13 +237,13 @@ public class AppStatisticsServiceImpl implements AppStatisticsService {
|
|||||||
Integer payOfMemberNum =statisticsMapper.countPayOfMember(query);
|
Integer payOfMemberNum =statisticsMapper.countPayOfMember(query);
|
||||||
//总访问人数
|
//总访问人数
|
||||||
// 通过任何途径访问到小程序的总人数,包括授权用户和未授权用户。
|
// 通过任何途径访问到小程序的总人数,包括授权用户和未授权用户。
|
||||||
Integer totalVisitorOfMemberNum =statisticsMapper.countTotalVisitorOfMember(query);
|
Integer totalVisitorOfMemberNum =statsQueryService.countTotalVisitorOfMember(query);
|
||||||
// Integer totalVisitorOfMemberNum =scanCodeVisitorOfMemberNum;
|
// Integer totalVisitorOfMemberNum =scanCodeVisitorOfMemberNum;
|
||||||
//生成视频条数
|
//生成视频条数
|
||||||
// 仅指代生成的Vlog条数,不包含录像原片。
|
// 仅指代生成的Vlog条数,不包含录像原片。
|
||||||
Integer completeOfVideoNum =statisticsMapper.countCompleteOfVideo(query);
|
Integer completeOfVideoNum =statsQueryService.countCompleteOfVideo(query);
|
||||||
//预览视频条数
|
//预览视频条数
|
||||||
Integer previewOfVideoNum =statisticsMapper.countPreviewOfVideo(query);
|
Integer previewOfVideoNum =statsQueryService.countPreviewOfVideo(query);
|
||||||
//支付订单数
|
//支付订单数
|
||||||
Integer payOfOrderNum =statisticsMapper.countPayOfOrder(query);
|
Integer payOfOrderNum =statisticsMapper.countPayOfOrder(query);
|
||||||
//支付订单金额
|
//支付订单金额
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.ycwl.basic.service.pc.impl;
|
package com.ycwl.basic.service.pc.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.ycwl.basic.clickhouse.service.StatsQueryService;
|
||||||
import com.ycwl.basic.mapper.BrokerRecordMapper;
|
import com.ycwl.basic.mapper.BrokerRecordMapper;
|
||||||
import com.ycwl.basic.model.pc.broker.entity.BrokerRecord;
|
import com.ycwl.basic.model.pc.broker.entity.BrokerRecord;
|
||||||
import com.ycwl.basic.model.pc.broker.req.BrokerRecordReqQuery;
|
import com.ycwl.basic.model.pc.broker.req.BrokerRecordReqQuery;
|
||||||
@@ -11,8 +13,9 @@ import com.ycwl.basic.service.pc.BrokerRecordService;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author:longbinbin
|
* @Author:longbinbin
|
||||||
@@ -22,6 +25,8 @@ import java.util.List;
|
|||||||
public class BrokerRecordServiceImpl implements BrokerRecordService {
|
public class BrokerRecordServiceImpl implements BrokerRecordService {
|
||||||
@Autowired
|
@Autowired
|
||||||
private BrokerRecordMapper brokerRecordMapper;
|
private BrokerRecordMapper brokerRecordMapper;
|
||||||
|
@Autowired
|
||||||
|
private StatsQueryService statsQueryService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageInfo<BrokerRecordRespVO> pageQuery(BrokerRecordReqQuery brokerRecordReqQuery) {
|
public PageInfo<BrokerRecordRespVO> pageQuery(BrokerRecordReqQuery brokerRecordReqQuery) {
|
||||||
@@ -58,7 +63,52 @@ public class BrokerRecordServiceImpl implements BrokerRecordService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DailySummaryRespVO> getDailySummaryByBrokerId(Long brokerId, Date startTime, Date endTime) {
|
public List<DailySummaryRespVO> getDailySummaryByBrokerId(Long brokerId, Date startTime, Date endTime) {
|
||||||
return brokerRecordMapper.getDailySummaryByBrokerId(brokerId, startTime, endTime);
|
// 从 MySQL 获取订单数据
|
||||||
|
List<HashMap<String, Object>> orderStats = brokerRecordMapper.getDailyOrderStats(brokerId, startTime, endTime);
|
||||||
|
|
||||||
|
// 从 ClickHouse/MySQL 获取扫码数据
|
||||||
|
List<HashMap<String, Object>> scanStats = statsQueryService.getDailyScanStats(brokerId, startTime, endTime);
|
||||||
|
|
||||||
|
// 将扫码数据转换为 Map 便于查找
|
||||||
|
Map<String, Long> scanCountByDate = new HashMap<>();
|
||||||
|
if (scanStats != null) {
|
||||||
|
for (HashMap<String, Object> stat : scanStats) {
|
||||||
|
Object dateObj = stat.get("date");
|
||||||
|
String dateKey = dateObj != null ? DateUtil.formatDate((Date) dateObj) : null;
|
||||||
|
Object scanCountObj = stat.get("scanCount");
|
||||||
|
Long scanCount = scanCountObj != null ? ((Number) scanCountObj).longValue() : 0L;
|
||||||
|
if (dateKey != null) {
|
||||||
|
scanCountByDate.put(dateKey, scanCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并数据
|
||||||
|
List<DailySummaryRespVO> result = new ArrayList<>();
|
||||||
|
for (HashMap<String, Object> orderStat : orderStats) {
|
||||||
|
DailySummaryRespVO vo = new DailySummaryRespVO();
|
||||||
|
|
||||||
|
Object dateObj = orderStat.get("date");
|
||||||
|
if (dateObj instanceof Date) {
|
||||||
|
vo.setDate((Date) dateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
String dateKey = dateObj != null ? DateUtil.formatDate((Date) dateObj) : null;
|
||||||
|
vo.setScanCount(scanCountByDate.getOrDefault(dateKey, 0L));
|
||||||
|
|
||||||
|
Object orderCountObj = orderStat.get("orderCount");
|
||||||
|
vo.setOrderCount(orderCountObj != null ? ((Number) orderCountObj).longValue() : 0L);
|
||||||
|
|
||||||
|
Object totalOrderPriceObj = orderStat.get("totalOrderPrice");
|
||||||
|
vo.setTotalOrderPrice(totalOrderPriceObj != null ? new BigDecimal(totalOrderPriceObj.toString()) : BigDecimal.ZERO);
|
||||||
|
|
||||||
|
Object totalBrokerPriceObj = orderStat.get("totalBrokerPrice");
|
||||||
|
vo.setTotalBrokerPrice(totalBrokerPriceObj != null ? new BigDecimal(totalBrokerPriceObj.toString()) : BigDecimal.ZERO);
|
||||||
|
|
||||||
|
result.add(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.ycwl.basic.service.pc.impl;
|
|||||||
|
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
import com.github.pagehelper.PageInfo;
|
import com.github.pagehelper.PageInfo;
|
||||||
|
import com.ycwl.basic.clickhouse.service.StatsQueryService;
|
||||||
import com.ycwl.basic.mapper.BrokerMapper;
|
import com.ycwl.basic.mapper.BrokerMapper;
|
||||||
import com.ycwl.basic.model.pc.broker.entity.BrokerEntity;
|
import com.ycwl.basic.model.pc.broker.entity.BrokerEntity;
|
||||||
import com.ycwl.basic.model.pc.broker.req.BrokerReqQuery;
|
import com.ycwl.basic.model.pc.broker.req.BrokerReqQuery;
|
||||||
@@ -27,6 +28,8 @@ public class BrokerServiceImpl implements BrokerService {
|
|||||||
private BrokerMapper brokerMapper;
|
private BrokerMapper brokerMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ScenicRepository scenicRepository;
|
private ScenicRepository scenicRepository;
|
||||||
|
@Autowired
|
||||||
|
private StatsQueryService statsQueryService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageInfo<BrokerRespVO> pageQuery(BrokerReqQuery brokerReqQuery) {
|
public PageInfo<BrokerRespVO> pageQuery(BrokerReqQuery brokerReqQuery) {
|
||||||
@@ -41,11 +44,14 @@ public class BrokerServiceImpl implements BrokerService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
Map<Long, String> scenicNames = scenicRepository.batchGetScenicNames(scenicIds);
|
Map<Long, String> scenicNames = scenicRepository.batchGetScenicNames(scenicIds);
|
||||||
|
|
||||||
// 设置景区名称
|
// 设置景区名称和扫码次数
|
||||||
list.forEach(item -> {
|
list.forEach(item -> {
|
||||||
if (item.getScenicId() != null) {
|
if (item.getScenicId() != null) {
|
||||||
item.setScenicName(scenicNames.get(item.getScenicId()));
|
item.setScenicName(scenicNames.get(item.getScenicId()));
|
||||||
}
|
}
|
||||||
|
// 从 ClickHouse/MySQL 查询分销员扫码次数
|
||||||
|
Integer scanCount = statsQueryService.countBrokerScanCount(item.getId());
|
||||||
|
item.setBrokerScanCount(scanCount != null ? scanCount.longValue() : 0L);
|
||||||
});
|
});
|
||||||
|
|
||||||
PageInfo<BrokerRespVO> pageInfo = new PageInfo(list);
|
PageInfo<BrokerRespVO> pageInfo = new PageInfo(list);
|
||||||
@@ -64,11 +70,14 @@ public class BrokerServiceImpl implements BrokerService {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
Map<Long, String> scenicNames = scenicRepository.batchGetScenicNames(scenicIds);
|
Map<Long, String> scenicNames = scenicRepository.batchGetScenicNames(scenicIds);
|
||||||
|
|
||||||
// 设置景区名称
|
// 设置景区名称和扫码次数
|
||||||
list.forEach(item -> {
|
list.forEach(item -> {
|
||||||
if (item.getScenicId() != null) {
|
if (item.getScenicId() != null) {
|
||||||
item.setScenicName(scenicNames.get(item.getScenicId()));
|
item.setScenicName(scenicNames.get(item.getScenicId()));
|
||||||
}
|
}
|
||||||
|
// 从 ClickHouse/MySQL 查询分销员扫码次数
|
||||||
|
Integer scanCount = statsQueryService.countBrokerScanCount(item.getId());
|
||||||
|
item.setBrokerScanCount(scanCount != null ? scanCount.longValue() : 0L);
|
||||||
});
|
});
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ spring:
|
|||||||
lifecycle:
|
lifecycle:
|
||||||
timeout-per-shutdown-phase: 60s
|
timeout-per-shutdown-phase: 60s
|
||||||
|
|
||||||
|
# ClickHouse 配置
|
||||||
|
clickhouse:
|
||||||
|
enabled: true # true=ClickHouse, false=MySQL兜底
|
||||||
|
datasource:
|
||||||
|
jdbc-url: jdbc:clickhouse://100.64.0.7:8123/zt
|
||||||
|
username: default
|
||||||
|
password: ZhEnTuAi
|
||||||
|
driver-class-name: com.clickhouse.jdbc.ClickHouseDriver
|
||||||
|
maximum-pool-size: 10
|
||||||
|
minimum-idle: 2
|
||||||
|
|
||||||
# Feign配置(简化版,基于Nacos服务发现)
|
# Feign配置(简化版,基于Nacos服务发现)
|
||||||
feign:
|
feign:
|
||||||
client:
|
client:
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ spring:
|
|||||||
lifecycle:
|
lifecycle:
|
||||||
timeout-per-shutdown-phase: 60s
|
timeout-per-shutdown-phase: 60s
|
||||||
|
|
||||||
|
# ClickHouse 配置
|
||||||
|
clickhouse:
|
||||||
|
enabled: true # 设置为 true 启用 ClickHouse,false 使用 MySQL 兜底
|
||||||
|
datasource:
|
||||||
|
jdbc-url: jdbc:clickhouse://100.64.0.7:8123/zt
|
||||||
|
username: default
|
||||||
|
password: ZhEnTuAi
|
||||||
|
driver-class-name: com.clickhouse.jdbc.ClickHouseDriver
|
||||||
|
maximum-pool-size: 20
|
||||||
|
minimum-idle: 5
|
||||||
|
|
||||||
# 生产环境日志级别
|
# 生产环境日志级别
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
</delete>
|
</delete>
|
||||||
<select id="list" resultType="com.ycwl.basic.model.pc.broker.resp.BrokerRespVO">
|
<select id="list" resultType="com.ycwl.basic.model.pc.broker.resp.BrokerRespVO">
|
||||||
select b.id, scenic_id, b.`name`, b.phone, b.broker_enable, b.broker_rate, b.status,
|
select b.id, scenic_id, b.`name`, b.phone, b.broker_enable, b.broker_rate, b.status,
|
||||||
(select count(1) from t_stats_record s where s.action = "CODE_SCAN" and s.identifier = b.id) as broker_scan_count,
|
|
||||||
(select count(1) from broker_record r where r.broker_id = b.id) as broker_order_count,
|
(select count(1) from broker_record r where r.broker_id = b.id) as broker_order_count,
|
||||||
(select sum(order_price) from broker_record r where r.broker_id = b.id) as broker_order_amount,
|
(select sum(order_price) from broker_record r where r.broker_id = b.id) as broker_order_amount,
|
||||||
(select min(r.create_time) from broker_record r where r.broker_id = b.id) as first_broker_date,
|
(select min(r.create_time) from broker_record r where r.broker_id = b.id) as first_broker_date,
|
||||||
|
|||||||
@@ -107,4 +107,30 @@
|
|||||||
</set>
|
</set>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<!-- 按日期统计分销员订单数据(不含扫码统计) -->
|
||||||
|
<select id="getDailyOrderStats" resultType="java.util.HashMap">
|
||||||
|
WITH RECURSIVE
|
||||||
|
date_series AS (SELECT DATE(#{startTime}) AS date
|
||||||
|
UNION ALL
|
||||||
|
SELECT DATE_ADD(date, INTERVAL 1 DAY)
|
||||||
|
FROM date_series
|
||||||
|
WHERE date < DATE(#{endTime}))
|
||||||
|
SELECT ds.date,
|
||||||
|
COALESCE(os.orderCount, 0) AS orderCount,
|
||||||
|
COALESCE(os.totalOrderPrice, 0) AS totalOrderPrice,
|
||||||
|
COALESCE(os.totalBrokerPrice, 0) AS totalBrokerPrice
|
||||||
|
FROM date_series ds
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT DATE(create_time) AS date,
|
||||||
|
COUNT(DISTINCT id) AS orderCount,
|
||||||
|
COALESCE(SUM(order_price), 0) AS totalOrderPrice,
|
||||||
|
COALESCE(SUM(broker_price), 0) AS totalBrokerPrice
|
||||||
|
FROM broker_record
|
||||||
|
WHERE broker_id = #{brokerId}
|
||||||
|
AND DATE(create_time) BETWEEN DATE(#{startTime}) AND DATE(#{endTime})
|
||||||
|
GROUP BY DATE(create_time)
|
||||||
|
) os ON ds.date = os.date
|
||||||
|
ORDER BY ds.date
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -531,4 +531,24 @@
|
|||||||
order by r.create_time desc limit 1
|
order by r.create_time desc limit 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计分销员扫码次数 -->
|
||||||
|
<select id="countBrokerScanCount" resultType="java.lang.Integer">
|
||||||
|
SELECT count(1) AS count
|
||||||
|
FROM t_stats_record
|
||||||
|
WHERE action = 'CODE_SCAN'
|
||||||
|
AND identifier = #{brokerId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 按日期统计分销员扫码数据 -->
|
||||||
|
<select id="getDailyScanStats" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
DATE(create_time) AS date,
|
||||||
|
COUNT(DISTINCT id) AS scanCount
|
||||||
|
FROM t_stats_record
|
||||||
|
WHERE action = 'CODE_SCAN'
|
||||||
|
AND identifier = #{brokerId}
|
||||||
|
AND DATE(create_time) BETWEEN DATE(#{startTime}) AND DATE(#{endTime})
|
||||||
|
GROUP BY DATE(create_time)
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -151,4 +151,26 @@
|
|||||||
order by create_time desc
|
order by create_time desc
|
||||||
limit 1
|
limit 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据 face_id 列表统计已完成任务的用户数 -->
|
||||||
|
<select id="countCompletedTaskMembersByFaceIds" resultType="java.lang.Integer">
|
||||||
|
SELECT COUNT(DISTINCT member_id) AS count
|
||||||
|
FROM task
|
||||||
|
WHERE status = 1
|
||||||
|
AND face_id IN
|
||||||
|
<foreach collection="faceIds" item="faceId" open="(" separator="," close=")">
|
||||||
|
#{faceId}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据 face_id 列表统计已完成任务数 -->
|
||||||
|
<select id="countCompletedTasksByFaceIds" resultType="java.lang.Integer">
|
||||||
|
SELECT COUNT(1) AS count
|
||||||
|
FROM task
|
||||||
|
WHERE status = 1
|
||||||
|
AND face_id IN
|
||||||
|
<foreach collection="faceIds" item="faceId" open="(" separator="," close=")">
|
||||||
|
#{faceId}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
269
src/main/resources/mapper/clickhouse/ClickHouseStatsMapper.xml
Normal file
269
src/main/resources/mapper/clickhouse/ClickHouseStatsMapper.xml
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.ycwl.basic.clickhouse.mapper.ClickHouseStatsMapper">
|
||||||
|
|
||||||
|
<!-- 统计预览视频人数 -->
|
||||||
|
<select id="countPreviewVideoOfMember" resultType="java.lang.Integer">
|
||||||
|
SELECT ifNull(count(1), 0) AS count
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'LOAD'
|
||||||
|
AND r.identifier = 'pages/videoSynthesis/buy'
|
||||||
|
AND JSONExtractString(r.params, 'share') = ''
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND r.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND r.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY s.member_id
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计扫码访问人数 -->
|
||||||
|
<select id="countScanCodeOfMember" resultType="java.lang.Integer">
|
||||||
|
SELECT ifNull(count(1), 0) AS count
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'LAUNCH'
|
||||||
|
AND JSONExtractInt(r.params, 'scene') IN (1047, 1048, 1049)
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND s.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND s.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY s.member_id
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计推送订阅人数 -->
|
||||||
|
<select id="countPushOfMember" resultType="java.lang.Integer">
|
||||||
|
SELECT ifNull(count(1), 0) AS count
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'PERM_REQ'
|
||||||
|
AND r.identifier = 'NOTIFY'
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND r.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND r.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY s.member_id
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计上传头像人数 -->
|
||||||
|
<select id="countUploadFaceOfMember" resultType="java.lang.Integer">
|
||||||
|
SELECT ifNull(count(1), 0) AS count
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'FACE_UPLOAD'
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND s.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND s.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY s.member_id
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 获取上传人脸的 face_id 列表(用于与 MySQL task 表关联) -->
|
||||||
|
<select id="listFaceIdsWithUpload" resultType="java.lang.String">
|
||||||
|
SELECT DISTINCT r.identifier
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'FACE_UPLOAD'
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND s.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND s.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计总访问人数 -->
|
||||||
|
<select id="countTotalVisitorOfMember" resultType="java.lang.Integer">
|
||||||
|
SELECT ifNull(count(1), 0) AS count
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'LAUNCH'
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND s.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND s.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY s.member_id
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计预览视频条数 -->
|
||||||
|
<select id="countPreviewOfVideo" resultType="java.lang.Integer">
|
||||||
|
SELECT ifNull(count(1), 0) AS count
|
||||||
|
FROM (
|
||||||
|
SELECT 1
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC' AND identifier = toString(#{scenicId})
|
||||||
|
)
|
||||||
|
AND r.action = 'LOAD'
|
||||||
|
AND r.identifier = 'pages/videoSynthesis/buy'
|
||||||
|
AND JSONExtractString(r.params, 'id') != ''
|
||||||
|
AND JSONExtractString(r.params, 'share') = ''
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND s.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND s.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY JSONExtractString(r.params, 'id')
|
||||||
|
)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 获取用户分销员 ID 列表 -->
|
||||||
|
<select id="getBrokerIdListForUser" resultType="java.lang.Long">
|
||||||
|
SELECT toInt64(r.identifier) AS identifier
|
||||||
|
FROM (
|
||||||
|
SELECT identifier, max(r.create_time) AS createTime
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.action = 'CODE_SCAN'
|
||||||
|
AND s.member_id = #{memberId}
|
||||||
|
<if test="startTime != null">
|
||||||
|
AND r.create_time >= #{startTime}
|
||||||
|
</if>
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND r.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
GROUP BY identifier
|
||||||
|
) sub
|
||||||
|
ORDER BY createTime DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 获取用户最近进入类型 -->
|
||||||
|
<select id="getUserRecentEnterType" resultType="java.lang.Long">
|
||||||
|
SELECT JSONExtractInt(r.params, 'scene') AS scene
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.action = 'LAUNCH'
|
||||||
|
AND s.member_id = #{memberId}
|
||||||
|
<if test="endTime != null">
|
||||||
|
AND r.create_time <= #{endTime}
|
||||||
|
</if>
|
||||||
|
ORDER BY r.create_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 获取用户项目 ID 列表 -->
|
||||||
|
<select id="getProjectIdListForUser" resultType="java.lang.Long">
|
||||||
|
SELECT toInt64(r.identifier) AS identifier
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE s.member_id = #{memberId}
|
||||||
|
AND r.action = 'ENTER_PROJECT'
|
||||||
|
AND r.create_time < #{endTime}
|
||||||
|
AND r.create_time > #{startTime}
|
||||||
|
ORDER BY r.create_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 统计分销员扫码次数 -->
|
||||||
|
<select id="countBrokerScanCount" resultType="java.lang.Integer">
|
||||||
|
SELECT count(1) AS count
|
||||||
|
FROM t_stats_record
|
||||||
|
WHERE action = 'CODE_SCAN'
|
||||||
|
AND identifier = toString(#{brokerId})
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 按日期统计分销员扫码数据 -->
|
||||||
|
<select id="getDailyScanStats" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
toDate(create_time) AS date,
|
||||||
|
count(DISTINCT id) AS scanCount
|
||||||
|
FROM t_stats_record
|
||||||
|
WHERE action = 'CODE_SCAN'
|
||||||
|
AND identifier = toString(#{brokerId})
|
||||||
|
AND toDate(create_time) BETWEEN toDate(#{startTime}) AND toDate(#{endTime})
|
||||||
|
GROUP BY toDate(create_time)
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 按小时统计扫码人数 -->
|
||||||
|
<select id="scanCodeMemberChartByHour" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
formatDateTime(s.create_time, '%m-%d %H') AS t,
|
||||||
|
count(DISTINCT s.member_id) AS count
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC'
|
||||||
|
<if test="scenicId != null">
|
||||||
|
AND identifier = toString(#{scenicId})
|
||||||
|
</if>
|
||||||
|
)
|
||||||
|
AND r.action = 'LAUNCH'
|
||||||
|
AND JSONExtractInt(r.params, 'scene') IN (1047, 1048, 1049)
|
||||||
|
AND s.create_time BETWEEN #{startTime} AND #{endTime}
|
||||||
|
GROUP BY formatDateTime(s.create_time, '%m-%d %H')
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 按日期统计扫码人数 -->
|
||||||
|
<select id="scanCodeMemberChartByDate" resultType="java.util.HashMap">
|
||||||
|
SELECT
|
||||||
|
formatDateTime(s.create_time, '%m-%d') AS t,
|
||||||
|
count(DISTINCT s.member_id) AS count
|
||||||
|
FROM t_stats_record r
|
||||||
|
INNER JOIN t_stats s ON r.trace_id = s.trace_id
|
||||||
|
WHERE r.trace_id IN (
|
||||||
|
SELECT trace_id FROM t_stats_record
|
||||||
|
WHERE action = 'ENTER_SCENIC'
|
||||||
|
<if test="scenicId != null">
|
||||||
|
AND identifier = toString(#{scenicId})
|
||||||
|
</if>
|
||||||
|
)
|
||||||
|
AND r.action = 'LAUNCH'
|
||||||
|
AND JSONExtractInt(r.params, 'scene') IN (1047, 1048, 1049)
|
||||||
|
AND s.create_time BETWEEN #{startTime} AND #{endTime}
|
||||||
|
GROUP BY formatDateTime(s.create_time, '%m-%d')
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
Reference in New Issue
Block a user