feat(oa): 添加 AI 对话管理功能

- 新增 AI配置、对话历史、对话消息等相关的实体类、Mapper、Service 和 Controller
- 实现 AI 对话管理的基础功能,包括创建对话、发送消息、结束对话等- 集成 DeepSeek AI 服务,实现与 AI 模型的交互
- 添加 token消耗和费用计算相关逻辑
This commit is contained in:
2025-08-04 14:24:18 +08:00
parent 2fa67cb3c1
commit c93959e351
21 changed files with 1808 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
package com.ruoyi.oa.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import org.springframework.format.annotation.DateTimeFormat;
/**
* AI配置对象 sys_oa_ai_config
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_oa_ai_config")
public class SysOaAiConfig extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 配置ID
*/
@TableId(value = "config_id")
private Long configId;
/**
* 配置名称
*/
private String configName;
/**
* API密钥
*/
private String apiKey;
/**
* 基础URL
*/
private String baseUrl;
/**
* 模型名称
*/
private String modelName;
/**
* 最大重试次数
*/
private Integer maxRetries;
/**
* 温度参数
*/
private BigDecimal temperature;
/**
* 最大token数
*/
private Integer maxTokens;
/**
* 状态(0:禁用 1:启用)
*/
private Integer status;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}

View File

@@ -0,0 +1,77 @@
package com.ruoyi.oa.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import org.springframework.format.annotation.DateTimeFormat;
/**
* AI对话历史对象 sys_oa_ai_conversation
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_oa_ai_conversation")
public class SysOaAiConversation extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 对话ID
*/
@TableId(value = "conversation_id")
private Long conversationId;
/**
* 对话标题
*/
private String conversationTitle;
/**
* 用户ID
*/
private Long userId;
/**
* AI模型名称
*/
private String modelName;
/**
* 对话状态(0:已结束 1:进行中)
*/
private Integer status;
/**
* 总消耗token数
*/
private Integer totalTokens;
/**
* 总费用
*/
private BigDecimal totalCost;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}

View File

@@ -0,0 +1,70 @@
package com.ruoyi.oa.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import org.springframework.format.annotation.DateTimeFormat;
/**
* AI对话详情对象 sys_oa_ai_message
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_oa_ai_message")
public class SysOaAiMessage extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 消息ID
*/
@TableId(value = "message_id")
private Long messageId;
/**
* 对话ID
*/
private Long conversationId;
/**
* 角色(user/assistant)
*/
private String role;
/**
* 消息内容
*/
private String content;
/**
* 消耗token数
*/
private Integer tokens;
/**
* 费用
*/
private BigDecimal cost;
/**
* 消息顺序
*/
private Integer messageOrder;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}

View File

@@ -0,0 +1,72 @@
package com.ruoyi.oa.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.util.Date;
import java.math.BigDecimal;
/**
* AI配置业务对象 sys_oa_ai_config
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SysOaAiConfigBo extends BaseEntity {
/**
* 配置ID
*/
@NotNull(message = "配置ID不能为空", groups = { EditGroup.class })
private Long configId;
/**
* 配置名称
*/
@NotBlank(message = "配置名称不能为空", groups = { AddGroup.class, EditGroup.class })
private String configName;
/**
* API密钥
*/
@NotBlank(message = "API密钥不能为空", groups = { AddGroup.class, EditGroup.class })
private String apiKey;
/**
* 基础URL
*/
@NotBlank(message = "基础URL不能为空", groups = { AddGroup.class, EditGroup.class })
private String baseUrl;
/**
* 模型名称
*/
private String modelName;
/**
* 最大重试次数
*/
private Integer maxRetries;
/**
* 温度参数
*/
private BigDecimal temperature;
/**
* 最大token数
*/
private Integer maxTokens;
/**
* 状态(0:禁用 1:启用)
*/
private Integer status;
}

View File

@@ -0,0 +1,61 @@
package com.ruoyi.oa.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.util.Date;
import java.math.BigDecimal;
/**
* AI对话历史业务对象 sys_oa_ai_conversation
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SysOaAiConversationBo extends BaseEntity {
/**
* 对话ID
*/
@NotNull(message = "对话ID不能为空", groups = { EditGroup.class })
private Long conversationId;
/**
* 对话标题
*/
@NotBlank(message = "对话标题不能为空", groups = { AddGroup.class, EditGroup.class })
private String conversationTitle;
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long userId;
/**
* AI模型名称
*/
private String modelName;
/**
* 对话状态(0:已结束 1:进行中)
*/
private Integer status;
/**
* 总消耗token数
*/
private Integer totalTokens;
/**
* 总费用
*/
private BigDecimal totalCost;
}

