news 2026/6/1 9:47:58

山东大学软件学院项目实训-创新实训-计科智伴(五)——个人博客(从接口对接到边界问题修复的完整记录)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
山东大学软件学院项目实训-创新实训-计科智伴(五)——个人博客(从接口对接到边界问题修复的完整记录)

前后端联调实战:从接口对接到边界问题修复的完整记录


项目背景

本项目是一个基于uni-app的智能学习平台前端,后端采用Spring Boot框架,使用Session + UserHolder的认证方式。前端需要对接后端提供的 RESTful API 和 SSE 流式接口,实现用户认证、智能问答、学习计划、错题本等核心功能。


一、基础设施搭建:请求层封装

1.1 响应格式适配

后端统一使用Result格式返回数据:

{"ok":1,"msg":null,"data":{}}

其中ok: 1表示成功,ok: 0表示失败。前端原有的响应格式为{ code: 200, message, data },需要在响应拦截器中进行映射:

// utils/request.jssuccess:(res)=>{if(res.statusCode===200){constdata=res.data// 适配后端 Result 格式if(data.ok!==undefined){if(data.ok===1){resolve({code:200,message:'success',data:data.data})}else{uni.showToast({title:data.msg||'请求失败',icon:'none'})reject(newError(data.msg))}return}// 兼容前端原有格式if(data.code===200){resolve(data)}}}

技术要点:

  • 响应拦截器同时兼容后端Result格式和前端原有格式,保证平滑过渡
  • 业务错误统一通过uni.showToast提示用户
  • 401 状态码自动跳转登录页并清除本地 Token

1.2 Session 认证支持

后端使用 Session 认证而非 JWT,前端需要配置withCredentials: true确保请求自动携带 Cookie:

