Просмотр исходного кода

Merge remote-tracking branch 'origin/master'

yjwang 1 месяц назад
Родитель
Сommit
79dcde819b
19 измененных файлов с 339 добавлено и 65 удалено
  1. 5 2
      fs-admin/src/main/java/com/fs/task/CrmCustomerAiProcessingTask.java
  2. 2 5
      fs-company/src/main/java/com/fs/company/controller/company/GeneralCustomerEntryController.java
  3. 1 0
      fs-company/src/main/java/com/fs/framework/config/SecurityConfig.java
  4. 4 6
      fs-company/src/test/java/com/mixLiu/test/mixLiuTester.java
  5. 2 0
      fs-service/src/main/java/com/fs/company/domain/CompanyVoiceRobotic.java
  6. 12 2
      fs-service/src/main/java/com/fs/company/service/impl/CompanyVoiceRoboticServiceImpl.java
  7. 45 8
      fs-service/src/main/java/com/fs/company/service/impl/GeneralCustomerEntryServiceImpl.java
  8. 3 3
      fs-service/src/main/java/com/fs/company/util/PhoneNumberUtil.java
  9. 14 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java
  10. 5 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  11. 22 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  12. 12 7
      fs-service/src/main/java/com/fs/crm/mapper/CrmCustomerAnalyzeMapper.java
  13. 2 0
      fs-service/src/main/java/com/fs/crm/mapper/CrmCustomerMapper.java
  14. 9 7
      fs-service/src/main/java/com/fs/crm/service/ICrmCustomerAnalyzeService.java
  15. 125 11
      fs-service/src/main/java/com/fs/crm/service/impl/CrmCustomerAnalyzeServiceImpl.java
  16. 8 8
      fs-service/src/main/java/com/fs/crm/utils/CrmCustomerAiTagUtil.java
  17. 4 0
      fs-service/src/main/resources/mapper/company/CompanyWxClientMapper.xml
  18. 19 6
      fs-service/src/main/resources/mapper/crm/CrmCustomerAnalyzeMapper.xml
  19. 45 0
      fs-user-app/src/main/java/com/fs/app/controller/course/XiaoShouYiCourseController.java

+ 5 - 2
fs-admin/src/main/java/com/fs/task/CrmCustomerAiProcessingTask.java

@@ -1,5 +1,6 @@
 package com.fs.task;
 
