尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Django美容院优质客户筛选系统:基于RFM模型的毕设设计与实现

Django美容院优质客户筛选系统:基于RFM模型的毕设设计与实现 1. 先搞清楚美容院优质客户筛选系统到底在筛什么想用Django做一套美容院相关毕设项目的人大概率会搜到类似的标题基于Python的美容院优质客户筛选系统。这个方向确实常青我也在最近完整梳理并跑通过一套这样的系统聊聊从需求分析到落地实现的东西。它表面上是个管理系统实际核心就一件事把客户按“值不值得花精力维护”排个序让美容师、店长知道该把回访、优惠、赠品投给谁。这个项目非常适合正在选毕设题目、又不想选电商或进销存那种烂大街题材的学生也适合想快速体验Django全流程开发的刚入门朋友。它能让一个人同时练习用户认证、多表查询、业务算法、页面交互而且业务故事很好讲论文答辩不尴尬。完整交付物通常包含程序源码、设计文档、代码讲解和定制化修改但说实话真正决定项目水平的是筛选逻辑做得是否合理而不是页面好不好看。1.1 美容院需要的不是“最大客户”而是“最该维护的客户”美容院和普通零售最大的不同在于客户的消费是高频、强关系、强预存性质的。一个客户可能半年才来一次但一次充了两万另一个客户每周都来但每次只做基础护理。如果只按消费总额排序后者容易被忽略但后者恰恰是门店人气和员工提成的来源。优质客户筛选就不能只看单维度数据必须把“最近什么时候来”“多长时间来一次”“一共花了多少钱”“卡里还剩多少钱”这些信息合并起来看。所以这个系统的业务目标不是简单的客户列表而是给每个客户打一个“值得维护”的综合分再按分数划分层级。低分客户可以少花精力甚至定期清理高分客户则自动进入回访清单、生日提醒、优惠券发放名单。这也是这套系统真正区别于普通增删改查管理系统的核心卖点。1.2 优质客户的评价维度其实可以在论文里拆成四个词我梳理这套项目时把筛选规则通俗化为四个词近度、频度、额度、稳定度。近度指最近一次到店时间频度指固定周期内到店次数额度指标消费总金额或单次均消稳定度可以结合连续到店月数、预约失约率、卡金使用率来看。真正写代码的时候没有必要把全部维度都做进去选两到三个核心维度做成可配置权重反而更好展示老师追问起来也更容易应对。下面这张表是我在实际项目里常用的维度参考你可以按自己的需求增删字段但它能帮你快速理解系统背后要处理的数据结构。维度数据来源简单定义示例近度 R到店记录距离今天最近一次消费天数3天前、45天前频度 F消费记录近90天到店次数2次、12次额度 M消费记录/充值记录累计消费总额或卡内余额5280元、12000元稳定度 S预约/到店记录连续到店月数连续3个月未断这四项数据在数据库里不会全放在一张表里而是分散在客户档案、消费记录、预约记录、储值卡记录等多张表中。筛选算法要做的就是把分散的数据聚合起来算成一个分数。理解了这一点你就知道为什么毕设里Django的ORM多表查询和聚合函数会被重点考察。2. 为什么把宝押在Django Python上很多同学选技术栈时会纠结用Java做后台不是更主流吗用原生PHP不是更快吗我的看法是做毕设要选的是“你能在两个月内搞定、且能讲清楚原理”的框架而不是“看起来最厉害”的框架。Python本身语法比Java简洁Django自带Admin后台、ORM、认证体系和模板系统对一个人完成全栈开发特别友好。2.1 Django适合这种中小型业务系统的三个硬理由第一个理由Django自带后台。美容院系统需要维护员工、客户、项目、套餐等基础数据全套手写页面非常耗时间而Django的Admin后台只需注册模型就能用初期可以拿来当数据管理入口后期再逐步替换成自定义页面。第二个理由ORM写起来像查字典。你用Python类描述表结构用QuerySet做查询不用手写复杂SQL就能完成客户筛选。比如在代码里筛选“近30天未到店且累计消费超过1000元的客户”只需要调用filter方法不需要自己拼JOIN。第三个理由模板系统能快速出页面。Django的模板语言可以在HTML里直接渲染变量和循环列表配合Bootstrap这类前端框架做出一个能看的界面并不困难。这套组合的最大收益是“开发速度快”因为你不需要维护前后端分离架构不用写一堆接口文档可以在一个项目里把表单提交、数据保存、页面刷新整个闭环跑通。对毕业设计这种周期短、评分看重完成度的场景这是非常务实的方案。2.2 关于“程序文档代码讲解一条龙定制”的一点看法现在网上很多毕设项目都强调“程序文档代码讲解一条龙定制”本质上是为了降低学生上手门槛。我不反对买现成源码但强烈建议拿到项目后立即做两件事第一件事把项目在本地完整跑起来把数据库清空重新迁移确认自己真的知道启动流程第二件事读懂核心筛选算法把关键变量、权重、判断条件在代码里找出来。答辩时老师最爱问的恰恰是“这个评分是怎么算的”和“如果权重改变会怎么样”这比问“Django请求生命周期是什么”更能检验你是否真的做过。另外“一条龙定制”通常意味着拿到的源码会带好几套不同风格的页面或功能你不需要全部消化但要学会删除冗余功能。很多模板代码里会有无用的App、重复的静态文件、测试页面保留与筛选业务相关的部分删掉其他花哨功能不仅项目显得干净你读代码的压力也会小很多。2.3 环境准备版本匹配是第一个坑Django项目最常见的启动失败原因不是代码有问题而是Python和Django版本不匹配。我建议新手直接选Python 3.10或3.11搭配Django 4.2。Django 4.2是一个LTS版本支持时间长网上资料多主流云服务器的Python版本也能兼容。如果你手里的老教程用的是Django 2.x那部分语法在4.x里可能已经变了比如很多url配置方式、admin装饰器写法都要调整。本地开发环境我推荐用虚拟环境避免把依赖装进系统Python。用下面三条命令就能完成基础环境搭建python -m venv venv source venv/bin/activate pip install django4.2Windows环境下第二条命令要改成venv\Scripts\activate。装完之后可以用python -m django --version检查版本号。如果你拿到的源码里还有requirements.txt可以用pip install -r requirements.txt一次性安装依赖。遇到pip安装慢的问题可以临时指定下载源这是网络问题不是代码问题别慌。3. 数据库模型设计筛选系统的地基我见过很多毕设代码业务逻辑写得还行但数据库表设计非常随意所有字段堆在一张表里。这种设计能跑、能出结果但一加筛选条件就变得非常吃力。优质客户筛选系统至少需要五张核心表用户表、客户信息表、消费记录表、服务记录表、筛选结果表另外可能会加上项目表和套餐表。下面逐个说一下设计思路。3.1 用户与权限让店长、前台、美容师看到不同内容Django的auth应用已经提供了User模型我们可以直接用AbstractUser扩展字段不自己从零建用户表。按美容院的业务场景用户角色可以简单分成三档管理员能查看全部客户和评分结果店长能执行筛选并导出报表前台只能录入和维护客户信息。使用Django的Group和Permission就可以实现不需要在表结构里放一个“角色”字符串字段到处判断。实际建模时我习惯使用is_staff加一个自定义字段role同时用Django的信号或装饰器在视图层做权限控制。这样做的好处是代码直观权限判断时只需要读一个角色字段。坏处是如果权限规则再多一些还是建议用Django自带的权限框架。下面是一个简化版用户模型的示例from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): ROLE_CHOICES ( (admin, 管理员), (manager, 店长), (reception, 前台), ) real_name models.CharField(姓名, max_length30, blankTrue) role models.CharField(角色, max_length20, choicesROLE_CHOICES, defaultreception)3.2 客户档案、消费记录和服务记录的三表联动客户信息表应该保存姓名、性别、手机号、生日、来源渠道、卡余额这些相对静态的信息。消费记录表保存每次消费的项目、金额、折扣、支付方式和消费时间。服务记录表保存具体做过的护理项目、服务美容师、操作时长和客户评价。这三张表通过外键关联到客户表是后面计算频度、额度的数据来源。这里有一个关键设计消费记录和服务记录不要混在一张表。虽然一次到店可能既做了面部护理又做了身体护理但消费记录关注的是“付了多少钱”服务记录关注的是“做了什么项目”统计口径不一样。比如计算“本月到店次数”时应该统计服务记录去重后的到店日期而不是统计消费笔数因为一次消费可能拆好几笔。很多新手在这里踩坑导致频度统计翻倍。客户信息表的字段可以这样设计class Customer(models.Model): name models.CharField(姓名, max_length50) phone models.CharField(手机号, max_length20, uniqueTrue) birthday models.DateField(生日, nullTrue, blankTrue) source models.CharField(来源渠道, max_length50, blankTrue) balance models.DecimalField(卡内余额, max_digits10, decimal_places2, default0) created_at models.DateTimeField(建档时间, auto_now_addTrue) class ConsumptionRecord(models.Model): customer models.ForeignKey(Customer, on_deletemodels.CASCADE, verbose_name客户) amount models.DecimalField(实付金额, max_digits10, decimal_places2) item_name models.CharField(消费项目, max_length100) consume_time models.DateTimeField(消费时间)3.3 筛选结果表把“评分结果”变成可查询的业务数据很多项目把筛选结果算完之后只显示在页面上不落库这样的话一旦重新筛选历史结果就丢了。我建议单独建一张筛选结果表字段包括客户外键、综合评分、客户等级、最近消费时间、频次、累计金额、筛选时间。这样做有什么好处你可以在后台看到每次筛选的完整快照也可以把它当作一份经营报表用Django Admin轻松查看和导出。结果表还可以支持“黑名单”或“沉睡客户”标记方便前台做回访。比如连续90天未到店且评分低于某个阈值的客户系统自动打上“沉睡”标签这个标签存到结果表里而不是直接改客户主表能避免业务数据被评分过程反向污染。3.4 Django执行查询与删除对象的高频操作这里必须单独聊一下Django的查询和删除因为它是热词里被搜得最多的内容。Django查询对象主要靠Model.objects删除对象有三种常见方式调用模型的delete()方法删除单条或QuerySet使用filter().delete()批量删除以及级联删除。下面这段代码覆盖了最常见的三种写法# 查询近30天未到店且余额大于1000的客户 from django.db.models import Q from django.utils import timezone from datetime import timedelta threshold_date timezone.now().date() - timedelta(days30) customers Customer.objects.filter(Q(balance__gt1000) Q(last_visit_date__ltthreshold_date)) # 删除单个客户 customer Customer.objects.get(pk1) customer.delete() # 批量删除符合条件的客户 Customer.objects.filter(level沉睡客户).delete()需要注意delete()返回的是一个元组比如(2, {app.Customer: 2})第一个数字表示总共删了多少条。如果你依赖主表带外键到子表删除子表记录时这里还有一个坑子表外键的on_delete如果设置为PROTECT主表记录会被Django拦截你必须先删子表或把外键设为SET_NULL。这也是答辩时老师很喜欢追问的一个点。4. 核心筛选逻辑从RFM模型到客户分级既然核心是“优质客户筛选”评分算法就是整个项目的灵魂。我建议不要用那种写死的一串if-else因为客户数量一多阈值就不好维护而且论文很难写。比较合理的做法是用RFM模型思想结合美容院的字段做二次改造然后用加权求和得出综合分。4.1 RFM模型的三个基础指标怎么算RFM是三个字母分别代表Recency最近消费时间、Frequency消费频率、Monetary消费金额。美容院场景下R可以定义成“距离上次到店的天数”F定义成“最近90天内的到店次数”M定义成“最近90天的累计消费金额”。计算时需要在客户表上跑Python脚本或使用Django的聚合函数。下面是一个简单可用的评分函数思路def calculate_customer_score(customer, nowNone): if now is None: now timezone.now().date() last_visit customer.last_visit_date if last_visit is None: return 0 days (now - last_visit).days r_score 5 if days 7 else 3 if days 30 else 1 f_score min(5, customer.visit_count_last_90_days) m_score 5 if customer.amount_last_90_days 5000 else \ 3 if customer.amount_last_90_days 2000 else 1 total r_score * 0.4 f_score * 0.3 m_score * 0.3 return round(total, 2)分数区间可以自己定。比如大于4分是A类优质客户2到4分是B类潜力客户小于2分是C类普通客户。权重方面美容院这种预存消费业态里R的权重通常比M高因为客户一旦很久不来办卡余额再高也存在流失风险。权重放在配置里方便修改别写死在计算函数里否则改一次要重新跑全量数据。4.2 把筛选逻辑封装成独立服务函数为了让视图代码干净建议把评分逻辑放到services.py或utils.py里不要在views.py里写一大段。比如我在项目里放了score_service.py里面定义refresh_all_scores()和refresh_customer_score(customer_id)两个函数。前者适合每天定时全量更新后者适合前台修改客户信息后单条触发。这个封装带来的另一个好处是方便测试。你可以在Django shell里直接调用函数看看某个客户算出来的分数是否符合预期。答辩时也能跟老师说“评分算法是独立模块不依赖页面和路由方便单元测试”。这句话在专业度上加分非常明显。4.3 数据刷新既要能手动触发也要能定时跑美容院的数据不是实时产生一次的客户每次消费后系统里的近度、频度、额度都会发生变化。所以筛选结果必须可以刷新。最简单的方式是在筛选页面放一个“重新计算”按钮点击后遍历所有活跃客户并更新筛选结果表。如果客户量不大几千条数据在Django里遍历也就几秒完全够用。如果还想做得更“专业”一点可以写一个Django管理命令python manage.py refresh_customer_scores用BaseCommand接收参数比如--days30这样部署到服务器后可以配定时任务。不要在视图里用time.sleep或后台线程做异步那会在本地跑通但部署后极容易出问题。毕设阶段用管理命令加按钮触发已经算是超出预期了。4.4 筛选页面怎么展示列表、搜索和详情筛选结果算完之后页面需要给出可操作的出口。我的建议是做一个筛选结果列表页展示客户姓名、手机号、消费频次、累计金额、综合评分、等级、最后到店时间顶部提供按等级筛选、按时间区间搜索、按手机号搜索。每个客户点进去可以看详细消费记录和服务记录这一步对前台实际使用非常重要。页面不用做多炫但“搜索”和“分页”一定要有。没有搜索功能的管理系统数据一多就难用演示效果也差。Django的分页用Paginator搜索用filter(phone__icontains...)全部是查文档就能学会的基础功能但组合起来会让整套系统体验拔高一个层级。5. 项目落地从模型到页面的完整链路模型设计好之后剩下的工作就是路由、视图、模板三件套。很多新手容易把注意力放在前端特效上结果后端逻辑很单薄。我自己做这个项目时会先把核心链路走通录入客户、录入消费、查看客户详情、执行筛选、分级展示。这个主流程能跑通再考虑加图标、加图表、加导出。5.1 页面组织与Django模板结构我习惯按业务模块分目录不是把所有模板堆在templates根目录下。比如模板目录可以这样分customer/放客户管理页面screening/放筛选结果页面statistics/放统计报表页面。模板继承用base.html放公共头部、侧边栏和底部子模板继承后只需填写中间内容块代码重复率会大大降低。导航菜单也值得认真做。在base.html中用一个列表维护菜单项每个菜单项指定URL名称然后用Django模板的{% url %}反向解析。这样哪怕后面改变了路由路径只要URL名称不变页面里的链接就不用逐个改。这也是Django的最佳实践之一。5.2 视图函数的组织类视图还是函数视图对这个项目来说函数视图和类视图都可以但更推荐在筛选结果页使用类视图的ListView因为它自带分页逻辑模板里可以直接用page_obj变量。客户新增和编辑可以用CreateView和UpdateView需要定制的地方再用FormView。不过如果你前期对类视图不熟悉用函数视图更稳别为了炫技卡在源码里出不来。下面是一个筛选结果页的函数视图示例from django.shortcuts import render from django.core.paginator import Paginator from .models import ScreeningResult def screening_result_list(request): level request.GET.get(level, ) qs ScreeningResult.objects.select_related(customer).order_by(-score) if level: qs qs.filter(customer_levellevel) paginator Paginator(qs, 20) page request.GET.get(page, 1) results paginator.get_page(page) return render(request, screening/result_list.html, {results: results})这个视图里用了select_related是因为筛选结果表通过外键关联客户表列表页要展示客户姓名和电话不加这个会引出N1查询问题。虽然几千条数据看不出区别但写上select_related说明你懂性能优化面试和答辩都能聊得更好。5.3 数据导出让结果变成Excel或CSV美容院经营者要求导出客户名单是常态。Django里最简单的导出是生成CSV文件用标准库csv加HttpResponse设置好文件头即可。如果数据里带中文记得把响应头中的charset设为utf-8并在开头加上\ufeff否则用Excel打开会乱码。这段小经验经常被忽略但演示时被老师打开文件看到乱码会非常影响印象分。如果想做得更专业可以安装openpyxl导出Excel把客户等级用不同颜色标出来。注意Excel模板里不要直接用超链接指向内网地址导出文件给别人看的时候那些地址没有意义。这个功能不算核心但能让项目显得完整。6. 常见问题与排查技巧实录交付一套项目后问得最多的不是“怎么用”而是“为什么跑不起来”。这里把我实际操作中遇到的典型问题整理成速查表你可以按图索骥省掉很多在搜索引擎里翻来翻去的时间。症状常见原因处理方法页面报ModuleNotFoundError: No module named django虚拟环境没激活或没装依赖激活虚拟环境后执行pip install -r requirements.txt迁移时报no such table没有执行makemigrations/migrate在manage.py所在目录执行两条迁移命令Admin后台打不开报403CSRF或权限问题检查是否配置登录、是否用超级管理员账号静态文件加载失败STATICFILES_DIRS没配置或STATIC_URL不对检查settings.py和模板中的{% static %}标签中文显示乱码数据库编码或文件编码问题确认数据库连接配置OPTIONS里设置了utf8mb4文件保存为UTF-8delete()触发ProtectedError外键约束阻止删除检查模型外键on_delete临时改为SET_NULL或先删关联子表时间比较报错使用了naive datetime使用make_aware或统一用Django的timezone.now()6.1 迁移命令顺序最容易被忽略很多人一式跑起来急先看了页面发现表格不存在才想起没做迁移。Django的数据库同步依赖两个命令python manage.py makemigrations负责生成迁移文件python manage.py migrate负责把迁移应用到数据库。拿到旧项目后不要直接跑服务器先执行这两条命令再创建超级管理员顺序不能乱。如果模型文件名发生变化、删除了字段迁移可能会出现冲突。最简单的解决方式是把数据库里的旧表删掉重新迁移但毕设演示时不想丢数据就尽量在开发前期调整模型不要在答辩前大幅改表结构。6.2 路由配置里带参数时不要写错顺序在Django里URL路径参数的写法是int:pk、str:name这类路径转换器如果你写成pk在Django 4.x里会报无法匹配路由。另外视图函数如果是def customer_detail(request, pk)URL里的参数名必须对应pk。新人最容易在这里遇到TypeError或缺参数的问题解决办法是仔细看控制台报错里最后几行它会明确告诉你缺哪个参数。6.3 本地正常、服务器说不通的问题部署到云服务器时最常见的坑是ALLOWED_HOSTS没有加服务器IP或域名。Django默认只允许localhost你把项目布置到新环境后访问IP会得到一个Bad Request (400)。修改settings.py里的ALLOWED_HOSTS [*]虽然不推荐用于生产但毕设演示阶段为了快速上线是可以接受的。另一个常见问题是静态文件Django框架只在DEBUGFalse时才启用staticfiles服务想要快速看到效果可以用python manage.py runserver加--insecure参数或者用Nginx配静态目录。7. 从这套项目里我总结出的六条实操心得项目做了一遍之后我最想分享的不是哪行代码而是几个容易被忽略但很重要的习惯。这些习惯不会直接写在代码里但会让你的开发过程舒服很多也能让答辩/评审觉得这个项目是“真做过”的。第一每天跑一次迁移和测试。哪怕只改了一个模型字段也要先迁移再写页面不要等到功能全部写完再一次性迁移否则出错了很难定位是哪一步改坏的。第二多用Django Admin做数据验收。自己手工往前台录太慢直接在Admin里快速造数据然后回前台观察显示效果。第三保存代码前先用python manage.py check检查这个命令能提前发现很多低级错误。第四客户和消费记录一定要设置时间字段用DateTimeField而不是DateField因为统计的时候会涉及“某天到店几次”这种精确时间判断。第五权重参数尽量放在配置文件或数据库字典表里不要在视图里写死数字方便后面调整。第六目录命名和App命名要明确比如screening、customer、statistics别用app01、test这种名字改起来非常痛苦。这些经验用一句话总结就是把项目当做一个给客户用的真实系统去做而不是当作业余练习。思考边界情况比如“客户没有消费记录时评分怎么算”“客户被删除时消费记录怎么办”才是高质量毕业设计的真正分水岭。8. 最后给接手这套项目的人一点实在话如果你手里正握着这么一套美容院优质客户筛选系统源码准备改改就交我劝你别把“跑通”当成目标。把核心评分算法的每一步在纸上画一遍把R、F、M三个字母对应的中文全称背下来把on_deletemodels.CASCADE是什么意思解释清楚才是真正保住你的成绩。我个人的体会是Django项目的难点从来不是写代码而是理解数据怎么流动客户消费了一笔消费记录表多了一行客户表的最近消费时间要不要同步更新如果不同步筛选算法从哪里拿“最近消费日期”。这套系统后续还能扩展很多方向比如给客户增加标签、做生日提醒、按美容师业绩统计、用图表展示每月到店趋势。不过这些是加分项核心逻辑一定要稳。如果你能把“为什么这个权重设置成0.4、0.3、0.3”讲明白比你会不会用Django的Signals更让人信服。最后再补一句拿到项目后先删掉重复的无用代码只保留自己讲得清楚的部分不贪多哪怕页面少一点也比全屏都是装饰效果但业务逻辑一张白纸的项目靠谱得多。
返回列表