feat(pet): 添加宠物医疗上下文功能模块

- 新增宠物医疗上下文实体类BizPetMedicalContext
- 创建宠物医疗上下文业务对象BizPetMedicalContextBo
- 实现宠物医疗上下文控制器BizPetMedicalContextController
- 添加宠物医疗上下文数据访问层BizPetMedicalContextMapper
- 实现宠物医疗上下文服务层BizPetMedicalContextServiceImpl
- 创建宠物医疗上下文视图对象BizPetMedicalContextVo
- 生成前端API接口文件api.d.ts.vm和api.ts.vm
- 创建Vue组件模板index.vue.vm和操作抽屉operate-drawer.vue.vm
- 更新代码生成配置generator.yml支持宠物模块
- 添加树形表格支持index-tree.vue.vm模板
This commit is contained in:
dev
2026-02-25 18:05:42 +08:00
parent b236048a24
commit ae14e54b8c
68 changed files with 1558 additions and 31 deletions
@@ -0,0 +1,38 @@
package org.dromara.chat.client;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class ChatSdk {
private final HttpClient client = HttpClient.newHttpClient();
private final String baseUrl;
private String token;
public ChatSdk(String baseUrl) {
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
}
public ChatSdk withToken(String token) {
this.token = token;
return this;
}
public String chat(String jsonBody) {
HttpRequest.Builder b = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/api/ai/chat"))
.header("Content-Type", "application/json");
if (token != null && !token.isEmpty()) {
b.header("Authorization", token);
}
HttpRequest req = b.POST(HttpRequest.BodyPublishers.ofString(jsonBody)).build();
try {
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
return resp.body();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,37 @@
package org.dromara.chat.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class StompWebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws/chat").setAllowedOriginPatterns("*");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
ThreadPoolTaskScheduler scheduler = taskScheduler();
registry.enableSimpleBroker("/topic")
.setHeartbeatValue(new long[]{30000, 30000})
.setTaskScheduler(scheduler);
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.setThreadNamePrefix("stomp-heartbeat-");
scheduler.initialize();
return scheduler;
}
}
@@ -0,0 +1,97 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.chat.service.AiKnowledgeChatService;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 知识库聊天控制器
*
* @author pengles
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/chat/knowledge")
public class AIKnowledgeChatController extends BaseController {
private final AiKnowledgeChatService knowledgeChatService;
/**
* 基于知识库进行智能问答
*/
@SaCheckPermission("chat:knowledge:chat")
@Log(title = "知识库问答", businessType = BusinessType.OTHER)
@PostMapping("/chat")
public R<ChatMessageVo> chatWithKnowledge(
@RequestParam Long sessionId,
@RequestParam Long userId,
@RequestParam String question,
@RequestParam Long knowledgeId,
@RequestParam String modelName) {
ChatMessageVo response = knowledgeChatService.chatWithKnowledge(sessionId, userId, question, knowledgeId, modelName);
return R.ok(response);
}
/**
* 创建知识库聊天会话
*/
@SaCheckPermission("chat:knowledge:session")
@Log(title = "创建知识库会话", businessType = BusinessType.INSERT)
@PostMapping("/session")
public R<ChatSessionVo> createKnowledgeSession(
@RequestParam Long userId,
@RequestParam Long knowledgeId,
@RequestParam(required = false) String sessionTitle) {
ChatSessionVo session = knowledgeChatService.createKnowledgeSession(userId, knowledgeId, sessionTitle);
return R.ok(session);
}
/**
* 搜索知识库内容
*/
@SaCheckPermission("chat:knowledge:search")
@GetMapping("/search")
public R<List<String>> searchKnowledgeContent(
@RequestParam Long knowledgeId,
@RequestParam String query,
@RequestParam(defaultValue = "5") Integer limit) {
List<String> results = knowledgeChatService.searchKnowledgeContent(knowledgeId, query, limit);
return R.ok(results);
}
/**
* 构建知识库上下文
*/
@SaCheckPermission("chat:knowledge:context")
@GetMapping("/context")
public R<String> buildKnowledgeContext(
@RequestParam Long knowledgeId,
@RequestParam String question) {
String context = knowledgeChatService.buildKnowledgeContext(knowledgeId, question);
return R.ok(context);
}
/**
* 获取知识库聊天历史
*/
@SaCheckPermission("chat:knowledge:history")
@GetMapping("/history/{sessionId}")
public R<List<ChatMessageVo>> getKnowledgeChatHistory(
@PathVariable Long sessionId,
@RequestParam(defaultValue = "20") Integer limit) {
List<ChatMessageVo> history = knowledgeChatService.getKnowledgeChatHistory(sessionId, limit);
return R.ok(history);
}
}
@@ -0,0 +1,105 @@
package org.dromara.pet.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.pet.domain.vo.BizPetMedicalContextVo;
import org.dromara.pet.domain.bo.BizPetMedicalContextBo;
import org.dromara.pet.service.IBizPetMedicalContextService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
* 宠物医疗上下文
*
* @author shuo
* @date 2026-02-25
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/pet/petMedicalContext")
public class BizPetMedicalContextController extends BaseController {
private final IBizPetMedicalContextService bizPetMedicalContextService;
/**
* 查询宠物医疗上下文列表
*/
@SaCheckPermission("pet:petMedicalContext:list")
@GetMapping("/list")
public TableDataInfo<BizPetMedicalContextVo> list(BizPetMedicalContextBo bo, PageQuery pageQuery) {
return bizPetMedicalContextService.queryPageList(bo, pageQuery);
}
/**
* 导出宠物医疗上下文列表
*/
@SaCheckPermission("pet:petMedicalContext:export")
@Log(title = "宠物医疗上下文", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(BizPetMedicalContextBo bo, HttpServletResponse response) {
List<BizPetMedicalContextVo> list = bizPetMedicalContextService.queryList(bo);
ExcelUtil.exportExcel(list, "宠物医疗上下文", BizPetMedicalContextVo.class, response);
}
/**
* 获取宠物医疗上下文详细信息
*
* @param id 主键
*/
@SaCheckPermission("pet:petMedicalContext:query")
@GetMapping("/{id}")
public R<BizPetMedicalContextVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(bizPetMedicalContextService.queryById(id));
}
/**
* 新增宠物医疗上下文
*/
@SaCheckPermission("pet:petMedicalContext:add")
@Log(title = "宠物医疗上下文", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody BizPetMedicalContextBo bo) {
return toAjax(bizPetMedicalContextService.insertByBo(bo));
}
/**
* 修改宠物医疗上下文
*/
@SaCheckPermission("pet:petMedicalContext:edit")
@Log(title = "宠物医疗上下文", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody BizPetMedicalContextBo bo) {
return toAjax(bizPetMedicalContextService.updateByBo(bo));
}
/**
* 删除宠物医疗上下文
*
* @param ids 主键串
*/
@SaCheckPermission("pet:petMedicalContext:remove")
@Log(title = "宠物医疗上下文", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(bizPetMedicalContextService.deleteWithValidByIds(List.of(ids), true));
}
}
@@ -0,0 +1,111 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.bo.ChatConfigBo;
import org.dromara.chat.domain.vo.ChatConfigVo;
import org.dromara.chat.service.ChatConfigService;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 聊天配置控制器
*
* @author pengles
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/chat/config")
public class ChatConfigController extends BaseController {
private final ChatConfigService chatConfigService;
/**
* 查询聊天配置列表
*/
@SaCheckPermission("chat:config:list")
@GetMapping("/list")
public TableDataInfo<ChatConfigVo> list(ChatConfigBo bo, PageQuery pageQuery) {
return chatConfigService.queryPageList(bo, pageQuery);
}
/**
* 导出聊天配置列表
*/
@SaCheckPermission("chat:config:export")
@Log(title = "聊天配置", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ChatConfigBo bo, HttpServletResponse response) {
List<ChatConfigVo> list = chatConfigService.queryList(bo);
ExcelUtil.exportExcel(list, "聊天配置", ChatConfigVo.class, response);
}
/**
* 获取聊天配置详细信息
*/
@SaCheckPermission("chat:config:query")
@GetMapping("/{id}")
public R<ChatConfigVo> getInfo(@PathVariable Long id) {
return R.ok(chatConfigService.queryById(id));
}
/**
* 新增聊天配置
*/
@SaCheckPermission("chat:config:add")
@Log(title = "聊天配置", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody ChatConfigBo bo) {
return toAjax(chatConfigService.insertByBo(bo));
}
/**
* 修改聊天配置
*/
@SaCheckPermission("chat:config:edit")
@Log(title = "聊天配置", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody ChatConfigBo bo) {
return toAjax(chatConfigService.updateByBo(bo));
}
/**
* 删除聊天配置
*/
@SaCheckPermission("chat:config:remove")
@Log(title = "聊天配置", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@PathVariable Long[] ids) {
return toAjax(chatConfigService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据配置分类查询配置列表
*/
@SaCheckPermission("chat:config:query")
@GetMapping("/category/{category}")
public R<List<ChatConfigVo>> getByCategory(@PathVariable String category) {
return R.ok(chatConfigService.queryListByCategory(category));
}
/**
* 根据配置名称查询配置
*/
@SaCheckPermission("chat:config:query")
@GetMapping("/name/{configName}")
public R<ChatConfigVo> getByConfigName(@PathVariable String configName) {
return R.ok(chatConfigService.queryByConfigName(configName));
}
}
@@ -0,0 +1,120 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.bo.ChatMessageBo;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.chat.service.ChatMessageService;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 聊天消息控制器
*
* @author pengles
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/chat/message")
public class ChatMessageController extends BaseController {
private final ChatMessageService chatMessageService;
/**
* 查询聊天消息列表
*/
@SaCheckPermission("chat:message:list")
@GetMapping("/list")
public TableDataInfo<ChatMessageVo> list(ChatMessageBo bo, PageQuery pageQuery) {
return chatMessageService.queryPageList(bo, pageQuery);
}
/**
* 导出聊天消息列表
*/
@SaCheckPermission("chat:message:export")
@Log(title = "聊天消息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ChatMessageBo bo, HttpServletResponse response) {
List<ChatMessageVo> list = chatMessageService.queryList(bo);
ExcelUtil.exportExcel(list, "聊天消息", ChatMessageVo.class, response);
}
/**
* 获取聊天消息详细信息
*/
@SaCheckPermission("chat:message:query")
@GetMapping("/{id}")
public R<ChatMessageVo> getInfo(@PathVariable Long id) {
return R.ok(chatMessageService.queryById(id));
}
/**
* 新增聊天消息
*/
@SaCheckPermission("chat:message:add")
@Log(title = "聊天消息", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody ChatMessageBo bo) {
return toAjax(chatMessageService.insertByBo(bo));
}
/**
* 修改聊天消息
*/
@SaCheckPermission("chat:message:edit")
@Log(title = "聊天消息", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody ChatMessageBo bo) {
return toAjax(chatMessageService.updateByBo(bo));
}
/**
* 删除聊天消息
*/
@SaCheckPermission("chat:message:remove")
@Log(title = "聊天消息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@PathVariable Long[] ids) {
return toAjax(chatMessageService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据会话ID查询消息列表
*/
@SaCheckPermission("chat:message:query")
@GetMapping("/session/{sessionId}")
public R<List<ChatMessageVo>> getBySessionId(@PathVariable Long sessionId) {
return R.ok(chatMessageService.queryListBySessionId(sessionId));
}
/**
* 根据用户ID查询消息列表
*/
@SaCheckPermission("chat:message:query")
@GetMapping("/user/{userId}")
public R<List<ChatMessageVo>> getByUserId(@PathVariable Long userId) {
return R.ok(chatMessageService.queryListByUserId(userId));
}
/**
* 根据会话ID和用户ID查询消息列表
*/
@SaCheckPermission("chat:message:query")
@GetMapping("/session/{sessionId}/user/{userId}")
public R<List<ChatMessageVo>> getBySessionIdAndUserId(@PathVariable Long sessionId, @PathVariable Long userId) {
return R.ok(chatMessageService.queryListBySessionIdAndUserId(sessionId, userId));
}
}
@@ -0,0 +1,129 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.bo.ChatModelBo;
import org.dromara.chat.domain.vo.ChatModelVo;
import org.dromara.chat.service.ChatModelService;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 聊天模型控制器
*
* @author pengles
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/chat/model")
public class ChatModelController extends BaseController {
private final ChatModelService chatModelService;
/**
* 查询聊天模型列表
*/
@SaCheckPermission("chat:model:list")
@GetMapping("/list")
public TableDataInfo<ChatModelVo> list(ChatModelBo bo, PageQuery pageQuery) {
return chatModelService.queryPageList(bo, pageQuery);
}
/**
* 导出聊天模型列表
*/
@SaCheckPermission("chat:model:export")
@Log(title = "聊天模型", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ChatModelBo bo, HttpServletResponse response) {
List<ChatModelVo> list = chatModelService.queryList(bo);
ExcelUtil.exportExcel(list, "聊天模型", ChatModelVo.class, response);
}
/**
* 获取聊天模型详细信息
*/
@SaCheckPermission("chat:model:query")
@GetMapping("/{id}")
public R<ChatModelVo> getInfo(@PathVariable Long id) {
return R.ok(chatModelService.queryById(id));
}
/**
* 新增聊天模型
*/
@SaCheckPermission("chat:model:add")
@Log(title = "聊天模型", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody ChatModelBo bo) {
return toAjax(chatModelService.insertByBo(bo));
}
/**
* 修改聊天模型
*/
@SaCheckPermission("chat:model:edit")
@Log(title = "聊天模型", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody ChatModelBo bo) {
return toAjax(chatModelService.updateByBo(bo));
}
/**
* 删除聊天模型
*/
@SaCheckPermission("chat:model:remove")
@Log(title = "聊天模型", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@PathVariable Long[] ids) {
return toAjax(chatModelService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据模型分类查询模型列表
*/
@SaCheckPermission("chat:model:query")
@GetMapping("/category/{category}")
public R<List<ChatModelVo>> getByCategory(@PathVariable String category) {
return R.ok(chatModelService.queryListByCategory(category));
}
/**
* 根据模型名称查询模型
*/
@SaCheckPermission("chat:model:query")
@GetMapping("/name/{modelName}")
public R<ChatModelVo> getByModelName(@PathVariable String modelName) {
return R.ok(chatModelService.queryByModelName(modelName));
}
/**
* 查询显示的模型列表
*/
@SaCheckPermission("chat:model:query")
@GetMapping("/show")
public R<List<ChatModelVo>> getShowModelList() {
return R.ok(chatModelService.queryShowModelList());
}
/**
* 根据模型类型查询模型列表
*/
@SaCheckPermission("chat:model:query")
@GetMapping("/type/{modelType}")
public R<List<ChatModelVo>> getByModelType(@PathVariable String modelType) {
return R.ok(chatModelService.queryListByModelType(modelType));
}
}
@@ -0,0 +1,267 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.ai.domain.dto.AiChatRequest;
import org.dromara.ai.domain.dto.AiChatResponse;
import org.dromara.ai.service.AiChatService;
import org.dromara.chat.domain.bo.ChatMessageBo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.chat.service.ChatMessageService;
import org.dromara.chat.service.ChatSessionService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.http.MediaType;
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.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 开放聊天接口
*
* @author pengles
*/
@Slf4j
@SaCheckLogin
@RestController
@RequiredArgsConstructor
@RequestMapping("/chat")
public class ChatOpenApiController {
private final AiChatService aiChatService;
private final ChatSessionService chatSessionService;
private final ChatMessageService chatMessageService;
@PostMapping(value = "/send", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter send(@RequestBody Map<String, Object> body) {
SseEmitter emitter = new SseEmitter(TimeUnit.MINUTES.toMillis(10));
Long userId;
try {
userId = LoginHelper.getUserId();
} catch (Exception e) {
emitter.completeWithError(new RuntimeException("未登录"));
return emitter;
}
try {
AiChatRequest req = toRequest(body);
req.setUserId(String.valueOf(userId));
// 获取或创建会话
String firstMsgContent = "";
if (req.getMessages() != null && !req.getMessages().isEmpty()) {
firstMsgContent = req.getMessages().get(req.getMessages().size() - 1).getContent();
}
Long sessionId = getOrCreateSession(userId, req.getSessionId(), firstMsgContent);
req.setSessionId(String.valueOf(sessionId));
// 如果是新会话,尝试通知前端sessionId
if (StrUtil.isBlank(req.getSessionId())) {
try {
emitter.send(SseEmitter.event().name("session").data("{\"sessionId\":\"" + sessionId + "\"}"));
} catch (IOException e) {
log.error("发送会话ID失败", e);
}
}
// 保存用户消息
saveUserMessage(sessionId, userId, firstMsgContent, req.getModel());
final StringBuilder fullText = new StringBuilder();
final Long finalSessionId = sessionId;
final String modelName = req.getModel();
aiChatService.chatStream(req, new AiChatService.StreamCallback() {
@Override
public void onChunk(String chunk) {
String c = normalizeChunk(chunk);
if ("[DONE]".equals(c)) {
// ignore
} else {
fullText.append(c);
try {
emitter.send(toOpenAiDelta(c, false));
} catch (IOException e) {
log.debug("发送SSE失败", e);
}
}
}
@Override
public void onComplete(AiChatResponse response) {
try {
emitter.send(toOpenAiDelta("", true));
emitter.send("[DONE]");
emitter.complete();
// 保存AI响应消息
saveAiMessage(finalSessionId, userId, fullText.toString(), modelName);
} catch (IOException e) {
log.error("发送SSE完成标记失败", e);
emitter.completeWithError(e);
}
}
@Override
public void onError(Throwable error) {
log.error("AI聊天出错", error);
try {
emitter.send("{\"error\":\"" + error.getMessage().replace("\"", "\\\"") + "\"}");
} catch (IOException ignored) {
}
emitter.completeWithError(error);
}
});
} catch (Exception e) {
log.error("处理聊天请求失败", e);
try {
emitter.send("{\"error\":\"" + e.getMessage().replace("\"", "\\\"") + "\"}");
} catch (IOException ignored) {
}
emitter.completeWithError(e);
}
return emitter;
}
private Long getOrCreateSession(Long userId, String sessionIdStr, String firstMsgContent) {
if (StrUtil.isNotBlank(sessionIdStr)) {
Long sessionId = Long.parseLong(sessionIdStr);
ChatSessionVo session = chatSessionService.queryById(sessionId);
if (session == null) {
throw new RuntimeException("会话不存在");
}
if (!session.getUserId().equals(userId)) {
throw new RuntimeException("无权访问该会话");
}
return sessionId;
} else {
String title = "新对话";
if (StrUtil.isNotBlank(firstMsgContent)) {
title = StringUtils.substring(firstMsgContent, 0, 20);
}
ChatSessionVo session = chatSessionService.createNewSession(userId, title);
return session.getId();
}
}
private void saveUserMessage(Long sessionId, Long userId, String content, String modelName) {
if (StrUtil.isNotBlank(content)) {
ChatMessageBo userMsg = new ChatMessageBo();
userMsg.setSessionId(sessionId);
userMsg.setUserId(userId);
userMsg.setRole("user");
userMsg.setContent(content);
userMsg.setModelName(modelName);
userMsg.setCreateTime(new Date());
chatMessageService.insertByBo(userMsg);
}
}
private void saveAiMessage(Long sessionId, Long userId, String content, String modelName) {
if (StrUtil.isNotBlank(content)) {
ChatMessageBo aiMsg = new ChatMessageBo();
aiMsg.setSessionId(sessionId);
aiMsg.setUserId(userId);
aiMsg.setRole("assistant");
aiMsg.setContent(content);
aiMsg.setModelName(modelName);
aiMsg.setCreateTime(new Date());
aiMsg.setTotalTokens(0L);
aiMsg.setDeductCost(BigDecimal.ZERO);
chatMessageService.insertByBo(aiMsg);
}
}
private AiChatRequest toRequest(Map<String, Object> body) {
AiChatRequest req = new AiChatRequest();
req.setModel(asString(body.get("model")));
req.setTemperature(asDouble(body.get("temperature")));
req.setTopP(asDouble(body.get("top_p")));
req.setPresencePenalty(asDouble(body.get("presence_penalty")));
req.setFrequencyPenalty(asDouble(body.get("frequency_penalty")));
req.setMaxTokens(asInt(body.get("max_tokens")));
req.setStream(Boolean.TRUE);
req.setSessionId(asString(body.get("sessionId")));
Object msgs = body.get("messages");
if (msgs instanceof List<?> list) {
java.util.ArrayList<AiChatRequest.AiMessage> arr = new java.util.ArrayList<>();
for (Object o : list) {
if (o instanceof Map<?, ?> m) {
AiChatRequest.AiMessage am = new AiChatRequest.AiMessage();
am.setRole(asString(m.get("role")));
am.setContent(asString(m.get("content")));
arr.add(am);
}
}
req.setMessages(arr);
}
return req;
}
private String asString(Object o) {
return o == null ? null : String.valueOf(o);
}
private Double asDouble(Object o) {
try {
return o == null ? null : Double.valueOf(String.valueOf(o));
} catch (Exception e) {
return null;
}
}
private Integer asInt(Object o) {
try {
return o == null ? null : Integer.valueOf(String.valueOf(o));
} catch (Exception e) {
return null;
}
}
private String toOpenAiDelta(String content, boolean finish) {
String c = content == null ? "" : content;
String esc = c.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t");
String finishReason = finish ? "\"stop\"" : "null";
return "{\"choices\":[{\"delta\":{\"content\":\"" + esc + "\"},\"finish_reason\":" + finishReason + "}]}";
}
private String normalizeChunk(String chunk) {
if (chunk == null) return "";
String s = chunk.trim();
if ("[DONE]".equals(s)) return "[DONE]";
try {
Map<?, ?> m = JsonUtils.parseObject(s, Map.class);
if (m != null) {
Object choices = m.get("choices");
if (choices instanceof List<?> list && !list.isEmpty()) {
Object first = list.get(0);
if (first instanceof Map<?, ?> fm) {
Object delta = fm.get("delta");
if (delta instanceof Map<?, ?> dm) {
Object c = dm.get("content");
if (c == null) c = dm.get("reasoning_content");
if (c != null) return String.valueOf(c);
}
Object text = fm.get("text");
if (text != null) return String.valueOf(text);
}
}
}
} catch (Exception ignore) {
}
return s;
}
}
@@ -0,0 +1,122 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.bo.ChatSessionBo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.chat.service.ChatSessionService;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 聊天会话控制器
*
* @author pengles
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/chat/session")
public class ChatSessionController extends BaseController {
private final ChatSessionService chatSessionService;
/**
* 查询聊天会话列表
*/
@SaCheckPermission("chat:session:list")
@GetMapping("/list")
public TableDataInfo<ChatSessionVo> list(ChatSessionBo bo, PageQuery pageQuery) {
return chatSessionService.queryPageList(bo, pageQuery);
}
/**
* 导出聊天会话列表
*/
@SaCheckPermission("chat:session:export")
@Log(title = "聊天会话", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ChatSessionBo bo, HttpServletResponse response) {
List<ChatSessionVo> list = chatSessionService.queryList(bo);
ExcelUtil.exportExcel(list, "聊天会话", ChatSessionVo.class, response);
}
/**
* 获取聊天会话详细信息
*/
@SaCheckPermission("chat:session:query")
@GetMapping("/{id}")
public R<ChatSessionVo> getInfo(@PathVariable Long id) {
return R.ok(chatSessionService.queryById(id));
}
/**
* 新增聊天会话
*/
@SaCheckPermission("chat:session:add")
@Log(title = "聊天会话", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody ChatSessionBo bo) {
return toAjax(chatSessionService.insertByBo(bo));
}
/**
* 修改聊天会话
*/
@SaCheckPermission("chat:session:edit")
@Log(title = "聊天会话", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody ChatSessionBo bo) {
return toAjax(chatSessionService.updateByBo(bo));
}
/**
* 删除聊天会话
*/
@SaCheckPermission("chat:session:remove")
@Log(title = "聊天会话", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@PathVariable Long[] ids) {
return toAjax(chatSessionService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据用户ID查询会话列表
*/
@SaCheckPermission("chat:session:query")
@GetMapping("/user/{userId}")
public R<List<ChatSessionVo>> getByUserId(@PathVariable Long userId) {
return R.ok(chatSessionService.queryListByUserId(userId));
}
/**
* 创建新会话
*/
@SaCheckPermission("chat:session:add")
@Log(title = "聊天会话", businessType = BusinessType.INSERT)
@PostMapping("/create")
public R<ChatSessionVo> createNewSession(@RequestParam Long userId, @RequestParam(required = false) String sessionTitle) {
return R.ok(chatSessionService.createNewSession(userId, sessionTitle));
}
/**
* 更新会话标题
*/
@SaCheckPermission("chat:session:edit")
@Log(title = "聊天会话", businessType = BusinessType.UPDATE)
@PutMapping("/{sessionId}/title")
public R<Void> updateSessionTitle(@PathVariable Long sessionId, @RequestParam String sessionTitle) {
return toAjax(chatSessionService.updateSessionTitle(sessionId, sessionTitle));
}
}
@@ -0,0 +1,43 @@
package org.dromara.chat.controller;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.bo.ChatMessageBo;
import org.dromara.chat.service.ChatMessageService;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.util.Map;
@Controller
@RequiredArgsConstructor
public class ChatStompController {
private final SimpMessagingTemplate messagingTemplate;
private final ChatMessageService chatMessageService;
@MessageMapping("/chat/send")
public void send(@Payload Map<String, Object> payload) {
Long sessionId = toLong(payload.get("sessionId"));
Long userId = toLong(payload.get("userId"));
String content = String.valueOf(payload.get("content"));
ChatMessageBo bo = new ChatMessageBo();
bo.setSessionId(sessionId);
bo.setUserId(userId);
bo.setContent(content);
bo.setRole("user");
chatMessageService.insertByBo(bo);
messagingTemplate.convertAndSend("/topic/session/" + sessionId, Map.of(
"sessionId", sessionId, "userId", userId, "content", content));
}
private Long toLong(Object o) {
if (o == null) return null;
if (o instanceof Number) return ((Number) o).longValue();
try {
return Long.parseLong(String.valueOf(o));
} catch (Exception e) {
return null;
}
}
}
@@ -0,0 +1,129 @@
package org.dromara.chat.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.bo.ChatUsageTokenBo;
import org.dromara.chat.domain.vo.ChatUsageTokenVo;
import org.dromara.chat.service.ChatUsageTokenService;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 聊天Token使用记录控制器
*
* @author pengles
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/chat/token")
public class ChatUsageTokenController extends BaseController {
private final ChatUsageTokenService chatUsageTokenService;
/**
* 查询聊天Token使用记录列表
*/
@SaCheckPermission("chat:token:list")
@GetMapping("/list")
public TableDataInfo<ChatUsageTokenVo> list(ChatUsageTokenBo bo, PageQuery pageQuery) {
return chatUsageTokenService.queryPageList(bo, pageQuery);
}
/**
* 导出聊天Token使用记录列表
*/
@SaCheckPermission("chat:token:export")
@Log(title = "聊天Token使用记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ChatUsageTokenBo bo, HttpServletResponse response) {
List<ChatUsageTokenVo> list = chatUsageTokenService.queryList(bo);
ExcelUtil.exportExcel(list, "聊天Token使用记录", ChatUsageTokenVo.class, response);
}
/**
* 获取聊天Token使用记录详细信息
*/
@SaCheckPermission("chat:token:query")
@GetMapping("/{id}")
public R<ChatUsageTokenVo> getInfo(@PathVariable Long id) {
return R.ok(chatUsageTokenService.queryById(id));
}
/**
* 新增聊天Token使用记录
*/
@SaCheckPermission("chat:token:add")
@Log(title = "聊天Token使用记录", businessType = BusinessType.INSERT)
@PostMapping
public R<Void> add(@Validated @RequestBody ChatUsageTokenBo bo) {
return toAjax(chatUsageTokenService.insertByBo(bo));
}
/**
* 修改聊天Token使用记录
*/
@SaCheckPermission("chat:token:edit")
@Log(title = "聊天Token使用记录", businessType = BusinessType.UPDATE)
@PutMapping
public R<Void> edit(@Validated @RequestBody ChatUsageTokenBo bo) {
return toAjax(chatUsageTokenService.updateByBo(bo));
}
/**
* 删除聊天Token使用记录
*/
@SaCheckPermission("chat:token:remove")
@Log(title = "聊天Token使用记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@PathVariable Long[] ids) {
return toAjax(chatUsageTokenService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据用户ID查询Token使用记录列表
*/
@SaCheckPermission("chat:token:query")
@GetMapping("/user/{userId}")
public R<List<ChatUsageTokenVo>> getByUserId(@PathVariable Long userId) {
return R.ok(chatUsageTokenService.queryListByUserId(userId));
}
/**
* 根据模型名称查询Token使用记录列表
*/
@SaCheckPermission("chat:token:query")
@GetMapping("/model/{modelName}")
public R<List<ChatUsageTokenVo>> getByModelName(@PathVariable String modelName) {
return R.ok(chatUsageTokenService.queryListByModelName(modelName));
}
/**
* 获取用户总Token使用量
*/
@SaCheckPermission("chat:token:query")
@GetMapping("/total/user/{userId}")
public R<Long> getTotalTokenByUserId(@PathVariable Long userId) {
return R.ok(chatUsageTokenService.getTotalTokenByUserId(userId));
}
/**
* 获取模型总Token使用量
*/
@SaCheckPermission("chat:token:query")
@GetMapping("/total/model/{modelName}")
public R<Long> getTotalTokenByModelName(@PathVariable String modelName) {
return R.ok(chatUsageTokenService.getTotalTokenByModelName(modelName));
}
}
@@ -0,0 +1,70 @@
package org.dromara.pet.domain;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 宠物医疗上下文对象 biz_pet_medical_context
*
* @author shuo
* @date 2026-02-25
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("biz_pet_medical_context")
public class BizPetMedicalContext extends
BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 关联宠物ID
*/
private Long petId;
/**
* 过敏史
*/
private String allergies;
/**
* 疫苗接种记录
*/
private String vaccinations;
/**
* 慢性病史
*/
private String chronicConditions;
/**
* 日常用药
*/
private String regularMedications;
/**
* 业务更新时间
*/
private Date updatedAt;
/**
* 租户编号
*/
private String tenantId;
}
@@ -0,0 +1,88 @@
package org.dromara.chat.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 聊天配置对象 chat_config
*
* @author pengles
*/
@Data
@TableName("chat_config")
public class ChatConfig implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 配置分类
*/
private String category;
/**
* 配置名称
*/
private String configName;
/**
* 配置值
*/
private String configValue;
/**
* 配置字典
*/
private String configDict;
/**
* 版本号
*/
private String version;
/**
* 删除标志(0代表存在 2代表删除)
*/
@TableLogic
private String delFlag;
/**
* 更新IP
*/
private String updateIp;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,91 @@
package org.dromara.chat.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 聊天消息对象 chat_message
*
* @author pengles
*/
@Data
@TableName("chat_message")
public class ChatMessage implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 会话ID
*/
private Long sessionId;
/**
* 用户ID
*/
private Long userId;
/**
* 消息内容
*/
private String content;
/**
* 角色(user/assistant/system
*/
private String role;
/**
* 扣费成本
*/
private BigDecimal deductCost;
/**
* 总token数
*/
private Long totalTokens;
/**
* 模型名称
*/
private String modelName;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,100 @@
package org.dromara.chat.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 聊天模型对象 chat_model
*
* @author pengles
*/
@Data
@TableName("chat_model")
public class ChatModel implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 模型分类
*/
private String category;
/**
* 模型名称
*/
private String modelName;
/**
* 模型描述
*/
private String modelDescribe;
/**
* 模型价格
*/
private BigDecimal modelPrice;
/**
* 模型类型
*/
private String modelType;
/**
* 是否显示(0隐藏 1显示)
*/
private String modelShow;
/**
* 系统提示词
*/
private String systemPrompt;
/**
* API主机地址
*/
private String apiHost;
/**
* API密钥
*/
private String apiKey;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,70 @@
package org.dromara.chat.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 聊天会话对象 chat_session
*
* @author pengles
*/
@Data
@TableName("chat_session")
public class ChatSession implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 会话标题
*/
private String sessionTitle;
/**
* 会话内容
*/
private String sessionContent;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
@@ -0,0 +1,48 @@
package org.dromara.chat.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 聊天使用令牌对象 chat_usage_token
*
* @author pengles
*/
@Data
@TableName("chat_usage_token")
public class ChatUsageToken implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(value = "id")
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 令牌数量
*/
private Long token;
/**
* 模型名称
*/
private String modelName;
/**
* 总令牌数
*/
private Long totalToken;
}
@@ -0,0 +1,73 @@
package org.dromara.pet.domain.bo;
import org.dromara.pet.domain.BizPetMedicalContext;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
/**
* 宠物医疗上下文业务对象 biz_pet_medical_context
*
* @author shuo
* @date 2026-02-25
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = BizPetMedicalContext.class,reverseConvertGenerate =false)
public class BizPetMedicalContextBo extends BaseEntity {
/**
* 主键ID
*/
private Long id;
/**
* 关联宠物ID
*/
private Long petId;
/**
* 过敏史
*/
@NotBlank(message = "过敏史不能为空", groups = { AddGroup.class, EditGroup.class })
private String allergies;
/**
* 疫苗接种记录
*/
@NotBlank(message = "疫苗接种记录不能为空", groups = { AddGroup.class, EditGroup.class })
private String vaccinations;
/**
* 慢性病史
*/
@NotBlank(message = "慢性病史不能为空", groups = { AddGroup.class, EditGroup.class })
private String chronicConditions;
/**
* 日常用药
*/
@NotBlank(message = "日常用药不能为空", groups = { AddGroup.class, EditGroup.class })
private String regularMedications;
/**
* 业务更新时间
*/
@NotNull(message = "业务更新时间不能为空", groups = { AddGroup.class, EditGroup.class })
private Date updatedAt;
/**
* 租户编号
*/
@NotBlank(message = "租户编号不能为空", groups = { AddGroup.class, EditGroup.class })
private String tenantId;
}
@@ -0,0 +1,62 @@
package org.dromara.chat.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.chat.domain.ChatConfig;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* 聊天配置业务对象 chat_config
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ChatConfig.class, reverseConvertGenerate = false)
public class ChatConfigBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 配置分类
*/
@NotBlank(message = "配置分类不能为空", groups = {AddGroup.class, EditGroup.class})
private String category;
/**
* 配置名称
*/
@NotBlank(message = "配置名称不能为空", groups = {AddGroup.class, EditGroup.class})
private String configName;
/**
* 配置值
*/
@NotBlank(message = "配置值不能为空", groups = {AddGroup.class, EditGroup.class})
private String configValue;
/**
* 配置字典
*/
private String configDict;
/**
* 版本号
*/
private String version;
/**
* 更新IP
*/
private String updateIp;
}
@@ -0,0 +1,76 @@
package org.dromara.chat.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.chat.domain.ChatMessage;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
/**
* 聊天消息业务对象 chat_message
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ChatMessage.class, reverseConvertGenerate = false)
public class ChatMessageBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 会话ID
*/
@NotNull(message = "会话ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long sessionId;
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long userId;
/**
* 消息类型(text/image/file
*/
@NotBlank(message = "消息类型不能为空", groups = {AddGroup.class, EditGroup.class})
private String messageType;
/**
* 消息内容
*/
@NotBlank(message = "消息内容不能为空", groups = {AddGroup.class, EditGroup.class})
private String content;
/**
* 角色(user/assistant/system
*/
@NotBlank(message = "角色不能为空", groups = {AddGroup.class, EditGroup.class})
private String role;
/**
* 扣费成本
*/
private BigDecimal deductCost;
/**
* 总token数
*/
private Long totalTokens;
/**
* 模型名称
*/
private String modelName;
}
@@ -0,0 +1,78 @@
package org.dromara.chat.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.chat.domain.ChatModel;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
/**
* 聊天模型业务对象 chat_model
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ChatModel.class, reverseConvertGenerate = false)
public class ChatModelBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 模型分类
*/
@NotBlank(message = "模型分类不能为空", groups = {AddGroup.class, EditGroup.class})
private String category;
/**
* 模型名称
*/
@NotBlank(message = "模型名称不能为空", groups = {AddGroup.class, EditGroup.class})
private String modelName;
/**
* 模型描述
*/
private String modelDescribe;
/**
* 模型价格
*/
private BigDecimal modelPrice;
/**
* 模型类型
*/
private String modelType;
/**
* 是否显示(0隐藏 1显示)
*/
private String modelShow;
/**
* 系统提示词
*/
private String systemPrompt;
/**
* API主机地址
*/
private String apiHost;
/**
* API密钥
*/
private String apiKey;
}
@@ -0,0 +1,46 @@
package org.dromara.chat.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.chat.domain.ChatSession;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* 聊天会话业务对象 chat_session
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ChatSession.class, reverseConvertGenerate = false)
public class ChatSessionBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long userId;
/**
* 会话标题
*/
@NotBlank(message = "会话标题不能为空", groups = {AddGroup.class, EditGroup.class})
private String sessionTitle;
/**
* 会话内容
*/
private String sessionContent;
}
@@ -0,0 +1,53 @@
package org.dromara.chat.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.dromara.chat.domain.ChatUsageToken;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import java.io.Serial;
import java.io.Serializable;
/**
* 聊天使用令牌业务对象 chat_usage_token
*
* @author pengles
*/
@Data
@AutoMapper(target = ChatUsageToken.class, reverseConvertGenerate = false)
public class ChatUsageTokenBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = {EditGroup.class})
private Long id;
/**
* 用户ID
*/
@NotNull(message = "用户ID不能为空", groups = {AddGroup.class, EditGroup.class})
private Long userId;
/**
* 令牌数量
*/
@NotNull(message = "令牌数量不能为空", groups = {AddGroup.class, EditGroup.class})
private Long token;
/**
* 模型名称
*/
private String modelName;
/**
* 总令牌数
*/
private Long totalToken;
}
@@ -0,0 +1,31 @@
package org.dromara.chat.domain.request;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* 聊天请求基础类
*
* @author pengles
*/
@Data
public class ChatRequest {
/**
* 会话ID
*/
@NotBlank(message = "会话ID不能为空")
private String sessionId;
/**
* 消息内容
*/
@NotBlank(message = "消息内容不能为空")
private String message;
/**
* 模型名称
*/
private String modelName;
}
@@ -0,0 +1,40 @@
package org.dromara.chat.domain.request;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 多Provider聊天请求
*
* @author pengles
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class MultiProviderChatRequest extends ChatRequest {
/**
* Provider名称列表
*/
@NotNull(message = "Provider列表不能为空")
@NotEmpty(message = "Provider列表不能为空")
private List<String> providerNames;
/**
* 是否合并结果
*/
private Boolean mergeResults = true;
/**
* 最大并发数
*/
private Integer maxConcurrency = 3;
/**
* 超时时间(秒)
*/
private Integer timeoutSeconds = 30;
}
@@ -0,0 +1,46 @@
package org.dromara.chat.domain.request;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 回答质量评估请求
*
* @author pengles
* @date 2024-01-01
*/
@Data
public class RateRequest {
/**
* 消息ID
*/
@NotBlank(message = "消息ID不能为空")
private String messageId;
/**
* 评分(1-5分)
*/
@NotNull(message = "评分不能为空")
@Min(value = 1, message = "评分不能小于1")
@Max(value = 5, message = "评分不能大于5")
private Integer rating;
/**
* 反馈内容
*/
private String feedback;
/**
* 评估维度
*/
private String dimension;
/**
* 是否匿名
*/
private Boolean anonymous = false;
}
@@ -0,0 +1,82 @@
package org.dromara.pet.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.pet.domain.BizPetMedicalContext;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 宠物医疗上下文视图对象 biz_pet_medical_context
*
* @author shuo
* @date 2026-02-25
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = BizPetMedicalContext.class)
public class BizPetMedicalContextVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 关联宠物ID
*/
@ExcelProperty(value = "关联宠物ID")
private Long petId;
/**
* 过敏史
*/
@ExcelProperty(value = "过敏史")
private String allergies;
/**
* 疫苗接种记录
*/
@ExcelProperty(value = "疫苗接种记录")
private String vaccinations;
/**
* 慢性病史
*/
@ExcelProperty(value = "慢性病史")
private String chronicConditions;
/**
* 日常用药
*/
@ExcelProperty(value = "日常用药")
private String regularMedications;
/**
* 业务更新时间
*/
@ExcelProperty(value = "业务更新时间")
private Date updatedAt;
/**
* 租户编号
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
}
@@ -0,0 +1,89 @@
package org.dromara.chat.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.chat.domain.ChatConfig;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 聊天配置视图对象 chat_config
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ChatConfig.class)
public class ChatConfigVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 配置分类
*/
@ExcelProperty(value = "配置分类")
private String category;
/**
* 配置名称
*/
@ExcelProperty(value = "配置名称")
private String configName;
/**
* 配置值
*/
@ExcelProperty(value = "配置值")
private String configValue;
/**
* 配置字典
*/
@ExcelProperty(value = "配置字典")
private String configDict;
/**
* 版本号
*/
@ExcelProperty(value = "版本号")
private String version;
/**
* 删除标志
*/
@ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=正常,2=删除")
private String delFlag;
/**
* 更新IP
*/
@ExcelProperty(value = "更新IP")
private String updateIp;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,87 @@
package org.dromara.chat.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.chat.domain.ChatMessage;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 聊天消息视图对象 chat_message
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ChatMessage.class)
public class ChatMessageVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 会话ID
*/
@ExcelProperty(value = "会话ID")
private Long sessionId;
/**
* 用户ID
*/
@ExcelProperty(value = "用户ID")
private Long userId;
/**
* 消息内容
*/
@ExcelProperty(value = "消息内容")
private String content;
/**
* 角色(user/assistant/system
*/
@ExcelProperty(value = "角色")
private String role;
/**
* 扣费成本
*/
@ExcelProperty(value = "扣费成本")
private BigDecimal deductCost;
/**
* 总token数
*/
@ExcelProperty(value = "总token数")
private Long totalTokens;
/**
* 模型名称
*/
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,102 @@
package org.dromara.chat.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.chat.domain.ChatModel;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 聊天模型视图对象 chat_model
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ChatModel.class)
public class ChatModelVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 模型分类
*/
@ExcelProperty(value = "模型分类")
private String category;
/**
* 模型名称
*/
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 模型描述
*/
@ExcelProperty(value = "模型描述")
private String modelDescribe;
/**
* 模型价格
*/
@ExcelProperty(value = "模型价格")
private BigDecimal modelPrice;
/**
* 模型类型
*/
@ExcelProperty(value = "模型类型")
private String modelType;
/**
* 是否显示(0隐藏 1显示)
*/
@ExcelProperty(value = "是否显示", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=隐藏,1=显示")
private String modelShow;
/**
* 系统提示词
*/
@ExcelProperty(value = "系统提示词")
private String systemPrompt;
/**
* API主机地址
*/
@ExcelProperty(value = "API主机地址")
private String apiHost;
/**
* API密钥
*/
@ExcelProperty(value = "API密钥")
private String apiKey;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,62 @@
package org.dromara.chat.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.chat.domain.ChatSession;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 聊天会话视图对象 chat_session
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ChatSession.class)
public class ChatSessionVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 用户ID
*/
@ExcelProperty(value = "用户ID")
private Long userId;
/**
* 会话标题
*/
@ExcelProperty(value = "会话标题")
private String sessionTitle;
/**
* 会话内容
*/
@ExcelProperty(value = "会话内容")
private String sessionContent;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,68 @@
package org.dromara.chat.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.chat.domain.ChatUsageToken;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 聊天Token使用记录视图对象 chat_usage_token
*
* @author pengles
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ChatUsageToken.class)
public class ChatUsageTokenVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 用户ID
*/
@ExcelProperty(value = "用户ID")
private Long userId;
/**
* 使用token数
*/
@ExcelProperty(value = "使用token数")
private Long token;
/**
* 模型名称
*/
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 总token数
*/
@ExcelProperty(value = "总token数")
private Long totalToken;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
}
@@ -0,0 +1,15 @@
package org.dromara.pet.mapper;
import org.dromara.pet.domain.BizPetMedicalContext;
import org.dromara.pet.domain.vo.BizPetMedicalContextVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 宠物医疗上下文Mapper接口
*
* @author shuo
* @date 2026-02-25
*/
public interface BizPetMedicalContextMapper extends BaseMapperPlus<BizPetMedicalContext, BizPetMedicalContextVo> {
}
@@ -0,0 +1,14 @@
package org.dromara.chat.mapper;
import org.dromara.chat.domain.ChatConfig;
import org.dromara.chat.domain.vo.ChatConfigVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 聊天配置Mapper接口
*
* @author pengles
*/
public interface ChatConfigMapper extends BaseMapperPlus<ChatConfig, ChatConfigVo> {
}
@@ -0,0 +1,14 @@
package org.dromara.chat.mapper;
import org.dromara.chat.domain.ChatMessage;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 聊天消息Mapper接口
*
* @author pengles
*/
public interface ChatMessageMapper extends BaseMapperPlus<ChatMessage, ChatMessageVo> {
}
@@ -0,0 +1,14 @@
package org.dromara.chat.mapper;
import org.dromara.chat.domain.ChatModel;
import org.dromara.chat.domain.vo.ChatModelVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 聊天模型Mapper接口
*
* @author pengles
*/
public interface ChatModelMapper extends BaseMapperPlus<ChatModel, ChatModelVo> {
}
@@ -0,0 +1,14 @@
package org.dromara.chat.mapper;
import org.dromara.chat.domain.ChatSession;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 聊天会话Mapper接口
*
* @author pengles
*/
public interface ChatSessionMapper extends BaseMapperPlus<ChatSession, ChatSessionVo> {
}
@@ -0,0 +1,14 @@
package org.dromara.chat.mapper;
import org.dromara.chat.domain.ChatUsageToken;
import org.dromara.chat.domain.vo.ChatUsageTokenVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* 聊天Token使用记录Mapper接口
*
* @author pengles
*/
public interface ChatUsageTokenMapper extends BaseMapperPlus<ChatUsageToken, ChatUsageTokenVo> {
}
@@ -0,0 +1,65 @@
package org.dromara.chat.service;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import java.util.List;
/**
* 知识库聊天服务接口
*
* @author pengles
*/
public interface AiKnowledgeChatService {
/**
* 基于知识库进行智能问答
*
* @param sessionId 会话ID
* @param userId 用户ID
* @param question 用户问题
* @param knowledgeId 知识库ID
* @param modelName 使用的模型名称
* @return 聊天回复消息
*/
ChatMessageVo chatWithKnowledge(Long sessionId, Long userId, String question, Long knowledgeId, String modelName);
/**
* 创建知识库聊天会话
*
* @param userId 用户ID
* @param knowledgeId 知识库ID
* @param sessionTitle 会话标题
* @return 聊天会话
*/
ChatSessionVo createKnowledgeSession(Long userId, Long knowledgeId, String sessionTitle);
/**
* 搜索相关知识库内容
*
* @param knowledgeId 知识库ID
* @param query 查询内容
* @param limit 返回数量限制
* @return 相关知识库内容列表
*/
List<String> searchKnowledgeContent(Long knowledgeId, String query, Integer limit);
/**
* 构建知识库上下文
*
* @param knowledgeId 知识库ID
* @param question 用户问题
* @return 构建的上下文内容
*/
String buildKnowledgeContext(Long knowledgeId, String question);
/**
* 获取知识库聊天历史
*
* @param sessionId 会话ID
* @param limit 返回数量限制
* @return 聊天历史消息列表
*/
List<ChatMessageVo> getKnowledgeChatHistory(Long sessionId, Integer limit);
}
@@ -0,0 +1,60 @@
package org.dromara.chat.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.chat.domain.ChatConfig;
import org.dromara.chat.domain.bo.ChatConfigBo;
import org.dromara.chat.domain.vo.ChatConfigVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Collection;
import java.util.List;
/**
* 聊天配置Service接口
*
* @author pengles
*/
public interface ChatConfigService extends IService<ChatConfig> {
/**
* 查询聊天配置
*/
ChatConfigVo queryById(Long id);
/**
* 查询聊天配置列表
*/
TableDataInfo<ChatConfigVo> queryPageList(ChatConfigBo bo, PageQuery pageQuery);
/**
* 查询聊天配置列表
*/
List<ChatConfigVo> queryList(ChatConfigBo bo);
/**
* 新增聊天配置
*/
Boolean insertByBo(ChatConfigBo bo);
/**
* 修改聊天配置
*/
Boolean updateByBo(ChatConfigBo bo);
/**
* 校验并批量删除聊天配置
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据配置分类查询配置列表
*/
List<ChatConfigVo> queryListByCategory(String category);
/**
* 根据配置名称查询配置
*/
ChatConfigVo queryByConfigName(String configName);
}
@@ -0,0 +1,65 @@
package org.dromara.chat.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.chat.domain.ChatMessage;
import org.dromara.chat.domain.bo.ChatMessageBo;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Collection;
import java.util.List;
/**
* 聊天消息Service接口
*
* @author pengles
*/
public interface ChatMessageService extends IService<ChatMessage> {
/**
* 查询聊天消息
*/
ChatMessageVo queryById(Long id);
/**
* 查询聊天消息列表
*/
TableDataInfo<ChatMessageVo> queryPageList(ChatMessageBo bo, PageQuery pageQuery);
/**
* 查询聊天消息列表
*/
List<ChatMessageVo> queryList(ChatMessageBo bo);
/**
* 新增聊天消息
*/
Boolean insertByBo(ChatMessageBo bo);
/**
* 修改聊天消息
*/
Boolean updateByBo(ChatMessageBo bo);
/**
* 校验并批量删除聊天消息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据会话ID查询消息列表
*/
List<ChatMessageVo> queryListBySessionId(Long sessionId);
/**
* 根据用户ID查询消息列表
*/
List<ChatMessageVo> queryListByUserId(Long userId);
/**
* 根据会话ID和用户ID查询消息列表
*/
List<ChatMessageVo> queryListBySessionIdAndUserId(Long sessionId, Long userId);
}
@@ -0,0 +1,70 @@
package org.dromara.chat.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.chat.domain.ChatModel;
import org.dromara.chat.domain.bo.ChatModelBo;
import org.dromara.chat.domain.vo.ChatModelVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Collection;
import java.util.List;
/**
* 聊天模型Service接口
*
* @author pengles
*/
public interface ChatModelService extends IService<ChatModel> {
/**
* 查询聊天模型
*/
ChatModelVo queryById(Long id);
/**
* 查询聊天模型列表
*/
TableDataInfo<ChatModelVo> queryPageList(ChatModelBo bo, PageQuery pageQuery);
/**
* 查询聊天模型列表
*/
List<ChatModelVo> queryList(ChatModelBo bo);
/**
* 新增聊天模型
*/
Boolean insertByBo(ChatModelBo bo);
/**
* 修改聊天模型
*/
Boolean updateByBo(ChatModelBo bo);
/**
* 校验并批量删除聊天模型
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据模型分类查询模型列表
*/
List<ChatModelVo> queryListByCategory(String category);
/**
* 根据模型名称查询模型
*/
ChatModelVo queryByModelName(String modelName);
/**
* 查询显示的模型列表
*/
List<ChatModelVo> queryShowModelList();
/**
* 根据模型类型查询模型列表
*/
List<ChatModelVo> queryListByModelType(String modelType);
}
@@ -0,0 +1,65 @@
package org.dromara.chat.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.chat.domain.ChatSession;
import org.dromara.chat.domain.bo.ChatSessionBo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Collection;
import java.util.List;
/**
* 聊天会话Service接口
*
* @author pengles
*/
public interface ChatSessionService extends IService<ChatSession> {
/**
* 查询聊天会话
*/
ChatSessionVo queryById(Long id);
/**
* 查询聊天会话列表
*/
TableDataInfo<ChatSessionVo> queryPageList(ChatSessionBo bo, PageQuery pageQuery);
/**
* 查询聊天会话列表
*/
List<ChatSessionVo> queryList(ChatSessionBo bo);
/**
* 新增聊天会话
*/
Boolean insertByBo(ChatSessionBo bo);
/**
* 修改聊天会话
*/
Boolean updateByBo(ChatSessionBo bo);
/**
* 校验并批量删除聊天会话
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据用户ID查询会话列表
*/
List<ChatSessionVo> queryListByUserId(Long userId);
/**
* 创建新会话
*/
ChatSessionVo createNewSession(Long userId, String sessionTitle);
/**
* 更新会话标题
*/
Boolean updateSessionTitle(Long sessionId, String sessionTitle);
}
@@ -0,0 +1,70 @@
package org.dromara.chat.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.chat.domain.ChatUsageToken;
import org.dromara.chat.domain.bo.ChatUsageTokenBo;
import org.dromara.chat.domain.vo.ChatUsageTokenVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.Collection;
import java.util.List;
/**
* 聊天Token使用记录Service接口
*
* @author pengles
*/
public interface ChatUsageTokenService extends IService<ChatUsageToken> {
/**
* 查询聊天Token使用记录
*/
ChatUsageTokenVo queryById(Long id);
/**
* 查询聊天Token使用记录列表
*/
TableDataInfo<ChatUsageTokenVo> queryPageList(ChatUsageTokenBo bo, PageQuery pageQuery);
/**
* 查询聊天Token使用记录列表
*/
List<ChatUsageTokenVo> queryList(ChatUsageTokenBo bo);
/**
* 新增聊天Token使用记录
*/
Boolean insertByBo(ChatUsageTokenBo bo);
/**
* 修改聊天Token使用记录
*/
Boolean updateByBo(ChatUsageTokenBo bo);
/**
* 校验并批量删除聊天Token使用记录
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 根据用户ID查询Token使用记录列表
*/
List<ChatUsageTokenVo> queryListByUserId(Long userId);
/**
* 根据模型名称查询Token使用记录列表
*/
List<ChatUsageTokenVo> queryListByModelName(String modelName);
/**
* 统计用户总Token使用量
*/
Long getTotalTokenByUserId(Long userId);
/**
* 统计模型总Token使用量
*/
Long getTotalTokenByModelName(String modelName);
}
@@ -0,0 +1,68 @@
package org.dromara.pet.service;
import org.dromara.pet.domain.vo.BizPetMedicalContextVo;
import org.dromara.pet.domain.bo.BizPetMedicalContextBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 宠物医疗上下文Service接口
*
* @author shuo
* @date 2026-02-25
*/
public interface IBizPetMedicalContextService {
/**
* 查询宠物医疗上下文
*
* @param id 主键
* @return 宠物医疗上下文
*/
BizPetMedicalContextVo queryById(Long id);
/**
* 分页查询宠物医疗上下文列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 宠物医疗上下文分页列表
*/
TableDataInfo<BizPetMedicalContextVo> queryPageList(BizPetMedicalContextBo bo, PageQuery pageQuery);
/**
* 查询符合条件的宠物医疗上下文列表
*
* @param bo 查询条件
* @return 宠物医疗上下文列表
*/
List<BizPetMedicalContextVo> queryList(BizPetMedicalContextBo bo);
/**
* 新增宠物医疗上下文
*
* @param bo 宠物医疗上下文
* @return 是否新增成功
*/
Boolean insertByBo(BizPetMedicalContextBo bo);
/**
* 修改宠物医疗上下文
*
* @param bo 宠物医疗上下文
* @return 是否修改成功
*/
Boolean updateByBo(BizPetMedicalContextBo bo);
/**
* 校验并批量删除宠物医疗上下文信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}
@@ -0,0 +1,198 @@
package org.dromara.chat.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.ai.domain.dto.AiChatRequest;
import org.dromara.ai.domain.dto.AiChatResponse;
import org.dromara.ai.service.AiChatService;
import org.dromara.chat.domain.bo.ChatMessageBo;
import org.dromara.chat.domain.bo.ChatSessionBo;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.chat.service.AiKnowledgeChatService;
import org.dromara.chat.service.ChatMessageService;
import org.dromara.chat.service.ChatSessionService;
import org.dromara.knowledge.domain.vo.KnowledgeInfoVo;
import org.dromara.knowledge.domain.vo.KnowledgeSearchResultVo;
import org.dromara.knowledge.service.KnowledgeInfoService;
import org.dromara.knowledge.service.KnowledgeVectorService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* 知识库聊天服务实现
*
* @author pengles
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class AiKnowledgeChatServiceImpl implements AiKnowledgeChatService {
private final ChatSessionService chatSessionService;
private final ChatMessageService chatMessageService;
private final KnowledgeInfoService knowledgeInfoService;
private final KnowledgeVectorService knowledgeVectorService;
private final AiChatService aiChatService;
@Override
@Transactional(rollbackFor = Exception.class)
public ChatMessageVo chatWithKnowledge(Long sessionId, Long userId, String question, Long knowledgeId, String modelName) {
// 验证会话是否存在
ChatSessionVo session = chatSessionService.queryById(sessionId);
if (ObjectUtil.isNull(session)) {
throw new RuntimeException("会话不存在");
}
// 验证知识库是否存在
KnowledgeInfoVo knowledge = knowledgeInfoService.queryById(knowledgeId);
if (ObjectUtil.isNull(knowledge)) {
throw new RuntimeException("知识库不存在");
}
// 保存用户问题
ChatMessageBo userMessageBo = new ChatMessageBo();
userMessageBo.setSessionId(sessionId);
userMessageBo.setUserId(userId);
userMessageBo.setMessageType("text");
userMessageBo.setContent(question);
userMessageBo.setRole("user");
userMessageBo.setModelName(modelName);
userMessageBo.setCreateTime(new Date());
chatMessageService.insertByBo(userMessageBo);
try {
AiChatRequest req = new AiChatRequest()
.setUserId(String.valueOf(userId))
.setSessionId(String.valueOf(sessionId))
.setModel(modelName)
.setEnableKnowledge(true)
.setKnowledgeId(String.valueOf(knowledgeId))
.setKnowledgeLimit(5)
.setKnowledgeThreshold(0.7)
.setMessages(java.util.List.of(
new AiChatRequest.AiMessage().setRole("user").setContent(question)
));
AiChatResponse resp = aiChatService.chat(req);
String aiAnswer = "";
if (resp != null && resp.getChoices() != null && !resp.getChoices().isEmpty()) {
AiChatRequest.AiMessage msg = resp.getChoices().get(0).getMessage();
if (msg != null && StrUtil.isNotBlank(msg.getContent())) {
aiAnswer = msg.getContent();
}
}
if (StrUtil.isBlank(aiAnswer)) {
throw new RuntimeException("AI服务返回空回答");
}
// 保存AI回复
ChatMessageBo aiMessageBo = new ChatMessageBo();
aiMessageBo.setSessionId(sessionId);
aiMessageBo.setUserId(userId);
aiMessageBo.setMessageType("text");
aiMessageBo.setContent(aiAnswer);
aiMessageBo.setRole("assistant");
aiMessageBo.setModelName(modelName);
aiMessageBo.setTotalTokens(0L); // 简化版本不统计token
aiMessageBo.setDeductCost(BigDecimal.ZERO); // 简化版本不计费
aiMessageBo.setCreateTime(new Date());
chatMessageService.insertByBo(aiMessageBo);
return chatMessageService.queryById(aiMessageBo.getId());
} catch (Exception e) {
log.error("知识库聊天失败 - 用户: {}, 会话: {}, 知识库: {}", userId, sessionId, knowledgeId, e);
// 保存错误回复
ChatMessageBo errorMessageBo = new ChatMessageBo();
errorMessageBo.setSessionId(sessionId);
errorMessageBo.setUserId(userId);
errorMessageBo.setMessageType("text");
errorMessageBo.setContent("抱歉,AI服务暂时不可用,请稍后重试。");
errorMessageBo.setRole("assistant");
errorMessageBo.setModelName(modelName);
errorMessageBo.setCreateTime(new Date());
chatMessageService.insertByBo(errorMessageBo);
return chatMessageService.queryById(errorMessageBo.getId());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public ChatSessionVo createKnowledgeSession(Long userId, Long knowledgeId, String sessionTitle) {
// 验证知识库是否存在
KnowledgeInfoVo knowledge = knowledgeInfoService.queryById(knowledgeId);
if (ObjectUtil.isNull(knowledge)) {
throw new RuntimeException("知识库不存在");
}
// 如果没有提供标题,使用知识库名称作为默认标题
if (StrUtil.isBlank(sessionTitle)) {
sessionTitle = "基于" + knowledge.getKnowledgeName() + "的对话";
}
// 创建会话
ChatSessionBo sessionBo = new ChatSessionBo();
sessionBo.setUserId(userId);
sessionBo.setSessionTitle(sessionTitle);
sessionBo.setSessionContent("知识库ID: " + knowledgeId);
sessionBo.setCreateTime(new Date());
chatSessionService.insertByBo(sessionBo);
return chatSessionService.queryById(sessionBo.getId());
}
@Override
public List<String> searchKnowledgeContent(Long knowledgeId, String query, Integer limit) {
try {
log.info("搜索知识库内容 - 知识库: {}, 查询: {}, 限制: {}", knowledgeId, query, limit);
List<KnowledgeSearchResultVo> searchResults = knowledgeVectorService.vectorSimilaritySearch(
String.valueOf(knowledgeId), query, "default", limit != null ? limit : 5, 0.7
);
return searchResults.stream().map(KnowledgeSearchResultVo::getContent).toList();
} catch (Exception e) {
log.error("搜索知识库内容失败 - 知识库: {}, 查询: {}", knowledgeId, query, e);
return Collections.emptyList();
}
}
@Override
public String buildKnowledgeContext(Long knowledgeId, String question) {
// 搜索相关知识库内容
List<String> relevantContent = searchKnowledgeContent(knowledgeId, question, 3);
StringBuilder context = new StringBuilder();
context.append("以下是相关的知识库内容:\n\n");
for (int i = 0; i < relevantContent.size(); i++) {
context.append("参考资料 ").append(i + 1).append(": ");
context.append(relevantContent.get(i)).append("\n\n");
}
return context.toString();
}
@Override
public List<ChatMessageVo> getKnowledgeChatHistory(Long sessionId, Integer limit) {
// 获取会话的聊天历史
List<ChatMessageVo> messages = chatMessageService.queryListBySessionId(sessionId);
// 如果指定了限制数量,返回最近的消息
if (limit != null && limit > 0 && messages.size() > limit) {
return messages.subList(Math.max(0, messages.size() - limit), messages.size());
}
return messages;
}
}
@@ -0,0 +1,138 @@
package org.dromara.pet.service.impl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.pet.domain.bo.BizPetMedicalContextBo;
import org.dromara.pet.domain.vo.BizPetMedicalContextVo;
import org.dromara.pet.domain.BizPetMedicalContext;
import org.dromara.pet.mapper.BizPetMedicalContextMapper;
import org.dromara.pet.service.IBizPetMedicalContextService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* 宠物医疗上下文Service业务层处理
*
* @author shuo
* @date 2026-02-25
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class BizPetMedicalContextServiceImpl implements IBizPetMedicalContextService {
private final BizPetMedicalContextMapper baseMapper;
/**
* 查询宠物医疗上下文
*
* @param id 主键
* @return 宠物医疗上下文
*/
@Override
public BizPetMedicalContextVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 分页查询宠物医疗上下文列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 宠物医疗上下文分页列表
*/
@Override
public TableDataInfo<BizPetMedicalContextVo> queryPageList(BizPetMedicalContextBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<BizPetMedicalContext> lqw = buildQueryWrapper(bo);
Page<BizPetMedicalContextVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询符合条件的宠物医疗上下文列表
*
* @param bo 查询条件
* @return 宠物医疗上下文列表
*/
@Override
public List<BizPetMedicalContextVo> queryList(BizPetMedicalContextBo bo) {
LambdaQueryWrapper<BizPetMedicalContext> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<BizPetMedicalContext> buildQueryWrapper(BizPetMedicalContextBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<BizPetMedicalContext> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(BizPetMedicalContext::getId);
lqw.eq(bo.getPetId() != null, BizPetMedicalContext::getPetId, bo.getPetId());
lqw.eq(StringUtils.isNotBlank(bo.getAllergies()), BizPetMedicalContext::getAllergies, bo.getAllergies());
lqw.eq(StringUtils.isNotBlank(bo.getVaccinations()), BizPetMedicalContext::getVaccinations, bo.getVaccinations());
lqw.eq(StringUtils.isNotBlank(bo.getChronicConditions()), BizPetMedicalContext::getChronicConditions, bo.getChronicConditions());
lqw.eq(StringUtils.isNotBlank(bo.getRegularMedications()), BizPetMedicalContext::getRegularMedications, bo.getRegularMedications());
lqw.eq(bo.getUpdatedAt() != null, BizPetMedicalContext::getUpdatedAt, bo.getUpdatedAt());
lqw.eq(StringUtils.isNotBlank(bo.getTenantId()), BizPetMedicalContext::getTenantId, bo.getTenantId());
return lqw;
}
/**
* 新增宠物医疗上下文
*
* @param bo 宠物医疗上下文
* @return 是否新增成功
*/
@Override
public Boolean insertByBo(BizPetMedicalContextBo bo) {
BizPetMedicalContext add = MapstructUtils.convert(bo, BizPetMedicalContext. class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改宠物医疗上下文
*
* @param bo 宠物医疗上下文
* @return 是否修改成功
*/
@Override
public Boolean updateByBo(BizPetMedicalContextBo bo) {
BizPetMedicalContext update = MapstructUtils.convert(bo, BizPetMedicalContext. class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(BizPetMedicalContext entity) {
//TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除宠物医疗上下文信息
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
* @return 是否删除成功
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}
@@ -0,0 +1,129 @@
package org.dromara.chat.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.ChatConfig;
import org.dromara.chat.domain.bo.ChatConfigBo;
import org.dromara.chat.domain.vo.ChatConfigVo;
import org.dromara.chat.mapper.ChatConfigMapper;
import org.dromara.chat.service.ChatConfigService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
/**
* 聊天配置Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class ChatConfigServiceImpl extends ServiceImpl<ChatConfigMapper, ChatConfig> implements ChatConfigService {
/**
* 查询聊天配置
*/
@Override
public ChatConfigVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询聊天配置列表
*/
@Override
public TableDataInfo<ChatConfigVo> queryPageList(ChatConfigBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ChatConfig> lqw = buildQueryWrapper(bo);
Page<ChatConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询聊天配置列表
*/
@Override
public List<ChatConfigVo> queryList(ChatConfigBo bo) {
LambdaQueryWrapper<ChatConfig> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<ChatConfig> buildQueryWrapper(ChatConfigBo bo) {
LambdaQueryWrapper<ChatConfig> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getCategory()), ChatConfig::getCategory, bo.getCategory());
lqw.like(StringUtils.isNotBlank(bo.getConfigName()), ChatConfig::getConfigName, bo.getConfigName());
lqw.like(StringUtils.isNotBlank(bo.getConfigValue()), ChatConfig::getConfigValue, bo.getConfigValue());
lqw.eq(StringUtils.isNotBlank(bo.getVersion()), ChatConfig::getVersion, bo.getVersion());
return lqw;
}
/**
* 新增聊天配置
*/
@Override
public Boolean insertByBo(ChatConfigBo bo) {
ChatConfig add = MapstructUtils.convert(bo, ChatConfig.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改聊天配置
*/
@Override
public Boolean updateByBo(ChatConfigBo bo) {
ChatConfig update = MapstructUtils.convert(bo, ChatConfig.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(ChatConfig entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除聊天配置
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据配置分类查询配置列表
*/
@Override
public List<ChatConfigVo> queryListByCategory(String category) {
LambdaQueryWrapper<ChatConfig> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatConfig::getCategory, category);
return baseMapper.selectVoList(lqw);
}
/**
* 根据配置名称查询配置
*/
@Override
public ChatConfigVo queryByConfigName(String configName) {
LambdaQueryWrapper<ChatConfig> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatConfig::getConfigName, configName);
return baseMapper.selectVoOne(lqw);
}
}
@@ -0,0 +1,151 @@
package org.dromara.chat.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.ChatMessage;
import org.dromara.chat.domain.bo.ChatMessageBo;
import org.dromara.chat.domain.vo.ChatMessageVo;
import org.dromara.chat.mapper.ChatMessageMapper;
import org.dromara.chat.service.ChatMessageService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
/**
* 聊天消息Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class ChatMessageServiceImpl extends ServiceImpl<ChatMessageMapper, ChatMessage> implements ChatMessageService {
/**
* 查询聊天消息
*/
@Override
public ChatMessageVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询聊天消息列表
*/
@Override
public TableDataInfo<ChatMessageVo> queryPageList(ChatMessageBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ChatMessage> lqw = buildQueryWrapper(bo);
Page<ChatMessageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询聊天消息列表
*/
@Override
public List<ChatMessageVo> queryList(ChatMessageBo bo) {
LambdaQueryWrapper<ChatMessage> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<ChatMessage> buildQueryWrapper(ChatMessageBo bo) {
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
lqw.eq(ObjectUtil.isNotNull(bo.getSessionId()), ChatMessage::getSessionId, bo.getSessionId());
lqw.eq(ObjectUtil.isNotNull(bo.getUserId()), ChatMessage::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getContent()), ChatMessage::getContent, bo.getContent());
lqw.eq(StringUtils.isNotBlank(bo.getRole()), ChatMessage::getRole, bo.getRole());
lqw.eq(StringUtils.isNotBlank(bo.getModelName()), ChatMessage::getModelName, bo.getModelName());
lqw.orderByDesc(ChatMessage::getCreateTime);
return lqw;
}
/**
* 新增聊天消息
*/
@Override
public Boolean insertByBo(ChatMessageBo bo) {
ChatMessage add = MapstructUtils.convert(bo, ChatMessage.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改聊天消息
*/
@Override
public Boolean updateByBo(ChatMessageBo bo) {
ChatMessage update = MapstructUtils.convert(bo, ChatMessage.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(ChatMessage entity) {
if (StringUtils.isBlank(entity.getContent())) {
throw new IllegalArgumentException("消息内容不能为空");
}
if (StringUtils.isBlank(entity.getRole())) {
entity.setRole("user");
}
}
/**
* 校验并批量删除聊天消息
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据会话ID查询消息列表
*/
@Override
public List<ChatMessageVo> queryListBySessionId(Long sessionId) {
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatMessage::getSessionId, sessionId);
lqw.orderByAsc(ChatMessage::getCreateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 根据用户ID查询消息列表
*/
@Override
public List<ChatMessageVo> queryListByUserId(Long userId) {
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatMessage::getUserId, userId);
lqw.orderByDesc(ChatMessage::getCreateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 根据会话ID和用户ID查询消息列表
*/
@Override
public List<ChatMessageVo> queryListBySessionIdAndUserId(Long sessionId, Long userId) {
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatMessage::getSessionId, sessionId);
lqw.eq(ChatMessage::getUserId, userId);
lqw.orderByAsc(ChatMessage::getCreateTime);
return baseMapper.selectVoList(lqw);
}
}
@@ -0,0 +1,150 @@
package org.dromara.chat.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.ChatModel;
import org.dromara.chat.domain.bo.ChatModelBo;
import org.dromara.chat.domain.vo.ChatModelVo;
import org.dromara.chat.mapper.ChatModelMapper;
import org.dromara.chat.service.ChatModelService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
/**
* 聊天模型Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class ChatModelServiceImpl extends ServiceImpl<ChatModelMapper, ChatModel> implements ChatModelService {
/**
* 查询聊天模型
*/
@Override
public ChatModelVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询聊天模型列表
*/
@Override
public TableDataInfo<ChatModelVo> queryPageList(ChatModelBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ChatModel> lqw = buildQueryWrapper(bo);
Page<ChatModelVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询聊天模型列表
*/
@Override
public List<ChatModelVo> queryList(ChatModelBo bo) {
LambdaQueryWrapper<ChatModel> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<ChatModel> buildQueryWrapper(ChatModelBo bo) {
LambdaQueryWrapper<ChatModel> lqw = Wrappers.lambdaQuery();
lqw.like(StringUtils.isNotBlank(bo.getCategory()), ChatModel::getCategory, bo.getCategory());
lqw.like(StringUtils.isNotBlank(bo.getModelName()), ChatModel::getModelName, bo.getModelName());
lqw.like(StringUtils.isNotBlank(bo.getModelDescribe()), ChatModel::getModelDescribe, bo.getModelDescribe());
lqw.eq(StringUtils.isNotBlank(bo.getModelType()), ChatModel::getModelType, bo.getModelType());
lqw.eq(StringUtils.isNotBlank(bo.getModelShow()), ChatModel::getModelShow, bo.getModelShow());
return lqw;
}
/**
* 新增聊天模型
*/
@Override
public Boolean insertByBo(ChatModelBo bo) {
ChatModel add = MapstructUtils.convert(bo, ChatModel.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改聊天模型
*/
@Override
public Boolean updateByBo(ChatModelBo bo) {
ChatModel update = MapstructUtils.convert(bo, ChatModel.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(ChatModel entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除聊天模型
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据模型分类查询模型列表
*/
@Override
public List<ChatModelVo> queryListByCategory(String category) {
LambdaQueryWrapper<ChatModel> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatModel::getCategory, category);
return baseMapper.selectVoList(lqw);
}
/**
* 根据模型名称查询模型
*/
@Override
public ChatModelVo queryByModelName(String modelName) {
LambdaQueryWrapper<ChatModel> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatModel::getModelName, modelName);
return baseMapper.selectVoOne(lqw);
}
/**
* 查询显示的模型列表
*/
@Override
public List<ChatModelVo> queryShowModelList() {
LambdaQueryWrapper<ChatModel> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatModel::getModelShow, "1");
return baseMapper.selectVoList(lqw);
}
/**
* 根据模型类型查询模型列表
*/
@Override
public List<ChatModelVo> queryListByModelType(String modelType) {
LambdaQueryWrapper<ChatModel> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatModel::getModelType, modelType);
return baseMapper.selectVoList(lqw);
}
}
@@ -0,0 +1,157 @@
package org.dromara.chat.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.ChatMessage;
import org.dromara.chat.domain.ChatSession;
import org.dromara.chat.domain.bo.ChatSessionBo;
import org.dromara.chat.domain.vo.ChatSessionVo;
import org.dromara.chat.mapper.ChatMessageMapper;
import org.dromara.chat.mapper.ChatSessionMapper;
import org.dromara.chat.service.ChatSessionService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.List;
/**
* 聊天会话Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class ChatSessionServiceImpl extends ServiceImpl<ChatSessionMapper, ChatSession> implements ChatSessionService {
private final ChatMessageMapper chatMessageMapper;
/**
* 查询聊天会话
*/
@Override
public ChatSessionVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询聊天会话列表
*/
@Override
public TableDataInfo<ChatSessionVo> queryPageList(ChatSessionBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ChatSession> lqw = buildQueryWrapper(bo);
Page<ChatSessionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询聊天会话列表
*/
@Override
public List<ChatSessionVo> queryList(ChatSessionBo bo) {
LambdaQueryWrapper<ChatSession> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<ChatSession> buildQueryWrapper(ChatSessionBo bo) {
LambdaQueryWrapper<ChatSession> lqw = Wrappers.lambdaQuery();
lqw.eq(ObjectUtil.isNotNull(bo.getUserId()), ChatSession::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getSessionTitle()), ChatSession::getSessionTitle, bo.getSessionTitle());
lqw.orderByDesc(ChatSession::getUpdateTime);
return lqw;
}
/**
* 新增聊天会话
*/
@Override
public Boolean insertByBo(ChatSessionBo bo) {
ChatSession add = MapstructUtils.convert(bo, ChatSession.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改聊天会话
*/
@Override
public Boolean updateByBo(ChatSessionBo bo) {
ChatSession update = MapstructUtils.convert(bo, ChatSession.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(ChatSession entity) {
if (StringUtils.isBlank(entity.getSessionTitle())) {
entity.setSessionTitle("新会话");
}
if (entity.getSessionTitle().length() > 50) {
entity.setSessionTitle(entity.getSessionTitle().substring(0, 50));
}
}
/**
* 校验并批量删除聊天会话
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// 删除关联的消息
LambdaQueryWrapper<ChatMessage> lqw = Wrappers.lambdaQuery();
lqw.in(ChatMessage::getSessionId, ids);
chatMessageMapper.delete(lqw);
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据用户ID查询会话列表
*/
@Override
public List<ChatSessionVo> queryListByUserId(Long userId) {
LambdaQueryWrapper<ChatSession> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatSession::getUserId, userId);
lqw.orderByDesc(ChatSession::getUpdateTime);
return baseMapper.selectVoList(lqw);
}
/**
* 创建新会话
*/
@Override
public ChatSessionVo createNewSession(Long userId, String sessionTitle) {
ChatSession session = new ChatSession();
session.setUserId(userId);
session.setSessionTitle(sessionTitle);
baseMapper.insert(session);
return baseMapper.selectVoById(session.getId());
}
/**
* 更新会话标题
*/
@Override
public Boolean updateSessionTitle(Long sessionId, String sessionTitle) {
ChatSession session = new ChatSession();
session.setId(sessionId);
session.setSessionTitle(sessionTitle);
return baseMapper.updateById(session) > 0;
}
}
@@ -0,0 +1,152 @@
package org.dromara.chat.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.dromara.chat.domain.ChatUsageToken;
import org.dromara.chat.domain.bo.ChatUsageTokenBo;
import org.dromara.chat.domain.vo.ChatUsageTokenVo;
import org.dromara.chat.mapper.ChatUsageTokenMapper;
import org.dromara.chat.service.ChatUsageTokenService;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
/**
* 聊天Token使用记录Service业务层处理
*
* @author pengles
*/
@RequiredArgsConstructor
@Service
public class ChatUsageTokenServiceImpl extends ServiceImpl<ChatUsageTokenMapper, ChatUsageToken> implements ChatUsageTokenService {
/**
* 查询聊天Token使用记录
*/
@Override
public ChatUsageTokenVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询聊天Token使用记录列表
*/
@Override
public TableDataInfo<ChatUsageTokenVo> queryPageList(ChatUsageTokenBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<ChatUsageToken> lqw = buildQueryWrapper(bo);
Page<ChatUsageTokenVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询聊天Token使用记录列表
*/
@Override
public List<ChatUsageTokenVo> queryList(ChatUsageTokenBo bo) {
LambdaQueryWrapper<ChatUsageToken> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private LambdaQueryWrapper<ChatUsageToken> buildQueryWrapper(ChatUsageTokenBo bo) {
LambdaQueryWrapper<ChatUsageToken> lqw = Wrappers.lambdaQuery();
lqw.eq(ObjectUtil.isNotNull(bo.getUserId()), ChatUsageToken::getUserId, bo.getUserId());
lqw.eq(StringUtils.isNotBlank(bo.getModelName()), ChatUsageToken::getModelName, bo.getModelName());
return lqw;
}
/**
* 新增聊天Token使用记录
*/
@Override
public Boolean insertByBo(ChatUsageTokenBo bo) {
ChatUsageToken add = MapstructUtils.convert(bo, ChatUsageToken.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
/**
* 修改聊天Token使用记录
*/
@Override
public Boolean updateByBo(ChatUsageTokenBo bo) {
ChatUsageToken update = MapstructUtils.convert(bo, ChatUsageToken.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
* 保存前的数据校验
*/
private void validEntityBeforeSave(ChatUsageToken entity) {
// TODO 做一些数据校验,如唯一约束
}
/**
* 校验并批量删除聊天Token使用记录
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) {
// TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 根据用户ID查询Token使用记录列表
*/
@Override
public List<ChatUsageTokenVo> queryListByUserId(Long userId) {
LambdaQueryWrapper<ChatUsageToken> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatUsageToken::getUserId, userId);
return baseMapper.selectVoList(lqw);
}
/**
* 根据模型名称查询Token使用记录列表
*/
@Override
public List<ChatUsageTokenVo> queryListByModelName(String modelName) {
LambdaQueryWrapper<ChatUsageToken> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatUsageToken::getModelName, modelName);
return baseMapper.selectVoList(lqw);
}
/**
* 统计用户总Token使用量
*/
@Override
public Long getTotalTokenByUserId(Long userId) {
LambdaQueryWrapper<ChatUsageToken> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatUsageToken::getUserId, userId);
lqw.select(ChatUsageToken::getToken);
List<ChatUsageToken> list = baseMapper.selectList(lqw);
return list.stream().mapToLong(ChatUsageToken::getToken).sum();
}
/**
* 统计模型总Token使用量
*/
@Override
public Long getTotalTokenByModelName(String modelName) {
LambdaQueryWrapper<ChatUsageToken> lqw = Wrappers.lambdaQuery();
lqw.eq(ChatUsageToken::getModelName, modelName);
lqw.select(ChatUsageToken::getToken);
List<ChatUsageToken> list = baseMapper.selectList(lqw);
return list.stream().mapToLong(ChatUsageToken::getToken).sum();
}
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.pet.mapper.BizPetMedicalContextMapper">
</mapper>
@@ -0,0 +1,56 @@
package org.dromara.chat.service;
import org.dromara.chat.domain.ChatSession;
import org.dromara.chat.domain.bo.ChatSessionBo;
import org.dromara.chat.mapper.ChatSessionMapper;
import org.dromara.chat.service.impl.ChatSessionServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
/**
* ChatSessionService单元测试
*/
@ExtendWith(MockitoExtension.class)
public class ChatSessionServiceTest {
@Mock
private ChatSessionMapper chatSessionMapper;
@InjectMocks
private ChatSessionServiceImpl chatSessionService;
@BeforeEach
public void setUp() {
}
@Test
public void testInsertByBo() {
ChatSessionBo bo = new ChatSessionBo();
bo.setUserId(1L);
bo.setSessionTitle("Test Session");
Mockito.when(chatSessionMapper.insert(Mockito.any(ChatSession.class))).thenReturn(1);
Boolean result = chatSessionService.insertByBo(bo);
Assertions.assertTrue(result);
Mockito.verify(chatSessionMapper).insert(Mockito.any(ChatSession.class));
}
@Test
public void testUpdateSessionTitle() {
Long sessionId = 1L;
String newTitle = "Updated Title";
Mockito.when(chatSessionMapper.updateById(Mockito.any(ChatSession.class))).thenReturn(1);
Boolean result = chatSessionService.updateSessionTitle(sessionId, newTitle);
Assertions.assertTrue(result);
Mockito.verify(chatSessionMapper).updateById(Mockito.any(ChatSession.class));
}
}