+import com.fs.crm.service.ICrmCustomerAnalyzeService;
 import com.google.common.collect.Lists;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -20,6 +21,7 @@ public class CrmCustomerAiProcessingTask {
     private final RedisTemplate redisTemplate;
 
     private static final String CRM_AI_REDIS_KEY = "crm:AI:data:processing";
+    private final ICrmCustomerAnalyzeService crmCustomerAnalyzeService;
 
     // 自定义线程池
     private final ExecutorService executorService = new ThreadPoolExecutor(
@@ -100,8 +102,9 @@ public class CrmCustomerAiProcessingTask {
                 // 获取数据
                 String customerId = data.get("customerId");
                 String dataJson = data.get("data");
-                //todo 业务!!!!!!1.ai沟通总结2.流失风险等级3.沟通摘要4.客户画像8.客户关注点9.客户意向度
-
+                String logId = data.get("logId");
+                //todo 业务!!!!!!1.ai沟通总结2.流失风险等级3.沟通摘要4.客户画像8.客户关注点9.客户意向度 //都要异步处理
+                crmCustomerAnalyzeService.aiGeneratedCustomerPortrait(customerId,dataJson,logId);
 
 
                 // 模拟业务处理

+ 2 - 5
fs-company/src/main/java/com/fs/company/controller/company/GeneralCustomerEntryController.java

@@ -4,10 +4,7 @@ import com.fs.common.core.domain.R;
 import com.fs.company.param.EntryCustomerParam;
 import com.fs.company.service.IGeneralCustomerEntryService;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 /**
  * @author MixLiu
@@ -22,7 +19,7 @@ public class GeneralCustomerEntryController {
     IGeneralCustomerEntryService iGeneralCustomerEntryService;
 
     @PostMapping("/entryCustomer")
-    public R entryCustomer(EntryCustomerParam param){
+    public R entryCustomer(@RequestBody EntryCustomerParam param){
         iGeneralCustomerEntryService.entryCustomer(param);
        return R.ok("success");
     }

+ 1 - 0
fs-company/src/main/java/com/fs/framework/config/SecurityConfig.java

@@ -135,6 +135,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
                 .antMatchers("/qw/user/selectCloudByCompany").anonymous()
                 .antMatchers("/live/LiveMixLiuTestOpen/**").anonymous()
                 .antMatchers("/app/common/callbackAfterSendSingleMsgCommand").anonymous()
+                .antMatchers("/company/general/customer/**").anonymous()
                 // 除上面外的所有请求全部需要鉴权认证
                 .anyRequest().authenticated()
                 .and()

+ 4 - 6
fs-company/src/test/java/com/mixLiu/test/mixLiuTester.java

@@ -33,12 +33,10 @@ public class mixLiuTester {
 
     @Test
     public void test11(){
-        FsDoctor doctorMap = new FsDoctor();
-        doctorMap.setDoctorId(1L);
-        doctorMap.setJpushId("123");
+        String substring = "18580336425".substring(7, 11);
+        System.out.println(substring);
 
-        if(doctorMap.getStatus() != 1){
-            System.out.println("开始删除处方医生");
-        }
     }
+
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyVoiceRobotic.java

@@ -131,6 +131,8 @@ public class CompanyVoiceRobotic {
     private String qwUserId;
     private Integer taskType;
     private Integer sceneType;
+    @TableField(exist = false)
+    private String sceneTypeName;
     private LocalTime availableStartTime;
     private LocalTime availableEndTime;
 }

+ 12 - 2
fs-service/src/main/java/com/fs/company/service/impl/CompanyVoiceRoboticServiceImpl.java

@@ -144,10 +144,20 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
     public List<CompanyVoiceRobotic> selectCompanyVoiceRoboticList(CompanyVoiceRobotic companyVoiceRobotic){
         return companyVoiceRoboticMapper.selectCompanyVoiceRoboticList(companyVoiceRobotic);
     }
+
     @Override
     @DataScope(deptAlias = "d", userAlias = "u")
-    public List<CompanyVoiceRobotic> selectCompanyVoiceRoboticListCompany(CompanyVoiceRobotic companyVoiceRobotic){
-        return companyVoiceRoboticMapper.selectCompanyVoiceRoboticListCompany(companyVoiceRobotic);
+    public List<CompanyVoiceRobotic> selectCompanyVoiceRoboticListCompany(CompanyVoiceRobotic companyVoiceRobotic) {
+        List<CompanyVoiceRobotic> companyVoiceRobotics = companyVoiceRoboticMapper.selectCompanyVoiceRoboticListCompany(companyVoiceRobotic);
+        List<SysDictData> taskSceneType = DictUtils.getDictCache("task_scene_type");
+        companyVoiceRobotics.forEach(a -> {
+            if (null != a.getSceneType()) {
+                taskSceneType.stream().filter(b -> b.getDictValue().equals(a.getSceneType().toString())).findFirst().ifPresent(c -> {
+                    a.setSceneTypeName(c.getDictLabel());
+                });
+            }
+        });
+        return companyVoiceRobotics;
     }
 
     /**

+ 45 - 8
fs-service/src/main/java/com/fs/company/service/impl/GeneralCustomerEntryServiceImpl.java

@@ -11,9 +11,11 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.entity.SysDictData;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.spring.SpringUtils;
+import com.fs.company.domain.CompanyConfig;
 import com.fs.company.domain.CompanyVoiceRobotic;
 import com.fs.company.mapper.CompanyVoiceRoboticMapper;
 import com.fs.company.param.EntryCustomerParam;
+import com.fs.company.service.ICompanyConfigService;
 import com.fs.company.service.ICompanyVoiceRoboticService;
 import com.fs.company.service.IGeneralCustomerEntryService;
 import com.fs.company.util.CryptoUtil;
@@ -39,6 +41,7 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
 import java.time.LocalTime;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
@@ -67,6 +70,8 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
     @Autowired
     private SysDictDataMapper sysDictDataMapper;
 
+    @Autowired
+    ICompanyConfigService companyConfigService;
     /**
      * 录入客户
      *
@@ -210,11 +215,28 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
         }
         //客户数据解析,是否包含对话图 对话图解析标签&意向度标识 todo 庄旭组在研发此功能
         if (StringUtils.isNotBlank(data.getDialogue())) {
-            JSONObject jsonObject = analysisDialogue(data.getDialogue());
-            if (jsonObject != null) {
-                data.setIntention(jsonObject.getString("intention"));
-                data.setTags(jsonObject.getString("tags"));
+            try {
+                List<CrmCustomerAiTagVo> aiTags = getAiTags(data.getDialogue());
+                if(null != aiTags && !aiTags.isEmpty()){
+                    String tags = data.getTags()==null?"":data.getTags();
+                    StringBuilder sb = new StringBuilder(tags);
+                    aiTags.forEach(a->{
+                        sb.append(a.getName()).append(",");
+                    });
+                    if(sb.length() >0 &&  ',' == sb.charAt(sb.length()-1)){
+                        data.setTags(sb.substring(0,sb.length()-1));
+                    }
+                    data.setTags(sb.toString());
+                }
+                //todo 意向度分析
+            } catch (JsonProcessingException e) {
+                log.error("导入用户标签数据解析异常", e);
             }
+//            JSONObject jsonObject = analysisDialogue(data.getDialogue());
+//            if (jsonObject != null) {
+//                data.setIntention(jsonObject.getString("intention"));
+//                data.setTags(jsonObject.getString("tags"));
+//            }
         }
         //客户数据插入
         insertCrmCustomer(data);
@@ -239,10 +261,18 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
             log.error("手机号格式错误,{}", param.getMobile());
             return false;
         }
-        Long  customerId = crmCustomerMapper.selectCrmCustomerByCrmMobile(param.getMobile());
-        // todo 添加配置是否允许重复客户导入
-        if( null != customerId && true){
-            return false;
+        //根据配置项是否允许重复手机号客户录入
+        CompanyConfig config=companyConfigService.selectCompanyConfigByKey(param.getCompanyId(),"cId.config");
+        if (null != config) {
+            String configValue = config.getConfigValue();
+            JSONObject configObj = JSONObject.parseObject(configValue);
+            if(configObj.containsKey("allowRepeatCustomer") && null != configObj.getBoolean("allowRepeatCustomer") && !configObj.getBoolean("allowRepeatCustomer")){
+                Long  customerId = crmCustomerMapper.selectCrmCustomerByCrmMobileAndCompanyId(param.getCompanyId(),param.getMobile());
+                if( null != customerId && true){
+                    log.error("手机号重复数据,{}", param.getMobile());
+                    return false;
+                }
+            }
         }
         return true;
     }
@@ -265,6 +295,13 @@ public class GeneralCustomerEntryServiceImpl implements IGeneralCustomerEntrySer
     public void insertCrmCustomer(EntryCustomerParam data){
         CrmCustomer insertObj = new CrmCustomer();
         BeanUtils.copyProperties(data,insertObj);
+        if(StringUtils.isBlank(insertObj.getCustomerName())){
+            insertObj.setCustomerName("客户"+insertObj.getMobile().substring(7,11));
+        }
+        insertObj.setIsLine(1);
+        if(null == insertObj.getCreateTime()){
+            insertObj.setCreateTime(new Date());
+        }
         crmCustomerMapper.insertCrmCustomer(insertObj);
         data.setCustomerId(insertObj.getCustomerId());
     }

+ 3 - 3
fs-service/src/main/java/com/fs/company/util/PhoneNumberUtil.java

@@ -56,20 +56,20 @@ public class PhoneNumberUtil {
     public static boolean isValid(String mobile) {
         // 空值校验
         if (StringUtils.isBlank(mobile)) {
-            log.warn("手机号校验失败:手机号为空");
+            log.error("手机号校验失败:手机号为空");
             return false;
         }
         // 去除首尾空格
         String trimmedMobile = mobile.trim();
         // 长度校验(手机号必须为11位)
         if (trimmedMobile.length() != MOBILE_LENGTH) {
-            log.warn("手机号校验失败:手机号长度不正确,当前长度={}", trimmedMobile.length());
+            log.error("手机号校验失败:手机号长度不正确,当前长度={}", trimmedMobile.length());
             return false;
         }
         // 格式校验(使用正则表达式)
         boolean isValid = MOBILE_PATTERN.matcher(trimmedMobile).matches();
         if (!isValid) {
-            log.warn("手机号校验失败:手机号格式不正确,mobile={}", trimmedMobile);
+            log.error("手机号校验失败:手机号格式不正确,mobile={}", trimmedMobile);
         }
         return isValid;
     }

+ 14 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseAnswerLogsMapper.java

@@ -118,6 +118,20 @@ public interface FsCourseAnswerLogsMapper
             "</script>"})
     FsCourseAnswerLogs selectRightLogByCourseVideo(@Param("videoId") Long videoId,@Param("userId") Long userId,@Param("qwUserId") String qwUserId);
 
+    @Select({"<script> " +
+            "select * from fs_course_answer_logs where video_id = #{videoId} and user_id = #{userId} and is_right = 1 " +
+            "<if test = 'qwUserId !=null '> " +
+            "and qw_user_id = #{qwUserId} " +
+            "</if>" +
+            "<if test = 'periodId !=null '> " +
+            " and period_id = #{periodId} " +
+            "</if>" +
+            "limit 1 " +
+            "</script>"})
+    FsCourseAnswerLogs selectRightLogByCourseVideoWithPeriodId(@Param("videoId") Long videoId,
+                                                               @Param("userId") Long userId,
+                                                               @Param("qwUserId") String qwUserId,
+                                                               @Param("periodId") Long periodId);
 
     @Select({"<script> " +
             "select count(0) from fs_course_answer_logs where video_id = #{videoId} and user_id = #{userId} and is_right = 0 " +

+ 5 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java

@@ -267,4 +267,9 @@ public interface IFsUserCourseVideoService extends IService<FsUserCourseVideo> {
      * @return list
      */
     List<OptionsVO> selectVideoOptionsByCourseId(Long courseId);
+
+    /**
+     * 获取销售易看课详情
+     */
+    ResponseResult<FsUserCourseVideoLinkDetailsVO> getXiaoShouYiCourseVideoDetails(FsUserCourseVideoLinkParam param);
 }

+ 22 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -1630,6 +1630,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         }
 
         FsCourseAnswerLogs rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
+        if("泽林文化".equals(signProjectName)){
+            rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideoWithPeriodId(param.getVideoId(), param.getUserId(), param.getQwUserId(),param.getPeriodId());
+        }
         if (rightLog == null) {
             logger.error("未答题:{}", param.getUserId());
             return R.error("未答题");
@@ -4630,5 +4633,24 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
             vo.setFsStoreProductScrms(new ArrayList<>());
         }
     }