View File

@@ -0,0 +1,62 @@
package com.ruoyi.oa.domain.bo;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.*;
import java.util.Date;
import java.math.BigDecimal;
/**
* AI对话详情业务对象 sys_oa_ai_message
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class SysOaAiMessageBo extends BaseEntity {
/**
* 消息ID
*/
@NotNull(message = "消息ID不能为空", groups = { EditGroup.class })
private Long messageId;
/**
* 对话ID
*/
@NotNull(message = "对话ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long conversationId;
/**
* 角色(user/assistant)
*/
@NotBlank(message = "角色不能为空", groups = { AddGroup.class, EditGroup.class })
private String role;
/**
* 消息内容
*/
@NotBlank(message = "消息内容不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
/**
* 消耗token数
*/
private Integer tokens;
/**
* 费用
*/
private BigDecimal cost;
/**
* 消息顺序
*/
private Integer messageOrder;
}

View File

@@ -0,0 +1,110 @@
package com.ruoyi.oa.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
/**
* AI配置视图对象 sys_oa_ai_config
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@ExcelIgnoreUnannotated
public class SysOaAiConfigVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 配置ID
*/
@ExcelProperty(value = "配置ID")
private Long configId;
/**
* 配置名称
*/
@ExcelProperty(value = "配置名称")
private String configName;
/**
* API密钥
*/
@ExcelProperty(value = "API密钥")
private String apiKey;
/**
* 基础URL
*/
@ExcelProperty(value = "基础URL")
private String baseUrl;
/**
* 模型名称
*/
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 最大重试次数
*/
@ExcelProperty(value = "最大重试次数")
private Integer maxRetries;
/**
* 温度参数
*/
@ExcelProperty(value = "温度参数")
private BigDecimal temperature;
/**
* 最大token数
*/
@ExcelProperty(value = "最大token数")
private Integer maxTokens;
/**
* 状态(0:禁用 1:启用)
*/
@ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "ai_config_status")
private Integer status;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
/**
* 创建者
*/
@ExcelProperty(value = "创建者")
private String createBy;
/**
* 更新者
*/
@ExcelProperty(value = "更新者")
private String updateBy;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -0,0 +1,98 @@
package com.ruoyi.oa.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
/**
* AI对话历史视图对象 sys_oa_ai_conversation
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@ExcelIgnoreUnannotated
public class SysOaAiConversationVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 对话ID
*/
@ExcelProperty(value = "对话ID")
private Long conversationId;
/**
* 对话标题
*/
@ExcelProperty(value = "对话标题")
private String conversationTitle;
/**
* 用户ID
*/
@ExcelProperty(value = "用户ID")
private Long userId;
/**
* AI模型名称
*/
@ExcelProperty(value = "AI模型名称")
private String modelName;
/**
* 对话状态(0:已结束 1:进行中)
*/
@ExcelProperty(value = "对话状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "ai_conversation_status")
private Integer status;
/**
* 总消耗token数
*/
@ExcelProperty(value = "总消耗token数")
private Integer totalTokens;
/**
* 总费用
*/
@ExcelProperty(value = "总费用")
private BigDecimal totalCost;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
/**
* 创建者
*/
@ExcelProperty(value = "创建者")
private String createBy;
/**
* 更新者
*/
@ExcelProperty(value = "更新者")
private String updateBy;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -0,0 +1,80 @@
package com.ruoyi.oa.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.common.annotation.ExcelDictFormat;
import com.ruoyi.common.convert.ExcelDictConvert;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.math.BigDecimal;
/**
* AI对话详情视图对象 sys_oa_ai_message
*
* @author ruoyi
* @date 2024-12-19
*/
@Data
@ExcelIgnoreUnannotated
public class SysOaAiMessageVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 消息ID
*/
@ExcelProperty(value = "消息ID")
private Long messageId;
/**
* 对话ID
*/
@ExcelProperty(value = "对话ID")
private Long conversationId;
/**
* 角色(user/assistant)
*/
@ExcelProperty(value = "角色", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "ai_message_role")
private String role;
/**
* 消息内容
*/
@ExcelProperty(value = "消息内容")
private String content;
/**
* 消耗token数
*/
@ExcelProperty(value = "消耗token数")
private Integer tokens;
/**
* 费用
*/
@ExcelProperty(value = "费用")
private BigDecimal cost;
/**
* 消息顺序
*/
@ExcelProperty(value = "消息顺序")
private Integer messageOrder;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 创建者
*/
@ExcelProperty(value = "创建者")
private String createBy;
}