
每到毕业季总有一批学弟学妹拿着同一个问题来找我想做一个能拿得出手的练手项目既要写代码、又要出论文、还得能在现场演示最好还能让人眼前一亮。我反复打磨后最满意的答案就是这套基于 Python 和 Django 的电商数据分析与用户行为可视化平台。它不是那种只画几张折线图的 Demo而是把数据分析、可视化大屏、大模型问答 Agent 串成了一条完整的技术链。这套平台最终能实现三件事从订单表和用户行为日志里算出销售指标用 ECharts 把人货场画成可以展示的看板再让 DeepSeek 这个国产大模型基于你自己的库表直接回答“最近哪个品类卖得好”这类业务问题。这篇文章会把项目从数据库建模、Django 接口开发到 ECharts 大屏、大模型 Agent 接入、最终部署的完整过程拆开讲每一步都带着我踩过的坑。手头有毕业设计需求、或者想从零把一个 Django 数据分析项目做完整的朋友可以直接照着这套思路复现。1. 项目定位与整体架构先把架构层面的事情说清楚。我把整个平台拆成了四层数据层、服务层、展示层、智能层。数据层承担商品、订单、用户行为日志的存储落在 MySQL 8.0 里服务层基于 Django 提供后台、权限管理和聚合查询接口展示层用 ECharts 做可视化大屏智能层就是 DeepSeek 大模型 Agent专门处理“这个月复购率是多少”这类自然语言查询。这个分层思路不是拍脑袋定的。电商数据分析项目最容易翻车的地方是所有人都在画图表、聊指标但没人说清楚数据从哪里来、怎么算、存哪里。把架构明确成四层之后每个功能模块的边界就清楚了答辩时被问到任何一个环节都能顺着链路往下讲不会东扯西扯。1.1 为什么选 Django 而不是 Flask如果你手头的项目只是做一个后台管理Flask 完全够用。但电商数据分析平台不是简单 CRUD它同时要求用户登录与角色权限、数据库迁移、后台管理界面、缓存、跨站防护这些能力。这些功能你当然都能手写但手写一遍之后答辩时被问到“为什么用 session 中间件”就已经很痛苦了更别说还要花时间做大模型接入。Django 的优势在于全家桶内置。ORM 直接操作数据库migrations 管表结构变更admin 后台改数据、看报表零成本auth 自带用户体系和权限分组。这些都是最成熟的实现你不需要重复造轮子可以把精力全部放在业务分析和 AI 能力上。另外有人会提 Spring Boot但 Java 技术栈玩数据分析要把 Python 生态绕一大圈对毕设来说性价比太低了。维度FlaskDjangoSpring Boot开发效率高但组件要自己拼高全家桶开箱即用中JVM 环境和配置成本高自带后台无有且完善有数据分析生态Python 生态和 Pandas 无缝Python 生态和 Pandas 无缝Java 生态需要额外桥接答辩表现力一般功能单薄强系统完整度高中偏工程化数据分析味弱我个人实测下来从建表到大屏出来Django 这套流程大约比 Flask 快一倍。对做毕设的人而言省下来的时间拿去打磨论文和演示效果价值更大。1.2 四层架构与数据流向数据流向是这样跑的前端访问看板调 Django 接口服务层用 ORM 做聚合查询先查 Redis 缓存没有才落库结果以 JSON 返回给 ECharts 渲染。当用户打开 AI 助手提问时前端把问题传给 DjangoDjango 先让 DeepSeek 把自然语言转成 SQL再交给一个只读数据库连接执行最后让大模型基于查询结果写一段带数字依据的回复。这套流程里最关键的一点是“数据层不直接暴露给展示层”。很多人喜欢在视图函数里写一大坨 pandas读数据库、算指标、拼 JSON全部堆在一起看起来很爽但后面加权限、加缓存、加 AI 时就会一团乱。我建议拆成几个原子功能每个函数只干一件事查数、算指标、格式化输出、渲染。模块划分大概是这样的数据采集模块负责从订单表、行为日志表读取原始数据或者用脚本生成模拟数据。数据清洗模块处理重复日志、时区偏移、测试账号污染。指标计算模块用 Django ORM 完成 GMV、转化率、复购率、用户漏斗等计算。可视化模块提供 JSON API配合 ECharts 渲染大屏。AI 问答模块对接 DeepSeek API完成自然语言到 SQL、SQL 执行、结果解释的闭环。数据来源上我的建议是优先用模拟脚本生成数据也可以接入公开的电商数据集。我不推荐直接拿爬虫去抓电商平台来源合规这件事在答辩评审眼里是加分项而且公开数据集字段干净、口径明确不用花大量时间做清洗。1.3 大模型 Agent 在这套系统里解决什么问题先说结论Agent 不是花架子它补上了传统可视化看板的短板。普通看板只能回答预设问题想看“上海最近七天哪个品类退款率最高”你得先写 SQL再画一张新图表流程很慢。而 Agent 接进来之后用户直接输入问题系统自动查库、算数、给出答案整个过程可以演示给别人看非常有冲击力。但我也要泼一点冷水。现在很多项目把 Agent 做成了纯粹的聊天机器人问它什么都能聊唯独不能查真实数据这是最容易被答辩老师拆穿的地方。我做的方案是两段式 Agent 管线第一阶段把问题转成可执行的 SELECT 语句第二阶段把查询结果组织成自然语言解释。“能落地”才是这个模块的核心价值不能只停留在 API 调用演示的层面。2. 数据库建模与用户行为采集数据建模是这套项目的地基。如果你把订单、商品、用户行为全塞进一张表后面写任何聚合查询都会痛不欲生。我最终的建模方案是五张核心表用户表、商品表、订单表、订单详情表、行为日志表。这里我只挑最核心的几张表展开讲。2.1 核心表结构设计商品、订单和用户都属于事实和主数据行为日志表比较特殊它是一张“事件流”表记录用户每一次浏览、加购、收藏、下单行为。我把这张表设计得非常窄只存事实不存任何加工后的指标后续做漏斗和路径分析全靠它。from django.db import models from django.conf import settings class Category(models.Model): name models.CharField(max_length64, uniqueTrue) class Product(models.Model): category models.ForeignKey(Category, on_deletemodels.PROTECT) title models.CharField(max_length200) price models.DecimalField(max_digits10, decimal_places2) brand models.CharField(max_length64, blankTrue) sales_count models.IntegerField(default0) created_at models.DateTimeField(auto_now_addTrue) class Order(models.Model): order_no models.CharField(max_length32, uniqueTrue) user models.ForeignKey(settings.AUTH_USER_MODEL, nullTrue, on_deletemodels.SET_NULL) product models.ForeignKey(Product, on_deletemodels.PROTECT) quantity models.IntegerField(default1) pay_amount models.DecimalField(max_digits10, decimal_places2) pay_status models.CharField(max_length16, defaultpaid) created_at models.DateTimeField(auto_now_addTrue) class BehaviorLog(models.Model): BEHAVIOR_CHOICES ( (view, 浏览商品), (cart, 加入购物车), (fav, 收藏商品), (buy, 提交订单), ) user models.ForeignKey(settings.AUTH_USER_MODEL, nullTrue, blankTrue, on_deletemodels.SET_NULL) session_id models.CharField(max_length64) product models.ForeignKey(Product, nullTrue, blankTrue, on_deletemodels.SET_NULL) behavior models.CharField(max_length16, choicesBEHAVIOR_CHOICES, db_indexTrue) created_at models.DateTimeField(auto_now_addTrue, db_indexTrue) class Meta: db_table behavior_log有几个设计点值得说明。类目表用on_deletemodels.PROTECT是用来防止删掉还在被引用的类目导致商品表数据悬空行为日志的behavior和created_at都建了索引因为大屏上最常见的查询就是“按时间段统计某种行为”商品表里的sales_count是一个冗余字段可以在生成数据时直接算好减少实时聚合压力。在毕设阶段我并不建议把表拆得太细。退款、优惠券、营销活动这些可以做但一定要考虑投入产出比。评审老师更关注你对核心业务场景的理解而不是有没有建满二十张表。表结构能自洽、能被业务问题驱动这才是重点。2.2 埋点日志的清洗规则做过真实日志分析的朋友都有体会原始行为日志脏得吓人。页面刷新导致同一用户几秒内连续浏览同一商品爬虫请求把浏览量刷爆时区不统一导致凌晨数据跑到前一天游客访问没有 user_id 导致漏斗断链。数据不清洗大屏上的数字就是错的AI 问答基于错误数据给出答案演示当场翻车。我用的清洗规则可以归纳成四条直接写进论文里也能撑起一个章节去重规则同一 session 下5 分钟内同一商品同一行为只保留一条防止刷新和误触。排除规则给测试账号加is_test标记分析时统一过滤避免演示数据把整体指标拉偏。时区规则数据库统一存 UTC前端展示层再转本地时区不要混用 aware 和 naive datetime。匿名规则游客日志用 session_id 标识漏斗分析时把未登录用户也纳入这样才不会因为登录门槛丢掉大半行为链路。毕设项目里有一个更大的技巧生成模拟数据的脚本直接把规则写好从源头就不产生脏数据。你可以故意造一部分异常样本说明清洗逻辑的必要性但不要让异常数据占比过高否则演示效果会非常难看。清洗逻辑要做更要讲得清楚这是答辩时很稳的技术点。2.3 ORM 聚合查询把指标变成 JSONDjango ORM 写聚合是真省心但要用对方式。很多人喜欢把数据全部 load 到 Python 里再用 pandas 算数据量小感觉还行行为日志一旦几十万条内存和时间都扛不住。正确思路是把聚合下推到数据库让 MySQL 先算完Django 只拿结果。下面是 GMV 趋势接口的核心代码from django.db.models import Sum, Count from django.db.models.functions import TruncDate from django.http import JsonResponse from django.utils import timezone from datetime import timedelta def gmv_trend_api(request): days int(request.GET.get(days, 30)) end timezone.now().replace(hour23, minute59, second59) start end - timedelta(daysdays - 1) rows ( Order.objects .filter(created_at__gtestart, created_at__lteend, pay_statuspaid) .annotate(dayTruncDate(created_at)) .values(day) .annotate(gmvSum(pay_amount), ordersCount(id)) .order_by(day) ) return JsonResponse({ dates: [r[day].strftime(%Y-%m-%d) for r in rows], gmv: [float(r[gmv]) for r in rows], orders: [r[orders] for r in rows], })TruncDate就是把时间字段截断成日期再按天分组是替代.extra(select...)写原生 SQL 的正确姿势。注意这里返回的是 JSON不是模板页面前端 ECharts 拿到这个结构直接就能渲染。用户漏斗我做了简化版统计每种行为下的去重用户数funnel [] for behavior, label in [(view, 浏览), (cart, 加购), (buy, 支付)]: users (BehaviorLog.objects .filter(behaviorbehavior) .values(user) .distinct() .count()) funnel.append({label: label, users: users})严格意义上真正的漏斗需要基于会话判断“先浏览、再加购、最后支付”的先后关系当前这个方案统计的是各类行为的全量去重人数口径上会偏乐观。毕设层面可以直接用但论文里要主动说明这个局限并提出基于会话路径的改进方向这反而会让工作显得更完整。3. 可视化层实现可视化层是整个平台的呈现门面也是最容易出彩、最容易翻车的部分。ECharts 如果只是想画一张图网上一搜一大把但要做成能演示的大屏选型、数据接口和性能都要设计清楚。3.1 ECharts 选型与 Django 数据接口我最终选了 ECharts原因很直接图表类型全地图、漏斗、热力图开箱即用Canvas 渲染几十万数据点也不会卡社区资源丰富大屏案例模板一大堆。相比之下 Chart.js 轻量但复杂图表要自己拼AntV 灵活但学习成本高D3.js 自由度最大但投入产出比太差毕设阶段真的没有必要。图表库特点大屏适配度ECharts类型全、性能好、中文文档完善很适合Chart.js轻量适合简单报表一般AntV G2灵活但需要额外学习语法可以D3.js最灵活开发成本高不推荐数据接口这块要养成一个习惯Django 只负责出 JSON不要试图用模板语法往 HTML 里嵌 JSON 数据否则别人看着都费劲你自己维护更头疼。接口写完再接前端脚本结构会清晰很多。def chart_data_api(request): data cache.get(gmv_trend_data) if data is None: data cal_gmv_trend() cache.set(gmv_trend_data, data, 300) return JsonResponse(data)前端直接 fetch 这个地址拿到数据再 setOptionfetch(/api/charts/gmv-trend/) .then(res res.json()) .then(data { const chart echarts.init(document.getElementById(trend)); chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.dates }, yAxis: { type: value }, series: [{ type: line, data: data.gmv, areaStyle: {} }] }); });这里要特别注意接口地址的命名规范。我在项目里统一用/api/charts/{chart-name}/返回的 JSON 永远是对应图表需要的结构。谁写坏了一眼就能看出来排查成本极低。3.2 看板上最值得做的四类图表大屏不是越花哨越好我建议优先做四类图表每类只解决一个明确业务问题。第一类是顶部 KPI 卡片展示昨日 GMV、订单量、支付用户数、整体转化率不用图表数字直接怼出来这是快速判断平台健康度的最直接方式。第二类是销售趋势用折线或面积图展示近 30 天销售额和订单量加上区间缩放组件让老师能直接看到日粒度波动。第三类是类目结构与商品排行用饼图展示类目占比配合横向柱状图看 Top10 商品回答“什么东西卖得好”的问题。第四类是用户转化漏斗用 ECharts 的 funnel 类型展示浏览到加购到支付的人数变化这是电商分析里最经典的分析模型之一。漏斗图的配置并不复杂series: [{ type: funnel, sort: descending, data: [ { name: 浏览, value: 128000 }, { name: 加购, value: 32000 }, { name: 支付, value: 21000 } ] }]我还会额外放一张小时热度热力图横轴是一周的几天纵轴是 24 小时颜色深浅代表订单量。这个图在电商场景里非常实用能直观看出周末和晚高峰的规律而且热力图的视觉效果在答辩时很吸引眼球。3.3 大屏布局与性能优化细节大屏默认按 1920×1080 设计我在项目里用整体缩放方案页面入口先检测浏览器宽高再给根容器设置缩放比例里面所有图表容器用百分比布局。这样投影仪、笔记本、大屏显示器都能正常展示不会出现图表变形或者被截断的情况。性能优化是重点。毕设阶段最容易踩的坑是页面加载时所有图表一起发请求后端瞬时压力奇高MySQL 直接被聚合查询打满。我的处理方式是把高频接口的聚合结果缓存到 Redis缓存时间 5 到 10 分钟看板展示完全够用。另外前端默认只加载近 90 天数据超出时用日期控件手动选择。这样既控制了查询范围也显得系统有设计感。如果后期想玩更高级的可以引入 Django Channels 用 WebSocket 做实时推送但说实话毕设阶段没必要定时轮询和缓存机制已经足够还可以避开 WebSocket 在服务部署时的各种麻烦。4. DeepSeek Agent把看板变成数据分析师大模型接入这一段是整个项目最提分的地方。把 DeepSeek 接进来之后用户不再需要对着图表找规律而是直接问“上个月销售额最高的三个商品是什么”系统自己查库、自己回答演示效果完全不同。4.1 从自然语言到 SQL 的完整链路我的实现方案是一个两段式 Agent 管线一共五步。第一步用户输入自然语言问题。第二步Agent 把问题、表结构描述、业务口径拼进 prompt让 DeepSeek 输出一个结构化 JSON里面只包含 SQL。第三步后端做安全性校验确认是单条 SELECT 语句。第四步执行 SQL从只读连接里取结果。第五步把查询结果再丢给大模型让它基于真实数字生成一段自然语言回复。这里必须强调的是“表结构描述”的质量。模型不瞎猜字段名全靠你在 prompt 里给它清晰的库表信息。我单独维护了一段 schema 文本把每张表的字段、类型和业务含义写清楚比如pay_amount是最终实付金额不含运费。只要这里写得准模型生成 SQL 的质量就会高很多。选 DeepSeek 而不是其他模型理由有三个中文电商场景理解能力强API 价格便宜接口兼容 OpenAI 风格改起来很快。对于毕设项目这个大模型的选择可以给论文加一个“模型选型分析”的亮点章节。4.2 在 Django 里封装大模型调用核心代码是这样包装的。我建了一个services/llm_agent.py把 API 调用和 SQL 校验独立出来视图函数只负责接请求、调服务、返回结果。import json import os import requests DEEPSEEK_URL https://api.deepseek.com/chat/completions DEEPSEEK_MODEL deepseek-chat def generate_sql(question, schema_desc): prompt ( f表结构\n{schema_desc}\n\n f业务问题{question}\n\n 请只输出 JSON格式为 {\sql\: \可执行的 SELECT 语句\}。 ) resp requests.post( DEEPSEEK_URL, headers{Authorization: fBearer {os.getenv(DEEPSEEK_API_KEY)}}, json{ model: DEEPSEEK_MODEL, messages: [ {role: system, content: 你是电商数据分析助手只输出可执行的 SELECT 语句。}, {role: user, content: prompt}, ], temperature: 0.1, response_format: {type: json_object}, }, timeout30, ) resp.raise_for_status() data resp.json() return json.loads(data[choices][0][message][content])[sql] def execute_read_only_sql(sql): from django.db import connection with connection.cursor() as cursor: cursor.execute(sql) columns [col[0] for col in cursor.description] rows cursor.fetchall() return columns, rows[:200]API Key 永远放在环境变量或者.env文件里提交代码前记得排除这是老生常谈但我还是要强调每年都有同学把密钥直接推到公开仓库里。接口调用必须要设超时我默认 30 秒前端再配合 loading 状态用户体验才会好。在 Django 视图里整个流程就是三个函数串起来生成 SQL、执行、生成解释。注意 CI/CD 或者 PythonAnywhere 这类环境对外部网络有限制本地开发没问题但部署到受限环境时大模型功能会挂可以在项目里加一个降级开关模型不可用时自动跳回纯 SQL 查询展示。4.3 防幻觉与防注入的底线这是整个 Agent 模块里我最想强调的部分。大模型会一本正经地胡说八道你可以让它帮忙写 SQL但不能让它替数据库做决定。我给自己定了三条安全底线写论文的时候也建议原样采纳。第一条数据库账号做权限隔离。我给系统单独建了一个只读账号只授予 SELECT 权限这样即使 SQL 生成得再离谱也删不掉表、改不了数据。这是最后的物理屏障。第二条SQL 做基础校验。生成结果必须是以 SELECT 开头把尾部分号和注释清理掉禁止多条语句。毕设阶段用正则和关键字检查就能防住大部分问题但在论文里要写明这个方案能防住什么、防不住什么线上系统一定要用真正的 SQL 解析器和白名单表名过滤避免被恶意构造的查询穿进去。第三条执行结果前置。模型回答的文本里不能出现凭空捏造的数字我会把执行结果和 SQL 一起喂给模型并明确要求它只能基于给定数据作答。这样就算模型想发挥也没有发挥空间。前端同时把 SQL 和结果原样展示出来用户可以直接核对模型要“撒谎”就难了。5. 部署、权限与常见问题排查很多人的项目本地跑得飞起一部署就各种翻车。最后这一部分我把上线相关的东西和遇到的高频问题都整理出来按着检查能少走很多弯路。5.1 基于 Django 内置权限做数据隔离电商平台的数据隔离是真实需求运营人员应该看到自己的业务数据不能看全站销售机密。Django 自带的 User、Group、Permission 本身就是一套 RBAC 实现不需要额外装第三方库。我给平台建了两类角色管理员和区域运营。区域运营登录后看到的图表接口会自动带上自己负责的筛选条件。这个逻辑写在接口层不写在按钮隐藏层。只隐藏前端按钮是自欺欺人接口必须校验权限。from django.contrib.auth.decorators import login_required, permission_required login_required permission_required(ecommerce.view_order, raise_exceptionTrue) def order_data_api(request): data compute_order_metrics(request.user) return JsonResponse(data, safeFalse)raise_exceptionTrue可以防止普通用户直接通过 URL 调用接口。这种程度的权限控制对毕设项目绰绰有余而且能让你在论文里多写一节“基于 RBAC 的数据权限设计”非常加分。5.2 从本机到服务器的部署要点本地跑runserver和线上部署完全是两码事。我整理了几个必踩的坑按顺序处理就不会慌。第一DEBUGTrue 改 False 之后静态文件会全部丢失。这时候要执行python manage.py collectstatic把所有静态文件收集到指定目录否则大屏页面只有 HTML没有任何 CSS 和 JS。第二线上不要用runserver扛并发。我用 Gunicorn 启动 Django 服务让 Nginx 统一托管前端静态资源和转发后端接口请求。Nginx 这边只需要一个简单的 server 块把/static/指到 collectstatic 后的目录其他请求全部转发到 8000 端口。第三数据库连接一定要显式声明字符集。我遇到过商品名称带 emoji 直接入库报错的情况最后把 MySQL 表改成 utf8mb4 才解决Django 连接参数里也要写charsetutf8mb4。第四缓存和队列要配好。Redis 缓存按下面的配置写在 settings 里CACHES { default: { BACKEND: django.core.cache.backends.redis.RedisCache, LOCATION: redis://127.0.0.1:6379/0, } }如果一台机器上同时跑 MySQL、Redis、Django内存会吃紧建议至少 2 核 4G 起步本地虚拟机也按这个标准预留。5.3 高频问题速查表我把我反复遇到、以及身边朋友问得最多的问题整理成了表格你排查时可以照着看。现象原因解决思路图表只显示一圈空白前端 JS 报错或接口未返回 JSON打开浏览器开发者工具看 Network 面板大屏首次加载很慢聚合查询未加缓存把高频图表接口缓存到 Redis5 分钟即可中文乱码数据库连接字符集不是 utf8mb4连接参数加 charset表结构改为 utf8mb4DeepSeek 返回超时网络波动或 prompt 过长设置 30 秒超时失败重试一次前端做 loadingAgent 生成的 SQL 语法错误表结构描述不准确补全字段注释要求模型输出 JSON接口权限被绕过只隐藏了前端按钮没有拦接口接口层加 login_required 和 permission_requiredRedis 连接失败服务未启动或地址错误先用 redis-cli ping 验证图表时间整体偏移 8 小时数据库存 UTC前端按 UTC 展示统一在接口层转成 Asia/Shanghai 再输出还有一个非常隐蔽的问题Django 的DateTimeField如果混合使用 aware 和 naive 时间ORM 查询会在一些版本里直接报警告但数据看起来又是对的特别容易忽略。我的习惯是全局设置USE_TZ True所有时间入库前都转成 UTC展示层再做一次转换一套规则走到底。最后分享一个我自己的开发顺序先建模型再写数据生成脚本然后做指标查询接到 ECharts 上最后再接 DeepSeek Agent。千万不要一上来就做 AI先把数据链条跑通后面每加一张图表都是复制一个 JSON 接口的事真正费时间的反而是前面打地基的部分。这套顺序我反复用了好几个项目每次都能让最终效果稳定且可控。