+
+    /**
+     * 获取销售易看课详情
+     */
+    @Override
+    public ResponseResult<FsUserCourseVideoLinkDetailsVO> getXiaoShouYiCourseVideoDetails(FsUserCourseVideoLinkParam param) {
+        // 1、获取视频详情、问题详情
+        ResponseResult<FsUserCourseVideoDetailsVO> videoDetails = this.getVideoDetails(param.getVideoId());
+        FsUserCourseVideoDetailsVO courseVideoDetails = videoDetails.getData() != null ? videoDetails.getData() : null;
+
+        long duration = 0L;
+        int isFinish = 0;
+        FsUserCourseVideoLinkDetailsVO vo = new FsUserCourseVideoLinkDetailsVO();
+        vo.setCourseVideoDetails(courseVideoDetails);
+        vo.setIsFinish(isFinish);
+        vo.setPlayDuration(duration);
+
+        return ResponseResult.ok(vo);
+    }
 }
 

+ 12 - 7
fs-service/src/main/java/com/fs/crm/mapper/CrmCustomerAnalyzeMapper.java

@@ -3,17 +3,18 @@ package com.fs.crm.mapper;
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.crm.domain.CrmCustomerAnalyze;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 客户聊天记录分析Mapper接口
- * 
+ *
  * @author fs
  * @date 2026-03-24
  */
 public interface CrmCustomerAnalyzeMapper extends BaseMapper<CrmCustomerAnalyze>{
     /**
      * 查询客户聊天记录分析
-     * 
+     *
      * @param id 客户聊天记录分析主键
      * @return 客户聊天记录分析
      */
@@ -21,7 +22,7 @@ public interface CrmCustomerAnalyzeMapper extends BaseMapper<CrmCustomerAnalyze>
 
     /**
      * 查询客户聊天记录分析列表
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 客户聊天记录分析集合
      */
@@ -29,7 +30,7 @@ public interface CrmCustomerAnalyzeMapper extends BaseMapper<CrmCustomerAnalyze>
 
     /**
      * 新增客户聊天记录分析
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 结果
      */
@@ -37,7 +38,7 @@ public interface CrmCustomerAnalyzeMapper extends BaseMapper<CrmCustomerAnalyze>
 
     /**
      * 修改客户聊天记录分析
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 结果
      */
@@ -45,7 +46,7 @@ public interface CrmCustomerAnalyzeMapper extends BaseMapper<CrmCustomerAnalyze>
 
     /**
      * 删除客户聊天记录分析
-     * 
+     *
      * @param id 客户聊天记录分析主键
      * @return 结果
      */
@@ -53,9 +54,13 @@ public interface CrmCustomerAnalyzeMapper extends BaseMapper<CrmCustomerAnalyze>
 
     /**
      * 批量删除客户聊天记录分析
-     * 
+     *
      * @param ids 需要删除的数据主键集合
      * @return 结果
      */
     int deleteCrmCustomerAnalyzeByIds(Long[] ids);
+
+    CrmCustomerAnalyze selectLatestOne(@Param("customerId") Long customerId);
+
+    int updateCustomerPortrait(@Param("customerId") Long customerId,@Param("customerPortrait") String customerPortrait);
 }

