news 2026/10/30 9:18:10

2026山东大学项目实训项目博客(八)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
2026山东大学项目实训项目博客(八)

宠声健康 AI:一个完整的前后端分离项目实战总结

前言

最近完成了「宠声健康 AI」项目的开发与优化,这是一个功能完整的宠物健康管理应用。整个项目采用了前后端分离的架构,前端使用 Vue 3 + Pinia + Vite,后端使用 Flask + SQLAlchemy + JWT,实现了从用户认证到宠物管理、文件上传等核心功能。

在这篇文章中,我将对整个项目进行一次系统的总结,分享架构设计、核心功能实现以及踩过的坑。


一、项目技术架构

技术栈一览

层级技术选择说明
前端Vue 3 + Pinia + Vite现代化前端技术栈,Composition API 写法
后端Flask + SQLAlchemy + JWT轻量级 Python Web 框架
认证JWT + localStorage无状态认证方案
存储SQLite轻量级关系型数据库

目录结构

travel_agent/ ├── backend/ # 后端服务 │ ├── routes/ # 路由模块 │ │ ├── auth.py # 认证相关 │ │ ├── pet.py # 宠物管理 │ │ └── ... │ ├── models.py # 数据模型 │ ├── utils/ # 工具函数 │ ├── static/ # 静态文件 │ └── app.py ├── frontend/ # 前端应用 │ ├── src/ │ │ ├── api/ # API 接口定义 │ │ ├── components/ # 公共组件 │ │ ├── stores/ # Pinia 状态管理 │ │ ├── utils/ # 工具函数 │ │ ├── views/ # 页面组件 │ │ └── main.js └── docs/ # 文档目录

二、核心功能实现

1. 统一响应格式设计

前后端分离架构中,统一的响应格式是协作的基础。我在后端封装了api_response函数,实现了以下功能:

defsnake_to_camel(snake_str):components=snake_str.split('_')returncomponents[0]+''.join(x.title()forxincomponents[1:])defapi_response(data=None,message="操作成功",code=200,status="success"):converted_data=convert_dict_keys(data)ifdataisnotNoneelseNonereturnjsonify({"status":status,"code":code,"message":message,"data":converted_data})

关键特性:

  • 自动将 Python 下划线命名转换为前端习惯的驼峰命名
  • 统一的响应结构:status、code、message、data
  • 支持递归转换嵌套数据结构

2. 宠物头像上传功能

这是这次优化的核心功能之一,从最初仅支持 URL 改为支持本地文件上传。

前端实现

1. 模板结构

<divclass="avatar-upload"><divclass="avatar-preview"@click="triggerFileInput"><imgv-if="previewAvatarUrl":src="previewAvatarUrl"alt="头像预览"/><divv-elseclass="avatar-placeholder"><span>点击上传</span></div></div><inputref="fileInputRef"type="file"accept="image/*"@change="handleFileChange"style="display:none"/></div>

2. 文件处理与预览

constfileInputRef=ref(null)constselectedFile=ref(null)constpreviewAvatarUrl=ref('')consthandleFileChange=(event)=>{constfile=event.target.files[0]if(!file)returnselectedFile.value=fileconstreader=newFileReader()reader.onload=(e)=>previewAvatarUrl.value=e.target.result reader.readAsDataURL(file)}

3. 智能上传逻辑

constonSave=async()=>{if(selectedFile.value){constformData=newFormData()formData.append('petName',form.petName)formData.append('avatar',selectedFile.value)awaitaddPet(formData)}else{awaitaddPet({petName:form.petName,avatarUrl:form.avatarUrl})}closeModal()awaitfetchPets()}
后端实现

1. 路由与配置

pet_bp=Blueprint('pet',__name__)UPLOAD_FOLDER=os.path.join(os.path.dirname(os.path.dirname(__file__)),'static','avatars')ALLOWED_EXTENSIONS={'png','jpg','jpeg','gif'}os.makedirs(UPLOAD_FOLDER,exist_ok=True)defallowed_file(filename):return'.'infilenameandfilename.rsplit('.',1)[1].lower()inALLOWED_EXTENSIONS

2. 添加宠物接口

@pet_bp.route('/',methods=['POST'])@jwt_required()defadd_pet():user_id=int(get_jwt_identity())user=User.query.filter_by(user_id=user_id).first()data=request.get_json(silent=True)avatar_file=request.files.get('avatar')# 同时支持 FormData 和 JSON 两种格式pet_name=data.get('pet_name')ordata.get('petName')ifdataelse\ request.form.get('pet_name')orrequest.form.get('petName')# 文件处理final_avatar_url=avatar_urlifavatar_fileandallowed_file(avatar_file.filename):original_filename=secure_filename(avatar_file.filename)unique_filename=f"{uuid.uuid4().hex}_{original_filename}"file_path=os.path.join(UPLOAD_FOLDER,unique_filename)avatar_file.save(file_path)final_avatar_url=unique_filename# 保存宠物信息new_pet=Pet(user_id=user.user_id,pet_name=pet_name,avatar_url=final_avatar_url)db.session.add(new_pet)db.session.commit()returnapi_response(data={"petId":new_pet.pet_id,"avatarUrl":return_avatar_url},code=201),201
request.js 适配

关键!FormData 不能手动设置 Content-Type,要让浏览器自动处理:

request.interceptors.request.use(config=>{consttoken=localStorage.getItem('token')if(token){config.headers.Authorization=`Bearer${token}`}if(!(config.datainstanceofFormData)){config.headers['Content-Type']='application/json'}returnconfig})

