diff --git a/src/main/java/com/rj/controller/AudioStatisticsController.java b/src/main/java/com/rj/controller/AudioStatisticsController.java new file mode 100644 index 0000000..084ca65 --- /dev/null +++ b/src/main/java/com/rj/controller/AudioStatisticsController.java @@ -0,0 +1,173 @@ +package com.rj.controller; + +import com.rj.common.Result; +import com.rj.entity.AudioManagementStatistics; +import com.rj.scheduler.AudioStatisticsScheduler; +import com.rj.service.IAudioManagementStatisticsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.util.List; + +/** + * 门店录音统计控制器 + * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@Tag(name = "门店录音统计", description = "门店录音统计相关接口") +@RestController +@RequestMapping("/api/audio-statistics") +public class AudioStatisticsController { + + @Autowired + private IAudioManagementStatisticsService audioStatisticsService; + + @Autowired + private AudioStatisticsScheduler audioStatisticsScheduler; + + /** + * 手动生成指定日期的统计数据 + */ + @Operation(summary = "手动生成指定日期的统计数据", description = "手动触发指定日期的门店录音统计") + @PostMapping("/generate") + public Result generateStatistics( + @Parameter(description = "统计日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { + try { + int count = audioStatisticsScheduler.manualGenerateStatistics(date); + Result result = Result.ok(count); + result.setMsg("统计生成成功"); + return result; + } catch (Exception e) { + return Result.error("统计生成失败:" + e.getMessage()); + } + } + + /** + * 查询指定日期范围的统计数据 + */ + @Operation(summary = "查询指定日期范围的统计数据", description = "查询指定日期范围内的门店录音统计数据") + @GetMapping("/range") + public Result getStatisticsByDateRange( + @Parameter(description = "开始日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { + try { + List statistics = audioStatisticsService.getStatisticsByDateRange(startDate, endDate); + Result result = Result.ok(statistics); + result.setMsg("查询成功"); + return result; + } catch (Exception e) { + return Result.error("查询失败:" + e.getMessage()); + } + } + + /** + * 查询指定门店的统计数据 + */ + @Operation(summary = "查询指定门店的统计数据", description = "查询指定门店在指定日期范围内的录音统计数据") + @GetMapping("/dealership/{dealershipId}") + public Result getStatisticsByDealership( + @Parameter(description = "门店ID") + @PathVariable Long dealershipId, + @Parameter(description = "开始日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { + try { + List statistics = audioStatisticsService.getStatisticsByDealership(dealershipId, startDate, endDate); + Result result = Result.ok(statistics); + result.setMsg("查询成功"); + return result; + } catch (Exception e) { + return Result.error("查询失败:" + e.getMessage()); + } + } + + /** + * 查询指定销售人员的统计数据 + */ + @Operation(summary = "查询指定销售人员的统计数据", description = "查询指定销售人员在指定日期范围内的录音统计数据") + @GetMapping("/sales/{salesId}") + public Result getStatisticsBySales( + @Parameter(description = "销售人员ID") + @PathVariable String salesId, + @Parameter(description = "开始日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { + try { + List statistics = audioStatisticsService.getStatisticsBySales(salesId, startDate, endDate); + Result result = Result.ok(statistics); + result.setMsg("查询成功"); + return result; + } catch (Exception e) { + return Result.error("查询失败:" + e.getMessage()); + } + } + + /** + * 查询指定项目的统计数据 + */ + @Operation(summary = "查询指定项目的统计数据", description = "查询指定项目在指定日期范围内的录音统计数据") + @GetMapping("/project/{projectId}") + public Result getStatisticsByProject( + @Parameter(description = "项目ID") + @PathVariable String projectId, + @Parameter(description = "开始日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate, + @Parameter(description = "结束日期,格式:yyyy-MM-dd") + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) { + try { + List statistics = audioStatisticsService.getStatisticsByProject(projectId, startDate, endDate); + Result result = Result.ok(statistics); + result.setMsg("查询成功"); + return result; + } catch (Exception e) { + return Result.error("查询失败:" + e.getMessage()); + } + } + + /** + * 查询指定日期的统计数据 + */ + @Operation(summary = "查询指定日期的统计数据", description = "查询指定日期的所有门店录音统计数据") + @GetMapping("/date/{date}") + public Result getStatisticsByDate( + @Parameter(description = "统计日期,格式:yyyy-MM-dd") + @PathVariable @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { + try { + List statistics = audioStatisticsService.getStatisticsByDateRange(date, date); + Result result = Result.ok(statistics); + result.setMsg("查询成功"); + return result; + } catch (Exception e) { + return Result.error("查询失败:" + e.getMessage()); + } + } + + /** + * 获取统计概览(最近7天) + */ + @Operation(summary = "获取统计概览", description = "获取最近7天的门店录音统计概览") + @GetMapping("/overview") + public Result getStatisticsOverview() { + try { + LocalDate endDate = LocalDate.now().minusDays(1); // 昨天 + LocalDate startDate = endDate.minusDays(6); // 7天前 + List statistics = audioStatisticsService.getStatisticsByDateRange(startDate, endDate); + Result result = Result.ok(statistics); + result.setMsg("查询成功"); + return result; + } catch (Exception e) { + return Result.error("查询失败:" + e.getMessage()); + } + } +} diff --git a/src/main/java/com/rj/entity/AudioManagementStatistics.java b/src/main/java/com/rj/entity/AudioManagementStatistics.java new file mode 100644 index 0000000..aa7211d --- /dev/null +++ b/src/main/java/com/rj/entity/AudioManagementStatistics.java @@ -0,0 +1,107 @@ +package com.rj.entity; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import java.io.Serializable; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + *

+ * 门店录音统计表 + *

+ * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@TableName("audio_management_statistics") +@Schema(description="门店录音统计表") +public class AudioManagementStatistics implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @Schema(description = "门店ID") + @TableField("dealership_id") + private Long dealershipId; + + @Schema(description = "门店名称") + @TableField("dealership_name") + private String dealershipName; + + @Schema(description = "门店录音总时长(秒)") + @TableField("dealership_total_duration") + private Long dealershipTotalDuration; + + @Schema(description = "门店录音数量") + @TableField("dealership_recording_count") + private Integer dealershipRecordingCount; + + @Schema(description = "门店录音平均时长(秒)") + @TableField("dealership_avg_duration") + private BigDecimal dealershipAvgDuration; + + @Schema(description = "销售人员ID") + @TableField("sales_id") + private String salesId; + + @Schema(description = "销售人员姓名") + @TableField("sales_name") + private String salesName; + + @Schema(description = "销售人员录音总时长(秒)") + @TableField("sales_total_duration") + private Long salesTotalDuration; + + @Schema(description = "销售人员录音数量") + @TableField("sales_recording_count") + private Integer salesRecordingCount; + + @Schema(description = "销售人员录音平均时长(秒)") + @TableField("sales_avg_duration") + private BigDecimal salesAvgDuration; + + @Schema(description = "项目ID") + @TableField("project_id") + private String projectId; + + @Schema(description = "项目名称") + @TableField("project_name") + private String projectName; + + @Schema(description = "项目录音总时长(秒)") + @TableField("project_total_duration") + private Long projectTotalDuration; + + @Schema(description = "项目录音数量") + @TableField("project_recording_count") + private Integer projectRecordingCount; + + @Schema(description = "项目录音平均时长(秒)") + @TableField("project_avg_duration") + private BigDecimal projectAvgDuration; + + @Schema(description = "统计日期") + @TableField("statistics_date") + private LocalDate statisticsDate; + + @Schema(description = "创建时间") + @TableField("created_at") + private LocalDateTime createdAt; + + @Schema(description = "更新时间") + @TableField("updated_at") + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/rj/example/AudioStatisticsExample.java b/src/main/java/com/rj/example/AudioStatisticsExample.java new file mode 100644 index 0000000..d71efe8 --- /dev/null +++ b/src/main/java/com/rj/example/AudioStatisticsExample.java @@ -0,0 +1,215 @@ +package com.rj.example; + +import com.rj.entity.AudioManagementStatistics; +import com.rj.scheduler.AudioStatisticsScheduler; +import com.rj.service.IAudioManagementStatisticsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.util.List; + +/** + * 门店录音统计功能使用示例 + * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@Component +public class AudioStatisticsExample { + + @Autowired + private IAudioManagementStatisticsService audioStatisticsService; + + @Autowired + private AudioStatisticsScheduler audioStatisticsScheduler; + + /** + * 示例1:手动生成指定日期的统计数据 + */ + public void exampleGenerateStatistics() { + System.out.println("=== 示例1:手动生成指定日期的统计数据 ==="); + + // 生成昨天的统计数据 + LocalDate yesterday = LocalDate.now().minusDays(1); + int count = audioStatisticsScheduler.manualGenerateStatistics(yesterday); + + System.out.println("生成统计记录数: " + count); + System.out.println("统计日期: " + yesterday); + } + + /** + * 示例2:查询最近7天的统计数据 + */ + public void exampleQueryRecentStatistics() { + System.out.println("=== 示例2:查询最近7天的统计数据 ==="); + + LocalDate endDate = LocalDate.now().minusDays(1); // 昨天 + LocalDate startDate = endDate.minusDays(6); // 7天前 + + List statistics = audioStatisticsService.getStatisticsByDateRange(startDate, endDate); + + System.out.println("查询到统计记录数: " + statistics.size()); + System.out.println("查询日期范围: " + startDate + " 到 " + endDate); + + // 打印统计信息 + for (AudioManagementStatistics stat : statistics) { + System.out.println("门店: " + stat.getDealershipName() + + ", 日期: " + stat.getStatisticsDate() + + ", 录音数量: " + stat.getDealershipRecordingCount() + + ", 总时长: " + stat.getDealershipTotalDuration() + "秒" + + ", 平均时长: " + stat.getDealershipAvgDuration() + "秒"); + } + } + + /** + * 示例3:查询指定门店的统计数据 + */ + public void exampleQueryDealershipStatistics() { + System.out.println("=== 示例3:查询指定门店的统计数据 ==="); + + Long dealershipId = 1L; // 假设门店ID为1 + LocalDate startDate = LocalDate.now().minusDays(30); // 最近30天 + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsByDealership(dealershipId, startDate, endDate); + + System.out.println("门店ID: " + dealershipId); + System.out.println("查询到统计记录数: " + statistics.size()); + + // 计算该门店的总统计 + long totalDuration = 0; + int totalCount = 0; + + for (AudioManagementStatistics stat : statistics) { + totalDuration += stat.getDealershipTotalDuration(); + totalCount += stat.getDealershipRecordingCount(); + } + + System.out.println("门店总录音时长: " + totalDuration + "秒"); + System.out.println("门店总录音数量: " + totalCount); + if (totalCount > 0) { + System.out.println("门店平均录音时长: " + (totalDuration / totalCount) + "秒"); + } + } + + /** + * 示例4:查询指定销售人员的统计数据 + */ + public void exampleQuerySalesStatistics() { + System.out.println("=== 示例4:查询指定销售人员的统计数据 ==="); + + String salesId = "sales001"; // 假设销售人员ID + LocalDate startDate = LocalDate.now().minusDays(30); // 最近30天 + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsBySales(salesId, startDate, endDate); + + System.out.println("销售人员ID: " + salesId); + System.out.println("查询到统计记录数: " + statistics.size()); + + // 计算该销售人员的总统计 + long totalDuration = 0; + int totalCount = 0; + + for (AudioManagementStatistics stat : statistics) { + totalDuration += stat.getSalesTotalDuration(); + totalCount += stat.getSalesRecordingCount(); + } + + System.out.println("销售人员总录音时长: " + totalDuration + "秒"); + System.out.println("销售人员总录音数量: " + totalCount); + if (totalCount > 0) { + System.out.println("销售人员平均录音时长: " + (totalDuration / totalCount) + "秒"); + } + } + + /** + * 示例5:查询指定项目的统计数据 + */ + public void exampleQueryProjectStatistics() { + System.out.println("=== 示例5:查询指定项目的统计数据 ==="); + + String projectId = "project001"; // 假设项目ID + LocalDate startDate = LocalDate.now().minusDays(30); // 最近30天 + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsByProject(projectId, startDate, endDate); + + System.out.println("项目ID: " + projectId); + System.out.println("查询到统计记录数: " + statistics.size()); + + // 计算该项目的总统计 + long totalDuration = 0; + int totalCount = 0; + + for (AudioManagementStatistics stat : statistics) { + totalDuration += stat.getProjectTotalDuration(); + totalCount += stat.getProjectRecordingCount(); + } + + System.out.println("项目总录音时长: " + totalDuration + "秒"); + System.out.println("项目总录音数量: " + totalCount); + if (totalCount > 0) { + System.out.println("项目平均录音时长: " + (totalDuration / totalCount) + "秒"); + } + } + + /** + * 示例6:批量生成历史统计数据 + */ + public void exampleBatchGenerateStatistics() { + System.out.println("=== 示例6:批量生成历史统计数据 ==="); + + // 生成最近30天的统计数据 + LocalDate endDate = LocalDate.now().minusDays(1); + LocalDate startDate = endDate.minusDays(29); + + int totalCount = 0; + for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { + try { + int count = audioStatisticsScheduler.manualGenerateStatistics(date); + totalCount += count; + System.out.println("日期 " + date + " 生成统计记录数: " + count); + } catch (Exception e) { + System.err.println("日期 " + date + " 统计失败: " + e.getMessage()); + } + } + + System.out.println("批量生成完成,总记录数: " + totalCount); + } + + /** + * 运行所有示例 + */ + public void runAllExamples() { + System.out.println("开始运行门店录音统计功能示例..."); + System.out.println(); + + try { + exampleGenerateStatistics(); + System.out.println(); + + exampleQueryRecentStatistics(); + System.out.println(); + + exampleQueryDealershipStatistics(); + System.out.println(); + + exampleQuerySalesStatistics(); + System.out.println(); + + exampleQueryProjectStatistics(); + System.out.println(); + + exampleBatchGenerateStatistics(); + System.out.println(); + + } catch (Exception e) { + System.err.println("示例运行失败: " + e.getMessage()); + e.printStackTrace(); + } + + System.out.println("示例运行完成!"); + } +} diff --git a/src/main/java/com/rj/mapper/AudioManagementStatisticsMapper.java b/src/main/java/com/rj/mapper/AudioManagementStatisticsMapper.java new file mode 100644 index 0000000..4d00113 --- /dev/null +++ b/src/main/java/com/rj/mapper/AudioManagementStatisticsMapper.java @@ -0,0 +1,92 @@ +package com.rj.mapper; + +import com.rj.entity.AudioManagementStatistics; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +/** + *

+ * 门店录音统计表 Mapper 接口 + *

+ * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@Mapper +public interface AudioManagementStatisticsMapper extends BaseMapper { + + /** + * 按门店统计指定日期的录音数据 + * @param date 统计日期 + * @return 门店统计结果列表 + */ + @Select("SELECT " + + "dealership_id, " + + "dealership_name, " + + "SUM(duration * 60) as total_duration_seconds, " + + "COUNT(*) as recording_count, " + + "AVG(duration * 60) as avg_duration_seconds " + + "FROM audio_management " + + "WHERE DATE(recording_time) = #{date} " + + "AND dealership_id IS NOT NULL " + + "AND dealership_id != '' " + + "GROUP BY dealership_id, dealership_name") + List> getDealershipStatisticsByDate(@Param("date") LocalDate date); + + /** + * 按销售人员统计指定日期的录音数据 + * @param date 统计日期 + * @return 销售人员统计结果列表 + */ + @Select("SELECT " + + "sales_id, " + + "sales_name, " + + "SUM(duration * 60) as total_duration_seconds, " + + "COUNT(*) as recording_count, " + + "AVG(duration * 60) as avg_duration_seconds " + + "FROM audio_management " + + "WHERE DATE(recording_time) = #{date} " + + "AND sales_id IS NOT NULL " + + "AND sales_id != '' " + + "GROUP BY sales_id, sales_name") + List> getSalesStatisticsByDate(@Param("date") LocalDate date); + + /** + * 按项目统计指定日期的录音数据 + * @param date 统计日期 + * @return 项目统计结果列表 + */ + @Select("SELECT " + + "project_id, " + + "project_name, " + + "SUM(duration * 60) as total_duration_seconds, " + + "COUNT(*) as recording_count, " + + "AVG(duration * 60) as avg_duration_seconds " + + "FROM audio_management " + + "WHERE DATE(recording_time) = #{date} " + + "AND project_id IS NOT NULL " + + "AND project_id != '' " + + "GROUP BY project_id, project_name") + List> getProjectStatisticsByDate(@Param("date") LocalDate date); + + /** + * 检查指定日期的统计数据是否已存在 + * @param date 统计日期 + * @return 统计记录数量 + */ + @Select("SELECT COUNT(*) FROM audio_management_statistics WHERE statistics_date = #{date}") + int countByStatisticsDate(@Param("date") LocalDate date); + + /** + * 删除指定日期的统计数据 + * @param date 统计日期 + * @return 删除的记录数 + */ + int deleteByStatisticsDate(@Param("date") LocalDate date); +} diff --git a/src/main/java/com/rj/scheduler/AudioStatisticsScheduler.java b/src/main/java/com/rj/scheduler/AudioStatisticsScheduler.java new file mode 100644 index 0000000..d6f015b --- /dev/null +++ b/src/main/java/com/rj/scheduler/AudioStatisticsScheduler.java @@ -0,0 +1,58 @@ +package com.rj.scheduler; + +import com.rj.service.IAudioManagementStatisticsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; + +/** + * 门店录音统计定时任务 + * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@Slf4j +@Component +public class AudioStatisticsScheduler { + + @Autowired + private IAudioManagementStatisticsService audioStatisticsService; + + /** + * 每天凌晨2点执行门店录音统计 + * 统计昨天的录音数据 + */ + @Scheduled(cron = "0 0 2 * * ?") + public void generateDailyAudioStatistics() { + try { + log.info("开始执行门店录音统计任务..."); + + LocalDate yesterday = LocalDate.now().minusDays(1); + int count = audioStatisticsService.generateStatisticsByDate(yesterday); + + log.info("门店录音统计任务执行完成,统计日期:{},生成记录数:{}", yesterday, count); + } catch (Exception e) { + log.error("门店录音统计任务执行失败", e); + } + } + + /** + * 手动触发统计任务(用于测试或补录数据) + * @param date 要统计的日期 + * @return 生成的记录数 + */ + public int manualGenerateStatistics(LocalDate date) { + try { + log.info("手动执行门店录音统计任务,统计日期:{}", date); + int count = audioStatisticsService.generateStatisticsByDate(date); + log.info("手动门店录音统计任务执行完成,统计日期:{},生成记录数:{}", date, count); + return count; + } catch (Exception e) { + log.error("手动门店录音统计任务执行失败,统计日期:{}", date, e); + throw e; + } + } +} diff --git a/src/main/java/com/rj/service/IAudioManagementStatisticsService.java b/src/main/java/com/rj/service/IAudioManagementStatisticsService.java new file mode 100644 index 0000000..495693a --- /dev/null +++ b/src/main/java/com/rj/service/IAudioManagementStatisticsService.java @@ -0,0 +1,66 @@ +package com.rj.service; + +import com.rj.entity.AudioManagementStatistics; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.time.LocalDate; +import java.util.List; + +/** + *

+ * 门店录音统计表 服务类 + *

+ * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +public interface IAudioManagementStatisticsService extends IService { + + /** + * 生成指定日期的门店录音统计数据 + * @param date 统计日期 + * @return 生成的统计记录数量 + */ + int generateStatisticsByDate(LocalDate date); + + /** + * 生成昨天的门店录音统计数据 + * @return 生成的统计记录数量 + */ + int generateYesterdayStatistics(); + + /** + * 查询指定日期范围的统计数据 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 统计记录列表 + */ + List getStatisticsByDateRange(LocalDate startDate, LocalDate endDate); + + /** + * 查询指定门店的统计数据 + * @param dealershipId 门店ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 统计记录列表 + */ + List getStatisticsByDealership(Long dealershipId, LocalDate startDate, LocalDate endDate); + + /** + * 查询指定销售人员的统计数据 + * @param salesId 销售人员ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 统计记录列表 + */ + List getStatisticsBySales(String salesId, LocalDate startDate, LocalDate endDate); + + /** + * 查询指定项目的统计数据 + * @param projectId 项目ID + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 统计记录列表 + */ + List getStatisticsByProject(String projectId, LocalDate startDate, LocalDate endDate); +} diff --git a/src/main/java/com/rj/service/impl/AudioManagementStatisticsServiceImpl.java b/src/main/java/com/rj/service/impl/AudioManagementStatisticsServiceImpl.java new file mode 100644 index 0000000..c97d83f --- /dev/null +++ b/src/main/java/com/rj/service/impl/AudioManagementStatisticsServiceImpl.java @@ -0,0 +1,264 @@ +package com.rj.service.impl; + +import com.rj.entity.AudioManagementStatistics; +import com.rj.mapper.AudioManagementStatisticsMapper; +import com.rj.service.IAudioManagementStatisticsService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + *

+ * 门店录音统计表 服务实现类 + *

+ * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@Service +public class AudioManagementStatisticsServiceImpl extends ServiceImpl implements IAudioManagementStatisticsService { + + @Override + @Transactional(rollbackFor = Exception.class) + public int generateStatisticsByDate(LocalDate date) { + // 先删除该日期的统计数据(如果存在) + baseMapper.deleteByStatisticsDate(date); + + // 查询该日期的门店录音统计数据 + List> dealershipStatsList = baseMapper.getDealershipStatisticsByDate(date); + + // 查询该日期的销售人员录音统计数据 + List> salesStatsList = baseMapper.getSalesStatisticsByDate(date); + + // 查询该日期的项目录音统计数据 + List> projectStatsList = baseMapper.getProjectStatisticsByDate(date); + + int count = 0; + + // 处理门店统计数据 + for (Map stat : dealershipStatsList) { + AudioManagementStatistics statistics = new AudioManagementStatistics(); + + // 设置门店信息 + Object dealershipIdObj = stat.get("dealership_id"); + if (dealershipIdObj != null) { + try { + statistics.setDealershipId(Long.valueOf(dealershipIdObj.toString())); + } catch (NumberFormatException e) { + // 如果dealership_id是UUID格式,使用hashCode作为ID + statistics.setDealershipId((long) dealershipIdObj.toString().hashCode()); + } + } else { + statistics.setDealershipId(0L); + } + + Object dealershipNameObj = stat.get("dealership_name"); + statistics.setDealershipName(dealershipNameObj != null ? dealershipNameObj.toString() : "未知门店"); + + // 设置门店录音统计数据 + Object totalDurationObj = stat.get("total_duration_seconds"); + if (totalDurationObj != null) { + statistics.setDealershipTotalDuration(Long.valueOf(totalDurationObj.toString())); + } else { + statistics.setDealershipTotalDuration(0L); + } + + Object recordingCountObj = stat.get("recording_count"); + if (recordingCountObj != null) { + statistics.setDealershipRecordingCount(Integer.valueOf(recordingCountObj.toString())); + } else { + statistics.setDealershipRecordingCount(0); + } + + // 计算门店平均时长 + if (statistics.getDealershipRecordingCount() > 0 && statistics.getDealershipTotalDuration() > 0) { + BigDecimal avgDuration = BigDecimal.valueOf(statistics.getDealershipTotalDuration()) + .divide(BigDecimal.valueOf(statistics.getDealershipRecordingCount()), 2, RoundingMode.HALF_UP); + statistics.setDealershipAvgDuration(avgDuration); + } else { + statistics.setDealershipAvgDuration(BigDecimal.ZERO); + } + + // 查找该门店对应的销售人员统计数据 + String dealershipId = dealershipIdObj != null ? dealershipIdObj.toString() : ""; + Map salesStat = findSalesStatByDealership(salesStatsList, dealershipId); + + if (salesStat != null) { + // 设置销售人员信息 + Object salesIdObj = salesStat.get("sales_id"); + statistics.setSalesId(salesIdObj != null ? salesIdObj.toString() : ""); + + Object salesNameObj = salesStat.get("sales_name"); + statistics.setSalesName(salesNameObj != null ? salesNameObj.toString() : "未知销售"); + + // 设置销售人员录音统计数据 + Object salesTotalDurationObj = salesStat.get("total_duration_seconds"); + if (salesTotalDurationObj != null) { + statistics.setSalesTotalDuration(Long.valueOf(salesTotalDurationObj.toString())); + } else { + statistics.setSalesTotalDuration(0L); + } + + Object salesRecordingCountObj = salesStat.get("recording_count"); + if (salesRecordingCountObj != null) { + statistics.setSalesRecordingCount(Integer.valueOf(salesRecordingCountObj.toString())); + } else { + statistics.setSalesRecordingCount(0); + } + + // 计算销售人员平均时长 + if (statistics.getSalesRecordingCount() > 0 && statistics.getSalesTotalDuration() > 0) { + BigDecimal salesAvgDuration = BigDecimal.valueOf(statistics.getSalesTotalDuration()) + .divide(BigDecimal.valueOf(statistics.getSalesRecordingCount()), 2, RoundingMode.HALF_UP); + statistics.setSalesAvgDuration(salesAvgDuration); + } else { + statistics.setSalesAvgDuration(BigDecimal.ZERO); + } + } else { + // 如果没有找到对应的销售人员数据,设置为空 + statistics.setSalesId(""); + statistics.setSalesName(""); + statistics.setSalesTotalDuration(0L); + statistics.setSalesRecordingCount(0); + statistics.setSalesAvgDuration(BigDecimal.ZERO); + } + + // 查找该门店对应的项目统计数据 + String dealershipIdForProject = dealershipIdObj != null ? dealershipIdObj.toString() : ""; + Map projectStat = findProjectStatByDealership(projectStatsList, dealershipIdForProject); + + if (projectStat != null) { + // 设置项目信息 + Object projectIdObj = projectStat.get("project_id"); + statistics.setProjectId(projectIdObj != null ? projectIdObj.toString() : ""); + + Object projectNameObj = projectStat.get("project_name"); + statistics.setProjectName(projectNameObj != null ? projectNameObj.toString() : "未知项目"); + + // 设置项目录音统计数据 + Object projectTotalDurationObj = projectStat.get("total_duration_seconds"); + if (projectTotalDurationObj != null) { + statistics.setProjectTotalDuration(Long.valueOf(projectTotalDurationObj.toString())); + } else { + statistics.setProjectTotalDuration(0L); + } + + Object projectRecordingCountObj = projectStat.get("recording_count"); + if (projectRecordingCountObj != null) { + statistics.setProjectRecordingCount(Integer.valueOf(projectRecordingCountObj.toString())); + } else { + statistics.setProjectRecordingCount(0); + } + + // 计算项目平均时长 + if (statistics.getProjectRecordingCount() > 0 && statistics.getProjectTotalDuration() > 0) { + BigDecimal projectAvgDuration = BigDecimal.valueOf(statistics.getProjectTotalDuration()) + .divide(BigDecimal.valueOf(statistics.getProjectRecordingCount()), 2, RoundingMode.HALF_UP); + statistics.setProjectAvgDuration(projectAvgDuration); + } else { + statistics.setProjectAvgDuration(BigDecimal.ZERO); + } + } else { + // 如果没有找到对应的项目数据,设置为空 + statistics.setProjectId(""); + statistics.setProjectName(""); + statistics.setProjectTotalDuration(0L); + statistics.setProjectRecordingCount(0); + statistics.setProjectAvgDuration(BigDecimal.ZERO); + } + + // 设置统计日期和时间 + statistics.setStatisticsDate(date); + statistics.setCreatedAt(LocalDateTime.now()); + statistics.setUpdatedAt(LocalDateTime.now()); + + // 保存统计记录 + save(statistics); + count++; + } + + return count; + } + + /** + * 根据门店ID查找对应的销售人员统计数据 + * @param salesStatsList 销售人员统计列表 + * @param dealershipId 门店ID + * @return 销售人员统计数据 + */ + private Map findSalesStatByDealership(List> salesStatsList, String dealershipId) { + // 这里需要根据业务逻辑来确定门店和销售人员的对应关系 + // 由于原始数据中可能没有直接的关联关系,这里返回第一个销售人员数据作为示例 + // 实际使用时需要根据具体的业务逻辑来调整 + if (!salesStatsList.isEmpty()) { + return salesStatsList.get(0); + } + return null; + } + + /** + * 根据门店ID查找对应的项目统计数据 + * @param projectStatsList 项目统计列表 + * @param dealershipId 门店ID + * @return 项目统计数据 + */ + private Map findProjectStatByDealership(List> projectStatsList, String dealershipId) { + // 这里需要根据业务逻辑来确定门店和项目的对应关系 + // 由于原始数据中可能没有直接的关联关系,这里返回第一个项目数据作为示例 + // 实际使用时需要根据具体的业务逻辑来调整 + if (!projectStatsList.isEmpty()) { + return projectStatsList.get(0); + } + return null; + } + + @Override + public int generateYesterdayStatistics() { + LocalDate yesterday = LocalDate.now().minusDays(1); + return generateStatisticsByDate(yesterday); + } + + @Override + public List getStatisticsByDateRange(LocalDate startDate, LocalDate endDate) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.between(AudioManagementStatistics::getStatisticsDate, startDate, endDate) + .orderByAsc(AudioManagementStatistics::getStatisticsDate) + .orderByAsc(AudioManagementStatistics::getDealershipId); + return list(queryWrapper); + } + + @Override + public List getStatisticsByDealership(Long dealershipId, LocalDate startDate, LocalDate endDate) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AudioManagementStatistics::getDealershipId, dealershipId) + .between(AudioManagementStatistics::getStatisticsDate, startDate, endDate) + .orderByAsc(AudioManagementStatistics::getStatisticsDate); + return list(queryWrapper); + } + + @Override + public List getStatisticsBySales(String salesId, LocalDate startDate, LocalDate endDate) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AudioManagementStatistics::getSalesId, salesId) + .between(AudioManagementStatistics::getStatisticsDate, startDate, endDate) + .orderByAsc(AudioManagementStatistics::getStatisticsDate); + return list(queryWrapper); + } + + @Override + public List getStatisticsByProject(String projectId, LocalDate startDate, LocalDate endDate) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AudioManagementStatistics::getProjectId, projectId) + .between(AudioManagementStatistics::getStatisticsDate, startDate, endDate) + .orderByAsc(AudioManagementStatistics::getStatisticsDate); + return list(queryWrapper); + } +} diff --git a/src/main/resources/mapper/AudioManagementStatisticsMapper.xml b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml new file mode 100644 index 0000000..12b0e90 --- /dev/null +++ b/src/main/resources/mapper/AudioManagementStatisticsMapper.xml @@ -0,0 +1,11 @@ + + + + + + + DELETE FROM audio_management_statistics + WHERE statistics_date = #{date} + + + diff --git a/src/main/sql/audio_management_statistics.sql b/src/main/sql/audio_management_statistics.sql new file mode 100644 index 0000000..b17d12c --- /dev/null +++ b/src/main/sql/audio_management_statistics.sql @@ -0,0 +1,73 @@ + + +-- 用于存储门店录音相关数据, 基础的原始数据,可以用于统计分析 + +CREATE TABLE `audio_management` ( + `id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '主键,UUID', + `recording_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '录音名称', + `recording_time` datetime NULL DEFAULT NULL COMMENT '录音时间', + `info_card_description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '信息卡', + `sales_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属销售ID', + `sales_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属销售名称', + `duration` decimal(10, 2) NULL DEFAULT 0.00 COMMENT '录音时长(分钟)', + `customer_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '客户ID', + `customer_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '客户姓名', + `customer_phone` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '客户手机号', + `upload_time` datetime NULL DEFAULT NULL COMMENT '上传时间', + `intention_level` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '意向级别(高意向, 中意向 , 低意向)', + `dealership_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属门店ID', + `dealership_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属门店名称', + `project_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属项目ID', + `project_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属项目名称', + `script_model` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '话术模型', + `upload_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '上传状态', + `sync_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '同步状态', + `is_merged` tinyint(1) NULL DEFAULT 0 COMMENT '是否合并(0否1是)', + `remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '录音备注', + `company_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '公司类型', + `edit_status` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '编辑状态', + `edit_time` datetime NULL DEFAULT NULL COMMENT '编辑时间', + `info_card_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '信息卡类型(如意向车型、购买情况、来访目的、试驾结果、付款方式、试驾专员等)', + `info_card_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '信息卡内容', + `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '录音管理表,含信息卡字段' ROW_FORMAT = Dynamic; + + + +-- 用于存储门店录音相关的统计数据 +CREATE TABLE `audio_management_statistics` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `dealership_id` bigint(20) NOT NULL COMMENT '门店ID', + `dealership_name` varchar(100) NOT NULL COMMENT '门店名称', + + -- 门店录音总时长相关字段 + `dealership_total_duration` bigint(20) NOT NULL DEFAULT 0 COMMENT '门店录音总时长(秒)', + `dealership_recording_count` int(11) NOT NULL DEFAULT 0 COMMENT '门店录音数量', + `dealership_avg_duration` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '门店录音平均时长(秒)', + + -- 个人录音相关字段 + `personal_total_duration` bigint(20) NOT NULL DEFAULT 0 COMMENT '个人录音总时长(秒)', + `personal_recording_count` int(11) NOT NULL DEFAULT 0 COMMENT '个人录音数量', + `personal_avg_duration` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '个人录音平均时长(秒)', + + -- 项目录音相关字段 + `project_recording_count` int(11) NOT NULL DEFAULT 0 COMMENT '项目录音总数', + `project_avg_duration` decimal(10,2) NOT NULL DEFAULT 0.00 COMMENT '项目录音平均时长(秒)', + + -- 统计日期 + `statistics_date` date NOT NULL COMMENT '统计日期', + + -- 时间戳字段 + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + + PRIMARY KEY (`id`), + KEY `idx_statistics_date` (`statistics_date`) COMMENT '统计日期索引', + KEY `idx_dealership_id` (`dealership_id`) COMMENT '门店ID索引', + KEY `idx_created_at` (`created_at`) COMMENT '创建时间索引' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='门店录音统计表'; + + + diff --git a/src/test/java/com/rj/service/AudioManagementStatisticsServiceTest.java b/src/test/java/com/rj/service/AudioManagementStatisticsServiceTest.java new file mode 100644 index 0000000..898f2e2 --- /dev/null +++ b/src/test/java/com/rj/service/AudioManagementStatisticsServiceTest.java @@ -0,0 +1,123 @@ +package com.rj.service; + +import com.rj.entity.AudioManagementStatistics; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.time.LocalDate; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * 门店录音统计服务测试类 + * + * @author 李中华 ,spllzh + * @since 2025-08-07 + */ +@SpringBootTest +@ActiveProfiles("test") +public class AudioManagementStatisticsServiceTest { + + @Autowired + private IAudioManagementStatisticsService audioStatisticsService; + + @Test + public void testGenerateStatisticsByDate() { + // 测试生成指定日期的统计数据 + LocalDate testDate = LocalDate.now().minusDays(1); + int count = audioStatisticsService.generateStatisticsByDate(testDate); + + assertTrue(count >= 0, "统计记录数应该大于等于0"); + System.out.println("生成统计记录数: " + count); + } + + @Test + public void testGenerateYesterdayStatistics() { + // 测试生成昨天的统计数据 + int count = audioStatisticsService.generateYesterdayStatistics(); + + assertTrue(count >= 0, "统计记录数应该大于等于0"); + System.out.println("生成昨天统计记录数: " + count); + } + + @Test + public void testGetStatisticsByDateRange() { + // 测试查询日期范围的统计数据 + LocalDate startDate = LocalDate.now().minusDays(7); + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsByDateRange(startDate, endDate); + + assertNotNull(statistics, "统计列表不应该为null"); + System.out.println("查询到统计记录数: " + statistics.size()); + + // 打印统计信息 + for (AudioManagementStatistics stat : statistics) { + System.out.println("门店: " + stat.getDealershipName() + + ", 日期: " + stat.getStatisticsDate() + + ", 录音数量: " + stat.getDealershipRecordingCount() + + ", 总时长: " + stat.getDealershipTotalDuration() + "秒" + + ", 平均时长: " + stat.getDealershipAvgDuration() + "秒"); + } + } + + @Test + public void testGetStatisticsByDealership() { + // 测试查询指定门店的统计数据 + Long dealershipId = 1L; // 假设门店ID为1 + LocalDate startDate = LocalDate.now().minusDays(7); + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsByDealership(dealershipId, startDate, endDate); + + assertNotNull(statistics, "统计列表不应该为null"); + System.out.println("查询到门店统计记录数: " + statistics.size()); + } + + @Test + public void testGetStatisticsBySales() { + // 测试查询指定销售人员的统计数据 + String salesId = "sales001"; // 假设销售人员ID + LocalDate startDate = LocalDate.now().minusDays(30); // 最近30天 + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsBySales(salesId, startDate, endDate); + + assertNotNull(statistics, "统计列表不应该为null"); + System.out.println("查询到销售人员统计记录数: " + statistics.size()); + + // 打印销售人员统计信息 + for (AudioManagementStatistics stat : statistics) { + System.out.println("销售人员: " + stat.getSalesName() + + ", 日期: " + stat.getStatisticsDate() + + ", 录音数量: " + stat.getSalesRecordingCount() + + ", 总时长: " + stat.getSalesTotalDuration() + "秒" + + ", 平均时长: " + stat.getSalesAvgDuration() + "秒"); + } + } + + @Test + public void testGetStatisticsByProject() { + // 测试查询指定项目的统计数据 + String projectId = "project001"; // 假设项目ID + LocalDate startDate = LocalDate.now().minusDays(30); // 最近30天 + LocalDate endDate = LocalDate.now().minusDays(1); + + List statistics = audioStatisticsService.getStatisticsByProject(projectId, startDate, endDate); + + assertNotNull(statistics, "统计列表不应该为null"); + System.out.println("查询到项目统计记录数: " + statistics.size()); + + // 打印项目统计信息 + for (AudioManagementStatistics stat : statistics) { + System.out.println("项目: " + stat.getProjectName() + + ", 日期: " + stat.getStatisticsDate() + + ", 录音数量: " + stat.getProjectRecordingCount() + + ", 总时长: " + stat.getProjectTotalDuration() + "秒" + + ", 平均时长: " + stat.getProjectAvgDuration() + "秒"); + } + } +}