+ 2 - 0
fs-service/src/main/java/com/fs/crm/mapper/CrmCustomerMapper.java

@@ -969,6 +969,8 @@ public interface CrmCustomerMapper extends BaseMapper<CrmCustomer> {
     @Select("select customer_id  from  crm_customer where mobile=#{remarkMobile} limit 1")
     Long selectCrmCustomerByCrmMobile(String remarkMobile);
 
+    @Select("select customer_id  from  crm_customer where company_id = #{companyId} and  mobile=#{remarkMobile} limit 1")
+    Long selectCrmCustomerByCrmMobileAndCompanyId(@Param("companyId") Long companyId, @Param("remarkMobile") String remarkMobile);
     /**
      *   根据userid和外部联系人id获取到客户详情
      */

+ 9 - 7
fs-service/src/main/java/com/fs/crm/service/ICrmCustomerAnalyzeService.java

@@ -6,14 +6,14 @@ import com.fs.crm.domain.CrmCustomerAnalyze;
 
 /**
  * 客户聊天记录分析Service接口
- * 
+ *
  * @author fs
  * @date 2026-03-24
  */
 public interface ICrmCustomerAnalyzeService extends IService<CrmCustomerAnalyze>{
     /**
      * 查询客户聊天记录分析
-     * 
+     *
      * @param id 客户聊天记录分析主键
      * @return 客户聊天记录分析
      */
@@ -21,7 +21,7 @@ public interface ICrmCustomerAnalyzeService extends IService<CrmCustomerAnalyze>
 
     /**
      * 查询客户聊天记录分析列表
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 客户聊天记录分析集合
      */
@@ -29,7 +29,7 @@ public interface ICrmCustomerAnalyzeService extends IService<CrmCustomerAnalyze>
 
     /**
      * 新增客户聊天记录分析
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 结果
      */
@@ -37,7 +37,7 @@ public interface ICrmCustomerAnalyzeService extends IService<CrmCustomerAnalyze>
 
     /**
      * 修改客户聊天记录分析
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 结果
      */
@@ -45,7 +45,7 @@ public interface ICrmCustomerAnalyzeService extends IService<CrmCustomerAnalyze>
 
     /**
      * 批量删除客户聊天记录分析
-     * 
+     *
      * @param ids 需要删除的客户聊天记录分析主键集合
      * @return 结果
      */
@@ -53,9 +53,11 @@ public interface ICrmCustomerAnalyzeService extends IService<CrmCustomerAnalyze>
 
     /**
      * 删除客户聊天记录分析信息
-     * 
+     *
      * @param id 客户聊天记录分析主键
      * @return 结果
      */
     int deleteCrmCustomerAnalyzeById(Long id);
+
+    void aiGeneratedCustomerPortrait(String customerId, String dataJson,String logId);
 }

+ 125 - 11
fs-service/src/main/java/com/fs/crm/service/impl/CrmCustomerAnalyzeServiceImpl.java

@@ -1,25 +1,45 @@
 package com.fs.crm.service.impl;
 
-import java.util.List;
-import com.fs.common.utils.DateUtils;
+import cn.hutool.core.lang.TypeReference;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import org.springframework.stereotype.Service;
-import com.fs.crm.mapper.CrmCustomerAnalyzeMapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.entity.SysDictData;
+import com.fs.common.utils.DateUtils;
 import com.fs.crm.domain.CrmCustomerAnalyze;
+import com.fs.crm.mapper.CrmCustomerAnalyzeMapper;
 import com.fs.crm.service.ICrmCustomerAnalyzeService;
+import com.fs.crm.utils.CrmCustomerAiTagUtil;
+import com.fs.system.mapper.SysDictDataMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 客户聊天记录分析Service业务层处理
- * 
+ *
  * @author fs
  * @date 2026-03-24
  */
 @Service
 public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyzeMapper, CrmCustomerAnalyze> implements ICrmCustomerAnalyzeService {
 
+    @Autowired
+    private SysDictDataMapper sysDictDataMapper;
     /**
      * 查询客户聊天记录分析
-     * 
+     *
      * @param id 客户聊天记录分析主键
      * @return 客户聊天记录分析
      */
@@ -31,7 +51,7 @@ public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyz
 
     /**
      * 查询客户聊天记录分析列表
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 客户聊天记录分析
      */
@@ -43,7 +63,7 @@ public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyz
 
     /**
      * 新增客户聊天记录分析
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 结果
      */
@@ -56,7 +76,7 @@ public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyz
 
     /**
      * 修改客户聊天记录分析
-     * 
+     *
      * @param crmCustomerAnalyze 客户聊天记录分析
      * @return 结果
      */
@@ -68,7 +88,7 @@ public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyz
 
     /**
      * 批量删除客户聊天记录分析
-     * 
+     *
      * @param ids 需要删除的客户聊天记录分析主键
      * @return 结果
      */
@@ -80,7 +100,7 @@ public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyz
 
     /**
      * 删除客户聊天记录分析信息
-     * 
+     *
      * @param id 客户聊天记录分析主键
      * @return 结果
      */
@@ -89,4 +109,98 @@ public class CrmCustomerAnalyzeServiceImpl extends ServiceImpl<CrmCustomerAnalyz
     {
         return baseMapper.deleteCrmCustomerAnalyzeById(id);
     }
+    private static final String AI_PORTRAIT = "crm_ai_portrait";
+    private static final ObjectMapper mapper = new ObjectMapper();
+    //ai获取客户画像
+    @Override
+    @Async
+    public void aiGeneratedCustomerPortrait(String customerId, String dataJson,String logId) {
+        Map<String, Object> stringObjectMap = buildRequestParam(Long.valueOf(customerId), dataJson);
+        R aiResponse = CrmCustomerAiTagUtil.callAiService(stringObjectMap, Long.valueOf(logId));
+
+        JSONObject root = JSON.parseObject(JSONUtil.toJsonStr(aiResponse));
+
+// 获取 data.responseData
+        JSONArray responseData = root.getJSONObject("data").getJSONArray("responseData");
+
+        JSONObject userInfo = null;
+
+// 遍历 responseData
+        for (int i = 0; i < responseData.size(); i++) {
+            JSONObject node = responseData.getJSONObject(i);
+            JSONArray historyPreview = node.getJSONArray("historyPreview");
+
+            if (historyPreview != null) {
+                for (int j = 0; j < historyPreview.size(); j++) {
+                    JSONObject historyItem = historyPreview.getJSONObject(j);
+
+                    // 找到 obj 为 "AI" 的项
+                    if ("AI".equals(historyItem.getString("obj"))) {
+                        String valueStr = historyItem.getString("value");
+                        JSONObject valueObj = JSON.parseObject(valueStr);
+                        userInfo = valueObj.getJSONObject("userInfo");
+                        break;
+                    }
+                }
+            }
+            if (userInfo != null) break;
+        }
+
+        if (userInfo != null) {
+            baseMapper.updateCustomerPortrait(Long.valueOf(customerId),userInfo.toString());
+        }
+    }
+    private Map<String, Object> buildRequestParam(Long customerId,
+                                                         String communication) {
+        Map<String, Object> requestParam = new HashMap<>();
+
+        // 获取各类数据
+        HashMap<String, Object> history = new HashMap<>();
+        history.put("history", communication);
+        Map<String, Object> userInfo = getUserInfo(customerId);
+        String likeRatio ="";
+        if (!userInfo.isEmpty()){
+            likeRatio = (String) userInfo.remove("likeRatio");
+        }
+        // 合并数据
+        requestParam.putAll(history);
+        requestParam.putAll(userInfo);
+
+        // 设置其他参数
+        requestParam.put("tagInfos", Collections.emptyList());
+        requestParam.put("isRepository", "");
+        requestParam.put("userContent", "");
+        requestParam.put("aiContent", "");
+        requestParam.put("likeRatio", likeRatio);
+        requestParam.put("modelType","客户画像");
+
+        return requestParam;
+    }
+    private Map<String, Object> getUserInfo(Long customerId) {
+        CrmCustomerAnalyze crmCustomerAnalyze = baseMapper.selectLatestOne(customerId);
+        if (ObjectUtil.isEmpty(crmCustomerAnalyze))throw new RuntimeException("客户信息不存在");
+        HashMap<String, String> userInfo = new HashMap<String, String>();
+//        userInfo.put("name", crmCustomerAnalyze.getCustomerName()==null?"" : crmCustomerAnalyze.getCustomerName());
+        List<SysDictData> portraits = sysDictDataMapper.selectDictDataByType(AI_PORTRAIT);
+        List<String> dictValue = portraits.stream().map(SysDictData::getDictValue).collect(Collectors.toList());
+        Map<String, String> portraitMap = portraits.stream().collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
+
+        if (crmCustomerAnalyze.getCustomerPortraitJson() != null){
+            Map<String, String> portraitList = JSON.parseObject(
+                    crmCustomerAnalyze.getCustomerPortraitJson(),
+                    new TypeReference<Map<String, String>>() {}
+            );
+            portraitList.forEach((k, v)->{
+                if(ObjectUtil.isNotEmpty(portraitMap.get(k)))userInfo.put(portraitMap.get(k),v);
+            });
+
+        }else {
+            dictValue.forEach(o->{
+                userInfo.put(o, "");
+            });
+        }
+        HashMap<String, Object> result = new HashMap<>();
+        result.put("userInfo", userInfo);
+        return result;
+    }
 }

+ 8 - 8
fs-service/src/main/java/com/fs/crm/utils/CrmCustomerAiTagUtil.java

@@ -76,17 +76,18 @@ public class CrmCustomerAiTagUtil {
         // 4. 解析响应并保存
         List<CrmCustomerAiTagVo> results = parseAiResponse(aiResponse, customerId);
 
-        // 5. 异步保存到Redis,后续调用ai分析其他数据
-        saveToRedisAsync(customerId, aiResponse);
+        // 5. 异步保存到Redis,后续调用ai分析其他数据 //暂时只传聊天记录
+        saveToRedisAsync(customerId, (String) requestParam.get("history"),logId);
         return results;
     }
-    private static void saveToRedisAsync(Long customerId, R aiResponse) {
+    private static void saveToRedisAsync(Long customerId, String aiResponse, Long logId) {
         // 使用线程池异步保存,避免影响主流程
         CompletableFuture.runAsync(() -> {
             try {
                 Map<String, Object> dataMap = new HashMap<>();
                 dataMap.put("customerId", customerId);
                 dataMap.put("data", aiResponse);
+                dataMap.put("logId", logId);
                 dataMap.put("timestamp", System.currentTimeMillis());
 
                 RedisTemplate<String, Object> redisTemplate = SpringUtils.getBean(RedisTemplate.class);
@@ -121,7 +122,7 @@ public class CrmCustomerAiTagUtil {
                 .map(tag -> buildTagVo(tag, customerId))
                 .collect(Collectors.toList());
     }
-    private static R callAiService(Map<String, Object> requestParam, Long logId) {
+    public static R callAiService(Map<String, Object> requestParam, Long logId) {
         try {
             String requestJson = mapper.writeValueAsString(requestParam);
 
@@ -172,6 +173,7 @@ public class CrmCustomerAiTagUtil {
         requestParam.put("userContent", "");
         requestParam.put("aiContent", "");
         requestParam.put("likeRatio", likeRatio);
+        requestParam.put("modelType","ai标签");
 
         return requestParam;
     }
@@ -342,8 +344,8 @@ public class CrmCustomerAiTagUtil {
             customerMapper.insertCrmCustomerInfo(crmCustomerInfo);
         }
         HashMap<String, String> userInfo = new HashMap<String, String>();
-        userInfo.put("name", crmCustomerInfo.getName());
-        userInfo.put("sex", crmCustomerInfo.getSex());
+        userInfo.put("name", crmCustomerInfo.getName()==null?"" : crmCustomerInfo.getName());
+        userInfo.put("sex", crmCustomerInfo.getSex()==null?"" : crmCustomerInfo.getSex().toString());
         userInfo.put("age", "");
         userInfo.put("city", "");
         userInfo.put("habits", "");
@@ -369,8 +371,6 @@ public class CrmCustomerAiTagUtil {
         result.put("history", history);
         ArrayList<Map> maps = new ArrayList<>();
         //存入crm_customer_info
-        HashMap<String, Object> time = new HashMap<>();
-        maps.add(time);
         communication.forEach(o->{
             String role = (String) o.get("role");
             String content = (String) o.get("content");

+ 4 - 0
fs-service/src/main/resources/mapper/company/CompanyWxClientMapper.xml

@@ -96,6 +96,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="remark != null">remark,</if>
             <if test="createTime != null">create_time,</if>
             <if test="traceId != null">trace_id,</if>
+            <if test="roboticId != null">robotic_id,</if>
+            <if test="customerId != null">customer_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="roboticWxId != null">#{roboticWxId},</if>
@@ -108,6 +110,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="remark != null">#{remark},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="traceId != null">#{traceId},</if>
+            <if test="roboticId != null">#{roboticId},</if>
+            <if test="customerId != null">#{customerId},</if>
          </trim>
     </insert>
 

+ 19 - 6
fs-service/src/main/resources/mapper/crm/CrmCustomerAnalyzeMapper.xml

@@ -3,7 +3,7 @@
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.fs.crm.mapper.CrmCustomerAnalyzeMapper">
-    
+
     <resultMap type="CrmCustomerAnalyze" id="CrmCustomerAnalyzeResult">
         <result property="id"    column="id"    />
         <result property="customerId"    column="customer_id"    />
@@ -28,7 +28,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectCrmCustomerAnalyzeList" parameterType="CrmCustomerAnalyze" resultMap="CrmCustomerAnalyzeResult">
         <include refid="selectCrmCustomerAnalyzeVo"/>
-        <where>  
+        <where>
             <if test="customerId != null "> and customer_id = #{customerId}</if>
             <if test="customerName != null  and customerName != ''"> and customer_name like concat('%', #{customerName}, '%')</if>
             <if test="customerPortraitJson != null  and customerPortraitJson != ''"> and customer_portrait_json = #{customerPortraitJson}</if>
@@ -43,12 +43,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="reserveStr != null  and reserveStr != ''"> and reserve_str = #{reserveStr}</if>
         </where>
     </select>
-    
+
     <select id="selectCrmCustomerAnalyzeById" parameterType="Long" resultMap="CrmCustomerAnalyzeResult">
         <include refid="selectCrmCustomerAnalyzeVo"/>
         where id = #{id}
     </select>
-        
+    <select id="selectLatestOne" resultType="com.fs.crm.domain.CrmCustomerAnalyze">
+        <include refid="selectCrmCustomerAnalyzeVo"/>
+        where
+        customer_id = #{customerId}
+        order by create_time desc limit 1
+    </select>
+
     <insert id="insertCrmCustomerAnalyze" parameterType="CrmCustomerAnalyze" useGeneratedKeys="true" keyProperty="id">
         insert into crm_customer_analyze
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -105,15 +111,22 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </trim>
         where id = #{id}
     </update>
+    <update id="updateCustomerPortrait">
+        update crm_customer_analyze
+        set customer_portrait_json = #{customerPortrait}
+        where customer_id = #{customerId}
+        ORDER BY create_time DESC
+        LIMIT 1
+    </update>
 
     <delete id="deleteCrmCustomerAnalyzeById" parameterType="Long">
         delete from crm_customer_analyze where id = #{id}
     </delete>
 
     <delete id="deleteCrmCustomerAnalyzeByIds" parameterType="String">
-        delete from crm_customer_analyze where id in 
+        delete from crm_customer_analyze where id in
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
     </delete>
-</mapper>
+</mapper>

+ 45 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/XiaoShouYiCourseController.java

@@ -0,0 +1,45 @@
+package com.fs.app.controller.course;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.param.FsUserCourseVideoFinishUParam;
+import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
+import com.fs.course.service.IFsUserCourseService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.vo.FsUserCourseVideoH5VO;
+import com.fs.course.vo.newfs.FsUserCourseVideoLinkDetailsVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Api("会员-看课接口")
+@RestController
+@RequestMapping(value = "/app/course/xsy")
+public class XiaoShouYiCourseController {
+
+    @Autowired
+    private IFsUserCourseService courseService;
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+
+    @ApiOperation("h5课程简介")
+    @GetMapping("/getH5CourseByVideoId")
+    public R getCourseByVideoId(@RequestParam("videoId") Long videoId)
+    {
+        FsUserCourseVideoH5VO course = courseService.selectFsUserCourseVideoH5VOByVideoId(videoId);
+        return R.ok().put("data",course);
+    }
+
+    @ApiOperation("H5课程详情")
+    @GetMapping("/videoDetails")
+    public ResponseResult<FsUserCourseVideoLinkDetailsVO> getCourseVideoDetails(FsUserCourseVideoLinkParam param) {
+        return courseVideoService.getXiaoShouYiCourseVideoDetails(param);
+    }
+
+    @ApiOperation("获取缓冲流量")
+    @PostMapping("/getInternetTraffic")
+    public R getInternetTraffic(@RequestBody FsUserCourseVideoFinishUParam param) {
+        return courseVideoService.getInternetTraffic(param);
+    }
+}