3. 401 认证错误处理

Token 过期时,需要给用户友好的提示并自动跳转:

request.interceptors.response.use(response=>{constres=response.dataif(res.code===200||res.status==='success'){returnres.data!==undefined?res.data:res}toast.error(res.message||'请求失败')returnPromise.reject(newError(res.message||'请求失败'))},error=>{if(error.response&&error.response.status===401){localStorage.removeItem('token')localStorage.removeItem('user')if(window.location.pathname!=='/login'){toast.error('登录已过期,请重新登录')setTimeout(()=>window.location.href='/login',1000)}}else{toast.error(error.response?.data?.message||error.message)}returnPromise.reject(error)})

4. Pinia 状态管理

采用 Pinia 的 setup 写法,模块化管理状态:

exportconstuseUserStore=defineStore('user',()=>{constuser=ref(null)constisLoggedIn=ref(false)constrestoreLogin=()=>{consttoken=localStorage.getItem('token')conststoredUser=localStorage.getItem('user')if(token&&storedUser){user.value=JSON.parse(storedUser)isLoggedIn.value=true}}constfetchUserInfo=async()=>{constuserData=awaitgetUserInfo()user.value=userData localStorage.setItem('user',JSON.stringify(userData))}return{user,isLoggedIn,restoreLogin,fetchUserInfo}})

三、踩过的坑与经验总结

1. z-index 层级问题

问题:用户下拉菜单被页面其他元素遮挡。

解决:

  • 使用2147483647(浏览器最大值)作为关键元素的 z-index
  • 层级关系:Modal > Navbar > Content
  • 确保有定位属性(relative/absolute/fixed)z-index 才生效
.navbar{position:relative;z-index:2147483647;}

2. FormData 处理问题

问题:发送 FormData 时强制设置Content-Type: multipart/form-data,导致后端解析失败。

解决:FormData 不要手动设置 Content-Type,让浏览器自动处理(会自动加上正确的 boundary)。

3. 文件上传安全

踩坑:直接使用用户上传的文件名,可能存在路径遍历攻击风险。

解决:

  • 使用secure_filename处理文件名
  • 配合 UUID 防止文件名冲突

4. 命名规范统一

经验:

  • 后端使用 Python 下划线命名(pet_name)
  • 前端使用 JavaScript 驼峰命名(petName)
  • 通过统一响应格式函数自动转换,避免前后端不一致问题

四、项目收尾与成果

经过这轮优化,项目已经具备了:

完善的用户认证体系:登录、登出、Token 管理
便捷的宠物管理功能:增删改查、头像上传
优秀的文件上传体验:本地文件 + URL 两种方式
流畅的前后端交互:统一响应、错误处理完善
清晰的代码架构:模块化设计、易于维护

后续优化方向

虽然核心功能已经完成,但仍有优化空间:

  1. 图片优化:上传前压缩、裁剪、生成缩略图
  2. 性能优化:CDN 加速、请求缓存、异步处理
  3. 功能扩展:更多宠物健康相关功能
  4. 部署上线:生产环境配置、运维自动化
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 3:24:26

macOS本地AI编程工作流配置:Ollama+VS Code+权限适配全指南

1. 项目概述&#xff1a;这不是“装个插件”&#xff0c;而是重构 macOS 上的 AI 编程工作流Claude Code Mac 配置指南——这个标题背后&#xff0c;藏着一个被严重低估的现实&#xff1a;它根本不是在 macOS 上安装一个叫“Claude Code”的独立应用&#xff0c;而是围绕本地大…

作者头像 李华
网站建设 2026/10/29 15:43:56

Netgear路由器变砖救星:3步掌握nmrpflash终极修复指南

Netgear路由器变砖救星&#xff1a;3步掌握nmrpflash终极修复指南 【免费下载链接】nmrpflash Netgear Unbrick Utility 项目地址: https://gitcode.com/gh_mirrors/nmr/nmrpflash 当你的Netgear路由器因为固件升级失败、意外断电或误操作而变成"砖头"时&…

作者头像 李华
网站建设 2026/10/19 17:56:45

商河县管道漏水检测本地专家团队指南

商河县管道漏水检测本地专家团队指南在商河县的生活和工作中&#xff0c;地下供水、供暖以及消防管道的安全性不容忽视&#xff0c;但管道暗藏地下&#xff0c;一旦发生漏水&#xff0c;往往难以察觉&#xff0c;导致较大损失。山东豪脉工程勘察有限公司&#xff0c;作为专业的…

作者头像 李华
网站建设 2026/10/24 12:46:11

GPT-5.5:首个具备任务闭环能力的数字协作者

1. 这不是又一个“更强的聊天机器人”&#xff1a;GPT-5.5的本质是一台可调度的数字协作者 你有没有过这种体验&#xff1a;给AI发一条指令&#xff0c;它回你三千字长篇大论&#xff0c;逻辑严密、文采斐然&#xff0c;但就是没干成你真正想让它干的那件事&#xff1f;比如你写…

作者头像 李华
网站建设 2026/10/20 2:32:17

DeepSeek v4百万上下文技术原理与工程落地指南

1. 这不是参数堆砌&#xff0c;而是工程能力的临界突破“DeepSeek v4全面支持百万上下文 token”——这句话在技术圈刷屏那天&#xff0c;我正调试一个需要跨12份PDF、7个Excel表格和3段会议录音做联合分析的客户项目。当时用的是v3模型&#xff0c;最大上下文撑死128K&#xf…

作者头像 李华