
个人主页艾莉丝努力练剑❄专栏传送门《C语言》《数据结构与算法》《C/C干货分享学习过程记录》《Linux操作系统编程详解》《笔试/面试常见算法从基础到进阶》《Python干货分享》⭐️为天地立心为生民立命为往圣继绝学为万世开太平 艾莉丝的简介文章目录1 ~ ChatServer 接口整体测试体系1.1 测试前置准备1.1.1 接口清单录入Apifox1.1.2 Apifox 环境配置1.1.3 会话 ID 生成规则2 ~ 逐个接口测试流程与业务逻辑2.1 GET /api/models 获取可用模型2.1.1 请求规约2.1.2 返回 JSON 结构2.1.3 测试校验点2.2 POST /api/session 创建会话2.2.1 请求规约2.2.2 返回 JSON 结构2.2.3 业务逻辑2.3 GET /api/sessions 获取会话列表2.3.1 请求规约2.3.2 返回 JSON 结构2.3.3 业务校验2.4 DELETE /api/session/{session_id} 删除会话2.4.1 请求规约2.4.2 返回 JSON2.4.3 业务校验2.5 GET /api/session/{session_id}/history 获取会话历史消息2.5.1 请求规约2.5.2 典型故障点代码 BUG 复盘2.5.3 校验标准2.6 POST /api/message 发送消息‑全量返回2.6.1 请求规约2.6.2 业务逻辑2.6.3 校验2.7 POST /api/message/async 发送消息‑SSE 流式增量返回2.7.1 请求规约2.7.2 SSE 流式协议关键知识点2.7.3 代码故障复盘2.7.4 测试校验点3 ~ 多模型兼容性测试3.1 测试策略3.2 外部模型环境依赖3.3 测试流程示例gpt‑4o‑mini4 ~ SQLite 数据库存储层关键要点4.1 数据表4.2 开发调试手段5 ~ 整体排错方法论结尾1 ~ ChatServer 接口整体测试体系1.1 测试前置准备1.1.1 接口清单录入Apifox共 7 个后端接口服务基于 http‑lib 实现路由注册表格请求方法接口路径接口功能POST/api/session创建会话GET/api/sessions获取会话列表GET/api/models获取可用模型列表DELETE/api/session/{session_id}删除指定会话GET/api/session/{session_id}/history获取会话历史消息POST/api/message发送消息‑全量返回POST/api/message/async发送消息‑流式增量返回// ChatServer 路由注册核心代码片段voidChatServer::setupHttpRoutes(){// 创建会话_chatServer-Post(pattern/api/session,handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleCreateSessionRequest(request,response);});// 获取会话列表_chatServer-Get(pattern/api/sessions,handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleGetSessionListsRequest(request,response);});// 获取可用模型_chatServer-Get(pattern/api/models,handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleGetModelListsRequest(request,response);});// 删除会话_chatServer-Delete(pattern/api/session/(.*),handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleDeleteSessionRequest(request,response);});// 获取会话历史消息_chatServer-Get(pattern/api/session/(.*)/history,handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleGetHistoryMessagesRequest(request,response);});// 发送消息-全量返回_chatServer-Post(pattern/api/message,handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleSendMessageRequest(request,response);});// 发送消息-流式增量返回_chatServer-Post(pattern/api/message/async,handler[this](consthttplib::Requestrequest,httplib::Responseresponse){handleSendMessageStreamRequest(request,response);});}1.1.2 Apifox 环境配置配置测试环境前置 URLhttp://服务器IP:端口示例http://192.168.100.238:8080环境变量配置各类大模型 API‑KEY 密钥接口录入规范严格对齐 HTTP 请求方法、Path 路径参数、Body JSON 参数测试阶段无需维护 Mock 响应结构体仅校验请求可达性、返回数据有效性两个核心指标Path 参数禁止携带$变量符号填入实际参数值该符号为 Apifox 变量标识直接填入会造成参数解析失败1.1.3 会话 ID 生成规则会话 ID 格式session_时间戳_序号序号由服务内部静态计数器维护服务器进程不重启计数器持续累加不会重置SessionManager 初始化会从数据库读取已有会话恢复计数器基数// 会话ID生成关键片段std::stringSessionManager::generateSessionId(){// _sessionCounter 静态成员变量进程生命周期持续自增_sessionCounter.fetch_add(1);std::time_t tstd::time(nullptr);returnsession_std::to_string(t)_std::to_string(_sessionCounter);}2 ~ 逐个接口测试流程与业务逻辑2.1 GET /api/models 获取可用模型2.1.1 请求规约HTTP 方法GET无请求参数2.1.2 返回 JSON 结构{success:true,message:get model lists success,data:[{name:deepseek-chat,desc:一款基于Deepseek的实用性强、功能强大的、中文优化的通用聊天模型用于生成自然语言回复适合用于聊天、问答、对话等场景}]}2.1.3 测试校验点HTTP 200success 字段为 truedata 数组返回全部配置好的模型元数据。2.2 POST /api/session 创建会话2.2.1 请求规约HTTP 方法POSTContent‑Type: application/json请求 Body{model:deepseek-chat}2.2.2 返回 JSON 结构{success:true,message:create session success,data:{model:deepseek‑chat,session_id:session_1789031979_00000001}}2.2.3 业务逻辑指定模型创建全新会话生成唯一 session_id会话初始化消息计数为 0写入 SQLite 数据库sessions表。2.3 GET /api/sessions 获取会话列表2.3.1 请求规约HTTP 方法GET无请求参数2.3.2 返回 JSON 结构{success:true,message:get session lists success,data:[{id:session_1789049548_00000002,model:deepseek‑r1:1.5b,created_at:1789049548,updated_at:1789049548,message_count:0}]}2.3.3 业务校验返回全部持久化会话集合message_count 统计该会话下问答消息总条数。2.4 DELETE /api/session/{session_id} 删除会话2.4.1 请求规约HTTP 方法DELETEPath 参数session_id会话唯一标识2.4.2 返回 JSON{success:true,message:delete session success}2.4.3 业务校验删除会话数据库记录再次调用获取会话列表接口该会话不再出现在 data 数组。2.5 GET /api/session/{session_id}/history 获取会话历史消息2.5.1 请求规约HTTP 方法GETPath 参数session_id2.5.2 典型故障点代码 BUG 复盘SQLite 执行判断条件错误sqlite3_step执行查询单条记录执行成功读取行返回SQLITE_ROW查询结束无更多数据返回SQLITE_DONE。原始错误判断返回值不等于SQLITE_DONE直接判定执行失败单条查询读完数据之后会进入SQLITE_DONE直接返回空会话指针提示 session not found。// 错误代码片段intrcsqlite3_step(stmt);if(rc!SQLITE_DONE){// 错误单条记录读取完毕 rcSQLITE_DONE误判为执行失败}// 修正后逻辑intrcsqlite3_step(stmt);if(rc!SQLITE_ROW){// 只有读到行才算有效数据SQLITE_DONE代表查询结束}Apifox 测试故障Path 参数误输入带$的模板变量标识后端拿到非法 session_id返回会话不存在。2.5.3 校验标准根据 session_id 读取 messages 表返回该会话完整历史问答消息集合。2.6 POST /api/message 发送消息‑全量返回2.6.1 请求规约POSTContent‑Type:application/json{session_id:session_xxxx,message:请讲一个笑话}2.6.2 业务逻辑根据 session_id 定位会话校验会话合法性将用户提问持久化写入 messages 表调用大模型 SDK 完成完整对话等待模型全部应答完成将模型完整回答写入 messages 表更新会话updated_at时间戳与message_count计数将完整模型应答一次性返回客户端。2.6.3 校验返回完整应答文本获取历史消息接口可以看到新增两条消息用户提问 模型回复。2.7 POST /api/message/async 发送消息‑SSE 流式增量返回2.7.1 请求规约POSTContent‑Type:application/json请求体与全量接口完全一致。2.7.2 SSE 流式协议关键知识点Response 的 Content‑Type 必须设置为text/event‑stream禁止使用 application/jsonapplication/json 会让客户端把整体当做 JSON 报文解析不会逐块流式渲染。SSE 报文格式data: 分片内容\n\n data: 分片内容\n\n data:[DONE]\n\ndata: [DONE]为流结束标记HTTP 长连接保持分块传输 chunked 编码。2.7.3 代码故障复盘预发送空数据块防客户端长等待的 BUG// 错误代码第二个参数lasttrue直接标记流结束连接立刻关闭writeChunk(,true);// 修复代码 lastfalse代表不是流的最后一块writeChunk(,false);writeChunk 第二个布尔参数 last 语义true 表示当前 chunk 是流的最后一个分片发送data:[DONE]结束 SSE 会话false 代表后续还有数据分片。响应头 content‑type 错误写成application/json客户端无法逐片解析事件流。// 错误response-set_chunked_content_provider(application/json,providerLambda);// 修正response-set_chunked_content_provider(text/event‑stream,providerLambda);// 流式请求处理修正后完整核心逻辑voidChatServer::handleSendMessageStreamRequest(consthttplib::Requestrequest,httplib::Responseresponse){std::string sessionIdrequest.json[session_id].asString();std::string messagerequest.json[message].asString();if(sessionId.empty()||message.empty()){response-status400;return;}response-status200;response-set_header(Cache‑Control,no‑cache);response-set_header(Connection,keep‑alive);// SSE必须使用 text/event‑streamresponse-set_chunked_content_provider(text/event‑stream,[this,sessionId,message](size_t offset,httplib::DataSinkdataSink)-bool{autowriteChunk[](conststd::stringchunk,boollast)-void{// 转SSE报文格式std::string sseDatadata: json::valueToQuotedString(chunk.c_str())\n\n;dataSink.write(sseData.c_str(),sseData.size());if(last){std::string doneDatadata: [DONE]\n\n;dataSink.write(doneData.c_str(),doneData.size());dataSink.done();}};// 发送空块防止客户端超时等待lastfalse流不关闭writeChunk(,false);// 调用SDK流式回调_chatSDK-sendMessageStream(sessionId,message,writeChunk);returntrue;});}2.7.4 测试校验点Apifox 时间线分片接收模型输出文本逐块到达流末尾收到data: [DONE]结束标记会话消息计数更新历史消息接口可以拿到完整问答。3 ~ 多模型兼容性测试3.1 测试策略基础会话、会话列表、删除会话、获取模型接口仅需针对一个模型完成验证。其余模型只需要重点验证全量返回、SSE 流式增量返回两个消息发送接口。3.2 外部模型环境依赖云端模型GPT 系列等服务器访问受限场景启动服务进程前需要配置代理环境变量。Ollama 本地模型无需外网代理服务本地访问 ollama 服务端口。3.3 测试流程示例gpt‑4o‑miniPOST/api/sessionbody 指定model:gpt‑4o‑mini创建会话拿到 session_id调用 POST/api/message全量接口发送提问校验完整应答返回调用 POST/api/message/async流式接口校验 SSE 分片输出正常。4 ~ SQLite 数据库存储层关键要点4.1 数据表sessions表存储会话元数据session_id、model 名称、created_at、updated_at、message_countmessages表存储会话所有对话消息关联 session_id 外键。4.2 开发调试手段使用 sqlite3 命令行工具直接打开 db 文件select 查询表数据确认业务数据是否真正持久化区分「内存对象正常」与「数据库持久化异常」两类 BUG。5 ~ 整体排错方法论分层定位Apifox 层检查请求 URL、Path 参数、JSON 请求体、请求头 Content‑Type排除参数拼写错误、混入 Apifox 模板变量符号。HTTP 服务层看服务终端日志确认请求已经抵达后端区分网络不通、路由未匹配、参数解析失败。SDK / 业务逻辑层日志打印关键入参出参区分 HTTP 链路正常业务逻辑 BUG。数据库层命令行直连 sqlite 数据库确认数据是否真正写入。修改 SDK 底层代码后完整操作流程修改源码执行编译make install更新系统静态库与头文件重新编译链接 ChatServer 可执行程序重启 ChatServer 服务执行接口复测。重点静态库更新之后上层服务必须重新编译链接否则运行程序依旧加载旧版本 SDK 二进制。结尾uu们本文的内容到这里就全部结束了艾莉丝在这里再次感谢您的阅读艾莉丝努力练剑C/C Linux 底层探索者 | 一个正在努力练剑的技术博主【关注】跟随我一起深耕技术领域见证每一次成长。❤️【点赞】让优质内容被更多人看见让知识传递更有力量。⭐【收藏】把核心知识点存好在需要时随时查、随时用。【评论】分享你的经验或疑问评论区一起交流避坑不要忘记给博主“一键四连”哦“今日练剑达成”“技术之路难免有困惑但同行的人会让前进更有方向。”结语希望对学习Linux相关内容的uu有所帮助不要忘记给博主“一键四连”哦往期回顾【AI大模型接入SDK】ChatServer问题修复博主在这里放了一只小狗大家看完了摸摸小狗放松一下吧૮₍ ˶ ˊ ᴥ ˋ˶₎ა