uni.request({url:BASE_URL+options.url,method:options.method||'GET',data:options.data||{},header:{'Content-Type':'application/json','Authorization':token?`Bearer${token}`:'','X-Platform':platform},withCredentials:true,// 关键配置// ...})

1.3 SSE 流式请求封装

智能问答模块需要接收后端 SSE(Server-Sent Events)流式响应。使用uni.requestenableChunked: true实现:

exportfunctionsseRequest(url,data,callbacks){letaborted=falseuni.request({url:BASE_URL+url,method:'POST',data:data,enableChunked:true,// 启用分块传输header:{'Content-Type':'application/json','Accept':'text/event-stream'// 声明接受 SSE},success:(res)=>{if(aborted)returnconsttext=res.dataconstlines=text.split('\n')for(constlineoflines){if(line.startsWith('data: ')){constcontent=line.substring(6)if(content==='[DONE]'){callbacks.onComplete?.()return}callbacks.onMessage?.(content)}}}})return{abort:()=>{aborted=true}}}

技术要点:

  • enableChunked: true启用 HTTP 分块传输,支持流式接收
  • SSE 格式解析:按行分割,提取data:开头的消息
  • [DONE]标记表示流结束
  • 返回abort方法用于取消请求(用户点击"停止生成"按钮时调用)

二、用户认证模块联调

2.1 登录接口对接

后端登录接口为POST /users/login,请求体为{ phone, password }。前端需要处理:

  1. 参数映射:前端输入框显示"手机号",实际发送使用phone字段
  2. 手机号验证:正则/^1[3-9]\d{9}$/验证格式
  3. Session 存储:后端创建 Session,前端通过 Cookie 自动维护
// api/index.jsexportfunctionlogin(data){constparams={phone:data.username,// 前端 username 字段映射为 phonepassword:data.password}returnpost('/users/login',params)}

2.2 注册功能实现

注册页面包含用户名、手机号、密码、年级、专业等字段。关键处理:

  1. 年级映射:前端中文选项映射为数字(大一→1,大二→2,…)
  2. 实时验证:输入框失焦时触发验证,显示红色错误提示
  3. 注册成功后跳转:1.5 秒后自动返回登录页

2.3 记住密码功能

使用本地存储实现记住密码,密码采用 Base64 + 字符位移的轻量级加密:

functionencryptPassword(password){// 字符位移 + Base64 编码constshifted=password.split('').map(c=>String.fromCharCode(c.charCodeAt(0)+3)).join('')returnbtoa(shifted)}functiondecryptPassword(encrypted){constshifted=atob(encrypted)returnshifted.split('').map(c=>String.fromCharCode(c.charCodeAt(0)-3)).join('')}

三、智能问答模块联调

3.1 文本流式问答

对接后端POST /api/ai/chat接口,使用 SSE 接收流式响应。在 Pinia store 中实现:

// store/chat.jsasyncsendChatMessage(message,subject=''){if(!this.conversationId){this.conversationId='sess_'+Date.now()+'_'+Math.random().toString(36).substr(2,9)}constrequestData={prompt:message,sessionId:this.conversationId,subject:subject,useRag:true}const{abort}=apiSendChat(requestData,{onMessage:(content)=>{this.updateStreamingContent(content)// 逐步更新流式内容},onComplete:()=>{if(this.streamingContent){this.addAssistantMessage(this.streamingContent)}this.stopStreaming()},onError:(error)=>{this.addAssistantMessage('抱歉,我暂时无法回答这个问题')this.stopStreaming()}})this.currentAbort=abort}

交互变化:

  • AI 生成期间输入框禁用,显示"停止生成"按钮
  • 点击停止按钮调用abort()中断 SSE 请求

3.2 多模态问答(看图答题)

对接后端POST /api/ai/chat/multimodal接口,支持图片上传:

asyncsendMultimodalMessage(imagePath,prompt=''){constresponse=awaitapiChatMultimodal(imagePath,prompt)constdata=response.data// 展示识别结果this.addUserMessage(prompt||'图片内容:'+data.recognizedText,data.imageUrl)// 展示 AI 回答this.addAssistantMessage(data.answer)// 置信度低时提示if(data.confidence<0.7){this.addAssistantMessage('⚠️ 图片识别置信度较低...')}}

3.3 对话历史加载

对接后端GET /ai/history/{type}/{chatId}接口,将后端消息格式转换为前端格式:

asyncloadChatHistory(conversationId=''){constres=awaitapiGetHistory(targetId,'chat')if(res.ok===1&&res.data&&Array.isArray(res.data)){this.messages=res.data.map(msg=>({id:msg.id||Date.now().toString(),role:msg.role||(msg.userId?'user':'assistant'),content:msg.content||msg.message||'',time:msg.timestamp?this.formatTimestamp(msg.timestamp):this.getCurrentTime(),image:msg.imageUrl||null}))}}

四、三阶段接口接入计划

4.1 第一阶段:高优先级接口(5 个)

接口说明关键处理
GET /users/profile获取个人信息字段映射:userIdidusernamename
GET /users/learning-profile获取学情画像传入userId参数
POST /api/mistakes添加错题请求体:{ qId, userAnswer }
POST /study-plans创建学习计划AI 自动生成,创建后自动获取任务列表
GET /api/onboarding/questions获取引导问题支持按步骤动态加载,失败时使用模拟数据降级

4.2 第二阶段:中优先级接口(6 个)

接口说明关键处理
POST /users/avatar上传头像multipart/form-data,字段名file
POST /users/profile创建用户画像提交问卷时调用,存储到本地
PUT /users/profile/{userId}更新用户画像路径参数 + 请求体
POST /api/exercises/next获取下一批练习难度参数为字符串medium
GET /api/exercises/targeted获取专项练习GET 方式,参数:kpId,difficulty,count
GET /ai/history/{type}获取会话列表返回会话 ID 数组

4.3 第三阶段:低优先级接口(4 个)

接口说明关键处理
GET /users/profile/{userId}获取用户画像路径参数userId
POST /api/user/materials上传学习资料multipart/form-data,额外参数userId,materialType
POST /fileUpload文件上传字段名imgFile(非默认file
GET /fileDownload/{filename}文件下载返回完整 URL,直接用于下载

五、边界问题排查与修复

在接口联调过程中,发现了 7 个边界问题,这些问题在代码审查阶段被逐一修复。

5.1 难度参数类型错误(3 处)

问题描述:前端将难度字符串easy/medium/hard映射为整数1/2/3,但后端文档明确要求difficulty字段为字符串类型

影响接口:

  • POST /api/exercises/next
  • POST /api/exercises/targeted
  • GET /api/exercises/targeted

后端文档示例:

{"userId":1,"knowledgePoint":"二叉树","difficulty":"medium",// 字符串类型!"count":5}

修复方案:移除整数映射,直接使用字符串。

// 修复前difficulty:data.difficulty?{easy:1,medium:2,hard:3}[data.difficulty]:undefined// 修复后difficulty:data.difficulty||'medium'

5.2 响应格式检查错误(3 处)

问题描述:前端使用res.code === 200检查响应,但request.js响应拦截器已将后端{ ok: 1, data }映射为{ code: 200, data },在 store 中应直接使用res.data访问业务数据。

影响文件:

  • store/chat.js中的loadChatHistoryloadChatSessionssendMultimodalMessage

修复方案:改为res.ok === 1检查(或直接依赖拦截器的错误处理)。

// 修复前if(res.code===200&&res.data&&Array.isArray(res.data)){// 修复后if(res.ok===1&&res.data&&Array.isArray(res.data)){

5.3 数据访问层级错误(1 处)

问题描述:submitSingleAnswerWithFeedback方法直接使用result.correct,但request.js返回的是{ code: 200, data: { correct, ... } },应该访问res.data.correct

修复方案:添加const result = res.data提取实际业务数据。

// 修复前constresult=awaitsubmitSingleAnswer(exerciseId,answer,userId)this.exerciseResults[this.currentIndex]={correct:result.correct===true||result.correct===1,// ...}// 修复后constres=awaitsubmitSingleAnswer(exerciseId,answer,userId)constresult=res.data// 提取业务数据this.exerciseResults[this.currentIndex]={correct:result.correct===true||result.correct===1,// ...}

六、前端待补充接口需求文档

在联调过程中,整理了28 个前端需要但后端尚未实现的接口,生成《前端待补充接口需求文档》提供给后端团队。

6.1 高优先级接口(12 个)

模块接口说明
任务PUT /tasks/{taskId}/status更新任务状态(支持 0/1/2)
练习GET /api/exercises/subjects获取科目列表
练习GET /api/exercises/subjects/{subjectId}/topics获取知识点列表
练习POST /api/exercises/{sessionId}/submit批量提交答案(考试模式)
引导GET /api/courses获取课程列表
引导POST /api/profile/generateAI 生成学情画像
错题PUT /api/mistakes/{mistakeId}/status标记错题状态
报告GET /api/report获取学情报告
用户GET /api/user/info获取用户信息
用户PUT /api/user/info更新用户信息
通知GET /api/notifications获取通知列表
通知GET /api/notifications/unread-count获取未读数量

6.2 中优先级接口(10 个)

包括获取推荐问题、清空对话历史、获取练习记录、上传错题(拍照识别)、获取错题统计、打卡签到、获取学习统计、获取能力成长趋势、通用文件上传、全部标记已读。

6.3 低优先级接口(6 个)

包括取消 AI 生成、通知设置(获取/保存)、获取成就列表、获取等级信息、导入用户画像。


七、技术总结

7.1 接口对接最佳实践

  1. 严格对照后端文档:每个接口的路径、参数类型、响应格式都要与文档一致
  2. 统一响应拦截器:在request.js中统一处理响应格式映射,store 层只需关注业务逻辑
  3. 降级方案:关键功能在接口失败时提供本地模拟数据,保证用户体验
  4. 错误处理:所有接口调用都添加 try-catch,错误信息通过uni.showToast提示用户

7.2 状态管理设计

使用 Pinia 进行状态管理,按模块拆分 store:

Store职责
user.js用户信息、登录状态、头像上传
chat.jsAI 对话消息、流式响应、会话历史
plan.js学习计划、任务列表、统计计算
exercise.js练习会话、答题记录、计时器
notification.js通知列表、未读数量、设置

7.3 经验教训

  1. 不要假设参数类型:后端文档说difficulty是字符串,就不要自作聪明映射为整数
  2. 注意响应层级request.js返回的是{ code: 200, data },store 中访问业务数据需要res.data
  3. 统一响应检查:使用res.ok === 1而非res.code === 200,保持与后端Result格式一致
  4. 文件上传字段名:不同接口可能使用不同的字段名(fileimgFileimage),需严格对照文档

八、项目技术栈

技术版本用途
uni-app最新跨端开发框架
Vue 33.x前端框架
Pinia2.x状态管理
Spring Boot2.x后端框架
SSE-流式响应
Session-用户认证

九、后续计划

  1. 等待后端补充高优先级接口,完成剩余功能联调
  2. 优化 SSE 在 App 端的兼容性测试
  3. 完善错题拍照上传功能
  4. 实现学情报告页面的数据可视化
  5. 生产环境 BASE_URL 配置和跨域处理

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/1 9:41:09

Gemini 2.5 Pro登顶Web开发基准测试:AI编程如何重塑开发者工作流

1. 项目概述&#xff1a;当AI模型开始“写”代码最近&#xff0c;AI圈子里有个消息传得挺广&#xff1a;Google的Gemini 2.5 Pro模型在多个针对Web开发的基准测试中&#xff0c;声称拿下了第一的位置。这个消息一出&#xff0c;无论是前端、后端还是全栈开发者&#xff0c;心里…

作者头像 李华
网站建设 2026/6/1 9:28:17

MATLAB绘图进阶:除了plot,你更该掌握这5个美化坐标轴的实用函数(xticks, set gca详解)

MATLAB坐标轴定制完全指南&#xff1a;从基础调整到高级美学控制当你第一次用MATLAB的plot函数画出那条完美的正弦曲线时&#xff0c;那种成就感无与伦比。但当你准备把这张图放进论文或演示文稿时&#xff0c;突然发现——默认的坐标轴看起来如此简陋。字体太小、刻度不合适、…

作者头像 李华
网站建设 2026/6/1 9:26:17

告别Docker Hub抽风:手把手教你用SSH给群晖NAS安装ddns-go动态域名服务

家庭网络自由之路&#xff1a;SSHddns-go实现群晖NAS动态域名解析 家里的NAS设备想要随时随地访问&#xff0c;却被动态IP地址困扰&#xff1f;Docker图形界面又时不时抽风&#xff1f;别担心&#xff0c;今天我们就来彻底解决这个问题。作为一名长期折腾家庭网络的老玩家&…

作者头像 李华