会话管理
本文档详细介绍了校园领域搜索问答系统中的会话管理功能模块的设计与实现。该模块旨在有效管理用户与系统之间的对话会话,确保对话的连贯性和一致性。本文将涵盖接口设计、数据库表结构、控制器实现以及服务层实现。
功能概述
会话管理模块主要负责以下功能:
- 创建会话:允许用户创建新的对话会话。
- 列出会话:提供用户所有对话会话的列表,支持分页查询。
- 设置会话名称:允许用户修改现有会话的名称。
- 删除会话:支持用户删除不需要的会话,采用软删除方式。
接口设计
1. 创建会话
请求方法:
POST
接口路径:
/api/v1/chat/create
请求参数:
参数 类型 描述 是否必填 name String 会话名称 是 school_id Long 学校 ID(可选,默认值为 1) 否 chat_type Integer 会话类型(默认值为 0) 否 type String 会话类型描述 否 app_id Long 应用 ID(可选) 否 响应格式:
{ "data": { "id": 12345, "name": "会话名称" }, "code": 1, "msg": null, "ok": true }
2. 列出会话
请求方法:
GET
接口路径:
/api/v1/chat/list
请求参数:
参数 类型 描述 是否必填 offset Integer 页码(默认值为 1) 否 limit Integer 每页数量(默认值为 10) 否 school_id Long 学校 ID(可选) 否 chat_type Integer 会话类型(可选) 否 响应格式:
{ "data": [ { "id": 12345, "name": "会话名称", "user_id": "user123", "school_id": 1, "type": "类型描述", "chat_type": 0, "create_time": "2024-07-16T12:34:36.651846Z", "update_time": "2024-07-16T12:34:36.651846Z" } // 更多会话 ], "code": 1, "msg": null, "ok": true }
3. 设置会话名称
请求方法:
PUT
接口路径:
/api/v1/chat/set/name
请求参数:
参数 类型 描述 是否必填 session_id Long 会话 ID 是 name String 新的会话名称 是 响应格式:
{ "data": null, "code": 1, "msg": null, "ok": true }
4. 删除会话
请求方法:
DELETE
接口路径:
/api/v1/chat/delete
请求参数:
参数 类型 描述 是否必填 session_id Long 会话 ID 是 响应格式:
{ "data": null, "code": 1, "msg": null, "ok": true }
控制器实现
ApiChatHandler
类负责处理与会话管理相关的 HTTP 请求,包括创建会话、列出会话、设置会话名称以及删除会话。以下是该类的实现代码及说明:
package com.litongjava.llm.handler;
import java.util.List;
import java.util.stream.Collectors;
import com.jfinal.kit.Kv;
import com.litongjava.db.TableResult;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.llm.service.LlmChatSessionService;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.util.CORSUtils;
import com.litongjava.tio.utils.hutool.StrUtil;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class ApiChatHandler {
/**
* 创建会话
*
* @param request HTTP请求对象
* @return HTTP响应对象
*/
public HttpResponse createSession(HttpRequest request) {
HttpResponse response = TioRequestContext.getResponse();
CORSUtils.enableCORS(response);
String userId = TioRequestContext.getUserIdString();
String name = request.getParam("name");
String schoolIdString = request.getParam("school_id");
Integer chatType = request.getInt("chat_type");
String type = request.getString("type");
Long appId = request.getLong("app_id");
if (chatType == null) {
chatType = 0;
}
RespBodyVo respBodyVo = null;
if (StrUtil.isBlank(name)) {
response.setStatus(400);
respBodyVo = RespBodyVo.fail("name cannot be empty");
}
if (StrUtil.isBlank(schoolIdString)) {
schoolIdString = "1";
}
Long school_id = Long.parseLong(schoolIdString);
TableResult<Kv> tr = Aop.get(LlmChatSessionService.class).create(userId, name, school_id, type, chatType, appId);
if (tr.isOk()) {
respBodyVo = RespBodyVo.ok(tr.getData());
} else {
respBodyVo = RespBodyVo.fail();
}
return response.setJson(respBodyVo);
}
/**
* 删除会话
*
* @param request HTTP请求对象
* @return HTTP响应对象
*/
public HttpResponse deleteSession(HttpRequest request) {
HttpResponse response = TioRequestContext.getResponse();
CORSUtils.enableCORS(response);
Long session_id = request.getLong("session_id");
log.info("session_id: {}", session_id);
if (session_id == null) {
return response.fail(RespBodyVo.fail("session_id can not be empty"));
}
String userId = TioRequestContext.getUserIdString();
int updateResult = Aop.get(LlmChatSessionService.class).softDelete(session_id, userId);
if (updateResult > 0) {
response.setJson(RespBodyVo.ok());
} else {
response.setJson(RespBodyVo.fail());
}
return response;
}
/**
* 列出会话
*
* @param request HTTP请求对象
* @return HTTP响应对象
*/
public HttpResponse listSession(HttpRequest request) {
HttpResponse response = TioRequestContext.getResponse();
CORSUtils.enableCORS(response);
Integer pageNo = request.getInt("offset");
Integer pageSize = request.getInt("limit");
Long schoolId = request.getLong("school_id");
Integer chatType = request.getInt("chat_type");
if (pageNo == null) {
pageNo = 1;
}
if (pageSize == null) {
pageSize = 10;
}
if (chatType == null) {
chatType = 0;
}
String userId = TioRequestContext.getUserIdString();
List<Kv> list = Aop.get(LlmChatSessionService.class).page(pageNo, pageSize, userId, schoolId, chatType);
return response.setJson(RespBodyVo.ok(list));
}
/**
* 设置会话名称
*
* @param request HTTP请求对象
* @return HTTP响应对象
*/
public HttpResponse setSessionName(HttpRequest request) {
HttpResponse response = TioRequestContext.getResponse();
CORSUtils.enableCORS(response);
Long sessionId = request.getLong("session_id");
String name = request.getParam("name");
if (sessionId == null) {
return response.fail(RespBodyVo.fail("session_id can not be empty"));
}
if (StrUtil.isEmpty(name)) {
return response.fail(RespBodyVo.fail("name can not be empty"));
}
String userId = TioRequestContext.getUserIdString();
boolean exists = Aop.get(LlmChatSessionService.class).exists(sessionId, userId);
if (!exists) {
return response.fail(RespBodyVo.fail("invalid session"));
}
int updateResult = Aop.get(LlmChatSessionService.class).updateSessionName(name, sessionId, userId);
RespBodyVo respBodyVo = null;
if (updateResult > 0) {
respBodyVo = RespBodyVo.ok();
} else {
respBodyVo = RespBodyVo.fail();
}
return response.setJson(respBodyVo);
}
}
控制器代码说明
createSession:
- 处理
/api/v1/chat/create
的POST
请求。 - 从请求中获取必要的参数,如
name
、school_id
、chat_type
等。 - 校验参数有效性,确保
name
不为空。 - 调用服务层
LlmChatSessionService.create
方法创建会话。 - 根据服务层返回结果构建响应。
- 处理
deleteSession:
- 处理
/api/v1/chat/delete
的DELETE
请求。 - 获取并校验
session_id
参数。 - 调用服务层
LlmChatSessionService.softDelete
方法软删除会话。 - 根据删除结果构建响应。
- 处理
listSession:
- 处理
/api/v1/chat/list
的GET
请求。 - 获取分页参数
offset
和limit
,设置默认值。 - 获取可选参数
school_id
和chat_type
。 - 调用服务层
LlmChatSessionService.page
方法获取会话列表。 - 构建并返回会话列表的响应。
- 处理
setSessionName:
- 处理
/api/v1/chat/set/name
的PUT
请求。 - 获取并校验
session_id
和name
参数。 - 检查会话是否存在且属于当前用户。
- 调用服务层
LlmChatSessionService.updateSessionName
方法更新会话名称。 - 根据更新结果构建响应。
- 处理
服务层实现
LlmChatSessionService
类负责与数据库交互,处理会话的创建、查询、更新和删除操作。以下是该类的实现代码及说明:
package com.litongjava.llm.service;
import java.util.ArrayList;
import java.util.List;
import com.jfinal.kit.Kv;
import com.litongjava.db.TableInput;
import com.litongjava.db.TableResult;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Row;
import com.litongjava.max.blog.consts.TableNames;
import com.litongjava.model.page.Page;
import com.litongjava.table.services.ApiTable;
import com.litongjava.tio.utils.snowflake.SnowflakeIdUtils;
public class LlmChatSessionService {
/**
* 创建会话
*
* @param userId 用户ID
* @param name 会话名称
* @param school_id 学校ID
* @param type 会话类型描述
* @param chat_type 会话类型
* @param appId 应用ID
* @return 创建结果,包含会话ID和名称
*/
public TableResult<Kv> create(String userId, String name, Long school_id, String type, Integer chat_type, Long appId) {
long id = SnowflakeIdUtils.id();
Row record = Row.by("id", id);
record.setTableName(TableNames.llm_chat_session);
record.set("name", name);
record.set("user_id", userId);
record.set("school_id", school_id);
record.set("type", type);
record.set("chat_type", chat_type);
if (appId != null) {
record.set("app_id", appId);
}
boolean save = Db.save(record);
if (save) {
return TableResult.ok(Kv.by("id", id).set("name", name));
} else {
return TableResult.fail();
}
}
/**
* 检查会话是否存在且属于当前用户
*
* @param id 会话ID
* @param userId 用户ID
* @return 存在返回true,否则返回false
*/
public boolean exists(Long id, String userId) {
String sql = "select count(1) from %s where id=? and user_id=? and deleted=0";
sql = String.format(sql, TableNames.llm_chat_session);
return Db.existsBySql(sql, id, userId);
}
/**
* 分页获取会话列表
*
* @param pageNo 页码
* @param pageSize 每页数量
* @param userId 用户ID
* @param schoolId 学校ID
* @param chat_type 会话类型
* @return 会话列表
*/
public List<Kv> page(int pageNo, int pageSize, String userId, Long schoolId, Integer chat_type) {
TableInput ti = TableInput.create();
ti.set("user_id", userId);
if (schoolId != null) {
ti.set("school_id", schoolId);
}
ti.set("chat_type", chat_type);
ti.set("deleted", 0);
ti.pageNo(pageNo).pageSize(pageSize);
ti.orderBy("create_time").asc(false);
TableResult<Page<Row>> result = ApiTable.page(TableNames.llm_chat_session, ti);
Page<Row> paginate = result.getData();
List<Row> list = paginate.getList();
List<Kv> kvs = new ArrayList<>();
for (Row record : list) {
kvs.add(record.toKv());
}
return kvs;
}
/**
* 更新会话名称
*
* @param name 新的会话名称
* @param id 会话ID
* @param userId 用户ID
* @return 更新影响的行数
*/
public int updateSessionName(String name, Long id, String userId) {
String sql = String.format("update %s set name=? where id=? and user_id=?", TableNames.llm_chat_session);
return Db.updateBySql(sql, name, id, userId);
}
/**
* 软删除会话
*
* @param id 会话ID
* @param userId 用户ID
* @return 更新影响的行数
*/
public int softDelete(Long id, String userId) {
String sql = "update %s set deleted=1 where id=? and user_id=?";
sql = String.format(sql, TableNames.llm_chat_session);
return Db.updateBySql(sql, id, userId);
}
}
服务层代码说明
create:
- 生成唯一的会话 ID(使用 Snowflake 算法)。
- 构建
Row
对象,设置会话相关字段。 - 保存会话记录到数据库。
- 返回创建结果,包括会话 ID 和名称。
exists:
- 检查指定的会话 ID 是否存在且属于当前用户,且未被删除。
- 返回布尔值,存在则返回
true
,否则返回false
。
page:
- 构建查询条件,设置用户 ID、学校 ID、会话类型及删除标志。
- 设置分页参数和排序方式。
- 调用
ApiTable.page
方法进行分页查询。 - 将查询结果转换为
List<Kv>
格式,便于前端使用。
updateSessionName:
- 更新指定会话的名称,确保会话属于当前用户。
- 执行更新操作,返回影响的行数。
softDelete:
- 对指定会话进行软删除,设置
deleted
字段为1
。 - 确保会话属于当前用户。
- 执行更新操作,返回影响的行数。
- 对指定会话进行软删除,设置
总结
本文档详细介绍了校园领域搜索问答系统中会话管理功能模块的设计与实现。通过定义清晰的 API 接口、设计合理的数据库表结构以及实现高效的控制器和服务层代码,该模块能够有效地管理用户的对话会话,确保系统的连贯性和一致性。未来,可以进一步优化会话管理功能,例如增加会话恢复功能、会话分类管理以及与其他模块的深度集成,以提升系统的整体用户体验和功能丰富性。