ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

养老院服务推荐系统实战:Django+Vue+混合推荐算法全解析

养老院服务推荐系统实战:Django+Vue+混合推荐算法全解析 做养老院服务推荐系统这个选题很多人第一反应是“又一个课设”。但真正把这个题目做成一个能跑、能演示、能答辩的完整项目远不是搭个框架写几个页面那么简单。前后端分离、推荐逻辑设计、数据库建模、权限管理一整套链路下来工作量相当扎实。这篇文章我按照自己的实际开发习惯把整个系统的设计与实现完整拆解一遍从技术选型到数据库设计再到后端接口、前端页面、推荐算法落地最后附上踩坑记录希望能给正在做同类项目的人一点参考。1. 项目概述与整体架构设计1.1 这到底是个什么样的系统养老院服务推荐系统核心业务是两句话一是把养老院提供的各类服务护理、康复、餐饮、文娱活动等按老人的实际情况做个性化推荐二是让管理员能维护老人档案、服务项目、推荐规则这些基础数据。用户端面对的可能是前台接待人员或老人家属真正的用户画像并不复杂关键是业务逻辑要清晰。我从一开始就确定了前后端分离的架构Vue负责页面展示和交互后端提供JSON格式的API接口。后端技术栈我在Django和Flask之间选了Django原因是题目虽然同时提到了django和flask但考虑到项目中包含用户认证、ORM建模、Admin后台这种基础需求Django自带的那套东西能省很多事所以最终确定Django DRF为主体Flask的用法会在后面对比分析。1.2 整体技术栈清单根据题目要求和我实际做下来的经验推荐的技术组合为后端Python 3.10 Django 4.2REST API使用Django REST Framework前端Vue 3 Vue Router Pinia Element Plus构建工具用Vite数据库MySQL作为主数据库Redis用于缓存推荐结果开发工具PyCharm前端部分也可以用VS Code但整体项目我还是全程用PyCharm搞定这里单独说一句PyCharm的用法。很多人用PyCharm打开前端项目就不知道怎么配了其实只需在PyCharm里设置Node.js解释器配合Terminal面板跑npm命令完全可以用一个IDE同时管前端、后端两个项目。专业版还支持数据库工具直接可视化查看MySQL数据省去来回切换的麻烦对这个项目的开发效率提升非常明显。1.3 模块划分整个系统划分为五大模块老人信息管理、服务项目管理、健康档案管理、服务推荐中心、系统管理。其中服务推荐中心是核心它要读取老人的健康数据和行为数据调用推荐算法生成个性化服务列表再把结果返回给前端展示是整个系统技术含量最高的部分。2. 技术选型的底层逻辑2.1 Django还是Flask别急着站队标题里django和flask同时出现说明很多人在这两个框架之间摇摆过。我的观点很直接做这个项目Django是更稳的选择。原因有几点第一这类系统一定有用户登录和管理员权限区分。Django自带认证体系、Session管理、CSRF防护Django REST Framework的Token认证也能直接用。Flask想做同样的效果得自己用Flask-JWT-Extended或者重新设计登录逻辑代码量不是多一星半点。第二后台管理界面是很多人忽略的隐藏需求。Django自带Admin后台我把模型定义好之后一个后台就能直接增删改查老人、服务项目、推荐规则数据而且还能按字段筛选、搜索这是答辩演示时非常好用的功能。第三ORM和数据库迁移。Django的ORM自带迁移机制我用makemigrations和migrate两条命令就能同步表结构。Flask配合SQLAlchemy也要自己管理迁移虽然也能用Flask-Migrate但多一层配置就多一份踩坑概率。不过Flask也不是完全没用。如果你的团队喜欢极致简洁的微服务或接口或者流程明确只用两三个接口Flask非常灵活。我实际用Flask重写过推荐接口模块代码可以精简到几十行。但项目一旦涉及用户系统、权限、多类数据表Flask的散点式结构会让新手很快失控。所以最终结论是主框架用Django推荐算法模块写成一个独立的服务用Flask做内部接口调用这样既满足标题参考框架的覆盖也能让你体会两种框架各自的定位。2.2 为什么前端一定要用Vue这个项目的前端典型特征是多表单、多列表、多数据联动。用传统JQuery写法每个页面的DOM操作和数据刷新逻辑都会很啰嗦Vue用数据驱动视图只需要维护一个data对象或响应式store页面的增删改查会自动刷新渲染。选Vue 3而不是Vue 2是因为Element Plus组件库已经完全适配Vue 3表单验证、表格分页、日期选择这些常用组件开箱即用。对不熟悉CSS的人来说这套组件库能让你用最少的时间搭出还算专业的管理界面可以省去大量调样式的精力。Vue项目的目录结构建议用Vite脚手架来创建。Vite的启动速度和热更新体验比Webpack好得多特别是页面组件多起来之后保存代码大概1秒内就能在浏览器看到变化开发效率提升明显。Vue Router的作用是把老人档案、服务推荐、系统设置这些页面路由统一管理实现页面跳转和侧边栏联动。2.3 推荐算法怎么选才合理推荐系统听起来很高端但在这个项目里要落地算法复杂度必须和业务匹配。养老院服务推荐属于“冷启动”明显的场景一个新老人进来没有历史行为数据怎么办答案是基于内容的推荐。核心逻辑是利用老人的健康档案慢性病、护理等级、生活偏好和服务项目标签适合高血压人群、适合半自理老人、康复训练类等做匹配。这个思路简单有效且非常容易在答辩时说清楚。在数据量积累到一定程度后可以引入基于用户的协同过滤。比如A老人和B老人的健康档案相似B对某个服务评价高就把B的偏好推荐给A。协同过滤的相似度计算我用的是余弦相似度实现简单不需要额外安装沉重的框架库。两种算法组合成混合推荐能兼顾冷启动和个性化。3. 数据建模与推荐逻辑设计3.1 数据库表结构怎么设计数据库设计是我做这个项目花时间最多的地方之一。表结构直接影响接口写起来顺不顺手。我一共设计了以下核心表表名用途关键字段elderly老人基础档案name, gender, birthday, room_no, care_levelhealth_profile健康档案elderly_id, chronic_disease, allergy, mobility_scoreservice_item服务项目name, category, price, suitable_tags, descriptionpreference老人偏好elderly_id, interest_tags, preferred_timerating评分记录elderly_id, service_id, score, commentrecommendation推荐记录elderly_id, service_id, source, score, create_timeuser系统用户username, password, role重点说一下service_item里的suitable_tags字段。这个字段存的是逗号分隔的标签字符串比如“糖尿病友好,低强度,室内”,老人健康档案里的tags是“糖尿病,不喜欢剧烈运动”。匹配的时候把两组标签做交集运算交集的个数就是基础匹配度分数。用逗号分隔会牺牲部分查询性能但对这种体量的项目完全够用而且实现起来直观。3.2 推荐算法的实现思路我推荐算法的完整计算过程分成三步第一步读取老人信息。根据老人ID获取健康档案、偏好记录和曾经的评分数据。第二步生成候选集。从服务项目表中筛选出状态为“上架”的项目然后计算每个服务与老人的匹配度。第三步计算最终分数并排序。匹配度分数由三部分加权相加标签匹配分权重0.5老人tags与服务suitable_tags的交集数量护理等级适配分权重0.3服务要求的护理等级与老人的护理等级匹配度相似老人协同分权重0.2找到档案相似的其他老人看他们对这个服务的评价加总之后排序取前10条写入recommendation表前端渲染时直接查推荐记录表就完事。3.3 用余弦相似度做协同过滤协同过滤部分我用余弦相似度计算老人之间的相似性。假设每个老人对应一个标签向量向量里的每一维是某个健康标签或兴趣标签的权重。两个老人之间的相似度就变成两个向量的夹角余弦值数值越接近1表示越相似。这个计算在代码里实现并不复杂从数据库读出标签构造向量然后调用cosine_similarity即可。但要注意的是老人多了之后两两计算复杂度是O(n^2)数据量超过几百条后性能会明显下降。我的处理方式是加一层缓存把计算结果存到Redis每天凌晨定时重新计算一次白天接口直接读缓存响应速度能控制在200毫秒以内这个方案兼顾了算法效果和工程性能。4. 后端API开发实操4.1 Django项目初始化与环境搭建项目环境是整个开发流程的第一步。我用的开发环境是Python 3.10用虚拟环境隔离依赖避免污染系统Python也便于后续环境迁移。虚拟环境创建好之后激活环境并安装依赖包核心依赖包括django、djangorestframework、django-cors-headers、mysqlclient、redis、pandas、numpy。装完后用django-admin创建项目和App项目名我用serverApp按模块拆成elderly、service、recommend、system三个应用避免所有模型堆在一个文件里。settings.py里的配置有几点需要特别说明。首先是把rest_framework和corsheaders加入INSTALLED_APPS其次是数据库配置改成MySQL连接然后是REST_FRAMEWORK的认证配置我选择的是TokenAuthentication配合Django自带的auth模块做用户登录。跨域问题用django-cors-headers解决否则Vue前端开发模式访问后端接口会被浏览器拦下这是新手很容易踩的第一个坑。4.2 模型定义与序列化Django的模型定义其实就是建表过程使用ORM语法创建模型类。我把model填好后执行两条命令就能完成建表python manage.py makemigrations python manage.py migrate完成模型定义后需要为每个模型写序列化器它负责把Python对象转成JSON格式传给前端。序列化器代码是整个后端最简单也最容易忽略的部分很多人只定义字段名不写嵌套关系导致前端拿到手里的是扁平结构反而增加了前端处理成本。我建议用模型序列化器配合嵌套序列化这样接口返回的数据结构可以直接对应前端页面表格列前后端联调时能少走很多弯路。4.3 推荐引擎接口的实现推荐引擎是整个系统最核心的接口我路由设计为/recommend/elderly_id/getGET请求即可调用。接口内部的执行流程是先尝试从Redis读取该老人的缓存推荐命中就直接返回没有缓存就走算法计算流程计算完成写入数据库的推荐记录表并更新Redis。这个设计的好处是第一次请求慢一点后续请求都很快作为演示用的课设项目也能展示出一定的性能优化意识。因为题目中也提到了flask部署我在做算法模块时也抽出了一个独立服务用Flask单独暴露推荐算法接口这样的微服务拆分思路在答辩时很加分。下面我贴出简化后的核心代码片段供参考def hybrid_recommend(elderly_id, top_n10): elderly Elderly.objects.get(idelderly_id) profile HealthProfile.objects.get(elderlyelderly) # 获取老人标签列表 user_tags set(profile.tags.split(,)) # 获取全部在架服务 services ServiceItem.objects.filter(statuson) scores [] for service in services: service_tags set(service.suitable_tags.split(,)) tag_hit len(user_tags service_tags) if tag_hit 0: continue # 标签匹配得分加权0.5 tag_score tag_hit * 0.5 # 护理等级匹配度加权0.3 level_score 0.3 if service.required_level elderly.care_level else 0 # 相似老人协同得分加权0.2 sim_score get_similar_user_score(elderly_id, service.id) * 0.2 total tag_score level_score sim_score scores.append((service.id, total)) scores.sort(keylambda x: x[1], reverseTrue) return [sid for sid, _ in scores[:top_n]]代码逻辑非常直白就是遍历服务并打分排序。但注意这里我用到了get_similar_user_score这个函数它内部就是用余弦相似度计算相似老人群体再聚合他们对某服务的评分。4.4 Flask推荐模块的嵌入方案有同学可能纠结标题里“django flask”同时出现到底怎么处理。我的做法是单独建一个flask_recommend模块里面实现同样的算法逻辑用Flask提供独立的推荐接口Django通过requests模块发起HTTP调用。这样做的好处是让项目同时用上了两个框架且在答辩时可以展开说说微服务拆分的思路。我实际跑下来双服务架构在这个项目里反而让调试麻烦了一点因为Flask服务要单独启动一个进程端口也要单独分配。如果你是第一次做全栈项目建议还是先用Django一把梭把所有接口都写在Django里Flask部分在文档和代码结构上预留位置即可开发压力小很多。5. Vue前端界面与交互实现5.1 项目初始化和目录结构前端我用Vite脚手架从零初始化Vue项目用命令创建项目之后再安装element-plus、axios、vue-router、pinia这几个必须的依赖。目录结构按页面维度做模块化主要包含视图、路由、状态管理、工具封装几个部分这样的好处是后端同学上手看前端代码时能快速定位文件。路由设计上我设置了登录页、首页仪表盘、老人管理、服务管理、推荐中心、系统管理这几个主路径。其中推荐中心是核心页面访问路径是/recommend通过路由守卫控制未登录用户访问。5.2 登录认证和路由守卫用户登录功能很多项目会忽略细节我这次在登录流程上花了些心思登录表单提交到后端接口之后后端返回当前用户信息前端把它存到Pinia和本地存储中同时在axios请求拦截器里自动附带认证信息保证后续每个接口请求都能通过认证校验。路由守卫方面我在全局前置守卫里检查本地存储有没有用户标记没有就跳转登录页。这样做能有效防止没有登录的人直接通过修改URL访问推荐中心页面算是项目在基础安全性上的一个展示点。5.3 推荐展示页面的核心逻辑推荐页是我前端代码里最花时间的部分。页面顶部是老人选择器下拉列表展示全部老人档案选中某个老人后页面主体会展示几个模块重点推荐服务卡片区、依据说明区、历史推荐记录区。重点推荐服务区的卡片里每个服务卡片包含服务名称、分类、价格和推荐理由。推荐理由后端单独生成比如“该老人有糖尿病史推荐低糖餐饮服务”“该老人护理等级为半自理推荐康复理疗项目”。展示推荐理由非常直观答辩时老师看到这个会觉得你考虑得很周全。前端调用推荐接口时我是这么处理的// 选中的老人发生变化时触发 const loadRecommendation async (elderlyId) { loading.value true try { const res await getRecommendation(elderlyId) recommendList.value res.data.items isLoading.value false } catch (err) { ElMessage.error(推荐服务加载失败) } }axios实例的封装略微处理了错误提示和后端返回格式的统一包装这样每一页调接口的代码都能精简很多。5.4 老人管理和服务管理页面的实现推荐系统的数据维护端包括老人档案管理和服务项目管理两个模块。老人管理页面使用表格组件展示全部老人列表支持按姓名查询和按护理等级筛选。表单里直接做多级联动例如选择老人后自动带出健康档案中相关慢性病信息的填写入口。服务管理页面负责养老服务项目维护。每一个服务关联分类、价格、适合标签和人***。标签选择我用的是Element Plus的select multiple组件用户通过下拉选方式维护服务标签的增减这样保证标签数据统一规范不会出现“糖尿病”“糖尿病人”这种同义不同词的标签维护推荐准确率。6. 推荐算法落地与性能优化细节6.1 冷启动问题怎么处理冷启动问题永远是新用户进来之后没有行为数据、没有评分记录推荐系统没法动手。我这里的方案是“规则兜底”当老人没有任何评分和偏好标签时直接用护理等级做最简单的规则推荐比如半自理老人优先推送康复理疗和陪护服务全自理老人推文娱活动和营养餐。此逻辑可以保证每个老人都能拿到推荐结果不会因为数据稀疏导致接口返回空列表这个细节在最终展示时很容易被当成亮点。6.2 打分权重的调整实验加权公式里的分值配比我是反复调过的。一开始标签匹配分权重给到0.7结果发现所有推荐都被标签数量多的服务抢占了而那些真正适合老人护理状态的服务反而排不上来。后来把护理等级适配分的权重提上去效果明显变好。最后确定0.5/0.3/0.2的比例。这些调整过程、对比效果都可以写进论文的实验分析章节里建议你也保留测试记录答辩时拿出你对比的数据会非常加分。6.3 Redis缓存推荐的实现方式写Redis缓存我用的Python标准库操作方式。推荐结果生成的key设置为“recommend:{elderly_id}”value用JSON序列化之后存入过期时间设置为6小时。这样设置是因为老人的健康档案短期内不会变动标签匹配结果也相对稳定6小时新鲜度足够。同时缓存还能扛住单用户反复查看推荐的请求压力降低数据库的查询频次。6.4 性能瓶颈分析和优化项目体量小正常情况下几乎不可能遇到性能问题。但如果演示时有人导入了上千条服务数据接口响应就会明显变慢。我做的优化点很简单服务列表只查状态为上架的项目预先处理好标签缓存减少不必要的非空判断。如果用MySQL可以给service_item表的status字段加索引查询速度会有明显提升。在做项目总结时写一句“通过索引与缓存机制有效缓解了大数据量下的性能压力”此类的内容也会显得比较专业。7. 常见问题与避坑指南7.1 前后端联调跨域报错前端访问后端接口报跨域错是最高频的问题。解决方法是后端安装django-cors-headers在settings.py里配置中间件和允许的域名白名单。开发模式下直接把CORS_ALLOW_ALL_ORIGINS设为True即可。此外需要注意前端axios请求里带着token请求头同样需要在跨域配置里允许Authorization头。7.2 Django的CSRF验证一直过不去前后端分离项目里前端通过axios请求接口时会遇到Django的CSRF拦截。解决方法是DRF的认证方式设置为TokenAuthentication再把需要跨过CSRF的接口加为不受CSRF保护。具体操作用csrf_exempt装饰器或者把API接口统一放到禁止CSRF保护的url规则里。很多同学第一次做Django全栈项目时会卡死在这里流程大概是知道、代码也写了但一直403核心原因往往就是Cookie里的CSRF token没有正确写入建议直接改API接口的方式最省心。7.3 Python版本和依赖包版本不匹配我用Python 3.10Django用的4.2这两个版本兼容性良好。但如果你是Python 3.8以下的老环境Django 4.2就装不上数据库驱动也可能编译失败。建议新建环境之前先确认Python版本用conda或者pyenv快速建一个3.10的环境避免在旧版本环境里浪费时间。7.4 Vue播放视频或展示PDF的问题推荐系统的服务里可能包含文娱活动视频或宣传文档前端展示时需要处理视频播放和PDF展示的场景。标题相关热词里出现了“vue播放m3u8免安装”和“vue image能显示pdf吗”这类问题我在项目中也踩到了类似的需求。视频这块如果服务宣传视频是m3u8格式的流媒体地址Vue里通常用video.js配合hls.js来播放不需要额外安装重型播放器。但要注意m3u8必须走HTTP协议访问本地直接file://协议是播放不了的联调时必须把视频文件丢到Nginx或后端静态目录通过HTTP地址访问。PDF展示其实更简单普通PDF用iframe嵌入就能看不需要额外引入js库。如果是图片版PDF或者需要更精细的控制可以引入pdfjs-dist来处理。我实际做的时候考虑到管理系统里的服务简介多数是图片文字没有强行引入第三方PDF插件避免增加前端依赖的体积和复杂度。7.5 PyCharm运行多个项目模块后端Django项目跑在一个端口前端Vue dev server跑在另一个端口Flask算法模块跑在第三个端口三个服务同时启动很容易乱。我最终的做法是在PyCharm里配置了三套运行配置分别对应django runserver、npm run dev、flask run前端页面里通过环境变量区分开发环境和生产环境这样切换项目时一键就能启动对应服务不用每次在多个终端里来回切目录。8. 部署上线的思路与扩展方向8.1 部署方案选择课程设计通常做到局域网可访问就够了。最简单的部署方式是在一台Windows机器上同时启动Django服务、Flask服务和Vue构建后的静态文件服务。Vue项目跑npm run build后生成dist目录Django里配一条静态文件路径指向dist目录这样只启动Django和Flask两个服务前端页面和API都走同一个域名完美避开跨域。8.2 生产环境的Nginx配置参考如果想把项目部署到Linux服务器Nginx反向代理是标准解法。我的配置思路是Nginx监听80端口前端静态文件直接由Nginx托管后端的API接口通过反向代理转发到Django那台服务上。一个简洁的Nginx配置示例server { listen 80; server_name your_domain_or_ip; # 前端静态文件 root /var/www/vue_dist; location / { try_files $uri $uri/ /index.html; } # 后端API反向代理 location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }配好之后一个站点同时管前端和API推荐性能有保障整体技术方案也完整。8.3 从课设到真实项目的扩展方向这个系统做完之后扩展空间其实蛮大。如果未来要做多养老院版本可以引入机构管理这一层每套数据按机构隔离如果想把推荐做得更精细可以接入老人穿戴设备的心率、睡眠数据构建实时健康画像推荐逻辑就从静态标签升级为动态规则引擎如果要做公众服务还可以给家属端做一个微信小程序让家属远程查看老人收到的服务推荐和护理计划。我个人做完这整个项目最深的体会是课设级别的项目最重要的不是算法多高级、技术多花哨而是逻辑通顺、数据完整、代码规范、能自圆其说。干脆利落地把一个业务闭环做完比堆砌一堆没跑通的技术名词要有效得多。哪怕选题再普通只要你把推荐链路、前后端联调、缓存优化这些环节都考虑到位答辩时讲出来就是一套完整的产品思维加工程能力的体现。最后分享一个实操小技巧每天开发前先跑一遍核心接口的冒烟测试比如推荐接口给同一老人返回的结果应该相对稳定。每次改动算法参数后手动调一次接口确认输出没有异常再去开发新功能。保持这种习惯能让你在演示前远离“昨天还能跑、今天突然不行了”的尴尬也让你对系统里每个模块的实际运行状态心里有数。
返回列表