package com.rj.scheduler; import com.rj.entity.CustomerProfileAnalysis; import com.rj.service.biz.impl.CustomerProfileAnalysisServiceImpl; 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.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.concurrent.ThreadLocalRandom; /** * 客户画像分析数据定时生成器 * 每天凌晨2:30插入模拟的客户画像分析数据 * * Author: 李中华 wx: spllzh email(qq): 28668817@qq.com * Date: 2025/1/3 */ @Slf4j @Component public class CustomerProfileMockInsertDataScheduler { // @Autowired // private ICustomerProfileAnalysisService customerProfileAnalysisService; @Autowired CustomerProfileAnalysisServiceImpl customerProfileAnalysisService;; // 项目数据 - 来自适配准真实.sql的project_management JSON private static final String[][] PROJECTS = { {"1957424785875087363", "奔驰C级销售项目"}, {"1957424785875087364", "奥迪A4L项目"}, {"1957424785875087365", "宝马3系项目"}, {"1957424785875087366", "雷克萨斯RX项目"}, {"1957424785875087367", "保时捷911项目"}, {"1957424785875087368", "威兹曼GT项目"} }; // 销售顾问数据 - 来自适配准真实.sql的sales_management JSON private static final String[][] SALES_PEOPLE = { {"1957424800000000001", "张明"}, {"1957424800000000002", "李华"}, {"1957424800000000003", "王强"}, {"1957424800000000004", "刘芳"}, {"1957424800000000005", "陈军"}, {"1957424800000000006", "赵敏"}, {"1957424800000000007", "孙丽"}, {"1957424800000000008", "周涛"} }; // 经销商门店数据 - 来自适配准真实.sql的经销商门店JSON private static final String[][] DEALERSHIPS = { {"华南区", "1957424710587330562", "广州奔驰4S店"}, {"华北区", "1957424711560409089", "北京奥迪4S店"}, {"华南区","1957424714953601026", "上海宝马4S店"}, {"华南区","1957424739267981313", "深圳雷克萨斯4S店"}, {"西南区","1957424740000000001", "成都保时捷4S店"}, {"西南区","1957424740000000017", "昆明威兹曼4S店"} }; // 大区数据 - 确保数据分布均衡 private static final String[] BIG_AREAS = { "华北区", "华东区", "华南区", "华中区", "西南区", "西北区", "东北区" }; // 单向开口率数据 - 来自适配准真实.sql的单向开口率JSON private static final String[][] OPENING_RATES = { {"1957424800000000001", "沃尔沃M90续航亮点"}, {"1957424800000000002", "lexus RX350智能互联"}, {"1957424800000000003", "别克品牌主张"}, {"1957424800000000004", "lexusRX470刹车问题"} }; // 客户姓名库 private static final String[] CUSTOMER_NAMES = { "王小明", "李小红", "张伟", "刘敏", "陈强", "杨丽", "赵刚", "黄娟", "周杰", "吴芳", "徐磊", "孙静", "马超", "朱琳", "胡军", "郭娜", "何涛", "罗雪", "高飞", "梁梅", "宋波", "唐丽", "韩强", "冯敏", "邓伟", "曹静", "彭军", "蒋娜", "薛涛", "方丽", "石强", "顾敏" }; // 客户电话前缀 private static final String[] PHONE_PREFIXES = { "138", "139", "150", "151", "152", "158", "159", "188", "189" }; // 购买类型 private static final String[] PURCHASE_TYPES = {"新购", "置换", "增购"}; // 付款方式 private static final String[] PAYMENT_WAYS = {"贷款", "全款", "分期"}; // 竞品品牌 private static final String[] COMPETITOR_BRANDS = {"奔驰", "宝马", "奥迪", "雷克萨斯", "保时捷", "威兹曼"}; // 购车用途 private static final String[] VEHICLE_USAGES = {"家用", "商用", "代步"}; // 实际驾驶人 private static final String[] PRIMARY_DRIVERS = {"本人", "家人", "其他"}; // 教育水平 private static final String[] EDUCATION_LEVELS = {"高中", "大专", "本科", "硕士", "博士"}; // 家庭结构 private static final String[] FAMILY_COMPOSITIONS = {"单身", "已婚无子女", "已婚有子女", "离异"}; // 职业 private static final String[] JOB_TITLES = {"教师", "工程师", "医生", "律师", "销售", "经理", "公务员", "自由职业"}; // 企业性质 private static final String[] COMPANY_TYPES = {"民营企业", "国企", "外企", "事业单位", "政府机关"}; // 所属行业 private static final String[] INDUSTRY_SECTORS = {"教育", "互联网", "金融", "制造业", "医疗", "房地产", "零售", "服务业"}; // 兴趣点 private static final String[] INTEREST_FOCUSES = {"智能驾驶", "油耗", "空间", "外观", "内饰", "动力", "安全", "环保"}; // 顾虑点 private static final String[] CONCERN_POINTS = {"保养成本", "保值率", "维修费用", "保险费用", "充电便利性", "续航里程"}; // 情感倾向 private static final String[] EMOTION_TENDENCIES = {"积极", "中性", "消极"}; /** * 每天凌晨2:30执行客户画像数据生成任务 */ @Scheduled(cron = "0 30 2 * * ?") public void generateDailyCustomerProfileData() { try { log.info("开始执行客户画像数据生成任务..."); // 生成今天的数据 LocalDateTime today = LocalDateTime.now().minusDays(0); int todayCount = generateCustomerProfileDataForDate(today); // 生成昨天的数据 LocalDateTime yesterday = LocalDateTime.now().minusDays(1); int count = generateCustomerProfileDataForDate(yesterday); // 生成前天天的数据 LocalDateTime yesterday2 = LocalDateTime.now().minusDays(2); int count2 = generateCustomerProfileDataForDate(yesterday2); log.info("客户画像数据生成任务执行完成,生成日期:{},生成记录数:{}", yesterday.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), count); log.info("客户画像数据生成任务执行完成,生成日期:{},生成记录数:{}", today.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), todayCount); } catch (Exception e) { log.error("客户画像数据生成任务执行失败", e); } } /** * 为指定日期生成客户画像数据 * * @param date 要生成数据的日期 * @return 生成的记录数 */ public int generateCustomerProfileDataForDate(LocalDateTime date) { try { log.info("开始为日期 {} 生成客户画像数据", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); // 每天生成20-50条记录,确保数据分布均匀 int recordCount = ThreadLocalRandom.current().nextInt(30, 50); int successCount = 0; for (int i = 0; i < recordCount; i++) { try { CustomerProfileAnalysis analysis = createRandomCustomerProfileAnalysis(date); boolean result = customerProfileAnalysisService.save(analysis); if (result) { successCount++; log.debug("成功生成第{}条客户画像数据 - 客户:{},门店:{},项目:{}", i + 1, analysis.getClientName(), analysis.getDealerName(), analysis.getProjectName()); } else { log.warn("保存第{}条客户画像数据失败", i + 1); } } catch (Exception e) { log.error("生成第{}条客户画像数据失败", i + 1, e); } } log.info("为日期 {} 生成客户画像数据完成,成功生成记录数:{}", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), successCount); return successCount; } catch (Exception e) { log.error("为日期 {} 生成客户画像数据失败", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), e); throw e; } } /** * 创建随机的客户画像分析数据 * * @param baseDate 基础日期 * @return 客户画像分析对象 */ private CustomerProfileAnalysis createRandomCustomerProfileAnalysis(LocalDateTime baseDate) { CustomerProfileAnalysis analysis = new CustomerProfileAnalysis(); ThreadLocalRandom random = ThreadLocalRandom.current(); // 基础字段 analysis.setProfileAnalysisRecordId("AI_" + System.currentTimeMillis() + "_" + random.nextInt(1000, 9999)); analysis.setAnalysisSceneType(random.nextInt(1, 5)); // 1-4随机 analysis.setRelatedBusinessId("BIZ_" + System.currentTimeMillis() + "_" + random.nextInt(1000, 9999)); // 随机选择经销商、项目、销售顾问、开口率,确保分布均匀 String[] dealership = DEALERSHIPS[random.nextInt(DEALERSHIPS.length)]; String[] project = PROJECTS[random.nextInt(PROJECTS.length)]; String[] salesPerson = SALES_PEOPLE[random.nextInt(SALES_PEOPLE.length)]; String[] openingRate = OPENING_RATES[random.nextInt(OPENING_RATES.length)]; // 设置经销商信息 analysis.setBigArea(dealership[0]); analysis.setDealerCode(dealership[1]); analysis.setDealerName(dealership[2]); // 设置项目信息 analysis.setProjectId(project[0]); analysis.setProjectName(project[1]); // 设置销售顾问信息 analysis.setSalesPersonId(salesPerson[0]); analysis.setSalesPersonName(salesPerson[1]); // 生成唯一ID long timestamp = System.currentTimeMillis(); int randomSuffix = random.nextInt(1000, 9999); analysis.setOpportunityId("OPP_" + timestamp + "_" + randomSuffix); analysis.setClientId("CLIENT_" + timestamp + "_" + randomSuffix); // 客户信息 analysis.setClientName(CUSTOMER_NAMES[random.nextInt(CUSTOMER_NAMES.length)]); analysis.setClientPhone(generateRandomPhone()); // 沟通时间 - 在基础日期的随机时间 LocalDateTime interactionTime = baseDate .withHour(random.nextInt(9, 18)) // 9-17点 .withMinute(random.nextInt(0, 60)) .withSecond(random.nextInt(0, 60)); analysis.setInteractionDate(interactionTime); // 客户画像信息字段 analysis.setPurchaseDate(String.valueOf(random.nextInt(1, 91))); // 1-90天 analysis.setPurchaseType(PURCHASE_TYPES[random.nextInt(PURCHASE_TYPES.length)]); analysis.setPaymentWay(PAYMENT_WAYS[random.nextInt(PAYMENT_WAYS.length)]); analysis.setCompetitorBrand(COMPETITOR_BRANDS[random.nextInt(COMPETITOR_BRANDS.length)]); analysis.setVehicleUsage(VEHICLE_USAGES[random.nextInt(VEHICLE_USAGES.length)]); analysis.setPrimaryDriver(PRIMARY_DRIVERS[random.nextInt(PRIMARY_DRIVERS.length)]); // 预算范围 int budgetMin = random.nextInt(20, 100); int budgetMax = budgetMin + random.nextInt(10, 50); analysis.setBudgetRange(budgetMin + "-" + budgetMax + "万"); // 教育水平 analysis.setEducationLevel(EDUCATION_LEVELS[random.nextInt(EDUCATION_LEVELS.length)]); // 家庭结构 analysis.setFamilyComposition(FAMILY_COMPOSITIONS[random.nextInt(FAMILY_COMPOSITIONS.length)]); // 月收入 int incomeMin = random.nextInt(5, 30); int incomeMax = incomeMin + random.nextInt(5, 20); analysis.setMonthlyIncome(incomeMin + "-" + incomeMax + "万"); // 职业信息 analysis.setJobTitle(JOB_TITLES[random.nextInt(JOB_TITLES.length)]); analysis.setCompanyType(COMPANY_TYPES[random.nextInt(COMPANY_TYPES.length)]); analysis.setIndustrySector(INDUSTRY_SECTORS[random.nextInt(INDUSTRY_SECTORS.length)]); // 兴趣和顾虑 analysis.setInterestFocus(INTEREST_FOCUSES[random.nextInt(INTEREST_FOCUSES.length)]); analysis.setConcernPoint(CONCERN_POINTS[random.nextInt(CONCERN_POINTS.length)]); analysis.setPurchaseQualification(random.nextBoolean() ? "是" : "否"); // 其他信息 - 利用新字段生成更详细的备注 analysis.setNotes(String.format("客户在%s门店咨询%s项目,销售顾问%s接待,关注点:%s,购车意向明确", dealership[1], project[1], salesPerson[1], openingRate[1])); analysis.setCurrentBrand(COMPETITOR_BRANDS[random.nextInt(COMPETITOR_BRANDS.length)]); analysis.setHomeAddress(generateRandomAddress()); analysis.setFamilySize(String.valueOf(random.nextInt(1, 6))); // 1-5人 analysis.setHobby("运动、旅游、音乐"); // 实体情感分析字段 - 随机生成情感倾向 generateEntityEmotions(analysis, random); // 系统字段 LocalDateTime now = LocalDateTime.now(); analysis.setCreatedAt(now); analysis.setUpdatedAt(now); analysis.setRecordVersion(0); analysis.setIsDeleted(0); analysis.setCreateSqlby("sched"); // 限制在8个字符以内 analysis.setUpdateSqlby("sched"); // 限制在8个字符以内 // 数据验证 validateCustomerProfileAnalysis(analysis); // 调试日志 - 输出生成的数据信息 log.debug("生成客户画像数据完成 - ID: {}, 客户: {}, 门店: {}, 项目: {}, 销售顾问: {}", analysis.getId(), analysis.getClientName(), analysis.getDealerName(), analysis.getProjectName(), analysis.getSalesPersonName()); return analysis; } /** * 生成实体情感分析数据 */ private void generateEntityEmotions(CustomerProfileAnalysis analysis, ThreadLocalRandom random) { String[] emotions = EMOTION_TENDENCIES; String[] reasons = {"客户对此方面表示满意", "客户对此方面持中性态度", "客户对此方面存在疑虑"}; // 品牌情感 int brandEmotion = random.nextInt(emotions.length); analysis.setEntityBrand(emotions[brandEmotion]); analysis.setEntityBrandReason(reasons[brandEmotion]); // 外观设计情感 int designEmotion = random.nextInt(emotions.length); analysis.setEntityDesign(emotions[designEmotion]); analysis.setEntityDesignReason(reasons[designEmotion]); // 颜色情感 int colorEmotion = random.nextInt(emotions.length); analysis.setEntityColor(emotions[colorEmotion]); analysis.setEntityColorReason(reasons[colorEmotion]); // 尺寸情感 int sizeEmotion = random.nextInt(emotions.length); analysis.setEntitySize(emotions[sizeEmotion]); analysis.setEntitySizeReason(reasons[sizeEmotion]); // 空间情感 int spaceEmotion = random.nextInt(emotions.length); analysis.setEntitySpace(emotions[spaceEmotion]); analysis.setEntitySpaceReason(reasons[spaceEmotion]); // 内饰情感 int interiorEmotion = random.nextInt(emotions.length); analysis.setEntityInterior(emotions[interiorEmotion]); analysis.setEntityInteriorReason(reasons[interiorEmotion]); // 音响情感 int audioEmotion = random.nextInt(emotions.length); analysis.setEntityAudio(emotions[audioEmotion]); analysis.setEntityAudioReason(reasons[audioEmotion]); // 车机情感 int ivinfoEmotion = random.nextInt(emotions.length); analysis.setEntityIvinfo(emotions[ivinfoEmotion]); analysis.setEntityIvinfoReason(reasons[ivinfoEmotion]); // 智驾情感 int fsdEmotion = random.nextInt(emotions.length); analysis.setEntityFsd(emotions[fsdEmotion]); analysis.setEntityFsdReason(reasons[fsdEmotion]); // 续航情感 int mileageEmotion = random.nextInt(emotions.length); analysis.setEntityMileage(emotions[mileageEmotion]); analysis.setEntityMileageReason(reasons[mileageEmotion]); // 安全情感 int safetyEmotion = random.nextInt(emotions.length); analysis.setEntitySafety(emotions[safetyEmotion]); analysis.setEntitySafetyReason(reasons[safetyEmotion]); // 环保情感 int ecoEmotion = random.nextInt(emotions.length); analysis.setEntityEco(emotions[ecoEmotion]); analysis.setEntityEcoReason(reasons[ecoEmotion]); // 绿牌/路权情感 int liscenseEmotion = random.nextInt(emotions.length); analysis.setEntityLiscense(emotions[liscenseEmotion]); analysis.setEntityLiscenseReason(reasons[liscenseEmotion]); // 价格情感 int priceEmotion = random.nextInt(emotions.length); analysis.setEntityPrice(emotions[priceEmotion]); analysis.setEntityPriceReason(reasons[priceEmotion]); // 购车权益情感 int benefitEmotion = random.nextInt(emotions.length); analysis.setEntityBenefit(emotions[benefitEmotion]); analysis.setEntityBenefitReason(reasons[benefitEmotion]); // 用车成本情感 int costEmotion = random.nextInt(emotions.length); analysis.setEntityCost(emotions[costEmotion]); analysis.setEntityCostReason(reasons[costEmotion]); // 性价比情感 int valueEmotion = random.nextInt(emotions.length); analysis.setEntityValue(emotions[valueEmotion]); analysis.setEntityValueReason(reasons[valueEmotion]); // 动力情感 int powerEmotion = random.nextInt(emotions.length); analysis.setEntityPower(emotions[powerEmotion]); analysis.setEntityPowerReason(reasons[powerEmotion]); // 操控情感 int controlEmotion = random.nextInt(emotions.length); analysis.setEntityControl(emotions[controlEmotion]); analysis.setEntityControlReason(reasons[controlEmotion]); // 置换政策情感 int tradeinEmotion = random.nextInt(emotions.length); analysis.setEntityTradein(emotions[tradeinEmotion]); analysis.setEntityTradeinReason(reasons[tradeinEmotion]); // 金融方案情感 int financeEmotion = random.nextInt(emotions.length); analysis.setEntityFinance(emotions[financeEmotion]); analysis.setEntityFinanceReason(reasons[financeEmotion]); // 质保情感 int warrantyEmotion = random.nextInt(emotions.length); analysis.setEntityWarranty(emotions[warrantyEmotion]); analysis.setEntityWarrantyReason(reasons[warrantyEmotion]); // 交付时间情感 int deliveryEmotion = random.nextInt(emotions.length); analysis.setEntityDelivery(emotions[deliveryEmotion]); analysis.setEntityDeliveryReason(reasons[deliveryEmotion]); } /** * 验证客户画像分析数据的完整性 * * @param analysis 客户画像分析对象 */ private void validateCustomerProfileAnalysis(CustomerProfileAnalysis analysis) { if (analysis == null) { throw new IllegalArgumentException("客户画像分析对象不能为空"); } // 验证必填字段 if (analysis.getProfileAnalysisRecordId() == null || analysis.getProfileAnalysisRecordId().trim().isEmpty()) { throw new IllegalArgumentException("AI分析记录ID不能为空"); } if (analysis.getDealerCode() == null || analysis.getDealerCode().trim().isEmpty()) { throw new IllegalArgumentException("经销商编码不能为空"); } if (analysis.getDealerName() == null || analysis.getDealerName().trim().isEmpty()) { throw new IllegalArgumentException("经销商门店名称不能为空"); } if (analysis.getProjectId() == null || analysis.getProjectId().trim().isEmpty()) { throw new IllegalArgumentException("项目ID不能为空"); } if (analysis.getProjectName() == null || analysis.getProjectName().trim().isEmpty()) { throw new IllegalArgumentException("项目名称不能为空"); } if (analysis.getSalesPersonId() == null || analysis.getSalesPersonId().trim().isEmpty()) { throw new IllegalArgumentException("销售顾问ID不能为空"); } if (analysis.getSalesPersonName() == null || analysis.getSalesPersonName().trim().isEmpty()) { throw new IllegalArgumentException("销售顾问姓名不能为空"); } if (analysis.getClientName() == null || analysis.getClientName().trim().isEmpty()) { throw new IllegalArgumentException("客户姓名不能为空"); } if (analysis.getClientPhone() == null || analysis.getClientPhone().trim().isEmpty()) { throw new IllegalArgumentException("客户电话不能为空"); } if (analysis.getInteractionDate() == null) { throw new IllegalArgumentException("沟通时间不能为空"); } log.debug("客户画像分析数据验证通过 - 客户:{},门店:{},项目:{},销售顾问:{}", analysis.getClientName(), analysis.getDealerName(), analysis.getProjectName(), analysis.getSalesPersonName()); } /** * 生成随机手机号 */ private String generateRandomPhone() { ThreadLocalRandom random = ThreadLocalRandom.current(); String prefix = PHONE_PREFIXES[random.nextInt(PHONE_PREFIXES.length)]; StringBuilder phone = new StringBuilder(prefix); for (int i = 0; i < 8; i++) { phone.append(random.nextInt(0, 10)); } return phone.toString(); } /** * 生成随机地址 */ private String generateRandomAddress() { String[] cities = {"北京市", "上海市", "广州市", "深圳市", "成都市", "杭州市", "南京市", "武汉市"}; String[] districts = {"朝阳区", "海淀区", "西城区", "东城区", "丰台区", "石景山区", "通州区", "昌平区"}; ThreadLocalRandom random = ThreadLocalRandom.current(); String city = cities[random.nextInt(cities.length)]; String district = districts[random.nextInt(districts.length)]; String street = "某某街道" + random.nextInt(1, 100) + "号"; return city + district + street; } /** * 手动触发数据生成任务(用于测试或补录数据) * * @param date 要生成数据的日期 * @return 生成的记录数 */ public int manualGenerateData(LocalDateTime date) { try { log.info("手动执行客户画像数据生成任务,生成日期:{}", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); int count = generateCustomerProfileDataForDate(date); log.info("手动客户画像数据生成任务执行完成,生成日期:{},生成记录数:{}", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), count); return count; } catch (Exception e) { log.error("手动客户画像数据生成任务执行失败,生成日期:{}", date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), e); throw e; } } }