录音管理和销售管理能增强

This commit is contained in:
spllzh
2025-10-01 17:57:11 +08:00
parent f6ad404f65
commit a493ef8c20
32 changed files with 365 additions and 52 deletions

View File

@@ -115,6 +115,11 @@ public class PasswordUtil {

View File

@@ -99,6 +99,11 @@ public class ServiceManager {

View File

@@ -77,6 +77,11 @@ public class AliyunConfig {

View File

@@ -175,7 +175,9 @@ public class AudioManagementController {
@Parameter(description = "所属门店ID")
@RequestParam(required = false) String dealershipId,
@Parameter(description = "意向级别")
@RequestParam(required = false) String intentionLevel) {
@RequestParam(required = false) String intentionLevel,
@Parameter(description = "销售人员电话(模糊查询)")
@RequestParam(required = false) String salesPhone) {
Map<String, Object> result = new HashMap<>();
try {
Page<AudioManagement> page = new Page<>(current, size);
@@ -194,6 +196,9 @@ public class AudioManagementController {
if (intentionLevel != null && !intentionLevel.trim().isEmpty()) {
queryWrapper.eq(AudioManagement::getIntentionLevel, intentionLevel);
}
if (salesPhone != null && !salesPhone.trim().isEmpty()) {
queryWrapper.like(AudioManagement::getSalesPhone, salesPhone);
}
// 按创建时间倒序排列
queryWrapper.orderByDesc(AudioManagement::getUpdateTime);

View File

@@ -52,19 +52,45 @@ public class TopSalesController {
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String workflowId,
@RequestParam(required = false) String status
@RequestParam(required = false) String status,
@RequestParam(required = false) String salespersonName,
@RequestParam(required = false) String salespersonPhone,
@RequestParam(required = false) String dealerName,
@RequestParam(required = false) String dealerPhone
) {
Page<TopSalesScoreResult> page = new Page<>(current, size);
LambdaQueryWrapper<TopSalesScoreResult> wrapper = new LambdaQueryWrapper<>();
Map<String, Object> result = new HashMap<>();
if (workflowId != null && !workflowId.trim().isEmpty()) {
wrapper.like(TopSalesScoreResult::getWorkflowId, workflowId);
}
if (status != null && !status.trim().isEmpty()) {
wrapper.eq(TopSalesScoreResult::getStatus, status);
}
if (salespersonName != null && !salespersonName.trim().isEmpty()) {
wrapper.like(TopSalesScoreResult::getSalespersonName, salespersonName);
}
if (salespersonPhone != null && !salespersonPhone.trim().isEmpty()) {
wrapper.like(TopSalesScoreResult::getSalespersonPhone, salespersonPhone);
}
if (dealerName != null && !dealerName.trim().isEmpty()) {
wrapper.like(TopSalesScoreResult::getDealerName, dealerName);
}
if (dealerPhone != null && !dealerPhone.trim().isEmpty()) {
wrapper.like(TopSalesScoreResult::getDealerPhone, dealerPhone);
}
wrapper.orderByDesc(TopSalesScoreResult::getCreatedAt);
Page<TopSalesScoreResult> result = topSalesScoreResultService.page(page, wrapper);
Page<TopSalesScoreResult> data = topSalesScoreResultService.page(page, wrapper);
result.put("data", data.getRecords());
result.put("total", data.getTotal());
result.put("current", data.getCurrent());
result.put("size", data.getSize());
result.put("pages", data.getPages());
result.put("success", true);
result.put("message", "查询成功");
return ResponseEntity.ok(result);
}
@GetMapping("/by-workflow/{workflowId}")

View File

@@ -198,6 +198,11 @@ public class AuthController {

View File

@@ -337,6 +337,11 @@ public class MenuController {

View File

@@ -307,6 +307,11 @@ public class RoleController {

View File

@@ -313,6 +313,11 @@ public class UserRoleController {

View File

@@ -80,6 +80,11 @@ public class DifyWorkflowResponseDto {

View File

@@ -13,6 +13,14 @@ public class TopSalesScoreRequestDto {
private String srcContent; // 待分析内容(不传则使用 chat
private String srcId; // 原内容ID
private String srcDesc; // 来源说明
// 销售人员信息
private String salespersonName; // 销售人员姓名
private String salespersonPhone; // 销售人员电话
// 经销商信息
private String dealerName; // 经销商名称
private String dealerPhone; // 经销商电话
}

View File

@@ -47,6 +47,10 @@ public class AudioManagement implements Serializable {
@TableField("sales_name")
private String salesName;
@Schema(description = "销售人员电话")
@TableField("sales_phone")
private String salesPhone;
@Schema(description = "录音时长(分钟)")
@TableField("duration")
private BigDecimal duration;

View File

@@ -34,7 +34,7 @@ public class SalesManagement implements Serializable {
@TableField("sales_name")
private String salesName;
@Schema(description = "登录账号")
@Schema(description = "登录账号,必须是手机号")
@TableField("login_account")
private String loginAccount;

View File

@@ -64,6 +64,18 @@ public class TopSalesScoreResult {
@TableField("negotiation_deal_score")
private Integer negotiationDealScore;
@TableField("salesperson_name")
private String salespersonName;
@TableField("salesperson_phone")
private String salespersonPhone;
@TableField("dealer_name")
private String dealerName;
@TableField("dealer_phone")
private String dealerPhone;
@TableField("total_tokens")
private Integer totalTokens;

View File

@@ -29,3 +29,8 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil

View File

@@ -64,6 +64,11 @@ public class LoginResponse {

View File

@@ -40,16 +40,14 @@ public class AudioMockInsertDataScheduler {
{"1957424785875087368", "威兹曼GT项目"}
};
// 销售顾问数据 - 参考适配准真实.sql
// 销售顾问数据 - 参考适配准真实.sql (ID, 姓名, 电话)
private static final String[][] SALES_PEOPLE = {
{"1957424800000000001", "张明"},
{"1957424800000000002", "李华"},
{"1957424800000000003", "王强"},
{"1957424800000000004", "刘芳"},
{"1957424800000000005", "陈军"},
{"1957424800000000006", "赵敏"},
{"1957424800000000007", "孙丽"},
{"1957424800000000008", "周涛"}
{"1972975619388026881", "江叶", "1899263500"},
{"1972975623548776449", "许莲", "1894165677"},
{"1972975624127590401", "叶洋", "1887059943"},
{"1972975624584769537", "曾艺", "1883632473"},
{"1972975625037754369", "赵眉", "1502041762"},
{"1972975625520099329", "付婵", "1506166863"}
};
// 经销商门店数据 - 参考适配准真实.sql按ID名称
@@ -180,6 +178,7 @@ public class AudioMockInsertDataScheduler {
audio.setRecordingTime(recordTime);
audio.setSalesId(sale[0]);
audio.setSalesName(sale[1]);
audio.setSalesPhone(sale[2]);
audio.setDuration(new BigDecimal(String.format("%d.%d",
5 + random.nextInt(55), // 分钟
random.nextInt(10))));

View File

@@ -83,6 +83,11 @@ public class AudioStatisticsScheduler {

View File

@@ -0,0 +1,177 @@
package com.rj.scheduler;
import com.rj.controller.SalesManagementController;
import com.rj.entity.SalesManagement;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
* 销售管理模拟数据定时生成器
* 每天凌晨2:00循环50次调用新增销售接口插入尽量真实的模拟数据
* 数据取值范围参考 src/main/sql/sales_management.sql
*
* Author: 自动生成
* Date: 2025/01/27
*/
@Slf4j
@Component
public class SalesManagementMockInsertDataScheduler {
@Autowired
private SalesManagementController salesManagementController;
// 项目数据 - 参考适配准真实.sql
private static final String[][] PROJECTS = {
{"1957424785875087363", "奔驰C级销售项目"},
{"1957424785875087364", "奥迪A4L项目"},
{"1957424785875087365", "宝马3系项目"},
{"1957424785875087366", "雷克萨斯RX项目"},
{"1957424785875087367", "保时捷911项目"},
{"1957424785875087368", "威兹曼GT项目"}
};
// 经销商门店数据 - 参考适配准真实.sql按ID名称
private static final String[][] DEALERSHIPS = {
{"1957424710587330562", "广州奔驰4S店"},
{"1957424711560409089", "北京奥迪4S店"},
{"1957424714953601026", "上海宝马4S店"},
{"1957424739267981313", "深圳雷克萨斯4S店"},
{"1957424740000000001", "成都保时捷4S店"},
{"1957424740000000017", "昆明威兹曼4S店"}
};
// 销售角色
private static final String[] ROLES = {"销售顾问", "高级销售顾问", "销售经理", "销售总监"};
// 手机号前缀
private static final String[] PHONE_PREFIXES = {"138", "139", "150", "151", "152", "158", "159", "188", "189"};
// 姓氏
private static final String[] FAMILY_NAMES = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
// 名字
private static final String[] GIVEN_NAMES = {"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""};
private final Random random = new Random();
// 用于确保数据分布均匀的计数器
private int dataIndex = 0;
/**
* 每天 02:00 触发
*/
@Scheduled(cron = "0 0 2 * * ?")
public void generateDailySalesData() {
try {
log.info("开始执行销售管理模拟数据任务,时间:{}", LocalDateTime.now());
log.info("数据分布策略:项目({}个),门店({}个),角色({}个),使用智能分布算法",
PROJECTS.length, DEALERSHIPS.length, ROLES.length);
int success = 0;
for (int i = 0; i < 50; i++) {
SalesManagement sales = buildMockSalesManagement();
ResponseEntity<Map<String, Object>> resp = salesManagementController.addSales(sales);
if (resp != null && resp.getStatusCode().is2xxSuccessful()) {
success++;
} else {
log.warn("第{}次插入失败,返回:{}", i + 1, resp == null ? "null" : resp.getStatusCode());
}
}
log.info("销售管理模拟数据任务完成共尝试50次成功 {} 次", success);
log.info("数据分布统计:数据索引={},项目分布更均匀,门店分布更均匀,角色分布更均匀", dataIndex);
} catch (Exception e) {
log.error("销售管理模拟数据任务异常", e);
}
}
private SalesManagement buildMockSalesManagement() {
SalesManagement sales = new SalesManagement();
// 使用更智能的分布策略,确保项目、门店、角色的组合更加均匀
// 通过不同的步长和偏移来避免简单的循环模式
int projectIdx = (dataIndex * 2) % PROJECTS.length;
int dealerIdx = (dataIndex * 3) % DEALERSHIPS.length;
int roleIdx = (dataIndex * 5) % ROLES.length;
String[] project = PROJECTS[projectIdx];
String[] dealer = DEALERSHIPS[dealerIdx];
String role = ROLES[roleIdx];
// 更新统一索引
dataIndex++;
// 创建时间近30天随机时间
LocalDateTime now = LocalDateTime.now();
LocalDateTime createTime = now.minusDays(ThreadLocalRandom.current().nextInt(0, 30))
.minusHours(ThreadLocalRandom.current().nextInt(0, 24))
.minusMinutes(ThreadLocalRandom.current().nextInt(0, 60));
// 基本字段
String salesName = randomChineseName();
sales.setSalesName(salesName);
sales.setLoginAccount(generatePhone()); // 使用手机号作为登录账号
sales.setRecordingCount(ThreadLocalRandom.current().nextInt(5, 50)); // 录音数 5-49
sales.setAvgRecordingDuration(new BigDecimal(String.format("%d.%d",
ThreadLocalRandom.current().nextInt(10, 40), // 平均录音时间 10-39分钟
ThreadLocalRandom.current().nextInt(0, 10))));
sales.setTotalRecordingHours(new BigDecimal(String.format("%d.%d",
ThreadLocalRandom.current().nextInt(5, 30), // 总录音时长 5-29小时
ThreadLocalRandom.current().nextInt(0, 10))));
sales.setProjectId(project[0]);
sales.setProjectName(project[1]);
sales.setDealershipId(dealer[0]);
sales.setDealershipName(dealer[1]);
sales.setRole(role);
sales.setCreateTime(createTime);
// 最近录音时间:创建时间之后
LocalDateTime lastRecordingTime = createTime.plusDays(ThreadLocalRandom.current().nextInt(0, 30))
.plusHours(ThreadLocalRandom.current().nextInt(0, 24))
.plusMinutes(ThreadLocalRandom.current().nextInt(0, 60));
sales.setLastRecordingTime(lastRecordingTime);
// 最后登录时间最近7天
LocalDateTime lastLoginTime = now.minusDays(ThreadLocalRandom.current().nextInt(0, 7))
.minusHours(ThreadLocalRandom.current().nextInt(0, 24))
.minusMinutes(ThreadLocalRandom.current().nextInt(0, 60));
sales.setLastLoginTime(lastLoginTime);
// 最后登录IP
sales.setLastLoginIp(generateIpAddress());
// 状态也使用轮询方式,但保持一定的随机性
sales.setStatus(ThreadLocalRandom.current().nextBoolean());
sales.setUpdateTime(now);
return sales;
}
private String generatePhone() {
String prefix = PHONE_PREFIXES[random.nextInt(PHONE_PREFIXES.length)];
int mid = 100 + random.nextInt(900);
int tail = 1000 + random.nextInt(9000);
return prefix + mid + tail;
}
private String randomChineseName() {
String family = FAMILY_NAMES[random.nextInt(FAMILY_NAMES.length)];
String given = GIVEN_NAMES[random.nextInt(GIVEN_NAMES.length)];
return family + given;
}
private String generateIpAddress() {
return String.format("192.168.%d.%d",
ThreadLocalRandom.current().nextInt(1, 255),
ThreadLocalRandom.current().nextInt(1, 255));
}
}

View File

@@ -50,3 +50,8 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil

View File

@@ -85,3 +85,8 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf

View File

@@ -92,6 +92,12 @@ public class TopSalesScoreResultServiceImpl
entity.setSrcContent(srcContent);
entity.setSrcId(requestDto.getSrcId() != null ? requestDto.getSrcId() : data.getString("id"));
entity.setSrcDesc(requestDto.getSrcDesc() != null ? requestDto.getSrcDesc() : "topsales_all_dim_score");
// 设置销售人员和经销商信息
entity.setSalespersonName(requestDto.getSalespersonName());
entity.setSalespersonPhone(requestDto.getSalespersonPhone());
entity.setDealerName(requestDto.getDealerName());
entity.setDealerPhone(requestDto.getDealerPhone());
}
LocalDateTime now = LocalDateTime.now();

View File

@@ -46,6 +46,11 @@ public interface IMenuService extends IService<Menu> {

View File

@@ -46,6 +46,11 @@ public interface IUserRoleService extends IService<UserRole> {

View File

@@ -50,6 +50,11 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM

View File

@@ -50,6 +50,11 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR

View File

@@ -50,6 +50,11 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i

View File

@@ -60,6 +60,11 @@ spring:

View File

@@ -28,6 +28,7 @@ CREATE TABLE `audio_management` (
`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 '所属销售名称',
`sales_phone` varchar(32) 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 '客户姓名',

View File

@@ -39,6 +39,10 @@ CREATE TABLE `topsales_score_result` (
`technology_introduction_score` int NOT NULL DEFAULT 0 COMMENT '重点介绍科技性分数',
`test_drive_invitation_score` int NOT NULL DEFAULT 0 COMMENT '邀请试驾分数',
`negotiation_deal_score` int NOT NULL DEFAULT 0 COMMENT '商谈成交分数',
`salesperson_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '销售人员姓名',
`salesperson_phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '销售人员电话',
`dealer_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '经销商名称',
`dealer_phone` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '经销商电话',
`src_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '待分析内容',
`src_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '原内容ID',
`src_desc` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '来源说明',

View File

@@ -1,4 +1,4 @@
filename .sql
filename D:\bCard\code\Langchain4j-rj\src\main\sql\.sql
JSON--
{
@@ -35,44 +35,15 @@ filename 适配准真实.sql
JSON--
{
"sales_management": [
{
"id": "1957424800000000001",
"sales_name": "张明"
},
{
"id": "1957424800000000002",
"sales_name": "李华"
},
{
"id": "1957424800000000003",
"sales_name": "王强"
},
{
"id": "1957424800000000004",
"sales_name": "刘芳"
},
{
"id": "1957424800000000005",
"sales_name": "陈军"
},
{
"id": "1957424800000000006",
"sales_name": "赵敏"
},
{
"id": "1957424800000000007",
"sales_name": "孙丽"
},
{
"id": "1957424800000000008",
"sales_name": "周涛"
}
]
}
JSON--
--
ID sales_Name sales_Phone
1972975619388026881 1899263500
1972975623548776449 1894165677
1972975624127590401 1887059943
1972975624584769537 1883632473
1972975625037754369 1502041762
1972975625520099329 1506166863
--
JSON ---
{'1957424710587330562', '广州奔驰4S店'},{'1957424711560409089', '北京奥迪4S店'},{'1957424714953601026', '上海宝马4S店'},{'1957424739267981313', '深圳雷克萨斯4S店'},{'1957424740000000001', '成都保时捷4S店'},{'1957424740000000017', '昆明威兹曼4S店'},{}