diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/constant/Constant.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/constant/Constant.java index e256aa2..db15973 100644 --- a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/constant/Constant.java +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/constant/Constant.java @@ -4,6 +4,8 @@ public class Constant { public static final String CHANNEL_DCC = "Channel_Dcc"; + public static final String CHANNEL_4IN1 = "Channel_4in1"; + public static final String DISPLAY_STATUS = "FINISHED"; public static final String INTELLIGENT_CUSTOMER_4IN1 = "INTELLIGENT_CUSTOMER_4IN1"; diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/corpus/ImCurMessageDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/corpus/ImCurMessageDTO.java new file mode 100644 index 0000000..1ab00a6 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/corpus/ImCurMessageDTO.java @@ -0,0 +1,38 @@ +package com.volvo.ai.analytic.center.dto.corpus; + +import lombok.Data; + +import java.util.Date; + +/** + * @description IM会话存档消息记录表-湖仓同步表 + * @author jlu + * @date 2025-07-17 + */ +@Data +public class ImCurMessageDTO { + + private static final long serialVersionUID = 1L; + + private Long id; + + private String sessionId; + + /** + * 消息内容文本 + */ + private String msgContent; + + /** + * 客户类型0:用户 1:百度 2:GPT 3:人工 4:唯都Agent + */ + private Integer customerType; + + private Long msgTimestamp; + + private String userDemand; + + public ImCurMessageDTO() { + } + +} \ No newline at end of file diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/ListSubscriptionsRequestDto.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/ListSubscriptionsRequestDto.java new file mode 100644 index 0000000..2e1a00c --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/ListSubscriptionsRequestDto.java @@ -0,0 +1,58 @@ +package com.volvo.ai.analytic.center.dto.event; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class ListSubscriptionsRequestDto { + + /** + * 所属事件通道ID + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "channel_id") + private String channelId; + + /** + * 偏移量,表示从此偏移量开始查询,偏移量不能小于0 + * 最小值:0 + * 最大值:100 + * 缺省值:0 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "offset") + private Integer offset; + + /** + * 每页显示的条目数量,不能小于0 + * 最小值:0 + * 最大值:100 + * 缺省值:15 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "limit") + private Integer limit; + + /** + * 指定查询排序 + * 缺省值:created_time:DESC + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "sort") + private String sort; + + /** + * 指定查询的事件订阅名称,精准匹配 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "name") + private String name; + + /** + * 指定查询的事件订阅名称,模糊匹配 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "fuzzy_name") + private String fuzzyName; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SendEventDto.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SendEventDto.java new file mode 100644 index 0000000..8fc6ced --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SendEventDto.java @@ -0,0 +1,36 @@ +package com.volvo.ai.analytic.center.dto.event; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +@Data +@Slf4j +@Schema(description = "") +public class SendEventDto { + + /** + * 事件来源上下文标识串,source+id可以唯一确定一个事件 + */ + @Schema(description = "事件来源上下文标识串,source+id可以唯一确定一个事件") + private String source; + + /** + * 事件类型 + */ + @Schema(description = "事件类型") + private String type; + + /** + * 事件发生的主题或对象,用以标识哪个具体对象发生了当前事件 + */ + @Schema(description = "事件发生的主题或对象,用以标识哪个具体对象发生了当前事件") + private String subject; + + /** + * 事件的负载内容 + */ + @Schema(description = "事件的负载内容") + private T data; + +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SourceChannelDto.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SourceChannelDto.java new file mode 100644 index 0000000..4f8441c --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SourceChannelDto.java @@ -0,0 +1,19 @@ +package com.volvo.ai.analytic.center.dto.event; + +import lombok.Data; + +@Data +public class SourceChannelDto { + + private String channelId; + + private String sourceName; + + public SourceChannelDto() { + } + + public SourceChannelDto(String channelId, String sourceName) { + this.channelId = channelId; + this.sourceName = sourceName; + } +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SourceDto.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SourceDto.java new file mode 100644 index 0000000..ad00a59 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SourceDto.java @@ -0,0 +1,12 @@ +package com.volvo.ai.analytic.center.dto.event; + +import lombok.Data; + +@Data +public class SourceDto { + + private String id; + private String name; + + private String channelId; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SubscriptionCreateReqDto.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SubscriptionCreateReqDto.java new file mode 100644 index 0000000..ac8d172 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SubscriptionCreateReqDto.java @@ -0,0 +1,52 @@ +package com.volvo.ai.analytic.center.dto.event; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.huaweicloud.sdk.eg.v1.model.SubscriptionSource; +import com.huaweicloud.sdk.eg.v1.model.SubscriptionTarget; +import lombok.Data; + +import java.util.List; + +@Data +public class SubscriptionCreateReqDto { + + /** + * 所属事件通道ID + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty(value = "channel_id") + private String channelId; + + /** + * 订阅名称,租户下唯一,由字母、数字、点、下划线和中划线组成,必须字母或数字开头 + * 最小长度:1 + * 最大长度:128 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("name") + private String name; + + /** + * 订阅描述 + * 最大长度:255 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("description") + private String description; + + /** + * 订阅的事件源 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("sources") + private SubscriptionSource sources = null; + + /** + * 事件目标列表,至少订阅一个事件目标 + */ + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonProperty("targets") + private List targets = null; + +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SubscriptionOperateReqDto.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SubscriptionOperateReqDto.java new file mode 100644 index 0000000..58aa622 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/event/SubscriptionOperateReqDto.java @@ -0,0 +1,17 @@ +package com.volvo.ai.analytic.center.dto.event; + +import lombok.Data; + +@Data +public class SubscriptionOperateReqDto { + + /** + * 事件订阅ID + */ + private String subscriptionId; + + /** + * 0-禁用 1-启用 + */ + private int status; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ClaimVerificationFileDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ClaimVerificationFileDTO.java index aa95c33..451a1f9 100644 --- a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ClaimVerificationFileDTO.java +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ClaimVerificationFileDTO.java @@ -33,4 +33,7 @@ public class ClaimVerificationFileDTO { //诊断报告 private JSONObject diagnosticReport; + + //诊断报告 + private JSONObject warrantyReport; } diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ConsultingRequestDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ConsultingRequestDTO.java new file mode 100644 index 0000000..df660e8 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/ConsultingRequestDTO.java @@ -0,0 +1,18 @@ +package com.volvo.ai.analytic.center.dto.req; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.util.Date; + +@Data +public class ConsultingRequestDTO { + private String businessType; // 新增字段 + private String text; + private String sourceCorpusId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date sourceCorpusTime; + + +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/SpokesManDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/SpokesManDTO.java new file mode 100644 index 0000000..04a82a8 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/SpokesManDTO.java @@ -0,0 +1,27 @@ +package com.volvo.ai.analytic.center.dto.req; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SpokesManDTO { + + private List files; + + private String platform; + + private String theme; + + private String communityRequestId; + + private String redNoteType; + + private String userId; + + private String postUrl; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/SummaryQuestionRequestDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/SummaryQuestionRequestDTO.java new file mode 100644 index 0000000..0fe2a34 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/req/SummaryQuestionRequestDTO.java @@ -0,0 +1,13 @@ +package com.volvo.ai.analytic.center.dto.req; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +@Data +public class SummaryQuestionRequestDTO { + + private String summaryReportId; + + private String preliminarySummaryResults; + +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/resp/ResultDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/resp/ResultDTO.java index c8b8551..7d47ced 100644 --- a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/resp/ResultDTO.java +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/dto/resp/ResultDTO.java @@ -16,4 +16,53 @@ public class ResultDTO implements Serializable { private String errMsg; private T data; + + public ResultDTO() { + } + + public static ResultDTO success() { + return success("200", "成功"); + } + + public static ResultDTO success(T t) { + return success("200", "成功", t); + } + + public static ResultDTO success(String successCode, String successMessage) { + return success(successCode, successMessage, null); + } + + public static ResultDTO success(String successCode, String successMessage, T t) { + ResultDTO responseDTO = new ResultDTO<>(); + responseDTO.setReturnCode(successCode); + responseDTO.setReturnMessage(successMessage); + responseDTO.setData(t); + return responseDTO; + } + + public static ResultDTO failure() { + return failure("500", "处理异常"); + } + + public static ResultDTO failure(T t) { + return failure("500", "处理异常", t); + } + + public static ResultDTO failure(String failureCode, String failureMessage) { + return failure(failureCode, failureMessage, null); + } + + public static ResultDTO failure(String failureMessage) { + return failure("500", failureMessage, null); + } + + public static ResultDTO failure(String failureCode, String failureMessage, T t) { + ResultDTO responseDTO = new ResultDTO<>(); + responseDTO.setReturnCode(failureCode); + responseDTO.setReturnMessage(failureMessage); + responseDTO.setData(t); + return responseDTO; + } + + } diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/DifySpokesManDTO.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/DifySpokesManDTO.java new file mode 100644 index 0000000..66159b3 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/DifySpokesManDTO.java @@ -0,0 +1,13 @@ +package com.volvo.ai.analytic.center.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DifySpokesManDTO { + + private String channel; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmCorpusQuestionReport.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmCorpusQuestionReport.java new file mode 100644 index 0000000..660975c --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmCorpusQuestionReport.java @@ -0,0 +1,125 @@ +package com.volvo.ai.analytic.center.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 单条语料分析结论表 + */ +@Data +@TableName("tm_corpus_question_report") +public class TmCorpusQuestionReport { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + + /** + * 主类别 + */ + @TableField("main_category") + private String mainCategory; + + /** + * 子类别 + */ + @TableField("sub_category") + private String subCategory; + + /** + * 置信度 + */ + @TableField("confidence") + private String confidence; + + /** + * 业务类型 + */ + @TableField("business_type") + private String businessType; + + /** + * 用户需求 + */ + @TableField("user_demand") + private String userDemand; + + /** + * 分析结果 + */ + @TableField("analysis_result_json") + private String analysisResultJson; + + + /** + * 删除标识(0-未删除,1-已删除) + */ + @TableField("is_deleted") + private Integer isDeleted; + + /** + * 记录版本号,即乐观锁 + */ + @TableField("versions") + private Integer versions; + + /** + * 创建人 + */ + @TableField("create_by") + private String createBy; + + /** + * 更新人 + */ + @TableField("update_by") + private String updateBy; + + /** + * 创建sql人 + */ + @TableField("create_sqlby") + private String createSqlby; + + /** + * 更新sql人 + */ + @TableField("update_sqlby") + private String updateSqlby; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + + /** + * 原语料外部主键 + */ + @TableField("source_corpus_id") + private String sourceCorpusId; + + /** + * 原语料时间 + */ + @TableField("source_corpus_time") + private Date sourceCorpusTime; + + +} \ No newline at end of file diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmCorpusQuestionSummaryReport.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmCorpusQuestionSummaryReport.java new file mode 100644 index 0000000..a834906 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmCorpusQuestionSummaryReport.java @@ -0,0 +1,105 @@ +package com.volvo.ai.analytic.center.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 语料分析初步总结表 + */ +@Data +@TableName("tm_corpus_question_summary_report") +public class TmCorpusQuestionSummaryReport { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 主类别 + */ + @TableField("main_category") + private String mainCategory; + + /** + * 子类别 + */ + @TableField("sub_category") + private String subCategory; + + /** + * 初步结果 + */ + @TableField("preliminary_summary_results") + private String preliminarySummaryResults; + + /** + * 原语料时间 + */ + @TableField("source_time") + private Date sourceTime; + + /** + * 关联id + */ + @TableField("summary_report_id") + private String summaryReportId; + + + /** + * 删除标识(0-未删除,1-已删除) + */ + @TableField("is_deleted") + private Integer isDeleted; + + /** + * 记录版本号,即乐观锁 + */ + @TableField("versions") + private Integer versions; + + /** + * 创建人 + */ + @TableField("create_by") + private String createBy; + + /** + * 更新人 + */ + @TableField("update_by") + private String updateBy; + + /** + * 创建sql人 + */ + @TableField("create_sqlby") + private String createSqlby; + + /** + * 更新sql人 + */ + @TableField("update_sqlby") + private String updateSqlby; + + /** + * 创建时间 + */ + @TableField("create_time") + private Date createTime; + + /** + * 更新时间 + */ + @TableField("update_time") + private Date updateTime; + +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmOdsAinpArrivalTestdriveVoiceInfo.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmOdsAinpArrivalTestdriveVoiceInfo.java new file mode 100644 index 0000000..007c61d --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmOdsAinpArrivalTestdriveVoiceInfo.java @@ -0,0 +1,232 @@ +package com.volvo.ai.analytic.center.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + + +/** + * @author Lingma (灵码) + * @description 湖仓同步AI铭牌到店试驾语料信息 (tm_ods_ainp_arrival_testdrive_voice_info) + * @date 2025-07-17 + */ +@Data +@TableName("tm_ods_ainp_arrival_testdrive_voice_info") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TmOdsAinpArrivalTestdriveVoiceInfo { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 客流id + */ + @TableField("cust_flow_id") + private Long custFlowId; + + /** + * 商机id + */ + @TableField("biz_opp_id") + private Long bizOppId; + + /** + * 试驾id + */ + @TableField("test_drive_id") + private Long testDriveId; + + /** + * 经销商代码 + */ + @TableField("dealer_code") + private String dealerCode; + + /** + * 线索状态 + */ + @TableField("leads_stat") + private Integer leadsStat; + + /** + * 厅接待意向车型 + */ + @TableField("showroom_reception_intention_veh_model") + private String showroomReceptionIntentionVehModel; + + /** + * 厅接待意向车型燃料类型 + */ + @TableField("showroom_veh_model_fuel_type") + private Integer showroomVehModelFuelType; + + /** + * 商机主意向车型 + */ + @TableField("main_intention_veh_model") + private String mainIntentionVehModel; + + /** + * 商机主意向车型燃料类型 + */ + @TableField("main_intention_veh_model_fuel_type") + private Integer mainIntentionVehModelFuelType; + + /** + * 试驾车型 + */ + @TableField("test_drive_veh_model") + private String testDriveVehModel; + + /** + * 试驾车型燃料类型 + */ + @TableField("test_drive_veh_model_fuel_type") + private Integer testDriveVehModelFuelType; + + /** + * 到店销售顾问id + */ + @TableField("arrival_sales_consultant_id") + private String arrivalSalesConsultantId; + + /** + * 到店销售顾问 + */ + @TableField("arrival_sales_consultant") + private String arrivalSalesConsultant; + + /** + * 到店接待顾问id + */ + @TableField("arrival_reception_consultant_id") + private String arrivalReceptionConsultantId; + + /** + * 到店接待顾问 + */ + @TableField("arrival_reception_consultant") + private String arrivalReceptionConsultant; + + /** + * 试驾销售顾问id + */ + @TableField("test_drive_sales_consultant_id") + private String testDriveSalesConsultantId; + + /** + * 试驾销售顾问 + */ + @TableField("test_drive_sales_consultant") + private String testDriveSalesConsultant; + + /** + * 试驾顾问id + */ + @TableField("test_drive_consultant_id") + private String testDriveConsultantId; + + /** + * 试驾顾问 + */ + @TableField("test_drive_consultant") + private String testDriveConsultant; + + /** + * 试驾开始时间 + */ + @TableField("test_drive_start_time") + private Date testDriveStartTime; + + /** + * 试驾结束时间 + */ + @TableField("test_drive_end_time") + private Date testDriveEndTime; + + /** + * 录音开始时间 + */ + @TableField("voice_start_time") + private String voiceStartTime; + + /** + * 录音结束时间 + */ + @TableField("voice_end_time") + private String voiceEndTime; + + /** + * 录音时长,单位秒 + */ + @TableField("voice_duration") + private Integer voiceDuration; + + /** + * 语料内容 + */ + @TableField("content") + private String content; + + /** + * 声道标识位 + */ + @TableField("is_mix") + private Byte isMix; + + /** + * 语料类型 + */ + @TableField("content_code") + private Byte contentCode; + + /** + * 分区字段 + */ + @TableField("pday") + private String pday; + + /** + * 创建时间 + */ + @TableField("created_time") + private Date createdTime; + + /** + * 创建人 + */ + @TableField("created_by") + private String createdBy; + + /** + * 更新时间 + */ + @TableField("updated_time") + private Date updatedTime; + + /** + * 更新人 + */ + @TableField("updated_by") + private String updatedBy; + + /** + * 删除标志位,0:未删除,1:已删除 + */ + @TableField("is_deleted") + private Byte isDeleted; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmOdsImRealtimeMessageCur.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmOdsImRealtimeMessageCur.java new file mode 100644 index 0000000..107ec37 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/entity/TmOdsImRealtimeMessageCur.java @@ -0,0 +1,171 @@ +package com.volvo.ai.analytic.center.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + + +/** + * @description 湖仓同步实时IM消息表 (tm_ods_im_realtime_message_cur) + * @date 2025-07-17 + */ +@Data +@TableName("tm_ods_im_realtime_message_cur") +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TmOdsImRealtimeMessageCur { + + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 消息唯一标识键 + */ + @TableField("msg_key") + private String msgKey; + + /** + * 会话ID + */ + @TableField("session_id") + private String sessionId; + + /** + * 消息内容 + */ + @TableField("msg_content") + private String msgContent; + + /** + * 消息类型,文本类型:TIMTextElem + */ + @TableField("msg_type") + private String msgType; + + /** + * 消息体 + */ + @TableField("msg_body") + private String msgBody; + + /** + * 客户类型0:用户 1:百度 2:GPT 3:人工 4:唯都Agent + */ + @TableField("customer_type") + private Integer customerType; + + /** + * 云端自定义数据 + */ + @TableField("cloud_custom_data") + private String cloudCustomData; + + /** + * 客服来源 + */ + @TableField("cs_source") + private String csSource; + + /** + * 发送方账号 + */ + @TableField("from_account") + private String fromAccount; + + /** + * 插入时间(原字符串格式转换为datetime) + */ + @TableField("insert_time") + private Date insertTime; + + /** + * 会员ID + */ + @TableField("member_id") + private String memberId; + + /** + * 消息标志位 + */ + @TableField("msg_flag_bits") + private Integer msgFlagBits; + + /** + * 消息随机数 + */ + @TableField("msg_random") + private Long msgRandom; + + /** + * 消息序列号 + */ + @TableField("msg_seq") + private String msgSeq; + + /** + * 消息时间戳(毫秒级) + */ + @TableField("msg_timestamp") + private Long msgTimestamp; + + /** + * 第三方平台OpenID + */ + @TableField("open_id") + private String openId; + + /** + * 来源标识 0:公众号,1:app + */ + @TableField("source_type") + private Integer sourceType; + + /** + * 接收方账号 + */ + @TableField("to_account") + private String toAccount; + + /** + * 创建时间 + */ + @TableField("created_time") + private Date createdTime; + + /** + * 创建人 + */ + @TableField("created_by") + private String createdBy; + + /** + * 更新时间 + */ + @TableField("updated_time") + private Date updatedTime; + + /** + * 更新人 + */ + @TableField("updated_by") + private String updatedBy; + + /** + * 删除标志位,0:未删除,1:已删除 + */ + @TableField("is_deleted") + private Byte isDeleted; +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/BusinessTypeEnum.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/BusinessTypeEnum.java index 9596e1e..dc2f221 100644 --- a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/BusinessTypeEnum.java +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/BusinessTypeEnum.java @@ -12,7 +12,7 @@ public enum BusinessTypeEnum { SMART_ASSISTANT("SMART_ASSISTANT", "智能助手-DCC"), SMART_ASSISTANT_QIWEI("SMART_ASSISTANT_QIWEI", "智能助手-企微"), INTELLIGENT_CUSTOMER("INTELLIGENT_CUSTOMER_4IN1", "智能客服-4合一"), - + SMART_ASSISTANT_4IN1("SMART_ASSISTANT_4IN1", "智能客服-4In1"), //索赔检核 CLAIM_VERIFICATION("CLAIM_VERIFICATION", "索赔检核流程"), CLAIM_VERIFICATION_FILE_ANALYSIS("99001", "索赔检核文件分析"), @@ -26,7 +26,18 @@ public enum BusinessTypeEnum { CASE("case", "舆情事件"), - SMART_ASSISTANT_NAMEPLATE("SMART_ASSISTANT_NAMEPLATE", "智能助手-铭牌") + SMART_ASSISTANT_NAMEPLATE("SMART_ASSISTANT_NAMEPLATE", "智能助手-铭牌"), + + IM_CORPUS_ANALYSIS("IM_CORPUS_ANALYSIS", "IM语料库"), + SUMMARY_REPORT("SUMMARY_REPORT", "问题总结"), + + + //特邀发言官 + SPOKESMAN("SPOKESMAN", "特邀发言官"), + //特邀发言官敏感词 + SPOKESMAN_SENSITIVE_WORD("SPOKESMAN_SENSITIVE_WORD", "特邀发言官敏感词"), + //特邀发言官审核规则 + SPOKESMAN_AUDIT_RULE("SPOKESMAN_AUDIT_RULE", "特邀发言官审核规则"), ; private String code; diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/ImCustomTypeEnums.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/ImCustomTypeEnums.java new file mode 100644 index 0000000..5185164 --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/ImCustomTypeEnums.java @@ -0,0 +1,27 @@ +package com.volvo.ai.analytic.center.enums; + +public enum ImCustomTypeEnums { + + CUSTOMER(0, "用户"), + BAIDU(1, "百度"), + GPT(2, "GPT"), + MANUAL(3, "人工"), + VOLVO_AGENT(4, "唯都Agent"), + ; + + private Integer code; + private String message; + + ImCustomTypeEnums(Integer code, String message) { + this.code = code; + this.message = message; + } + + public Integer getCode() { + return this.code; + } + + public String getMessage() { + return this.message; + } +} diff --git a/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/QuestionMainCategoryEnum.java b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/QuestionMainCategoryEnum.java new file mode 100644 index 0000000..8bed73b --- /dev/null +++ b/ai-analytic-center-api/src/main/java/com/volvo/ai/analytic/center/enums/QuestionMainCategoryEnum.java @@ -0,0 +1,26 @@ +package com.volvo.ai.analytic.center.enums; + +public enum QuestionMainCategoryEnum { + + CAR_PURCHASE_RELATED("CAR_PURCHASE_RELATED", "购车相关"), + AFTER_SALES_SERVICE("AFTER_SALES_SERVICE", "售后服务"), + USER_OPERATION("USER_OPERATION", "用户运营"), + CORPORATE_VALUES("CORPORATE_VALUES", "企业价值观"), + SCENE_NOT_CLEAR("SCENE_NOT_CLEAR", "场景不明确"), + ; + private String code; + private String message; + + QuestionMainCategoryEnum(String code, String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return this.code; + } + + public String getMessage() { + return this.message; + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/AiAnalyticCenterServiceApplication.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/AiAnalyticCenterServiceApplication.java index 7eddcf7..ccca816 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/AiAnalyticCenterServiceApplication.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/AiAnalyticCenterServiceApplication.java @@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.retry.annotation.EnableRetry; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.client.RestTemplate; @@ -18,6 +19,7 @@ import com.volvo.common.feign.annotation.EnableVolvoFeignClients; @MapperScan({"com.volvo.ai.analytic.center.mapper"}) @EnableDiscoveryClient @EnableRetry +@EnableAsync public class AiAnalyticCenterServiceApplication { public static void main(String[] args) { diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/config/HuaWeiEGConfig.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/config/HuaWeiEGConfig.java new file mode 100644 index 0000000..6c9ff4a --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/config/HuaWeiEGConfig.java @@ -0,0 +1,59 @@ +package com.volvo.ai.analytic.center.config; + +import com.huaweicloud.sdk.core.auth.BasicCredentials; +import com.huaweicloud.sdk.core.auth.ICredential; +import com.huaweicloud.sdk.core.http.HttpConfig; +import com.huaweicloud.sdk.eg.v1.EgClient; +import com.huaweicloud.sdk.eg.v1.region.EgRegion; +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * HuaWeiConfig 配置类 + */ +@Data +@Configuration +@RefreshScope +public class HuaWeiEGConfig { + + /** + * 创建 EventProperties 对象 + * @return EventProperties 对象 + */ + @Value("${huawei.cloud.EG.ak}") + private String ak; + @Value("${huawei.cloud.EG.sk}") + private String sk; + @Value("${huawei.cloud.EG.projectId}") + private String projectId; + @Value("${huawei.cloud.EG.endpoint}") + private String endpoint; + @Value("${huawei.cloud.EG.region}") + private String region; + + + /** + * 创建 EgClient 对象 + * @return EgClient 对象 + */ + @Bean + public EgClient egClient() { + // 创建 BasicCredentials 对象,并设置 accessKey 和 secretKey + ICredential auth = new BasicCredentials() + .withAk(ak) + .withSk(sk); + + // 创建 HttpConfig 对象 + HttpConfig httpConfig = HttpConfig.getDefaultHttpConfig(); + + // 创建 EgClient 对象,并设置认证信息、Http配置和区域 + return EgClient.newBuilder() + .withCredential(auth) + .withHttpConfig(httpConfig) + .withRegion(EgRegion.valueOf(region)) + .build(); + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/AiAnalysisDifyController.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/AiAnalysisDifyController.java index 30eceb0..c170448 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/AiAnalysisDifyController.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/AiAnalysisDifyController.java @@ -7,20 +7,19 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.volvo.ai.analytic.center.dto.req.AnalysisQueryReq; import com.volvo.ai.analytic.center.dto.req.AnalysisReq; +import com.volvo.ai.analytic.center.dto.req.SummaryQuestionRequestDTO; import com.volvo.ai.analytic.center.dto.resp.AnalysisResp; import com.volvo.ai.analytic.center.service.AiAnalysisDifyService; +import com.volvo.ai.analytic.center.service.UserQuestionSummaryService; import com.volvo.common.core.util.ResultMsg; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; -import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController() @@ -30,13 +29,14 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("analysis") public class AiAnalysisDifyController { - @Autowired - private RocketMQTemplate rocketMQTemplate; - @Autowired private AiAnalysisDifyService aiAnalysisDifyService; + @Autowired + private UserQuestionSummaryService userQuestionSummaryService; + @Value("${dify.corpusQuestion.appKey:z9B4hg9NFEikyfg81neDd1nl}") + private String corpusQuestionAppKey; @PostMapping("/updateByAiId") @ApiOperation(value = "更新dify结果") @@ -48,32 +48,52 @@ public class AiAnalysisDifyController { @PostMapping("/aiAnalyze") @ApiOperation(value = "Ai解析接口") - public AnalysisResp aiAnalyze(@Validated @RequestBody String message) { - log.info("aiAnalyze data: {}",message); + public AnalysisResp aiAnalyze(@Validated @RequestBody String message) { + log.info("aiAnalyze data: {}", message); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); // 允许未转义的控制字符 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); try { - AnalysisReq analysisReq = objectMapper.readValue(message, AnalysisReq.class); + AnalysisReq analysisReq = objectMapper.readValue(message, AnalysisReq.class); analysisReq.validate(); - return aiAnalysisDifyService.aiAnalyze(analysisReq); + return aiAnalysisDifyService.aiAnalyze(analysisReq); } catch (JsonProcessingException e) { - log.info("aiAnalyze data error: {}",e); + log.info("aiAnalyze data error: {}", e); return AnalysisResp.failed("解析失败"); } } @PostMapping("/query") @ApiOperation(value = "Ai解析结果查询") - public AnalysisResp query(@RequestBody @Validated AnalysisQueryReq analysisQueryReq) { - log.info("aiAnalyze query data: {}",analysisQueryReq); - return aiAnalysisDifyService.query(analysisQueryReq); + public AnalysisResp query(@RequestBody @Validated AnalysisQueryReq analysisQueryReq) { + log.info("aiAnalyze query data: {}", analysisQueryReq); + return aiAnalysisDifyService.query(analysisQueryReq); } + @PostMapping("/callback") @ApiOperation(value = "Ai解析结果查询") public AnalysisResp testCallback(@RequestBody AnalysisResp analysisResp) { - log.info("aiAnalyze testCallback data: {}",analysisResp); - return AnalysisResp.success("ok"); + log.info("aiAnalyze testCallback data: {}", analysisResp); + return AnalysisResp.success("ok"); + } + + + @PostMapping("/corpusSummaryQuestion") + @ApiOperation(value = "单条语料分析结论入库") + public AnalysisResp corpusSummaryQuestion( + @RequestHeader(value = "appKey", required = true) String appKey, + @RequestParam("summaryReportId") String summaryReportId, + @RequestParam("preliminarySummaryResults") String preliminarySummaryResults) { + log.info("aiAnalyze corpusSummaryQuestion appKey={},summaryReportId={},preliminarySummaryResults={}", appKey, summaryReportId, preliminarySummaryResults); + if (!corpusQuestionAppKey.equals(appKey)) { + return AnalysisResp.failed("appKey 不正确 "); + } + // 打印接收到的参数 + SummaryQuestionRequestDTO requestDTO = new SummaryQuestionRequestDTO(); + requestDTO.setSummaryReportId(summaryReportId); + requestDTO.setPreliminarySummaryResults(preliminarySummaryResults); + userQuestionSummaryService.updateSummery(requestDTO); + return AnalysisResp.success("ok"); } diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/EGEventSubscriptionController.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/EGEventSubscriptionController.java new file mode 100644 index 0000000..e0d124c --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/EGEventSubscriptionController.java @@ -0,0 +1,179 @@ +package com.volvo.ai.analytic.center.controller; + +import com.huaweicloud.sdk.eg.v1.model.*; +import com.volvo.ai.analytic.center.dto.event.ListSubscriptionsRequestDto; +import com.volvo.ai.analytic.center.dto.event.SendEventDto; +import com.volvo.ai.analytic.center.dto.event.SubscriptionCreateReqDto; +import com.volvo.ai.analytic.center.dto.event.SubscriptionOperateReqDto; +import com.volvo.ai.analytic.center.dto.resp.ResultDTO; +import com.volvo.ai.analytic.center.service.HuaWeiEGService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.concurrent.ExecutionException; + +/** + * 事件订阅管理 + */ +@Tag(name = "/event", description = "事件订阅管理") +@RestController +@RequestMapping("/event") +@Slf4j +public class EGEventSubscriptionController { + + @Resource + HuaWeiEGService huaWeiService; + /** + * 发布事件到事件通道 + */ + @Operation(summary = "发布事件到事件通道") + @PostMapping("/sendEvent") + public ResultDTO sendEvent(@RequestBody CloudEvents req, @RequestParam("channel") String channel) throws ExecutionException { + return ResultDTO.success(huaWeiService.sendEvent(req, "channel")); + } + + /** + * 创建事件订阅 + * + * @param req req + * @return 事件订阅ID + */ + @Operation(summary = "创建事件订阅") + @PostMapping("/subscription") + public ResultDTO createSubscription(@RequestBody SubscriptionCreateReqDto req) { + return ResultDTO.success(huaWeiService.createSubscription(req)); + } + + /** + * 创建事件订阅目标 + * + * @param req req + * @return 事件订阅目标ID + */ + @Operation(summary = "创建事件订阅目标") + @PostMapping("/subscriptionTarget") + public ResultDTO createSubscriptionTarget(@RequestBody CreateSubscriptionTargetRequest req) { + return ResultDTO.success(huaWeiService.createSubscriptionTarget(req)); + } + + /** + * 删除事件订阅 + * + * @param subscriptionId 事件订阅ID + * @return + */ + @Operation(summary = "删除事件订阅") + @DeleteMapping("/subscription/{subscriptionId}") + public ResultDTO deleteSubscription(@PathVariable("subscriptionId") String subscriptionId) { + huaWeiService.deleteSubscription(subscriptionId); + return ResultDTO.success(); + } + + /** + * 删除事件订阅目标 + * + * @param subscriptionId 事件订阅ID + * @param targetId 事件订阅目标ID + * @return + */ + @Operation(summary = "删除事件订阅目标") + @DeleteMapping("/subscriptionTarget/{subscriptionId}/{targetId}") + public ResultDTO deleteSubscriptionTarget(@PathVariable("subscriptionId") String subscriptionId, @PathVariable("targetId") String targetId) { + huaWeiService.deleteSubscriptionTarget(subscriptionId, targetId); + return ResultDTO.success(); + } + + /** + * 查询事件订阅列表 + * + * @param req req + * @return + */ + @Operation(summary = "查询事件订阅列表") + @PostMapping("/subscription/list") + public ResultDTO listSubscriptions(@RequestBody ListSubscriptionsRequestDto req) { + return ResultDTO.success(huaWeiService.listSubscriptions(req)); + } + + /** + * 启用禁用事件订阅 + * + * @param req req + * @return + */ + @Operation(summary = "启用禁用事件订阅") + @PostMapping("/subscription/enabled") + public ResultDTO operateSubscription(@RequestBody SubscriptionOperateReqDto req) { + huaWeiService.operateSubscription(req); + return ResultDTO.success(); + } + + /** + * 查询事件订阅详情 + * + * @param subscriptionId 事件订阅ID + * @return + */ + @Operation(summary = "查询事件订阅详情") + @GetMapping("/subscription/{subscriptionId}") + public ResultDTO showDetailOfSubscription(@PathVariable("subscriptionId") String subscriptionId) { + return ResultDTO.success(huaWeiService.showDetailOfSubscription(subscriptionId)); + } + + /** + * 查询事件订阅目标详情 + * + * @param subscriptionId 事件订阅ID + * @param targetId 事件订阅目标ID + * @return + */ + @Operation(summary = "查询事件订阅目标详情") + @GetMapping("/subscriptionTarget/{subscriptionId}/{targetId}") + public ResultDTO showDetailOfSubscriptionTarget(@PathVariable("subscriptionId") String subscriptionId, + @PathVariable("targetId") String targetId) { + return ResultDTO.success(huaWeiService.showDetailOfSubscriptionTarget(subscriptionId, targetId)); + } + + /** + * 更新事件订阅 + * + * @param req req + * @return + */ + @Operation(summary = "更新事件订阅") + @PutMapping("/subscription") + public ResultDTO updateSubscription(@RequestBody UpdateSubscriptionRequest req) { + huaWeiService.updateSubscription(req); + return ResultDTO.success(); + } + + /** + * 更新事件订阅源 + * + * @param req req + * @return + */ + @Operation(summary = "更新事件订阅源") + @PutMapping("/subscriptionSource") + public ResultDTO updateSubscriptionSource(@RequestBody UpdateSubscriptionSourceRequest req) { + huaWeiService.updateSubscriptionSource(req); + return ResultDTO.success(); + } + + /** + * 更新事件订阅目标 + * + * @param req req + * @return + */ + @Operation(summary = "更新事件订阅目标") + @PutMapping("/subscriptionTarget") + public ResultDTO updateSubscriptionTarget(@RequestBody UpdateSubscriptionTargetRequest req) { + huaWeiService.updateSubscriptionTarget(req); + return ResultDTO.success(); + } + +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/TestController.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/TestController.java index 0d97d7d..5244a17 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/TestController.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/controller/TestController.java @@ -40,6 +40,7 @@ public class TestController { @PostMapping("/mockMq") @ApiOperation(value = "补偿处理消息") public ResultMsg mockMq(@RequestBody String message) { + log.info("mockMq message:{}", message); JSONObject messageJson = JSONObject.parseObject(message); String topic = messageJson.getString("topic"); String tag = messageJson.getString("tag"); diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/CorpusFailJob.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/CorpusFailJob.java index 34207e3..638f9f0 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/CorpusFailJob.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/CorpusFailJob.java @@ -3,6 +3,7 @@ package com.volvo.ai.analytic.center.job; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.volvo.ai.analytic.center.dto.PageDto; import com.volvo.ai.analytic.center.dto.corpus.AicorpusTelephoneDTO; import com.volvo.ai.analytic.center.dto.corpus.CorpusReportDTO; @@ -12,10 +13,10 @@ import com.volvo.ai.analytic.center.entity.AiAnalysisRequestLogs; import com.volvo.ai.analytic.center.entity.TmNameplateCorpus; import com.volvo.ai.analytic.center.enums.BusinessTypeEnum; import com.volvo.ai.analytic.center.enums.CategoryEnum; +import com.volvo.ai.analytic.center.feign.DiFyFeign; import com.volvo.ai.analytic.center.mapper.AiAnalysisRequestLogsMapper; import com.volvo.ai.analytic.center.mapper.TmTelephoneCorpusMapper; import com.volvo.ai.analytic.center.service.*; -import com.volvo.ai.analytic.center.utils.FlowResultSplitUtil; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; @@ -32,6 +33,8 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.Arrays; +import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -67,11 +70,16 @@ public class CorpusFailJob { private String dccMqTipic; @Value("${rocketmq.consumer.intelligentCustomer.topic}") private String intelligentCustomerTipic; + + @Value("${rocketmq.producer.intelligentCustomer.topic}") + private String intelligentCustomerProducerTopic; + @Autowired private TmTelephoneCorpusMapper tmTelephoneCorpusMapper; @Autowired private TmNameplateCorpusService tmNameplateCorpusService; + private DiFyFeign diFyFeign; /** @@ -229,4 +237,95 @@ public class CorpusFailJob { } } } + + /** + * 智能客服-电话场景-四合一 一分钟一次 + */ + @XxlJob("intelligentCustomer4in1MobileFailTask") + @PostMapping("intelligentCustomer4in1MobileFailTask") + public void intelligentCustomer4in1MobileFailTask() { + + log.info(" intelligentCustomer4in1MobileFailTask 解析失败重试处理"); + Integer total = aiAnalysisErrorsService.queryCountAnalysisErrorList(Collections.singletonList(BusinessTypeEnum.SMART_ASSISTANT_4IN1.getCode())); + log.info("intelligentCustomer4in1MobileFailTask 语料解析失败重试处理数据量:{}", total); + int totalPages = PageDto.getTotalPages(total, pageSize); + log.info("intelligentCustomer4in1MobileFailTask 语料解析失败重试处理数据量:{},总页数:{}", total, totalPages); + for (int i = 1; i <= totalPages; i++) { + int offset = (i - 1) * pageSize; + + List aiAnalysisErrorsListlist = aiAnalysisErrorsService.queryAnalysisErrorList( + Collections.singletonList(BusinessTypeEnum.SMART_ASSISTANT_4IN1.getCode()), offset, pageSize); + if (CollectionUtils.isNotEmpty(aiAnalysisErrorsListlist)) { + log.info("intelligentCustomer4in1MobileFailTask语料解析失败重试处理 size:{}", aiAnalysisErrorsListlist.size()); + aiAnalysisErrorsListlist.forEach(aiAnalysisErrors -> { + try { + log.info("intelligentCustomer4in1MobileFailTask aiAnalysisRequestLogs-id:{}", aiAnalysisErrors.getAiAnalysisRequestId()); + String aiAnalysisRequestId = aiAnalysisErrors.getAiAnalysisRequestId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(AiAnalysisRequestLogs::getAiAnalysisRequestId, aiAnalysisRequestId); + AiAnalysisRequestLogs aiAnalysisRequestLogs = aiAnalysisRequestLogsMapper.selectOne(queryWrapper); + + log.info("intelligentCustomer4in1MobileFailTask aiAnalysisRequestLogs:{}", aiAnalysisRequestLogs); + + if (null != aiAnalysisRequestLogs) { + String difyRequest = aiAnalysisRequestLogs.getDifyRequest(); + log.info("intelligentCustomer4in1MobileFailTask aiAnalysisRequestLogs difyRequest:{}", aiAnalysisRequestLogs); + DiFyReq diFyReq = JSON.parseObject(difyRequest, DiFyReq.class); + Map map = new HashMap<>(); + map.put("inputs", diFyReq.getInputs()); + map.put("response_mode", "blocking"); + map.put("user", diFyReq.getUser()); + log.info("intelligentCustomer4in1MobileFailTask execDifyFlow dify request data:{}", map); + JSONObject difyResult = diFyFeign.runWorkflows("Bearer " + diFyReq.getFlowId(), map); + JSONObject data = difyResult.getJSONObject("data"); + log.info("intelligentCustomer4in1MobileFailTask execDifyFlow dify response aiID:{} , data:{}", aiAnalysisRequestId, data); + data.put("aiAnalysisRequestId", aiAnalysisRequestId); + + if (data.get("status").equals("succeeded")) { + JSONObject outputsJson = data.getJSONObject("outputs"); + String message = JSONObject.toJSONString(outputsJson); + log.info("intelligentCustomer4in1MobileFailTask send 4IN1mq {}", message); + rocketMqTemplate.asyncSend(intelligentCustomerProducerTopic, MessageBuilder.withPayload(message).build(), new SendCallback() { + @Override + public void onSuccess(SendResult sendResult) { + log.info(" intelligentCustomer4in1MobileFailTask 失败补偿 发送MQ成功 消息体:{}", message); + } + + @Override + public void onException(Throwable e) { + log.error("intelligentCustomer4in1MobileFailTask 失败补偿 送MQ异常 消息体:{}, 异常:", message, e); + } + }, 10000); + AiAnalysisErrors aiAnalysisErrors1 = new AiAnalysisErrors(); + aiAnalysisErrors1.setAiAnalysisRequestId(aiAnalysisRequestId); + aiAnalysisErrors1.setAiAnalysisErrorHandlingStatus("1"); + aiAnalysisErrorsService.updateAiAnalysisErrors(aiAnalysisErrors1); + aiAnalysisRequestLogsService.saveAiAnalysisRequestLogs(AiAnalysisRequestLogs.builder() + .aiAnalysisRequestId(data.getString("aiAnalysisRequestId")) + .businessResponse(message) + .difyResponse(data.toJSONString()) + .build()); + } else { + log.info("intelligentCustomer4in1MobileFailTask execDifyFlow failed, aiAnalysisRequestId:{} , data:{}", aiAnalysisRequestId, + data); + String errorMsg = data.getString("error"); + aiAnalysisErrors.setRetryCount(aiAnalysisErrors.getRetryCount() + 1); + aiAnalysisErrors.setUpdateBy("xxlJob"); + aiAnalysisErrors.setUpdateTime(new Date()); + aiAnalysisErrors.setAiAnalysisErrorMessage(errorMsg.length() > 1000 ? errorMsg.substring(0, 1000) : errorMsg); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(AiAnalysisErrors::getAiAnalysisRequestId, aiAnalysisRequestId); + aiAnalysisErrorsService.update(aiAnalysisErrors,updateWrapper); + } + } + } catch (Exception e) { + log.info("intelligentCustomer4in1MobileFailTask 语料解析失败补偿异常:{}", e); + aiAnalysisErrors.setAiAnalysisErrorHandlingStatus("0"); + aiAnalysisErrors.setRetryCount(aiAnalysisErrors.getRetryCount() + 1); + updateAiAnalysisErrors(aiAnalysisErrors, aiAnalysisErrors.getAiAnalysisRequestId()); + } + }); + } + } + } } diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/ImMessageAnalysisJob.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/ImMessageAnalysisJob.java new file mode 100644 index 0000000..bb88f38 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/ImMessageAnalysisJob.java @@ -0,0 +1,44 @@ +package com.volvo.ai.analytic.center.job; + +import com.volvo.ai.analytic.center.service.TmOdsImRealtimeMessageCurService; +import com.volvo.common.core.util.ResultMsg; +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@Component +@RestController +public class ImMessageAnalysisJob { + + @Autowired + private TmOdsImRealtimeMessageCurService tmOdsImRealtimeMessageCurService; + + /** + * 企微语料处理 + */ + @XxlJob("imMessageAnalysisTask") + public ResultMsg imMessageAnalysisTask(@RequestBody String paramJson) { + try { + // 获取任务参数 + String param = XxlJobHelper.getJobParam(); + if(StringUtils.isEmpty(param)){ + param = paramJson; + } + // 执行业务逻辑 + XxlJobHelper.log("任务参数: {}", param); + log.info("imMessageAnalysisTask IM语料分析处理:{}",param); + tmOdsImRealtimeMessageCurService.runImCorpusDify(param); + } catch (Exception e) { + log.error("imMessageAnalysisTask 定时任务处理消息异常",e.getMessage()); + throw new RuntimeException(e); + } + return ResultMsg.ok(); + } + +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/InvitedSpeakerJob.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/InvitedSpeakerJob.java new file mode 100644 index 0000000..25afd48 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/InvitedSpeakerJob.java @@ -0,0 +1,33 @@ +package com.volvo.ai.analytic.center.job; + + +import com.volvo.ai.analytic.center.service.SpokesManService; +import com.volvo.common.core.util.ResultMsg; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class InvitedSpeakerJob { + + @Autowired + private SpokesManService spokesmanService; + + + /** + * 邀约消息推送 + */ + @XxlJob("invitedSpeakerJobTask") + public ResultMsg invitedSpeakerJobTask() { + try { + log.info("invitedSpeakerJobTask 开始执行特邀发言官定时任务补偿处理消息"); + spokesmanService.spokesManTask(); + } catch (Exception e) { + log.error("invitedSpeakerJobTask 定时任务补偿处理消息异常",e.getMessage()); + throw new RuntimeException(e); + } + return ResultMsg.ok(); + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/UserQuestionSummaryJob.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/UserQuestionSummaryJob.java new file mode 100644 index 0000000..29fb684 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/job/UserQuestionSummaryJob.java @@ -0,0 +1,44 @@ +package com.volvo.ai.analytic.center.job; + +import com.volvo.ai.analytic.center.service.UserQuestionSummaryService; +import com.volvo.common.core.util.ResultMsg; +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@Component +@RestController +public class UserQuestionSummaryJob { + + @Autowired + private UserQuestionSummaryService userQuestionSummaryService; + + /** + * 企微语料处理 + */ + @XxlJob("userQuestionSummaryTask") + public ResultMsg userQuestionSummaryTask(@RequestBody String paramJson) { + try { + // 获取任务参数 + String param = XxlJobHelper.getJobParam(); + if(StringUtils.isEmpty(param)){ + param = paramJson; + } + // 执行业务逻辑 + XxlJobHelper.log("任务参数: {}", param); + log.info("userQuestionSummaryTask 用户问题总结处理:{}",param); + userQuestionSummaryService.runUserQuestionSummary(param); + } catch (Exception e) { + log.error("userQuestionSummaryTask 定时任务处理消息异常",e.getMessage()); + throw new RuntimeException(e); + } + return ResultMsg.ok(); + } + +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmCorpusQuestionReportMapper.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmCorpusQuestionReportMapper.java new file mode 100644 index 0000000..6695b99 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmCorpusQuestionReportMapper.java @@ -0,0 +1,21 @@ +package com.volvo.ai.analytic.center.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.volvo.ai.analytic.center.dto.corpus.ImCurMessageDTO; +import com.volvo.ai.analytic.center.entity.TmCorpusQuestionReport; + +import java.util.Date; +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 单条语料分析结论 + */ +@Mapper +public interface TmCorpusQuestionReportMapper extends BaseMapper { + + List queryQuestionReportList(@Param("statTime") Date statTime, @Param("endTime") Date endTime, @Param("mainCategory") String mainCategory); + +} \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmCorpusQuestionSummaryReportMapper.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmCorpusQuestionSummaryReportMapper.java new file mode 100644 index 0000000..3157dc2 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmCorpusQuestionSummaryReportMapper.java @@ -0,0 +1,12 @@ +package com.volvo.ai.analytic.center.mapper; + +import org.apache.ibatis.annotations.Param; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.volvo.ai.analytic.center.entity.TmCorpusQuestionSummaryReport; + +public interface TmCorpusQuestionSummaryReportMapper extends BaseMapper { + + int updateBySummaryReportId(@Param("summaryReportId") String summaryReportId, @Param("preliminarySummaryResults") String preliminarySummaryResults); + +} \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmOdsAinpArrivalTestdriveVoiceInfoMapper.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmOdsAinpArrivalTestdriveVoiceInfoMapper.java new file mode 100644 index 0000000..9f37e70 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmOdsAinpArrivalTestdriveVoiceInfoMapper.java @@ -0,0 +1,14 @@ +package com.volvo.ai.analytic.center.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.volvo.ai.analytic.center.entity.TmOdsAinpArrivalTestdriveVoiceInfo; +import org.apache.ibatis.annotations.Mapper; + +/** + * @description 湖仓同步AI铭牌到店试驾语料信息- + * @date 2025-07-17 + */ +@Mapper +public interface TmOdsAinpArrivalTestdriveVoiceInfoMapper extends BaseMapper { +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmOdsImRealtimeMessageCurMapper.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmOdsImRealtimeMessageCurMapper.java new file mode 100644 index 0000000..ce70cb4 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mapper/TmOdsImRealtimeMessageCurMapper.java @@ -0,0 +1,26 @@ +package com.volvo.ai.analytic.center.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.volvo.ai.analytic.center.dto.corpus.ImCurMessageDTO; +import com.volvo.ai.analytic.center.entity.TmOdsImRealtimeMessageCur; + +import java.util.List; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @description 湖仓同步实时IM消息表-Mapper (tm_ods_im_realtime_message_cur) + * @date 2025-07-17 + */ +@Mapper +public interface TmOdsImRealtimeMessageCurMapper extends BaseMapper { + + // 统计IM消息数据 + int countImRealtimeMessageCurByDate(@Param("statTime") Long statTime, @Param("endTime") Long endTime); + // 分页获取处理数据 + List queryImRealtimeMessageCurByDate(@Param("statTime") Long statTime, @Param("endTime") Long endTime, @Param("offset") int offset, @Param("pageSize") int pageSize); + + List queryImRealtimeMessageCurBySessionId(@Param("statTime") Long statTime, @Param("endTime") Long endTime, @Param("sessionId") String sessionId); +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusDccMqConsumer.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusDccMqConsumer.java index bf451b1..d583973 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusDccMqConsumer.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusDccMqConsumer.java @@ -1,8 +1,10 @@ package com.volvo.ai.analytic.center.mq; +import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.volvo.ai.analytic.center.constant.Constant; import com.volvo.ai.analytic.center.dto.corpus.AicorpusTelephoneDTO; import com.volvo.ai.analytic.center.service.TmTelephoneCorpusService; import lombok.extern.slf4j.Slf4j; @@ -14,6 +16,8 @@ import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RestController; +import java.util.Objects; + /** * @ClassName 湖仓发送过来的kafka消息自己发MQ自己消费 DCC通话记录 (目前接入的需求:4IN1的智能工单-电话场景,LTO跟进记录AI总结) * @Description @@ -46,7 +50,15 @@ public class CorpusDccMqConsumer implements RocketMQListener { String message = new String(messageExt.getBody()); log.info("dcc_mq message: " + message); AicorpusTelephoneDTO aicorpusTelephone = objectMapper.readValue(message, AicorpusTelephoneDTO.class); - tmTelephoneCorpusService.runTelephoneCorpusDify(aicorpusTelephone); + //DCC场景 + if(Objects.equals(aicorpusTelephone.getCategoryCode(), Constant.CHANNEL_DCC)){ + tmTelephoneCorpusService.runTelephoneCorpusDify(aicorpusTelephone); + } + //channel_4in1场景 + if(Objects.equals(aicorpusTelephone.getCategoryCode(), Constant.CHANNEL_4IN1)){ + log.info("channel_4in1 message: " + JSON.toJSONString(aicorpusTelephone)); + tmTelephoneCorpusService.runTelephoneCorpusDify4in1(aicorpusTelephone); + } log.info("corpusDccMqProducer mq 处理完成: {}", aicorpusTelephone.getSourceId()); log.info("dcc_mq 处理完成,耗时:{}", System.currentTimeMillis() - startTime); } catch (JsonProcessingException e) { diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusProcessKafkaProducer.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusProcessKafkaProducer.java index 7803125..9a2dd4a 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusProcessKafkaProducer.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusProcessKafkaProducer.java @@ -79,7 +79,7 @@ public class CorpusProcessKafkaProducer { tmTelephoneCorpus.setCreateTime(LocalDateTime.now()); // 条件:只处理dcc的 10s通话时间以上 log.info("CorpusProcessKafkaProducer getCategoryCode: {},audioFileId:{},sourceId:{}", tmTelephoneCorpus.getCategoryCode(), aicorpusTelephone.getAudioFileId(), aicorpusTelephone.getSourceId()); - if (Constant.CHANNEL_DCC.equals(tmTelephoneCorpus.getCategoryCode())) { + if (Constant.CHANNEL_DCC.equals(tmTelephoneCorpus.getCategoryCode())||Constant.CHANNEL_4IN1.equals(tmTelephoneCorpus.getCategoryCode())) { log.info(" dcc 语料开始处理"); tmTelephoneCorpusService.saveTelephoneCorpus(tmTelephoneCorpus); long startTimeDify = System.currentTimeMillis(); diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusQuestionConsumer.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusQuestionConsumer.java new file mode 100644 index 0000000..076fb0f --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusQuestionConsumer.java @@ -0,0 +1,82 @@ +package com.volvo.ai.analytic.center.mq; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.volvo.ai.analytic.center.constant.Constant; +import com.volvo.ai.analytic.center.entity.TmCorpusQuestionReport; +import com.volvo.ai.analytic.center.feign.DiFyFeign; +import com.volvo.ai.analytic.center.mapper.TmCorpusQuestionReportMapper; +import com.volvo.ai.analytic.center.utils.ConstantStr; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Component +@RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.corpus.questiongroup}", + topic = "${rocketmq.consumer.corpus.questiontopic}", consumeThreadNumber = 2) +@RefreshScope +public class CorpusQuestionConsumer implements RocketMQListener> { + + + @Value("${dify.corpusQuestion.token:app-erbXevAmaQiyg5QFPn2NpJ7G}") + private String corpusQuestionToken; + + @Autowired + TmCorpusQuestionReportMapper tmCorpusQuestionReportMapper; + @Autowired + private DiFyFeign diFyFeign; + + @Override + public void onMessage(Map inputMap) { + inputMap.put("businessType", Constant.CHANNEL_4IN1); + log.info("corpusQuestionConsumer {}", JSON.toJSONString(inputMap)); + log.info("corpusQuestionToken {}", corpusQuestionToken); + + Map requestMap = new HashMap<>(); + requestMap.put("inputs", inputMap); + requestMap.put("user", ConstantStr.CORPUS_QUESTION_USER); + requestMap.put("response_mode", "blocking"); + + try { + JSONObject difyResult = diFyFeign.runWorkflows("Bearer " + corpusQuestionToken, requestMap); + log.info("dify corpusQuestionResult {}", difyResult.toJSONString()); + JSONObject data = difyResult.getJSONObject("data"); + + if (data.get("status").equals("succeeded")) { + JSONObject outputJson = data.getJSONObject("outputs"); + JSONObject textJsonObject = JSONObject.parseObject(outputJson.getString("text")); + JSONObject consultingScenario = textJsonObject.getJSONObject("consultingScenario"); + String userDemand = textJsonObject.getString("userDemand"); + if (userDemand != null && userDemand.length() > 4000) { + log.warn("userDemand 字段长度超过 4000,已自动截取。原始长度:{}", userDemand.length()); + userDemand = userDemand.substring(0, 4000); + } + TmCorpusQuestionReport tmCorpusQuestionReport = new TmCorpusQuestionReport(); + tmCorpusQuestionReport.setBusinessType(outputJson.getString("businessType")); + tmCorpusQuestionReport.setSourceCorpusId(outputJson.getString("sourceCorpusId")); + tmCorpusQuestionReport.setUserDemand(userDemand); + Date sourceCorpusDate = DateUtil.parse(outputJson.getString("sourceCorpusTime"), "yyyy-MM-dd HH:mm:ss"); + tmCorpusQuestionReport.setAnalysisResultJson(outputJson.toJSONString()); + tmCorpusQuestionReport.setMainCategory(consultingScenario.getString("mainCategory")); + tmCorpusQuestionReport.setSubCategory(consultingScenario.getString("subCategory")); + tmCorpusQuestionReport.setConfidence(consultingScenario.getString("confidence")); + tmCorpusQuestionReport.setSourceCorpusTime(sourceCorpusDate); + tmCorpusQuestionReportMapper.insert(tmCorpusQuestionReport); + } else { + log.info("dify corpusQuestion fail {}", JSON.toJSONString(inputMap)); + } + } catch (Exception e) { + log.info("dify corpusQuestion Exception {}", e.getMessage()); + } + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusQuestionProducer.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusQuestionProducer.java new file mode 100644 index 0000000..ee9e22e --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/CorpusQuestionProducer.java @@ -0,0 +1,29 @@ +package com.volvo.ai.analytic.center.mq; + +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Map; + +@Component +@Slf4j +public class CorpusQuestionProducer { + + @Value("${rocketmq.producer.corpus.questiontopic}") + private String corpusQuestionTopic; + + + @Resource + private RocketMQTemplate rocketMQTemplate; + + public void sendCorpusQuestion(Map inputMap) { + + rocketMQTemplate.convertAndSend(corpusQuestionTopic, inputMap); + + } + + +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/InvitedSpeakerConsumer.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/InvitedSpeakerConsumer.java new file mode 100644 index 0000000..cdf0f3f --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/mq/InvitedSpeakerConsumer.java @@ -0,0 +1,25 @@ +package com.volvo.ai.analytic.center.mq; + +import com.volvo.ai.analytic.center.service.SpokesManService; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RocketMQMessageListener(consumerGroup = "${rocketmq.consumer.invitedSpeaker.group}", + topic = "${rocketmq.consumer.invitedSpeaker.topic}", + enableMsgTrace = true) +public class InvitedSpeakerConsumer implements RocketMQListener { + + @Autowired + private SpokesManService spokesmanService; + @Override + public void onMessage(MessageExt messageExt) { + log.info("特邀发言官 message: " + messageExt); + spokesmanService.spokesManCallBack(new String(messageExt.getBody())); + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/CloudService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/CloudService.java new file mode 100644 index 0000000..c07660e --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/CloudService.java @@ -0,0 +1,285 @@ +package com.volvo.ai.analytic.center.service; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.huaweicloud.sdk.core.exception.ClientRequestException; +import com.huaweicloud.sdk.eg.v1.model.*; +import com.volvo.ai.analytic.center.config.HuaWeiEGConfig; +import com.volvo.ai.analytic.center.dto.event.SendEventDto; +import com.volvo.ai.analytic.center.dto.event.SourceChannelDto; +import com.volvo.ai.analytic.center.dto.resp.ResultDTO; +import com.volvo.ai.analytic.center.utils.Signer; +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.core.data.PojoCloudEventData; +import io.cloudevents.jackson.JsonFormat; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutionException; + +@Slf4j +@Service +public class CloudService implements InitializingBean { + + @Resource + RestTemplate restTemplate; + + @Resource + HuaWeiEGService huaWeiService; + + @Resource + RedisTemplate redisTemplate; + + @Resource + HuaWeiEGConfig huaWeiEGConfig; + + private static final String HA_WEI_REQUEST = "hwReq={}"; + private static final String HA_WEI_RESPONSE = "hwRes={}"; + private static final String SEND_EVENT_MSG = "发布事件到事件通道异常"; + private static final String HTTPS = "https://"; + private static final String V1 = "/v1/"; + private static final String CHANNELS = "/channels/"; + private static final String EVENTS = "/events"; + private static final String BODY_PRE = "{\"events\":["; + private static final String BODY_END = "]}"; + private static final String APPLICATION = "APPLICATION"; + private static final String COMMA = ","; + + /** + * 事件通道缓存 + */ + private static final String SOURCE_KEY = "eSource"; + + /** + * 发送事件 + * + * @param req 发送事件的负载内容 + * @return 发送结果 + * @throws ExecutionException 如果执行异常 + */ + public ResultDTO sendEvent(SendEventDto req) throws ExecutionException { + String eventId = UUID.randomUUID().toString(); + return send(req, eventId); + } + + /** + * 发送事件 + * + * @param req 发送事件的请求数据 + * @param eventId 事件ID + * @return 发送事件的响应数据 + * @throws ExecutionException 如果执行异常 + */ + private ResultDTO send(SendEventDto req, String eventId) throws ExecutionException { + // 根据sourceId或sourceName获取通道数据 + SourceChannelDto dto = req.getSource().contains("-") ? getChannelBySourceId(req.getSource()) : getChannelBySourceName(req.getSource()); + + + + // 构建CloudEvent对象 + CloudEvent event = CloudEventBuilder.v1().withId(eventId) + .withSource(URI.create(dto.getSourceName())).withType(StringUtils.isEmpty(req.getType()) ? APPLICATION : req.getType()) + .withData(MediaType.APPLICATION_JSON_VALUE, PojoCloudEventData.wrap(req.getData(), JSON::toJSONBytes)) + .withSubject(req.getSubject()).withTime(OffsetDateTime.now()).build(); + + // 将CloudEvent对象转换为JSON字符串 + String b = new String(new JsonFormat().serialize(event), StandardCharsets.UTF_8); + String body = BODY_PRE + b + BODY_END; + return getResponseDTO(eventId, dto, body); + } + + /** + * 发送事件 + * + * @param req 发送事件的请求数据 + * @return 发送事件的响应数据 + * @throws ExecutionException 如果执行异常 + */ + private ResultDTO sendList(SendEventDto> req) throws ExecutionException { + // 根据sourceId或sourceName获取通道数据 + SourceChannelDto dto = req.getSource().contains("-") ? getChannelBySourceId(req.getSource()) : getChannelBySourceName(req.getSource()); + + List eventIdList = new ArrayList<>(); + List eventList = new ArrayList<>(); + for (Object data : req.getData()) { + String eventId = UUID.randomUUID().toString(); + eventIdList.add(eventId); + + // 构建CloudEvent对象 + CloudEvent event = CloudEventBuilder.v1().withId(eventId) + .withSource(URI.create(dto.getSourceName())).withType(StringUtils.isEmpty(req.getType()) ? APPLICATION : req.getType()) + .withData(MediaType.APPLICATION_JSON_VALUE, PojoCloudEventData.wrap(data, JSON::toJSONBytes)) + .withSubject(req.getSubject()).withTime(OffsetDateTime.now()).build(); + // 将CloudEvent对象转换为JSON字符串 + eventList.add(new String(new JsonFormat().serialize(event), StandardCharsets.UTF_8)); + } + + + String eId = String.join(COMMA, eventIdList); + String body = BODY_PRE + String.join(COMMA, eventList) + BODY_END; + return getResponseDTO(eId, dto, body); + } + + /** + * 发送请求 + * + * @param eventId + * @param dto + * @param body + * @return + */ + private ResultDTO getResponseDTO(String eventId, SourceChannelDto dto, String body) { + String url = HTTPS + huaWeiEGConfig.getEndpoint() + V1 + + huaWeiEGConfig.getProjectId() + CHANNELS + dto.getChannelId() + EVENTS; + + Map headers = null; + try { + // 对请求进行签名 + headers = Signer.sign(huaWeiEGConfig.getAk(), huaWeiEGConfig.getSk(), url, body, huaWeiEGConfig.getEndpoint()); + } catch (Exception e) { + log.error("sign", e); + + } + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setAll(headers); + log.info(HA_WEI_REQUEST, body); + + // 发送POST请求 + String resp = restTemplate.postForEntity(url, new HttpEntity<>(body.getBytes(StandardCharsets.UTF_8), httpHeaders), String.class).getBody(); + log.info(HA_WEI_RESPONSE, resp); + PutEventsResponse response1 = JSON.parseObject(resp, new TypeReference() { + }); + + if (response1.getFailedCount() != 0) { + List events = response1.getEvents(); + List eventIds = new ArrayList<>(); + List errorMsgList = new ArrayList<>(); + for (PutEventsRespEvents response : events) { + eventIds.add(response.getEventId()); + errorMsgList.add(response.getEventId() + ":" + response.getErrorMsg()); + } + ResultDTO res =new ResultDTO(); + res.setErrMsg(String.join(COMMA, errorMsgList)); + res.setData(String.join(COMMA, eventIds)); + res.setReturnCode("500"); + return res; + } + return ResultDTO.success(eventId); + } + + + /** + * 根据事件源ID查询事件通道详情,并返回事件通道信息 + * + * @param sourceId 事件源ID + * @return 事件通道信息 + * @throws ExecutionException 如果执行查询操作失败,抛出执行异常 + */ + @NotNull + private SourceChannelDto getChannelBySourceId(String sourceId) throws ExecutionException { + + //根据事件源ID查询详情 + ShowDetailOfEventSourceResponse response; + try { + response = huaWeiService.showDetailOfEventSource(sourceId); + } catch (ClientRequestException e) { + + throw e; + } + if (response.getHttpStatusCode() != 200) { + log.info("根据事件源ID未查询到事件通道"); + } + SourceChannelDto dto = new SourceChannelDto(response.getChannelId(), response.getName()); + redisTemplate.opsForHash().put(SOURCE_KEY, sourceId, dto); + + return dto; + } + + /** + * 根据事件源名称查询事件通道 + * + * @param source 事件源名称 + * @return 事件通道信息 + * @throws ExecutionException 如果执行过程中发生异常,抛出执行异常 + */ + @NotNull + private SourceChannelDto getChannelBySourceName(String source) throws ExecutionException { + + + ListEventSourcesRequest sourcesRequest = new ListEventSourcesRequest(); + sourcesRequest.setName(source); + sourcesRequest.setLimit(1); + sourcesRequest.setOffset(0); + //根据事件源名称查询事件通道 + ListEventSourcesResponse res = huaWeiService.listEventSources(sourcesRequest); + if (res.getTotal() == 1) { + CustomizeSourceInfo sourceInfo = res.getItems().get(0); + SourceChannelDto dto = new SourceChannelDto(sourceInfo.getChannelId(), sourceInfo.getName()); + redisTemplate.opsForHash().put(SOURCE_KEY, source, dto); + return dto; + } else { + log.info("根据事件源名称未查询到事件通道"); + } + return null; + + } + + /** + * 在属性设置之后执行的操作。 + * + * @throws Exception 如果在属性设置过程中发生异常 + */ + @Override + public void afterPropertiesSet() throws Exception { + // 创建ListEventSourcesRequest对象 + ListEventSourcesRequest request = new ListEventSourcesRequest(); + // 设置请求的Limit属性为500 + request.setLimit(500); + // 设置请求的FuzzyName属性为"source" + request.setFuzzyName("source"); + + // 调用huaWeiService的listEventSources方法,获取ListEventSourcesResponse对象 + ListEventSourcesResponse response = huaWeiService.listEventSources(request); + + // 如果response的HttpStatusCode属性不等于200,则抛出BussinessException异常 + if (response.getHttpStatusCode() != 200) { + log.info("查询事件源列表异常"); + } + + // 删除redis中的SOURCE_KEY键值对 + redisTemplate.delete(SOURCE_KEY); + + // 如果response的Items属性不为空 + if (!CollectionUtils.isEmpty(response.getItems())) { + // 遍历response的Items属性 + for (CustomizeSourceInfo info : response.getItems()) { + // 创建SourceChannelDto对象 + SourceChannelDto dto = new SourceChannelDto(info.getChannelId(), info.getName()); + + // 将dto存储到redis的SOURCE_KEY键的hash表中,使用info.getId()作为键,dto作为值 + redisTemplate.opsForHash().put(SOURCE_KEY, info.getId(), dto); + + // 将dto存储到redis的SOURCE_KEY键的hash表中,使用info.getName()作为键,dto作为值 + redisTemplate.opsForHash().put(SOURCE_KEY, info.getName(), dto); + } + } + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/CorpusQuestionService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/CorpusQuestionService.java new file mode 100644 index 0000000..97fb46a --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/CorpusQuestionService.java @@ -0,0 +1,7 @@ +package com.volvo.ai.analytic.center.service; + +import com.volvo.ai.analytic.center.dto.req.ConsultingRequestDTO; + +public interface CorpusQuestionService { + void runCorpusQuestionDify(ConsultingRequestDTO consultingRequestDTO); +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/HuaWeiEGService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/HuaWeiEGService.java new file mode 100644 index 0000000..0f5558e --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/HuaWeiEGService.java @@ -0,0 +1,493 @@ +package com.volvo.ai.analytic.center.service; + +import com.alibaba.fastjson.JSON; +import com.huaweicloud.sdk.eg.v1.EgClient; +import com.huaweicloud.sdk.eg.v1.model.*; +import com.volvo.ai.analytic.center.dto.event.ListSubscriptionsRequestDto; +import com.volvo.ai.analytic.center.dto.event.SubscriptionCreateReqDto; +import com.volvo.ai.analytic.center.dto.event.SubscriptionOperateReqDto; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +@Component +@Slf4j +public class HuaWeiEGService { + + private static final String HA_WEI_REQUEST = "hwReq={}"; + private static final String HA_WEI_RESPONSE = "hwRes={}"; + + @Resource + EgClient egClient; + + + /** + * 发布事件到事件通道 sdk + * + * @param event event + * @param channel channel + */ + public PutEventsResponse sendEvent(CloudEvents event, String channel) { + PutEventsRequest request = new PutEventsRequest(); + request.withChannelId(channel); + + List eventsList = new ArrayList<>(); + eventsList.add(event); + PutEventsReq body = new PutEventsReq(); + body.setEvents(eventsList); + request.withBody(body); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + PutEventsResponse response = egClient.putEvents(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建事件订阅 + */ + public CreateSubscriptionResponse createSubscription(SubscriptionCreateReqDto reqDto) { + CreateSubscriptionRequest request = new CreateSubscriptionRequest(); + + SubscriptionCreateReq body = new SubscriptionCreateReq(); + BeanUtils.copyProperties(reqDto, body); + List sources = new ArrayList<>(); + sources.add(reqDto.getSources()); + body.setSources(sources); + + request.withBody(body); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateSubscriptionResponse response = egClient.createSubscription(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建事件订阅目标 + */ + public CreateSubscriptionTargetResponse createSubscriptionTarget(CreateSubscriptionTargetRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateSubscriptionTargetResponse response = egClient.createSubscriptionTarget(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 删除事件订阅 + */ + public void deleteSubscription(String subscriptionId) { + DeleteSubscriptionRequest request = new DeleteSubscriptionRequest(); + request.setSubscriptionId(subscriptionId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteSubscriptionResponse response = egClient.deleteSubscription(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + } + + /** + * 删除事件订阅目标 + */ + public void deleteSubscriptionTarget(String subscriptionId, String targetId) { + DeleteSubscriptionTargetRequest request = new DeleteSubscriptionTargetRequest(); + request.setSubscriptionId(subscriptionId); + request.setTargetId(targetId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteSubscriptionTargetResponse response = egClient.deleteSubscriptionTarget(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + } + + /** + * 查询事件订阅列表 + */ + public ListSubscriptionsResponse listSubscriptions(ListSubscriptionsRequestDto requestDto) { + ListSubscriptionsRequest request = new ListSubscriptionsRequest(); + BeanUtils.copyProperties(requestDto, request); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListSubscriptionsResponse response = egClient.listSubscriptions(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 操作事件订阅 + */ + public OperateSubscriptionResponse operateSubscription(SubscriptionOperateReqDto req) { + OperateSubscriptionRequest request = new OperateSubscriptionRequest(); + + SubscriptionOperateReq body = new SubscriptionOperateReq(); + List subscriptionIds = new ArrayList<>(); + subscriptionIds.add(req.getSubscriptionId()); + body.setSubscriptionIds(subscriptionIds); + body.setOperation(req.getStatus() == 1 ? SubscriptionOperateReq.OperationEnum.ENABLE : SubscriptionOperateReq.OperationEnum.DISABLE); + request.withBody(body); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + OperateSubscriptionResponse response = egClient.operateSubscription(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件订阅详情 + */ + public ShowDetailOfSubscriptionResponse showDetailOfSubscription(String subscriptionId) { + ShowDetailOfSubscriptionRequest request = new ShowDetailOfSubscriptionRequest(); + request.setSubscriptionId(subscriptionId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ShowDetailOfSubscriptionResponse response = egClient.showDetailOfSubscription(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件订阅目标详情 + */ + public ShowDetailOfSubscriptionTargetResponse showDetailOfSubscriptionTarget(String subscriptionId, String targetId) { + ShowDetailOfSubscriptionTargetRequest request = new ShowDetailOfSubscriptionTargetRequest(); + request.setSubscriptionId(subscriptionId); + request.setTargetId(targetId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ShowDetailOfSubscriptionTargetResponse response = egClient.showDetailOfSubscriptionTarget(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 更新事件订阅 + */ + public UpdateSubscriptionResponse updateSubscription(UpdateSubscriptionRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateSubscriptionResponse response = egClient.updateSubscription(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 更新事件订阅源 + */ + public UpdateSubscriptionSourceResponse updateSubscriptionSource(UpdateSubscriptionSourceRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateSubscriptionSourceResponse response = egClient.updateSubscriptionSource(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 更新事件订阅目标 + */ + public UpdateSubscriptionTargetResponse updateSubscriptionTarget(UpdateSubscriptionTargetRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateSubscriptionTargetResponse response = egClient.updateSubscriptionTarget(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建自定义事件通道 + */ + public CreateChannelResponse createChannel(ChannelCreateReq body) { + CreateChannelRequest request = new CreateChannelRequest(); + request.withBody(body); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateChannelResponse response = egClient.createChannel(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 删除自定义事件通道 + */ + public DeleteChannelResponse deleteChannel(String channelId) { + DeleteChannelRequest request = new DeleteChannelRequest(); + request.withChannelId(channelId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteChannelResponse response = egClient.deleteChannel(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件通道列表 + */ + public ListChannelsResponse listChannels(ListChannelsRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListChannelsResponse response = egClient.listChannels(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件通道详情 + */ + public ShowDetailOfChannelResponse showDetailOfChannel(String channelId) { + ShowDetailOfChannelRequest request = new ShowDetailOfChannelRequest(); + request.withChannelId(channelId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ShowDetailOfChannelResponse response = egClient.showDetailOfChannel(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 更新自定义事件通道 + */ + public UpdateChannelResponse updateChannel(UpdateChannelRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateChannelResponse response = egClient.updateChannel(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建自定义事件源 + */ + public CreateEventSourceResponse createEventSource(CustomizeSourceCreateReq body) { + CreateEventSourceRequest request = new CreateEventSourceRequest(); + request.withBody(body); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateEventSourceResponse response = egClient.createEventSource(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 删除自定义事件源 + */ + public DeleteEventSourceResponse deleteEventSource(String sourceId) { + DeleteEventSourceRequest request = new DeleteEventSourceRequest(); + request.withSourceId(sourceId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteEventSourceResponse response = egClient.deleteEventSource(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件源列表 + */ + public ListEventSourcesResponse listEventSources(ListEventSourcesRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListEventSourcesResponse response = egClient.listEventSources(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件源详情 + */ + public ShowDetailOfEventSourceResponse showDetailOfEventSource(String sourceId) { + ShowDetailOfEventSourceRequest request = new ShowDetailOfEventSourceRequest(); + request.withSourceId(sourceId); + + return egClient.showDetailOfEventSource(request); + } + + /** + * 更新自定义事件源 + */ + public UpdateEventSourceResponse updateEventSource(UpdateEventSourceRequest request) { + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateEventSourceResponse response = egClient.updateEventSource(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建目标连接 + */ + public CreateConnectionResponse createConnection(ConnectionCreateReq body) { + CreateConnectionRequest request = new CreateConnectionRequest(); + request.withBody(body); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateConnectionResponse response = egClient.createConnection(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 删除目标连接 + */ + public DeleteConnectionResponse deleteConnection(String connectionId) { + DeleteConnectionRequest request = new DeleteConnectionRequest(); + request.withConnectionId(connectionId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteConnectionResponse response = egClient.deleteConnection(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询目标连接列表 + */ + public ListConnectionsResponse listConnections(ListConnectionsRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListConnectionsResponse response = egClient.listConnections(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询目标连接详情 + */ + public ShowDetailOfConnectionResponse showDetailOfConnection(String connectionId) { + ShowDetailOfConnectionRequest request = new ShowDetailOfConnectionRequest(); + request.withConnectionId(connectionId); + + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ShowDetailOfConnectionResponse response = egClient.showDetailOfConnection(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 更新目标连接 + */ + public UpdateConnectionResponse updateConnection(UpdateConnectionRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateConnectionResponse response = egClient.updateConnection(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件目标分类 + */ + public ListEventTargetResponse listEventTarget(ListEventTargetRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListEventTargetResponse response = egClient.listEventTarget(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建自定义事件模型 + */ + public CreateEventSchemaResponse createEventSchema(CustomizeSchemaCreateReq body) { + CreateEventSchemaRequest request = new CreateEventSchemaRequest(); + request.withBody(body); + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateEventSchemaResponse response = egClient.createEventSchema(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 创建自定义事件模型 + */ + public CreateEventSchemaVersionResponse createEventSchemaVersion(CreateEventSchemaVersionRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + CreateEventSchemaVersionResponse response = egClient.createEventSchemaVersion(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 删除事件模型 + */ + public DeleteEventSchemaResponse deleteEventSchema(String schemaId) { + DeleteEventSchemaRequest request = new DeleteEventSchemaRequest(); + request.withSchemaId(schemaId); + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteEventSchemaResponse response = egClient.deleteEventSchema(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 删除事件模型 + */ + public DeleteEventSchemaVersionResponse deleteEventSchemaVersion(String schemaId, Integer version) { + DeleteEventSchemaVersionRequest request = new DeleteEventSchemaVersionRequest(); + request.withSchemaId(schemaId); + request.withVersion(version); + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DeleteEventSchemaVersionResponse response = egClient.deleteEventSchemaVersion(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 事件模型自动发现 + */ + public DiscoverEventSchemaFromDataResponse discoverEventSchemaFromData(DiscoverEventSchemaFromDataReq body) { + DiscoverEventSchemaFromDataRequest request = new DiscoverEventSchemaFromDataRequest(); + request.withBody(body); + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + DiscoverEventSchemaFromDataResponse response = egClient.discoverEventSchemaFromData(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件模型列表 + */ + public ListEventSchemaResponse listEventSchema(ListEventSchemaRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListEventSchemaResponse response = egClient.listEventSchema(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件模型版本列表 + */ + public ListEventSchemaVersionsResponse listEventSchemaVersions(ListEventSchemaVersionsRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ListEventSchemaVersionsResponse response = egClient.listEventSchemaVersions(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件模型详情 + */ + public ShowDetailOfEventSchemaResponse showDetailOfEventSchema(String schemaId) { + ShowDetailOfEventSchemaRequest request = new ShowDetailOfEventSchemaRequest(); + request.withSchemaId(schemaId); + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ShowDetailOfEventSchemaResponse response = egClient.showDetailOfEventSchema(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 查询事件模型版本详情 + */ + public ShowDetailOfEventSchemaVersionResponse showDetailOfEventSchemaVersion(String schemaId, Integer version) { + ShowDetailOfEventSchemaVersionRequest request = new ShowDetailOfEventSchemaVersionRequest(); + request.withSchemaId(schemaId); + request.withVersion(version); + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + ShowDetailOfEventSchemaVersionResponse response = egClient.showDetailOfEventSchemaVersion(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + + /** + * 更新自定义事件模型 + */ + public UpdateEventSchemaResponse updateEventSchema(UpdateEventSchemaRequest request) { + log.info(HA_WEI_REQUEST, JSON.toJSONString(request)); + UpdateEventSchemaResponse response = egClient.updateEventSchema(request); + log.info(HA_WEI_RESPONSE, JSON.toJSONString(response)); + return response; + } + +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/SpokesManService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/SpokesManService.java new file mode 100644 index 0000000..26b3a82 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/SpokesManService.java @@ -0,0 +1,226 @@ +package com.volvo.ai.analytic.center.service; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.volvo.ai.analytic.center.dto.req.*; +import com.volvo.ai.analytic.center.entity.AiAnalysisErrors; +import com.volvo.ai.analytic.center.entity.AiAnalysisRequestLogs; +import com.volvo.ai.analytic.center.entity.AiAnalyticBusinessConfig; +import com.volvo.ai.analytic.center.enums.BusinessTypeEnum; +import com.volvo.ai.analytic.center.mapper.AiAnalysisErrorsMapper; +import com.volvo.ai.analytic.center.mapper.AiAnalysisRequestLogsMapper; +import com.volvo.ai.analytic.center.mapper.AiAnalyticBusinessConfigMapper; +import com.volvo.ai.analytic.center.utils.AiAnalysisUtils; +import com.volvo.ai.analytic.center.utils.PublishedTimeFormatter; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 社区特邀发言官服务 + * 该服务用于处理社区特邀发言官相关的业务逻辑 + */ +@Slf4j +@Service +public class SpokesManService { + + @Autowired + private AiAnalyticBusinessConfigMapper aiAnalyticBusinessConfigMapper; + + @Autowired + private DiFyService diFyService; + + @Autowired + private AiAnalysisRequestLogsMapper aiAnalysisRequestLogsMapper; + + @Autowired + private AiAnalysisErrorsMapper aiAnalysisErrorsMapper; + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + @Value("${dify.spokesMan.token}") + private String spokesManToken; + + @Value("${rocketmq.producer.InvitedSpeaker.topic}") + private String topic; + + /** + * 处理社区特邀发言官回调请求 + * + * @param req SpokesManDTO 请求对象 + */ + public void spokesManCallBack(String req) { + + // 生成ai分析请求id + String aiAnalysisRequestId = AiAnalysisUtils.getAiAnalysisRequestId(BusinessTypeEnum.COMMUNITYTARGET.getCode()); + try { + // 异步保存请求日志 + syncSaveRequestLogs(JSON.toJSONString(req), aiAnalysisRequestId); + //把req转成SpokesManDTO对象 + SpokesManDTO spokesManDTO = JSON.parseObject(req, SpokesManDTO.class); + if (spokesManDTO == null || spokesManDTO.getFiles() == null || spokesManDTO.getFiles().isEmpty() + || spokesManDTO.getPlatform() == null) { + log.error("社区特邀发言官入参错误"); + throw new RuntimeException("社区特邀发言官入参错误"); + } + //获取审核规则配置 + List auditRulesList = aiAnalyticBusinessConfigMapper.selectList( + Wrappers.lambdaQuery() + .eq(AiAnalyticBusinessConfig::getBusinessLine, BusinessTypeEnum.SPOKESMAN.getCode()) + .eq(AiAnalyticBusinessConfig::getConfigType, BusinessTypeEnum.SPOKESMAN_AUDIT_RULE.getCode()) + .eq(AiAnalyticBusinessConfig::getVerificationMethod, spokesManDTO.getPlatform()) + .eq(AiAnalyticBusinessConfig::getIsDeleted, 0) + .eq(AiAnalyticBusinessConfig::getConfigVersion, 1) + ); + log.info("社区特邀发言官审核规则配置:{}", auditRulesList.toArray()); + String auditRules = auditRulesList.stream() + .map(AiAnalyticBusinessConfig::getConfigData) + .collect(Collectors.joining(" ")); + JSONArray imageUrls = new JSONArray(); + + for (String url : spokesManDTO.getFiles()) { + DifyImageWorkFlow diFyImageWorkFlow = new DifyImageWorkFlow(); + diFyImageWorkFlow.setUrl(url); + imageUrls.add(diFyImageWorkFlow); + } + DiFyReq diFyReq = new DiFyReq(); + diFyReq.setUser(BusinessTypeEnum.SPOKESMAN.getCode()); + diFyReq.setFlowId(spokesManToken); + JSONObject inputs = new JSONObject(); + inputs.put("files", imageUrls); + inputs.put("platform", spokesManDTO.getPlatform()); + inputs.put("theme", spokesManDTO.getTheme()); + inputs.put("auditRule", auditRules); + diFyReq.setInputs(inputs); + //异步更新dify请求日志 + syncUpdateDiFyRequest(diFyReq, aiAnalysisRequestId); + //调用workflow + JSONObject difSpokesManResult = (JSONObject) diFyService.getDiFyObject(diFyReq); + difSpokesManResult.put("communityRequestId", spokesManDTO.getCommunityRequestId()); + difSpokesManResult.put("aiAnalysisRequestId", aiAnalysisRequestId); + //处理发布时间 + updatePublishedTime(difSpokesManResult); + //返回结果推送到社区的MQ + rocketMQTemplate.syncSend(topic, difSpokesManResult.toString()); + //异步更新dify响应日志 + syncUpdateDiFyResponse(difSpokesManResult, aiAnalysisRequestId); + } catch (Exception e) { + log.error("特邀发言官异常:{}", e.getMessage()); + //保存错误日志 + aiAnalysisErrorsMapper.insert(AiAnalysisErrors.builder() + .aiAnalysisRequestId(aiAnalysisRequestId) + .aiAnalysisErrorMessage(e.getMessage()) + .aiAnalysisRequestType(BusinessTypeEnum.SPOKESMAN.getCode()) + .build()); + } + } + //特邀发言补偿 + public void spokesManTask() { + try { + //捞取异常表中属于社区的异常数据 + List aiAnalysisErrors = aiAnalysisErrorsMapper.selectList(new LambdaQueryWrapper() + .eq(AiAnalysisErrors::getAiAnalysisRequestType, BusinessTypeEnum.SPOKESMAN.getCode()) + .eq(AiAnalysisErrors::getAiAnalysisErrorHandlingStatus, "0") + .lt(AiAnalysisErrors::getRetryCount, 4)); + if (aiAnalysisErrors != null && aiAnalysisErrors.size() > 0) { + //根据ai_analysis_request_id获取AiAnalysisRequestLogs表中的对应的dify_request字段 + for (AiAnalysisErrors aiAnalysisError : aiAnalysisErrors) { + try { + AiAnalysisRequestLogs aiAnalysisRequestLogs = aiAnalysisRequestLogsMapper.selectOne(new LambdaQueryWrapper() + .eq(AiAnalysisRequestLogs::getAiAnalysisRequestId, aiAnalysisError.getAiAnalysisRequestId())); + if (aiAnalysisRequestLogs != null) { + //获取dify_request字段 + String message = aiAnalysisRequestLogs.getBusinessRequest(); + String difyRequest = aiAnalysisRequestLogs.getDifyRequest(); + if (StringUtils.isNotEmpty(message) || StringUtils.isNotEmpty(difyRequest)) { + SpokesManDTO spokesManDTO = JSON.parseObject(message, SpokesManDTO.class); DiFyReq diFyReq = JSON.parseObject(difyRequest, DiFyReq.class); + //调用workflow + JSONObject difSpokesManResult = (JSONObject) diFyService.getDiFyObject(diFyReq); + difSpokesManResult.put("communityRequestId", spokesManDTO.getCommunityRequestId()); + difSpokesManResult.put("aiAnalysisRequestId", aiAnalysisError.getAiAnalysisRequestId()); + //处理发布时间 + updatePublishedTime(difSpokesManResult); + //返回结果推送到社区的MQ + rocketMQTemplate.syncSend(topic, difSpokesManResult.toString()); + //异步更新dify响应日志 + syncUpdateDiFyResponse(difSpokesManResult, aiAnalysisError.getAiAnalysisRequestId()); + } else { + aiAnalysisErrorsMapper.update(new AiAnalysisErrors(), new LambdaUpdateWrapper() + .eq(AiAnalysisErrors::getAiAnalysisRequestId, aiAnalysisError.getAiAnalysisRequestId()) + .set(AiAnalysisErrors::getAiAnalysisErrorHandlingStatus, "2")); + } + } + } catch (Exception e) { + log.error("补偿社区消息,AIID:{},异常:{}", aiAnalysisError.getAiAnalysisRequestId(), e); + aiAnalysisErrorsMapper.update(new AiAnalysisErrors(), new LambdaUpdateWrapper() + .eq(AiAnalysisErrors::getAiAnalysisRequestId, aiAnalysisError.getAiAnalysisRequestId()) + .set(AiAnalysisErrors::getRetryCount, aiAnalysisError.getRetryCount() + 1)); + } + } + } + } catch (Exception e) { + log.error("处理社区异常消息异常:{}", e); + } + } + public static void updatePublishedTime(JSONObject rootJson) { + String contentAnalysisResultStr = rootJson.getString("ContentAnalysisResult"); + if (contentAnalysisResultStr == null || contentAnalysisResultStr.isEmpty()) return; + + JSONObject contentAnalysisResult = JSON.parseObject(contentAnalysisResultStr); + JSONObject contentAnalysisReport = contentAnalysisResult.getJSONObject("contentAnalysisReport"); + if (contentAnalysisReport == null) return; + + JSONObject publishedContentOuter = contentAnalysisReport.getJSONObject("publishedContent"); + if (publishedContentOuter == null) return; + + JSONObject publishedContentInner = publishedContentOuter.getJSONObject("publishedContent"); + if (publishedContentInner == null) return; + + String publishedTime = publishedContentInner.getString("publishedTime"); + String formattedPublishedTime = PublishedTimeFormatter.parsePublishedTime(publishedTime); + + if (formattedPublishedTime != null) { + publishedContentInner.put("publishedTime", formattedPublishedTime); + publishedContentOuter.put("publishedContent", publishedContentInner); + contentAnalysisReport.put("publishedContent", publishedContentOuter); + contentAnalysisResult.put("contentAnalysisReport", contentAnalysisReport); + rootJson.put("ContentAnalysisResult", contentAnalysisResult.toJSONString()); + } + } + + + @Async + protected void syncSaveRequestLogs(String message, String aiAnalysisRequestId) { + aiAnalysisRequestLogsMapper.insert(AiAnalysisRequestLogs.builder() + .aiAnalysisRequestId(aiAnalysisRequestId) + .businessRequest(message) + .difyAgentKey(spokesManToken) + .aiAnalysisRequestType(BusinessTypeEnum.SPOKESMAN.getCode()) + .build()); + } + @Async + protected void syncUpdateDiFyRequest(Object obj, String aiAnalysisRequestId) { + aiAnalysisRequestLogsMapper.update(new AiAnalysisRequestLogs(), + new UpdateWrapper().set("dify_request", JSON.toJSONString(obj)) + .eq("ai_analysis_request_id", aiAnalysisRequestId)); + } + @Async + protected void syncUpdateDiFyResponse(JSONObject result, String aiAnalysisRequestId) { + aiAnalysisRequestLogsMapper.update(new AiAnalysisRequestLogs(), + new UpdateWrapper().set("dify_response", result.toJSONString()) + .eq("ai_analysis_request_id", aiAnalysisRequestId)); + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmOdsImRealtimeMessageCurService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmOdsImRealtimeMessageCurService.java new file mode 100644 index 0000000..ce2b71f --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmOdsImRealtimeMessageCurService.java @@ -0,0 +1,15 @@ +package com.volvo.ai.analytic.center.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.volvo.ai.analytic.center.entity.TmOdsImRealtimeMessageCur; +/** + * @description 电话语料表-同步表 + * @author BEJSON + * @date 2025-03-04 + */ +public interface TmOdsImRealtimeMessageCurService extends IService { + + void runImCorpusDify(String paramJson); + + +} \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmTelephoneCorpusService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmTelephoneCorpusService.java index 256518f..dfdf9eb 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmTelephoneCorpusService.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/TmTelephoneCorpusService.java @@ -17,6 +17,9 @@ public interface TmTelephoneCorpusService extends IService { void runTelephoneCorpusDify(AicorpusTelephoneDTO aicorpusTelephone); + void runTelephoneCorpusDify4in1(AicorpusTelephoneDTO aicorpusTelephone); + + String getCarModelList(); void sendMq(String tag, String message); diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/UserQuestionSummaryService.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/UserQuestionSummaryService.java new file mode 100644 index 0000000..1f797f8 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/UserQuestionSummaryService.java @@ -0,0 +1,18 @@ +package com.volvo.ai.analytic.center.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.volvo.ai.analytic.center.dto.req.SummaryQuestionRequestDTO; +import com.volvo.ai.analytic.center.entity.TmCorpusQuestionSummaryReport; +/** + * @description 用户问题总结 + * @author lujie + * @date 2025-07-23 + */ +public interface UserQuestionSummaryService extends IService { + + void runUserQuestionSummary(String paramJson); + + void updateSummery(SummaryQuestionRequestDTO summaryQuestionRequestDTO); + + +} \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/ClaimVerificationServiceImpl.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/ClaimVerificationServiceImpl.java index a0c64d6..e149a7d 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/ClaimVerificationServiceImpl.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/ClaimVerificationServiceImpl.java @@ -231,6 +231,7 @@ public class ClaimVerificationServiceImpl implements ClaimVerificationService { audit.put("diagnosticReport", claimVerificationFileAnalysisDTO.getDiagnosticReport() == null ? " " :claimVerificationFileAnalysisDTO.getDiagnosticReport().toString()); audit.put("preCheckReport", claimVerificationFileAnalysisDTO.getPreCheckReport() == null ? " " :claimVerificationFileAnalysisDTO.getPreCheckReport().toString()); audit.put("dtcReport", claimVerificationFileAnalysisDTO.getDtcReport() == null ? " " :claimVerificationFileAnalysisDTO.getDtcReport().toString()); + audit.put("warrantyReport", claimVerificationFileAnalysisDTO.getWarrantyReport() == null ? " " :claimVerificationFileAnalysisDTO.getWarrantyReport().toString()); //脱敏处理 List maskingRuleItems = dataMaskingRuleService.getDataMaskingRuleListByApplicationChannel(Constant.CHANNEL_DCC); @@ -244,16 +245,15 @@ public class ClaimVerificationServiceImpl implements ClaimVerificationService { @NotNull private static JSONObject getJsonObjectToNewBie(JSONObject diFyObject, ClaimVerificationFileDTO claimVerificationFileAnalysisDTO, String aiAnalysisRequestId) { - if (diFyObject.containsKey("answer") && !diFyObject.getJSONObject("answer").isEmpty()) { - diFyObject = diFyObject.getJSONObject("answer"); + JSONObject warrantyAuditReport = diFyObject.getJSONObject("warrantyAuditReport"); + if (warrantyAuditReport.containsKey("answer") && !warrantyAuditReport.getJSONObject("answer").isEmpty()) { + warrantyAuditReport = warrantyAuditReport.getJSONObject("answer"); } - String warrantyAuditReport = diFyObject.getString("warrantyAuditReport"); - warrantyAuditReport = StringEscapeUtils.unescapeJava(warrantyAuditReport); - JSONObject audiResToAfter = JSON.parseObject(warrantyAuditReport); + JSONObject data = new JSONObject(); data.put("newBieAfterSalesId", claimVerificationFileAnalysisDTO.getNewBieAfterSalesId()); data.put("businessType", claimVerificationFileAnalysisDTO.getBusinessType()); - data.put("warrantyAuditReport", audiResToAfter); + data.put("warrantyAuditReport", warrantyAuditReport); data.put("aiAnalysisRequestId", aiAnalysisRequestId); return data; } diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/CorpusQuestionServiceImpl.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/CorpusQuestionServiceImpl.java new file mode 100644 index 0000000..4ad6a80 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/CorpusQuestionServiceImpl.java @@ -0,0 +1,42 @@ +package com.volvo.ai.analytic.center.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.volvo.ai.analytic.center.dto.req.ConsultingRequestDTO; +import com.volvo.ai.analytic.center.entity.TmCorpusQuestionReport; +import com.volvo.ai.analytic.center.mapper.TmCorpusQuestionReportMapper; +import com.volvo.ai.analytic.center.service.CorpusQuestionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class CorpusQuestionServiceImpl implements CorpusQuestionService { + + @Autowired + TmCorpusQuestionReportMapper tmCorpusQuestionReportMapper; + + @Override + public void runCorpusQuestionDify(ConsultingRequestDTO consultingRequestDTO) { + String text = consultingRequestDTO.getText(); + JSONObject textJsonObject = JSONObject.parseObject(text); + JSONObject consultingScenario = textJsonObject.getJSONObject("consultingScenario"); + String userDemand = textJsonObject.getString("userDemand"); + if (userDemand != null && userDemand.length() > 4000) { + log.warn("userDemand 字段长度超过 4000,已自动截取。原始长度:{}", userDemand.length()); + userDemand = userDemand.substring(0, 4000); + } + TmCorpusQuestionReport tmCorpusQuestionReport = new TmCorpusQuestionReport(); + tmCorpusQuestionReport.setMainCategory(consultingScenario.getString("mainCategory")); + tmCorpusQuestionReport.setSubCategory(consultingScenario.getString("subCategory")); + tmCorpusQuestionReport.setUserDemand(userDemand); + + tmCorpusQuestionReport.setConfidence(consultingScenario.getString("confidence")); + tmCorpusQuestionReport.setBusinessType(consultingRequestDTO.getBusinessType()); + tmCorpusQuestionReport.setSourceCorpusId(consultingRequestDTO.getSourceCorpusId()); + tmCorpusQuestionReport.setSourceCorpusTime(consultingRequestDTO.getSourceCorpusTime()); + tmCorpusQuestionReport.setAnalysisResultJson(text); + tmCorpusQuestionReportMapper.insert(tmCorpusQuestionReport); + + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmOdsImRealtimeMessageCurServiceImpl.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmOdsImRealtimeMessageCurServiceImpl.java new file mode 100644 index 0000000..bf7901a --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmOdsImRealtimeMessageCurServiceImpl.java @@ -0,0 +1,224 @@ +package com.volvo.ai.analytic.center.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.volvo.ai.analytic.center.dto.PageDto; +import com.volvo.ai.analytic.center.dto.corpus.ImCurMessageDTO; +import com.volvo.ai.analytic.center.dto.req.ConsultingRequestDTO; +import com.volvo.ai.analytic.center.dto.req.DiFyReq; +import com.volvo.ai.analytic.center.dto.req.RunMaskingRuleInput; +import com.volvo.ai.analytic.center.entity.*; +import com.volvo.ai.analytic.center.enums.BusinessTypeEnum; +import com.volvo.ai.analytic.center.enums.ImCustomTypeEnums; +import com.volvo.ai.analytic.center.mapper.TmOdsImRealtimeMessageCurMapper; +import com.volvo.ai.analytic.center.service.*; +import com.volvo.ai.analytic.center.utils.ConstantStr; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + + +/** + * @description 企微语料表-同步表 + * @author jlu + * @date 2025-07-17 + */ +@RefreshScope +@Slf4j +@Service +public class TmOdsImRealtimeMessageCurServiceImpl extends ServiceImpl implements TmOdsImRealtimeMessageCurService { + + @Autowired + private TmOdsImRealtimeMessageCurMapper tmOdsImRealtimeMessageCurMapper; + + @Autowired + private DiFyService diFyService; + + @Value("${dify.corpus.imCurToken}") + private String imCurToken; + + @Value("${batch.size}") + public int pageSize = 100; + + @Autowired + private DataMaskingRuleService dataMaskingRuleService; + + @Autowired + private CorpusQuestionService corpusQuestionService; + + @Override + public void runImCorpusDify(String paramJson) { + long startTime = System.currentTimeMillis(); + log.info("runImCorpusDify paramJson {}", paramJson); + // 获取当前日期 + LocalDate today = LocalDate.now(); + // 获取前一天日期 + LocalDate yesterday = today.minusDays(1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + // 格式化输出 + String formattedDate = yesterday.toString(); // 默认格式为 yyyy-MM-dd + String statTimeString = formattedDate.concat(" 00:00:00"); + String endTimeString = formattedDate.concat(" 23:59:59"); + if (StringUtils.isNotBlank(paramJson)) { + JSONObject paramJsonObj = JSONObject.parseObject(paramJson); + if (null != paramJsonObj && paramJsonObj.containsKey("statTime") && paramJsonObj.containsKey("endTime")) { + statTimeString = paramJsonObj.getString("statTime"); + endTimeString = paramJsonObj.getString("endTime"); + } + } + Long finalStatTime = stringTimeToTimeStamp(statTimeString, formatter); + Long finalEndTime = stringTimeToTimeStamp(endTimeString, formatter); + + Long statTime = stringTimeToTimeStamp(statTimeString, formatter); + Long endTime = stringTimeToTimeStamp(endTimeString, formatter); + + Integer total = tmOdsImRealtimeMessageCurMapper.countImRealtimeMessageCurByDate(statTime, endTime); + int totalPages = PageDto.getTotalPages(total, pageSize); + + // 设置脱敏规则 + List maskingRuleItems = dataMaskingRuleService.getDataMaskingRuleListByApplicationChannel(BusinessTypeEnum.SMART_ASSISTANT_QIWEI.getCode()); + RunMaskingRuleInput runMaskingRuleInput = new RunMaskingRuleInput(); + runMaskingRuleInput.setDataMaskingRules(maskingRuleItems); + + // 创建线程池 + int optimalThreadPoolSize = Runtime.getRuntime().availableProcessors() + 1; + log.info("IM处理总数据量:{},总页数:{},获取的线程数:{}",total,totalPages,optimalThreadPoolSize); + ExecutorService executor = Executors.newFixedThreadPool(optimalThreadPoolSize); // 根据需求调整线程池大小 + + try { + for (int i = 1; i <= totalPages; i++) { + int offset = (i - 1) * pageSize; + List messageList = tmOdsImRealtimeMessageCurMapper.queryImRealtimeMessageCurByDate(statTime, endTime, offset, pageSize); + // 处理查询到的数据 + // 使用 CompletableFuture 并行处理 + CompletableFuture[] futures = messageList.stream() + .map(item -> CompletableFuture.runAsync(() -> { + try { + processItem(item, finalStatTime, finalEndTime, runMaskingRuleInput, formatter); + } catch (Exception e) { + log.error("处理IM语料失败: sessionId={}, 异常: {}", + item.getSessionId(), e.getMessage(), e); + } + }, executor)) + .toArray(CompletableFuture[]::new); + // 等待所有任务完成 + CompletableFuture.allOf(futures).join(); + } + // 关闭线程池 + executor.shutdown(); + } catch (Exception e) { + log.error("IM数据跑批异常",e); + } finally { + // 关闭线程池 + executor.shutdown(); + } + log.info("IM数据跑批结束 耗时:{}",System.currentTimeMillis()-startTime); + } + + private void processItem(ImCurMessageDTO item, Long statTime, Long endTime, RunMaskingRuleInput runMaskingRuleInput, DateTimeFormatter formatter) { + log.info("IM语料内容处理:sessionId:{}", item.getSessionId()); + + // 获取语料信息 + List contetnList = tmOdsImRealtimeMessageCurMapper.queryImRealtimeMessageCurBySessionId(statTime, endTime, item.getSessionId()); + + // 组装语料信息 + StringBuffer chatList = new StringBuffer(); + contetnList.forEach(contentItem -> { + if (item.getMsgTimestamp() == null) { + item.setMsgTimestamp(contentItem.getMsgTimestamp()); + } + String title = ""; + if(ImCustomTypeEnums.CUSTOMER.getCode().equals(contentItem.getCustomerType())){ + title="客户:"; + }else{ + title="顾问:"; + } + JSONObject contentJson = JSONObject.parseObject(contentItem.getMsgContent()); + String content = contentJson.getString("text"); + // 拼接 role 和 text + String chat = title.concat(content); + runMaskingRuleInput.setOldStr(chat); + String corpusChat = dataMaskingRuleService.runMaskingRule(runMaskingRuleInput); + chatList.append(corpusChat).append("\n"); + }); + Map inputMap = new HashMap<>(); + inputMap.put("chat", chatList.toString()); + inputMap.put("businessType", BusinessTypeEnum.IM_CORPUS_ANALYSIS.getCode()); + inputMap.put("sourceCorpusId", item.getSessionId()); + inputMap.put("sourceCorpusTime", item.getMsgTimestamp() != null ? timeStampToLocalDateTime(item.getMsgTimestamp(), formatter) : null); + + DiFyReq diFyImageReq = new DiFyReq(); + diFyImageReq.setUser(ConstantStr.corpus_user); + diFyImageReq.setFlowId(imCurToken); + diFyImageReq.setInputs(inputMap); + // 调用dify + JSONObject resultJsonObject = (JSONObject) diFyService.getDiFyObject(diFyImageReq); + log.info("runDify difyflow {}", resultJsonObject); + + // 保存结果 + if (resultJsonObject != null) { + ConsultingRequestDTO corpusQuestion = new ConsultingRequestDTO(); + corpusQuestion.setText(resultJsonObject.getString("text")); + corpusQuestion.setBusinessType(resultJsonObject.getString("businessType")); + corpusQuestion.setSourceCorpusId(resultJsonObject.getString("sourceCorpusId")); + String sourceCorpusTimeStr = resultJsonObject.getString("sourceCorpusTime"); + if (StringUtils.isNotBlank(sourceCorpusTimeStr)) { + try { + corpusQuestion.setSourceCorpusTime(DateUtil.parse(sourceCorpusTimeStr)); + } catch (Exception e) { + log.error("转换 sourceCorpusTime 为 Date 类型失败,时间字符串: {}", sourceCorpusTimeStr, e); + } + } + corpusQuestionService.runCorpusQuestionDify(corpusQuestion); + } + + } + + /** + * 时间字符串转时间戳秒级 + * @param stringTime + * @param formatter + * @return + */ + private Long stringTimeToTimeStamp(String stringTime, DateTimeFormatter formatter) { + if (StringUtils.isBlank(stringTime)) { + return null; + } + LocalDateTime localDateTime = LocalDateTime.parse(stringTime, formatter); + + // 转换为秒级时间戳 + return localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond(); + } + + /** + * 秒级时间戳转时间字符串 + * @param timeStamp + * @param formatter + * @return + */ + private String timeStampToLocalDateTime(Long timeStamp, DateTimeFormatter formatter) { + if (timeStamp == null) { + return null; + } + Instant instant = Instant.ofEpochSecond(timeStamp); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + return localDateTime.format(formatter); + } + +} \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmTelephoneCorpusServiceImpl.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmTelephoneCorpusServiceImpl.java index 85ea52c..de520f8 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmTelephoneCorpusServiceImpl.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/TmTelephoneCorpusServiceImpl.java @@ -19,7 +19,9 @@ import com.volvo.ai.analytic.center.enums.BusinessTypeEnum; import com.volvo.ai.analytic.center.enums.CategoryEnum; import com.volvo.ai.analytic.center.feign.RemoteCarModelClient; import com.volvo.ai.analytic.center.mapper.TmTelephoneCorpusMapper; +import com.volvo.ai.analytic.center.mq.CorpusQuestionProducer; import com.volvo.ai.analytic.center.service.*; +import com.volvo.ai.analytic.center.utils.AiAnalysisUtils; import com.volvo.ai.analytic.center.utils.ConstantStr; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; @@ -64,6 +66,9 @@ public class TmTelephoneCorpusServiceImpl extends ServiceImpl maskingRuleItems = dataMaskingRuleService.getDataMaskingRuleListByApplicationChannel(BusinessTypeEnum.SMART_ASSISTANT_4IN1.getCode()); + + RunMaskingRuleInput runMaskingRuleInput = new RunMaskingRuleInput(); + runMaskingRuleInput.setDataMaskingRules(maskingRuleItems); + + Map inputMap = new HashMap(); + DiFyReq diFyImageReq = new DiFyReq(); + diFyImageReq.setUser(ConstantStr.corpus_user); + diFyImageReq.setFlowId(channel4In1Token); + + + JSONObject jsonObject = JSONObject.parseObject( aicorpusTelephone.getDisplay()); + JSONArray segments = jsonObject.getJSONArray("segments"); + // Long audioDuration = jsonObject.getLong("audio_duration"); // 毫秒 + // if(audioDuration/1000 <=10){ + // log.info("电话语料时长小于10秒,不进行dify处理"); + // return; + // } + if (segments == null || segments.isEmpty()) { + log.info("电话语料内容segments为空,不进行dify处理"); + return; + } + // 遍历 segments + StringBuffer chatList = new StringBuffer(); + segments.stream() + .map(segment -> (JSONObject) segment) + .forEach(segment -> { + JSONObject result = segment.getJSONObject("result"); + String text = result.getString("text"); + JSONObject analysisInfo = result.getJSONObject("analysis_info"); + String role = analysisInfo.getString("role"); + String title=""; + if(role.equals("AGENT")){ + title="顾问"; + }else{ + title="客户"; + } + // 拼接 role 和 text + String chat = title + ": " + text; + runMaskingRuleInput.setOldStr(chat); + String corpusChat = dataMaskingRuleService.runMaskingRule(runMaskingRuleInput); + chatList.append(corpusChat).append("\n"); + + }); + inputMap.put("workOrderContent", chatList.toString()); + + Map questionInput = new HashMap(); + questionInput.put("chat", chatList.toString()); + questionInput.put("sourceCorpusId", aicorpusTelephone.getSourceId()); + questionInput.put("sourceCorpusTime", aicorpusTelephone.getTranscribeTime()); + + //发送单个问题消息 + corpusQuestionProducer.sendCorpusQuestion(questionInput); + + inputMap.put("workOrderId",aicorpusTelephone.getSourceId()); + + String oldAiAnalysisRequestId = aicorpusTelephone.getAiAnalysisRequestId(); + String newAiAnalysisRequestId = StringUtils.isEmpty(oldAiAnalysisRequestId)? AiAnalysisUtils.getAiAnalysisRequestId(BusinessTypeEnum.SMART_ASSISTANT_4IN1.getCode()): + oldAiAnalysisRequestId; + + inputMap.put("aiAnalysisRequestId",newAiAnalysisRequestId); + diFyImageReq.setInputs(inputMap); + CorpusReportDTO corpusReportDTO = new CorpusReportDTO(); + corpusReportDTO.setRecordId(aicorpusTelephone.getSourceId()); + + log.info("开始处理智能客服4IN1工作流,{}",diFyImageReq); + + // 获取配置 + JSONObject execDifyFlow = diFyService.executeDifyFlow(diFyImageReq, BusinessTypeEnum.SMART_ASSISTANT_4IN1.getCode(), JSONObject.toJSONString(corpusReportDTO),newAiAnalysisRequestId ); + log.info("runDify智能客服4IN1 {}",execDifyFlow); + String aiAnalysisRequestId = execDifyFlow.getString("aiAnalysisRequestId"); + if(null != execDifyFlow && execDifyFlow.get("status").equals("succeeded")){ + JSONObject outputsJson = execDifyFlow.getJSONObject("outputs"); + String message = JSONObject.toJSONString(outputsJson); + log.info("send 4IN1mq {}",message); + intelligentCustomerService.sendMq(message); + if(StringUtils.isNotEmpty(aicorpusTelephone.getAiAnalysisRequestId())){ + AiAnalysisErrors aiAnalysisErrors = new AiAnalysisErrors(); + aiAnalysisErrors.setAiAnalysisRequestId(aiAnalysisRequestId); + aiAnalysisErrors.setAiAnalysisErrorHandlingStatus("1"); + aiAnalysisErrorsService.updateAiAnalysisErrors(aiAnalysisErrors); + } + aiAnalysisRequestLogsService.saveAiAnalysisRequestLogs(AiAnalysisRequestLogs.builder().aiAnalysisRequestId(execDifyFlow.getString("aiAnalysisRequestId")).businessResponse(message).build()); + + } + + } + } + public String getCarModelList(){ try { long startTime = System.currentTimeMillis(); diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/UserQuestionSummaryServiceImpl.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/UserQuestionSummaryServiceImpl.java new file mode 100644 index 0000000..c6e4625 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/service/impl/UserQuestionSummaryServiceImpl.java @@ -0,0 +1,156 @@ +package com.volvo.ai.analytic.center.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.volvo.ai.analytic.center.dto.corpus.ImCurMessageDTO; +import com.volvo.ai.analytic.center.dto.req.DiFyReq; +import com.volvo.ai.analytic.center.dto.req.SummaryQuestionRequestDTO; +import com.volvo.ai.analytic.center.entity.*; +import com.volvo.ai.analytic.center.enums.BusinessTypeEnum; +import com.volvo.ai.analytic.center.enums.QuestionMainCategoryEnum; +import com.volvo.ai.analytic.center.mapper.TmCorpusQuestionReportMapper; +import com.volvo.ai.analytic.center.mapper.TmCorpusQuestionSummaryReportMapper; +import com.volvo.ai.analytic.center.service.*; +import com.volvo.ai.analytic.center.utils.AiAnalysisUtils; +import com.volvo.ai.analytic.center.utils.ConstantStr; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * @description 用户问题汇总 + * @author jlu + * @date 2025-07-17 + */ +@RefreshScope +@Slf4j +@Service +public class UserQuestionSummaryServiceImpl extends ServiceImpl implements UserQuestionSummaryService { + + @Autowired + private TmCorpusQuestionSummaryReportMapper tmCorpusQuestionSummaryReportMapper; + @Autowired + private TmCorpusQuestionReportMapper tmCorpusQuestionReportMapper; + + @Autowired + private DiFyService diFyService; + + @Value("${dify.corpus.reportSummaryToken}") + private String reportSummaryToken; + + @Override + public void runUserQuestionSummary(String paramJson) { + long startTime = System.currentTimeMillis(); + log.info("runUserQuestionSummary paramJson {}", paramJson); + // 获取当前日期 + LocalDate today = LocalDate.now(); + // 获取前一天日期 + LocalDate yesterday = today.minusDays(1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + // 格式化输出 + String formattedDate = yesterday.toString(); // 默认格式为 yyyy-MM-dd + String statTimeString = formattedDate.concat(" 00:00:00"); + String endTimeString = formattedDate.concat(" 23:59:59"); + if (StringUtils.isNotBlank(paramJson)) { + JSONObject paramJsonObj = JSONObject.parseObject(paramJson); + if (null != paramJsonObj && paramJsonObj.containsKey("statTime") && paramJsonObj.containsKey("endTime")) { + statTimeString = paramJsonObj.getString("statTime"); + endTimeString = paramJsonObj.getString("endTime"); + } + } + + Date statTime = stringTimeToDate(statTimeString, formatter); + Date endTime = stringTimeToDate(endTimeString, formatter); + + for (QuestionMainCategoryEnum enumValue : QuestionMainCategoryEnum.values()) { + String mainCategoryString = enumValue.getMessage(); + try { + processItem(statTime, endTime, mainCategoryString); + } catch (Exception e) { + log.error("处理问题汇总失败: 异常: {}", e.getMessage(), e); + } + } + log.info("处理问题结束 耗时:{}",System.currentTimeMillis()-startTime); + } + + + @Override + public void updateSummery(SummaryQuestionRequestDTO summaryQuestionRequestDTO) { + if (summaryQuestionRequestDTO == null || summaryQuestionRequestDTO.getSummaryReportId() == null || summaryQuestionRequestDTO.getPreliminarySummaryResults() ==null) { + return; + } + tmCorpusQuestionSummaryReportMapper.updateBySummaryReportId(summaryQuestionRequestDTO.getSummaryReportId(), summaryQuestionRequestDTO.getPreliminarySummaryResults().toString()); + } + + private void processItem(Date statTime, Date endTime, String mainCategoryString) { + log.info("用户问题总结处理:mainCategory:{}", mainCategoryString); + + // 获取分析结果信息 + List resultList = tmCorpusQuestionReportMapper.queryQuestionReportList(statTime, endTime, mainCategoryString); + + if (resultList == null || resultList.isEmpty()) { + return; + } + + TmCorpusQuestionSummaryReport summaryReport = new TmCorpusQuestionSummaryReport(); + summaryReport.setMainCategory(mainCategoryString); + summaryReport.setSourceTime(statTime); + String aiAnalysisRequestId = AiAnalysisUtils.getAiAnalysisRequestId(BusinessTypeEnum.SUMMARY_REPORT.getCode()); + summaryReport.setSummaryReportId(aiAnalysisRequestId); + tmCorpusQuestionSummaryReportMapper.insert(summaryReport); + + // 组装语料信息 + StringBuffer demandList = new StringBuffer(); + resultList.forEach(contentItem -> { + if (StringUtils.isNotBlank(contentItem.getUserDemand())) { + demandList.append(contentItem.getUserDemand()).append("\n"); + } + }); + Map inputMap = new HashMap<>(); + inputMap.put("chat", demandList.toString()); + inputMap.put("summaryReportId", aiAnalysisRequestId); + + DiFyReq diFyImageReq = new DiFyReq(); + diFyImageReq.setUser(ConstantStr.corpus_user); + diFyImageReq.setFlowId(reportSummaryToken); + diFyImageReq.setInputs(inputMap); + // 调用dify + JSONObject resultJsonObject = (JSONObject) diFyService.getDiFyObject(diFyImageReq); + log.info("runDify userQuestionSummary difyflow {}", resultJsonObject); + // 保存结果 + if (resultJsonObject != null) { + tmCorpusQuestionSummaryReportMapper.updateBySummaryReportId(aiAnalysisRequestId, resultJsonObject.getString("preliminarySummaryResults")); + } + + } + + /** + * 时间字符串转时间戳秒级 + * @param stringTime + * @param formatter + * @return + */ + private Date stringTimeToDate(String stringTime, DateTimeFormatter formatter) { + if (StringUtils.isBlank(stringTime)) { + return null; + } + LocalDateTime localDateTime = LocalDateTime.parse(stringTime, formatter); + + // 转换为秒级时间戳 + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } + +} \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/ConstantStr.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/ConstantStr.java index bb9c3cc..b5d0f55 100644 --- a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/ConstantStr.java +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/ConstantStr.java @@ -12,6 +12,9 @@ public class ConstantStr { public static final String CHANNEL_DCC = "Channel_Dcc"; public static final String corpus_user = "corpush_user"; + public static final String CORPUS_QUESTION_USER = "corpush_question_user"; + + public static final String CARMODELLIST_CACHEKEY = "ai_carmodellist"; public static final String INTELLIGENT_CUSTOMER_4IN1 = "INTELLIGENT_CUSTOMER_4IN1"; diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/PublishedTimeFormatter.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/PublishedTimeFormatter.java new file mode 100644 index 0000000..397bcab --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/PublishedTimeFormatter.java @@ -0,0 +1,59 @@ +package com.volvo.ai.analytic.center.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +@Slf4j +@Component +@SuppressWarnings("squid:S4784") +public class PublishedTimeFormatter { + private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public static String parsePublishedTime(String publishedTime) { + if (publishedTime == null || publishedTime.isEmpty() || publishedTime.equals("\"\"")) { + return null; // 空值直接返回null或按需处理 + } + + LocalDate now = LocalDate.now(); + + try { + // 处理 "X天前" 格式 + if (publishedTime.endsWith("天前")) { + int daysAgo = Integer.parseInt(publishedTime.replace("天前", "")); + return now.minus(daysAgo, ChronoUnit.DAYS).format(DateTimeFormatter.ISO_DATE); + } + + // 处理 "X月X日" 格式 + if (publishedTime.matches("\\d+月\\d+日")) { + // 提取月份和日期 + int monthStart = 0; + int monthEnd = publishedTime.indexOf("月"); + int dayStart = monthEnd + 1; + int dayEnd = publishedTime.indexOf("日"); + + int monthValue = Integer.parseInt(publishedTime.substring(monthStart, monthEnd)); + int dayValue = Integer.parseInt(publishedTime.substring(dayStart, dayEnd)); + + // 构建当年的 LocalDate 对象 + return LocalDate.of(now.getYear(), monthValue, dayValue) + .format(DateTimeFormatter.ISO_DATE); + } + + // 处理 "2025-05-06 23:00:00" 格式 + if (publishedTime.contains("-")) { + LocalDateTime parsed = LocalDateTime.parse(publishedTime, DATE_TIME_FORMAT); + return parsed.toLocalDate().format(DateTimeFormatter.ISO_DATE); + } + + } catch (Exception e) { + // 可记录异常或忽略错误格式 + log.error("无法解析时间:{} " ,publishedTime); + } + return null; // 未知格式返回null + } +} diff --git a/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/Signer.java b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/Signer.java new file mode 100644 index 0000000..000fa71 --- /dev/null +++ b/ai-analytic-center-biz/src/main/java/com/volvo/ai/analytic/center/utils/Signer.java @@ -0,0 +1,147 @@ +package com.volvo.ai.analytic.center.utils; + +import org.apache.commons.codec.digest.DigestUtils; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public final class Signer { + + private static final String AUTHORIZATION = "Authorization"; + private static final String SDK_HMAC_SHA256 = "SDK-HMAC-SHA256"; + private static final String LINE = "\n"; + private static final String ACCESS = " Access="; + private static final String SIGNED_HEADERS = ", SignedHeaders="; + private static final String SIGNATURE = ", Signature="; + private static final String SLASH = "/"; + private static final String HMAC_SHA256 = "HmacSHA256"; + private static final String POST = "POST"; + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'").withZone(ZoneId.of("UTC")); + + private static final String[] HEADERS = new String[]{"Host", "X-Sdk-Date"}; + + private static final String SIGNED_HEADERS_STR = getSignedHeadersString(HEADERS); + + private Signer() { + + } + + public static Map sign(String key, String secret, String url, String body, String host) throws NoSuchAlgorithmException, InvalidKeyException { + String singerDate = DATE_TIME_FORMATTER.format(ZonedDateTime.now()); + + Map headers = new HashMap<>(8); + headers.put(HEADERS[0], host); + headers.put(HEADERS[1], singerDate); + + String contentSha256 = toHex(hash(body)); + + String canonicalRequest = createCanonicalRequest(headers, getPath(url), POST, HEADERS, contentSha256, SIGNED_HEADERS_STR); + String stringToSign = createStringToSign(canonicalRequest, singerDate); + byte[] signingKey = secret.getBytes(StandardCharsets.UTF_8); + byte[] signature = sign(stringToSign.getBytes(StandardCharsets.UTF_8), signingKey, HMAC_SHA256); + headers.put(AUTHORIZATION, buildAuthorizationHeader(SIGNED_HEADERS_STR, signature, key)); + + return headers; + } + + private static String getPath(String url) { + int i = url.indexOf("://"); + if (i >= 0) { + url = url.substring(i + 3); + } + + i = url.indexOf(47); + return i >= 0 ? url.substring(i) : SLASH; + } + + private static String buildAuthorizationHeader(String signedHeadersStr, byte[] signature, String accessKey) { + return new StringBuilder(SDK_HMAC_SHA256).append(ACCESS).append(accessKey).append(SIGNED_HEADERS).append(signedHeadersStr).append(SIGNATURE).append(toHex(signature)).toString(); + } + + private static byte[] hash(String text) { + MessageDigest md = DigestUtils.getSha256Digest(); + md.update(text.getBytes(StandardCharsets.UTF_8)); + return md.digest(); + } + + private static byte[] sign(byte[] data, byte[] key, String algorithm) throws NoSuchAlgorithmException, InvalidKeyException { + Mac mac = Mac.getInstance(algorithm); + mac.init(new SecretKeySpec(key, algorithm)); + return mac.doFinal(data); + } + + private static String createCanonicalRequest(Map requestHeaders, String path, String method, + String[] signedHeaders, String contentSha256, String signedHeadersStr) { + return new StringBuilder(method).append(LINE).append(path) + .append((SLASH)).append(LINE).append(LINE).append(getCanonicalizedHeaderString(requestHeaders, signedHeaders)) + .append(LINE).append(signedHeadersStr).append(LINE).append(contentSha256).toString(); + } + + private static String createStringToSign(String canonicalRequest, String singerDate) { + return new StringBuilder(SDK_HMAC_SHA256).append(LINE).append(singerDate).append(LINE).append(toHex(hash(canonicalRequest))).toString(); + } + + private static String getCanonicalizedHeaderString(Map requestHeaders, String[] signedHeaders) { + StringBuilder buffer = new StringBuilder(); + String[] var5 = signedHeaders; + int var6 = signedHeaders.length; + + for (int var7 = 0; var7 < var6; ++var7) { + String header = var5[var7]; + String key = header.toLowerCase(); + String value = requestHeaders.get(header); + buffer.append(key).append(":"); + if (value != null) { + buffer.append(value.trim()); + } + buffer.append(LINE); + } + return buffer.toString(); + } + + private static String getSignedHeadersString(String[] signedHeaders) { + StringBuilder buffer = new StringBuilder(); + String[] var3 = signedHeaders; + int var4 = signedHeaders.length; + + for (int var5 = 0; var5 < var4; ++var5) { + String header = var3[var5]; + if (buffer.length() > 0) { + buffer.append(";"); + } + + buffer.append(header.toLowerCase()); + } + return buffer.toString(); + } + + private static String toHex(byte[] data) { + StringBuilder sb = new StringBuilder(data.length * 2); + byte[] var2 = data; + int var3 = data.length; + + for (int var4 = 0; var4 < var3; ++var4) { + byte b = var2[var4]; + String hex = Integer.toHexString(b); + if (hex.length() == 1) { + sb.append("0"); + } else if (hex.length() == 8) { + hex = hex.substring(6); + } + + sb.append(hex); + } + return sb.toString().toLowerCase(Locale.getDefault()); + } +} diff --git a/ai-analytic-center-biz/src/main/resources/mapper/TmCorpusQuestionReportMapper.xml b/ai-analytic-center-biz/src/main/resources/mapper/TmCorpusQuestionReportMapper.xml new file mode 100644 index 0000000..5955f0f --- /dev/null +++ b/ai-analytic-center-biz/src/main/resources/mapper/TmCorpusQuestionReportMapper.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/resources/mapper/TmCorpusQuestionSummaryReportMapper.xml b/ai-analytic-center-biz/src/main/resources/mapper/TmCorpusQuestionSummaryReportMapper.xml new file mode 100644 index 0000000..94ab11a --- /dev/null +++ b/ai-analytic-center-biz/src/main/resources/mapper/TmCorpusQuestionSummaryReportMapper.xml @@ -0,0 +1,11 @@ + + + + + + update tm_corpus_question_summary_report + set preliminary_summary_results = #{preliminarySummaryResults} + where summary_report_id = #{summaryReportId} + + + \ No newline at end of file diff --git a/ai-analytic-center-biz/src/main/resources/mapper/TmOdsImRealtimeMessageCurMapper.xml b/ai-analytic-center-biz/src/main/resources/mapper/TmOdsImRealtimeMessageCurMapper.xml new file mode 100644 index 0000000..2f8de71 --- /dev/null +++ b/ai-analytic-center-biz/src/main/resources/mapper/TmOdsImRealtimeMessageCurMapper.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ai-analytic-center-biz/target/classes/mapper/TmCorpusQuestionReportMapper.xml b/ai-analytic-center-biz/target/classes/mapper/TmCorpusQuestionReportMapper.xml new file mode 100644 index 0000000..5955f0f --- /dev/null +++ b/ai-analytic-center-biz/target/classes/mapper/TmCorpusQuestionReportMapper.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/ai-analytic-center-biz/target/classes/mapper/TmCorpusQuestionSummaryReportMapper.xml b/ai-analytic-center-biz/target/classes/mapper/TmCorpusQuestionSummaryReportMapper.xml new file mode 100644 index 0000000..94ab11a --- /dev/null +++ b/ai-analytic-center-biz/target/classes/mapper/TmCorpusQuestionSummaryReportMapper.xml @@ -0,0 +1,11 @@ + + + + + + update tm_corpus_question_summary_report + set preliminary_summary_results = #{preliminarySummaryResults} + where summary_report_id = #{summaryReportId} + + + \ No newline at end of file diff --git a/ai-analytic-center-biz/target/classes/mapper/TmOdsImRealtimeMessageCurMapper.xml b/ai-analytic-center-biz/target/classes/mapper/TmOdsImRealtimeMessageCurMapper.xml new file mode 100644 index 0000000..2f8de71 --- /dev/null +++ b/ai-analytic-center-biz/target/classes/mapper/TmOdsImRealtimeMessageCurMapper.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2947598..b5802bd 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,33 @@ org.springframework.boot spring-boot-starter-web + + + + org.springdoc + springdoc-openapi-ui + 1.8.0 + + + + com.huaweicloud.sdk + huaweicloud-sdk-eg + 3.1.36 + + + + com.huaweicloud.sdk + huaweicloud-sdk-core + 3.1.36 + + + + + io.cloudevents + cloudevents-json-jackson + 2.2.0 + +