基于Python的外卖订单数据分析可视化系统

基于Python的外卖订单数据分析可视化系统 毕业论文

毕业设计


一、项目名称:

基于Python的外卖订单数据分析可视化系统


【摘要】

外卖平台交易规模持续扩大,订单字段分散在 CSV 与业务库中,运营侧难以快速完成金额分布、取消率对比、城市 GMV 排行与短期趋势研判。本文设计并实现了基于 Python 与 Django 的外卖订单数据分析可视化系统,面向教学与课题演示场景,将 Heywhale 公开外卖订单数据集清洗入库后,以交互式图表支撑多维订单分析与算法辅助解读。

系统采用 B/S 架构,后端基于 Django 3.2,管理端集成 SimpleUI;数据库使用 MySQL(库名 takeaway),通过 mysqlclient / PyMySQL 驱动接入。前台基于 Bootstrap 模板、takeaway-theme.css 外卖橙主题与 ECharts 完成可视化。业务核心表为 OrderRecord(约 152000 条),字段涵盖订单号、用户 ID、商家 ID、菜品品类、订单金额、订单状态、城市与下单时间;前台用户由自建 User 表维护。数据经 scripts/fetch_takeaway_data.py 同步 Heywhale CSV,再由 manage.py import_orders 批量导入。分析层优先在 app/utils/takeaway_sql_bundles.py 中以 MySQL GROUP BY 聚合,配合 takeaway_charts.py 缓存 bundle API,避免每次请求将 15 万行全表装入 pandas。算法侧实现 Pearson 相关矩阵(抽样)、日 GMV 加权移动平均(WMA)短期预测、IQR 异常金额检测(可降级 P99)与日订单量自相关分析;不包含 协同过滤、KNN 推荐等与本数据集字段无关的能力。系统入口为 manage.py,运行端口 8040 ;前台登录路径 /app/login/(账号 admin/123456),登录后进入 /app/home/;后台为 Django 超管 SimpleUI /admin/admin/admin123)。中间件 AuthMiddleware 对未登录访问拦截,豁免 /app/login//app/register//admin/ 前缀。

测试与运行结果表明,系统能够完成登录鉴权、订单列表筛选导出、多维图表分析、时序对比、相关/预测/异常算法页、SimpleUI 后台维护与缓存刷新等闭环,界面清晰、响应稳定,可为外卖订单数据分析课题研究与教学演示提供可用支撑。

【关键词】 Django;外卖订单;数据可视化;ECharts;Pearson相关

【Abstract】

Food-delivery platforms generate massive order records scattered across CSV files and databases, making unified aggregation and visualization difficult. This thesis designs and implements a Python- and Django-based takeaway order data analysis and visualization system for teaching and demonstration. Heywhale open dataset orders are cleaned, imported and explored through interactive charts.

The system follows a B/S architecture. The backend uses Django 3.2 with SimpleUI; MySQL database name is takeaway. The frontend adopts Bootstrap, takeaway orange theme and ECharts. Core table OrderRecord holds about 152000 rows with order_id, user_id, merchant_id, food_category, order_amount, order_status, city and order_time. Data is synced by fetch_takeaway_data.py and imported by import_orders. Analysis prefers MySQL GROUP BY in takeaway_sql_bundles.py with cached bundle APIs instead of loading the full table into pandas on every request. Algorithms include Pearson correlation on samples, WMA daily GMV forecast, IQR anomaly detection with P99 fallback, and daily order autocorrelation. No collaborative filtering or KNN recommendation. Entry point is manage.py, port 8040 ; frontend login /app/login/ (admin/123456) to /app/home/; admin /admin/ (admin/admin123). AuthMiddleware exempts login, register and admin paths.

Results show that authentication, order list/export, multi-dimensional analysis, time-series comparison, correlation/prediction/anomaly pages, SimpleUI admin and cache invalidation work stably for course demos.

【Keywords】 Django; Takeaway Orders; Data Visualization; ECharts; Pearson Correlation


目 录

[1 绪 论](#1 绪 论)

  • [1.1 课题研究背景](#1.1 课题研究背景)
  • [1.2 课题研究的目的、意义](#1.2 课题研究的目的、意义)
  • [1.3 课题的国内外研究现状和发展动态](#1.3 课题的国内外研究现状和发展动态)
  • [1.4 研究内容](#1.4 研究内容)
  • [1.5 论文结构安排](#1.5 论文结构安排)

[2 相关技术](#2 相关技术)

  • [2.1 Python编程语言](#2.1 Python编程语言)
  • [2.2 Django Web框架](#2.2 Django Web框架)
  • [2.3 MySQL数据库](#2.3 MySQL数据库)
  • [2.4 ECharts可视化技术](#2.4 ECharts可视化技术)
  • [2.5 统计分析算法](#2.5 统计分析算法)
  • [2.6 技术特色与创新点](#2.6 技术特色与创新点)

[3 系统分析与设计](#3 系统分析与设计)

  • [3.1 系统总体设计](#3.1 系统总体设计)
    • [3.1.1 系统架构设计](#3.1.1 系统架构设计)
    • [3.1.2 系统数据流设计](#3.1.2 系统数据流设计)
    • [3.1.3 系统功能模块设计](#3.1.3 系统功能模块设计)
  • [3.2 系统详细设计](#3.2 系统详细设计)
    • [3.2.1 用户认证模块设计](#3.2.1 用户认证模块设计)
    • [3.2.2 订单数据管理模块设计](#3.2.2 订单数据管理模块设计)
    • [3.2.3 多维分析模块设计](#3.2.3 多维分析模块设计)
    • [3.2.4 时序与对比模块设计](#3.2.4 时序与对比模块设计)
    • [3.2.5 相关预测异常分析模块设计](#3.2.5 相关预测异常分析模块设计)
    • [3.2.6 SimpleUI后台模块设计](#3.2.6 SimpleUI后台模块设计)
  • [3.3 数据库设计](#3.3 数据库设计)
    • [3.3.1 数据库关系设计](#3.3.1 数据库关系设计)

[4 系统实现](#4 系统实现)

  • [4.0 系统整体实现流程](#4.0 系统整体实现流程)
  • [4.1 用户认证模块的实现](#4.1 用户认证模块的实现)
  • [4.2 订单数据管理模块的实现](#4.2 订单数据管理模块的实现)
  • [4.3 多维分析模块的实现](#4.3 多维分析模块的实现)
  • [4.4 时序与对比模块的实现](#4.4 时序与对比模块的实现)
  • [4.5 相关预测异常分析模块的实现](#4.5 相关预测异常分析模块的实现)
  • [4.6 SimpleUI后台模块的实现](#4.6 SimpleUI后台模块的实现)
  • [4.7 用户界面实现](#4.7 用户界面实现)
  • [4.8 系统集成与部署](#4.8 系统集成与部署)

[5 系统测试](#5 系统测试)

  • [5.1 系统功能测试](#5.1 系统功能测试)
    • [5.1.1 用户认证功能测试用例](#5.1.1 用户认证功能测试用例)
    • [5.1.2 数据管理功能测试用例](#5.1.2 数据管理功能测试用例)
    • [5.1.3 核心业务功能测试用例](#5.1.3 核心业务功能测试用例)
    • [5.1.4 数据分析功能测试用例](#5.1.4 数据分析功能测试用例)
    • [5.1.5 系统集成功能测试用例](#5.1.5 系统集成功能测试用例)
    • [5.1.6 用户界面功能测试用例](#5.1.6 用户界面功能测试用例)
  • [5.2 系统性能测试](#5.2 系统性能测试)
    • [5.2.1 响应时间性能分析](#5.2.1 响应时间性能分析)
    • [5.2.2 并发性能测试](#5.2.2 并发性能测试)
    • [5.2.3 数据库性能测试](#5.2.3 数据库性能测试)
    • [5.2.4 性能优化措施](#5.2.4 性能优化措施)

[6 总结与展望](#6 总结与展望)

  • [6.1 总结](#6.1 总结)
  • [6.2 展望](#6.2 展望)

参考文献

致谢


1 绪 论

1.1 课题研究背景

在线外卖已成为城市餐饮消费的重要渠道。平台侧与公开数据集中通常包含订单号、用户 ID、商家 ID、品类、金额、状态、城市与下单时间等字段;运营分析常见需求包括:汇总 GMV 与客单价、对比各城市订单量、识别取消率偏高的品类或时段、观察日订单量的周期规律,并对短期 GMV 做趋势外推。当样本规模达到十万级时,若每次分析都在应用层全表加载至内存,将导致响应变慢、部署成本上升,难以在毕业设计答辩环境中稳定演示。

在高校毕业设计与教学演示场景中,常见需求包括:一是需要一套可本地部署的系统,用统一库表承载约 15 万 条外卖订单样本;二是需要按 CSV 真实字段拆分分析页面,以 Web 图表完成筛选、对比与下钻;三是需要可讲解的统计与轻量算法(相关、预测、异常检测),并明确算法边界,避免虚构与本项目字段无关的推荐能力。基于上述背景,本课题以 Django 为主框架,构建外卖订单数据分析可视化系统:数据以 Heywhale 外卖订单数据集为主,前台 ECharts 负责展示,SimpleUI 后台负责订单与用户维护。系统默认端口 8040;聚合优先在 MySQL 侧完成,便于在万级至十万级数据量下保持亚秒级 bundle 响应。

1.2 课题研究的目的、意义

1.2.1 研究目的

本课题的主要目的包括:

  1. 建立以 OrderRecordUser 为核心的数据模型,覆盖订单八字段与前台用户资料,支撑统一查询与多维聚合。
  2. 实现前台会话登录、侧栏导航与多分析页展示,服务端口为 8040 ,登录路径 /app/login/,首页 /app/home/,后台 /admin/
  3. 基于 takeaway_sql_bundles.py 与 Django ORM 完成统计分析,前端通过 bundle API 与 ECharts 渲染。
  4. 通过 fetch_takeaway_data.pyimport_orders 完成 Heywhale CSV 同步与批量入库;提供 Pearson 相关、WMA 预测、IQR 异常检测与日订单自相关分析。
  5. 输出可运行、可演示的完整系统,默认前台账号 admin/123456、后台 admin/admin123,便于验收与二次扩展。
1.2.2 研究意义

理论意义:将 Web 开发、关系型聚合与轻量统计算法结合到统一课题中,形成「CSV 入库---SQL 聚合---缓存 bundle---图表页---算法说明」的可复用范式,有助于理解十万级样本下「数据库侧聚合优于全表 pandas」的工程取舍。

实践意义 :系统把约 152000 条订单、前台用户与 Django 超管分层管理,降低手工处理成本;分析页按字段维度拆分路由,便于逐模块讲解;算法代码路径清晰,便于核对结果与答辩演示。

社会意义:可视化结果有助于直观认识城市消费结构、取消率差异与时段热力分布,提升学生对真实外卖数据可用性的认识。

经济意义:本地化部署与开源技术栈降低课题演示成本;模块化 bundle 与路由设计便于后续替换数据源或扩展指标,减少重复开发投入。

1.3 课题的国内外研究现状和发展动态

1.3.1 国外研究现状

国外 O2O 与本地生活平台在交易分析与运营洞察方面积累较早。Uber Eats、DoorDash 等平台综合运用订单日志、用户行为与地理信息进行需求预测与异常监测;公开数据集与 Kaggle 竞赛也提供了可复现的订单样本。统计领域经典方法包括 Pearson/Spearman 相关、移动平均预测、IQR 与 Z-score 异常检测;时间序列自相关用于判断短期记忆性。可视化方面,ECharts、D3.js 等库被用于交互式统计图。本课题在教学场景下选用 SQL 聚合 + 抽样相关 + WMA 预测 + IQR 异常检测,并明确不引入与本数据集字段无关的协同过滤推荐。

1.3.2 国内研究现状

国内外卖平台在智能调度与运营分析方面进展显著,高校毕设中常见 Django + ECharts 的订单或销售可视化系统。部分系统侧重大屏态势,部分侧重 CRUD 与简单图表。本课题更强调「六大功能模块 + 按 CSV 字段拆分分析页 + Heywhale 真实数据 + SimpleUI 后台 + bundle API 缓存」,并以 MySQL 持久化订单,兼顾可解释统计与可复现部署。

1.3.3 发展趋势

未来订单数据分析将更强调实时增量、多维下钻与可解释预测。可视化从静态报表走向可筛选、可联动的交互分析;后端则更注重索引、聚合下推与缓存版本控制。对本系统而言,后续可扩展更稳健的密码哈希与更精细的时序模型,但仍应保持教学场景下结构清晰、部署简单的特点。

1.4 研究内容

本课题主要研究内容如下:

  1. 数据采集与入库 :设计面向 Heywhale 外卖订单 CSV 的同步与导入流程,支持 fetch_takeaway_data.py 落盘与 import_orders --truncate 批量写入 OrderRecord
  2. 用户与权限:实现自建用户表注册登录、会话保持与中间件拦截;Django 超管与前台用户分离。
  3. 订单数据管理:实现订单列表分页筛选、统计卡片与 CSV 导出;后台维护订单记录。
  4. 多维分析:实现金额、状态、品类、城市、用户、商家等分析页,对应独立 bundle API。
  5. 时序与对比:实现日订单量、小时×星期热力图、自相关与月度 GMV/订单量环比对比。
  6. 相关/预测/异常算法分析:实现 Pearson 相关矩阵、WMA 日 GMV 预测、IQR(可降级 P99)异常检测与线性趋势拟合。
  7. SimpleUI 后台 :实现 /admin/ 下订单与用户 CRUD、按月筛选与搜索。

1.5 论文结构安排

第 1 章绪论,说明背景、目的意义、研究现状与研究内容。第 2 章介绍 Python、Django、MySQL、ECharts、统计算法及相关技术特色。第 3 章给出系统架构、数据流、六大模块详细设计与数据库关系。第 4 章按模块说明实现流程、核心代码、访问路径与界面效果。第 5 章给出功能测试用例与性能测试分析。第 6 章总结工作并展望后续方向。文末附参考文献与致谢。


2 相关技术

2.1 Python编程语言

Python 具备清晰语法与丰富第三方库,适合 Web 后端与数据处理。本系统使用 Python 3.11 环境,依赖 requirements.txt 中的 Django 3.2、pandas、numpy、PyMySQL、mysqlclient、django-simpleui 等。导入脚本与 SQL bundle 使用标准库 csvhashlib;算法侧在相关、预测、异常 bundle 中使用 numpy 做矩阵与分位数计算;仅在必要时通过 cached_analysis_df 按筛选条件抽样加载 DataFrame。

2.1.1 数据处理与脚本组织

项目将数据同步独立于 scripts/fetch_takeaway_data.py,导入命令位于 app/management/commands/import_orders.py,与业务视图 app/views.py、工具模块 app/utils/takeaway_*.py 解耦。无真实 CSV 时可调用 scripts/generate_takeaway_data.py 生成约 5 万条模拟数据作为备用。

2.1.2 与 Django 的协作方式

业务入口为 manage.py,配置集中在 基于Django系统/settings.py。视图层负责页面渲染与导出接口,API 层 api_views.py 统一返回 JSON bundle;模型层映射 MySQL 表,模板层使用 Bootstrap 与 takeaway-theme.css 布局。

2.2 Django Web框架

Django 采用 MTV(Model-Template-View)组织方式。本系统 Django 版本为 3.2 ,应用名为 app,根路由在 基于Django系统/urls.py 挂载 /app//admin/

2.2.1 MTV模式与路由组织

模型定义于 app/models.py;页面视图集中在 views.py;模板分布在 templates/app/templates/app/urls.py 按认证、分析模块、订单列表、API bundle 分组注册路径,统一前缀为 /app/

2.2.2 会话与中间件

INSTALLED_APPS 包含 simpleuiapp 等。中间件链注册 middleware.auth.AuthMiddleware:未登录访问非豁免路径时重定向至 /app/login/。前台登录态以 session['username'] 标识;后台使用 Django 内置 auth_user 超管,与前台 User 表分离。

2.3 MySQL数据库

业务库名为 takeaway ,连接配置于 settings.py(默认 localhost:3306,用户 root,密码 123456,字符集 utf8mb4)。引擎为 django.db.backends.mysql

2.3.1 ORM与索引设计

OrderRecordorder_id 设置唯一索引,并在 user_idmerchant_idfood_categoryorder_statuscityorder_time 等字段建立索引;复合索引 idx_order_time_cityidx_order_time_cat 支撑按月与城市/品类筛选聚合。列表与分析大量使用 filter、原生 SQL GROUP BY 与分页。

2.3.2 初始化数据

课题演示以 Heywhale food_order_data.csv152000 条)导入为主。执行 python manage.py seed_admin 可创建前台用户 admin/123456 与 Django 超管 admin/admin123

2.4 ECharts可视化技术

前台分析页通过 static/js/takeaway_pages.jschart_helpers.js 调用 bundle API,用 ECharts 渲染柱状、饼图、折线、热力图、散点与相关矩阵热力图等。首页与各模块页共享月份/城市/品类筛选条 partials/month_filter_bar.html;主题色由 takeaway-theme.css 定义外卖橙 #fa541c 与 Ant Design Pro 风格侧栏。

2.5 统计分析算法

Pearson 相关矩阵correlation_bundle_sqlMOD(id,25)=0 抽样约 1/25 行,计算订单金额、下单小时、星期、品类编码、是否完成等变量的 Pearson 相关系数,并输出小时---金额散点样本。不等于因果推断,页面附带算法说明文案。

加权移动平均(WMA)预测prediction_bundle_sql 基于最近 14 天日 GMV,以 0.5/0.3/0.2 权重对最近 3 天加权,滑动向前预测未来 7 天,适合短期趋势外推,不适用于大促突变场景。

IQR 异常检测anomaly_bundle_sql 对完成单金额抽样,按 Q1−1.5×IQRQ3+1.5×IQR 确定阈值;若经典 IQR 无异常点则放宽为 k=1.0 ,仍无点则改用抽样 P99 作为相对偏高阈值,保证答辩页有可讲样本。

自相关分析time_series_bundle_sql 对日订单量序列计算滞后 1~14 天自相关系数,用于判断短期记忆性。

线性趋势拟合:预测页对日客单价均值做一元线性回归,得到趋势斜率辅助解读消费升级或降级方向。

2.6 技术特色与创新点

算法方面 :相关分析采用数据库抽样 + numpy 矩阵,避免全表进内存;预测采用可讲解的 WMA 而非黑盒深度模型;异常检测明确 IQR 与 P99 降级策略,虚构协同过滤或 KNN 推荐。

数据处理方面 :万级至十万级数据优先 MySQL GROUP BY 聚合;bundle 结果经 LocMemCache 与版本号 takeaway:cache_version 缓存;OrderRecord 增删改触发 invalidate_takeaway_cache

可视化特色:分析维度与 CSV 字段一一对应;侧栏覆盖 12 个业务分析页;外卖橙主题统一品牌感。

架构优势:页面视图与 bundle API 分离,前端并行请求多图表;SimpleUI 后台与前台 Session 分离。

体验方面 :订单列表支持月/城市/品类筛选与 20/50/100 分页;导出 UTF-8 BOM CSV;课题演示以 8040 端口本地运行为准。


3 系统分析与设计

3.1 系统总体设计

3.1.1 系统架构设计

系统采用经典三层 B/S 结构:表现层为登录注册页、首页仪表盘、各分析模板、订单列表与 SimpleUI 后台;业务层包含会话认证、SQL 聚合、bundle 缓存、轻量算法与 CSV 导出;数据层为 MySQL takeaway 库及 data/raw/ 下 CSV。浏览器通过 HTTP 访问 8040 端口,Django 处理页面与 API 请求,聚合与算法在服务端完成后以 JSON 驱动 ECharts 渲染。

图3.1 系统架构图
#mermaid-svg-4BMM6SHAMNbck55R{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4BMM6SHAMNbck55R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4BMM6SHAMNbck55R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4BMM6SHAMNbck55R .error-icon{fill:#552222;}#mermaid-svg-4BMM6SHAMNbck55R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4BMM6SHAMNbck55R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4BMM6SHAMNbck55R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4BMM6SHAMNbck55R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4BMM6SHAMNbck55R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4BMM6SHAMNbck55R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4BMM6SHAMNbck55R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4BMM6SHAMNbck55R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4BMM6SHAMNbck55R .marker.cross{stroke:#333333;}#mermaid-svg-4BMM6SHAMNbck55R svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4BMM6SHAMNbck55R p{margin:0;}#mermaid-svg-4BMM6SHAMNbck55R .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4BMM6SHAMNbck55R .cluster-label text{fill:#333;}#mermaid-svg-4BMM6SHAMNbck55R .cluster-label span{color:#333;}#mermaid-svg-4BMM6SHAMNbck55R .cluster-label span p{background-color:transparent;}#mermaid-svg-4BMM6SHAMNbck55R .label text,#mermaid-svg-4BMM6SHAMNbck55R span{fill:#333;color:#333;}#mermaid-svg-4BMM6SHAMNbck55R .node rect,#mermaid-svg-4BMM6SHAMNbck55R .node circle,#mermaid-svg-4BMM6SHAMNbck55R .node ellipse,#mermaid-svg-4BMM6SHAMNbck55R .node polygon,#mermaid-svg-4BMM6SHAMNbck55R .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4BMM6SHAMNbck55R .rough-node .label text,#mermaid-svg-4BMM6SHAMNbck55R .node .label text,#mermaid-svg-4BMM6SHAMNbck55R .image-shape .label,#mermaid-svg-4BMM6SHAMNbck55R .icon-shape .label{text-anchor:middle;}#mermaid-svg-4BMM6SHAMNbck55R .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4BMM6SHAMNbck55R .rough-node .label,#mermaid-svg-4BMM6SHAMNbck55R .node .label,#mermaid-svg-4BMM6SHAMNbck55R .image-shape .label,#mermaid-svg-4BMM6SHAMNbck55R .icon-shape .label{text-align:center;}#mermaid-svg-4BMM6SHAMNbck55R .node.clickable{cursor:pointer;}#mermaid-svg-4BMM6SHAMNbck55R .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4BMM6SHAMNbck55R .arrowheadPath{fill:#333333;}#mermaid-svg-4BMM6SHAMNbck55R .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4BMM6SHAMNbck55R .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4BMM6SHAMNbck55R .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4BMM6SHAMNbck55R .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4BMM6SHAMNbck55R .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4BMM6SHAMNbck55R .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4BMM6SHAMNbck55R .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4BMM6SHAMNbck55R .cluster text{fill:#333;}#mermaid-svg-4BMM6SHAMNbck55R .cluster span{color:#333;}#mermaid-svg-4BMM6SHAMNbck55R div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4BMM6SHAMNbck55R .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4BMM6SHAMNbck55R rect.text{fill:none;stroke-width:0;}#mermaid-svg-4BMM6SHAMNbck55R .icon-shape,#mermaid-svg-4BMM6SHAMNbck55R .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4BMM6SHAMNbck55R .icon-shape p,#mermaid-svg-4BMM6SHAMNbck55R .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4BMM6SHAMNbck55R .icon-shape .label rect,#mermaid-svg-4BMM6SHAMNbck55R .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4BMM6SHAMNbck55R .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4BMM6SHAMNbck55R .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4BMM6SHAMNbck55R :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据访问层
业务逻辑层
表现层
登录/注册页
数据概览首页
多维分析页
相关/预测/异常页
订单列表与导出
SimpleUI后台
AuthMiddleware 会话认证
views 页面视图
api_views bundle接口
takeaway_sql_bundles SQL聚合
takeaway_charts 缓存层
Pearson/WMA/IQR/自相关
CSV导出
MySQL: takeaway
Heywhale CSV
fetch/import_orders

3.1.2 系统数据流设计

从外部看,前台用户提交账号口令进入系统,管理员通过 SimpleUI 维护订单与用户,Heywhale 数据集向导入脚本提供 CSV 源。系统内部完成鉴权、筛选聚合、图表 bundle、算法计算与入库。

图3.2 顶层数据流图
#mermaid-svg-5xN3rGic6Otdvpn4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5xN3rGic6Otdvpn4 .error-icon{fill:#552222;}#mermaid-svg-5xN3rGic6Otdvpn4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5xN3rGic6Otdvpn4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5xN3rGic6Otdvpn4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5xN3rGic6Otdvpn4 .marker.cross{stroke:#333333;}#mermaid-svg-5xN3rGic6Otdvpn4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5xN3rGic6Otdvpn4 p{margin:0;}#mermaid-svg-5xN3rGic6Otdvpn4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 .cluster-label text{fill:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 .cluster-label span{color:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 .cluster-label span p{background-color:transparent;}#mermaid-svg-5xN3rGic6Otdvpn4 .label text,#mermaid-svg-5xN3rGic6Otdvpn4 span{fill:#333;color:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 .node rect,#mermaid-svg-5xN3rGic6Otdvpn4 .node circle,#mermaid-svg-5xN3rGic6Otdvpn4 .node ellipse,#mermaid-svg-5xN3rGic6Otdvpn4 .node polygon,#mermaid-svg-5xN3rGic6Otdvpn4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5xN3rGic6Otdvpn4 .rough-node .label text,#mermaid-svg-5xN3rGic6Otdvpn4 .node .label text,#mermaid-svg-5xN3rGic6Otdvpn4 .image-shape .label,#mermaid-svg-5xN3rGic6Otdvpn4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-5xN3rGic6Otdvpn4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5xN3rGic6Otdvpn4 .rough-node .label,#mermaid-svg-5xN3rGic6Otdvpn4 .node .label,#mermaid-svg-5xN3rGic6Otdvpn4 .image-shape .label,#mermaid-svg-5xN3rGic6Otdvpn4 .icon-shape .label{text-align:center;}#mermaid-svg-5xN3rGic6Otdvpn4 .node.clickable{cursor:pointer;}#mermaid-svg-5xN3rGic6Otdvpn4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5xN3rGic6Otdvpn4 .arrowheadPath{fill:#333333;}#mermaid-svg-5xN3rGic6Otdvpn4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5xN3rGic6Otdvpn4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5xN3rGic6Otdvpn4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5xN3rGic6Otdvpn4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5xN3rGic6Otdvpn4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5xN3rGic6Otdvpn4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5xN3rGic6Otdvpn4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5xN3rGic6Otdvpn4 .cluster text{fill:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 .cluster span{color:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5xN3rGic6Otdvpn4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5xN3rGic6Otdvpn4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-5xN3rGic6Otdvpn4 .icon-shape,#mermaid-svg-5xN3rGic6Otdvpn4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5xN3rGic6Otdvpn4 .icon-shape p,#mermaid-svg-5xN3rGic6Otdvpn4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5xN3rGic6Otdvpn4 .icon-shape .label rect,#mermaid-svg-5xN3rGic6Otdvpn4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5xN3rGic6Otdvpn4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5xN3rGic6Otdvpn4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5xN3rGic6Otdvpn4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 账号/筛选/分析请求
CRUD操作
food_order_data.csv
页面与图表JSON
管理反馈
同步落盘
业务记录
前台用户
外卖订单数据分析可视化系统
后台管理员
外部数据源

Heywhale CSV
data/raw 目录
MySQL takeaway

图3.3 0层数据流图
#mermaid-svg-meQYxNKYQJXbRI1q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-meQYxNKYQJXbRI1q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-meQYxNKYQJXbRI1q .error-icon{fill:#552222;}#mermaid-svg-meQYxNKYQJXbRI1q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-meQYxNKYQJXbRI1q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-meQYxNKYQJXbRI1q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-meQYxNKYQJXbRI1q .marker.cross{stroke:#333333;}#mermaid-svg-meQYxNKYQJXbRI1q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-meQYxNKYQJXbRI1q p{margin:0;}#mermaid-svg-meQYxNKYQJXbRI1q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-meQYxNKYQJXbRI1q .cluster-label text{fill:#333;}#mermaid-svg-meQYxNKYQJXbRI1q .cluster-label span{color:#333;}#mermaid-svg-meQYxNKYQJXbRI1q .cluster-label span p{background-color:transparent;}#mermaid-svg-meQYxNKYQJXbRI1q .label text,#mermaid-svg-meQYxNKYQJXbRI1q span{fill:#333;color:#333;}#mermaid-svg-meQYxNKYQJXbRI1q .node rect,#mermaid-svg-meQYxNKYQJXbRI1q .node circle,#mermaid-svg-meQYxNKYQJXbRI1q .node ellipse,#mermaid-svg-meQYxNKYQJXbRI1q .node polygon,#mermaid-svg-meQYxNKYQJXbRI1q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-meQYxNKYQJXbRI1q .rough-node .label text,#mermaid-svg-meQYxNKYQJXbRI1q .node .label text,#mermaid-svg-meQYxNKYQJXbRI1q .image-shape .label,#mermaid-svg-meQYxNKYQJXbRI1q .icon-shape .label{text-anchor:middle;}#mermaid-svg-meQYxNKYQJXbRI1q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-meQYxNKYQJXbRI1q .rough-node .label,#mermaid-svg-meQYxNKYQJXbRI1q .node .label,#mermaid-svg-meQYxNKYQJXbRI1q .image-shape .label,#mermaid-svg-meQYxNKYQJXbRI1q .icon-shape .label{text-align:center;}#mermaid-svg-meQYxNKYQJXbRI1q .node.clickable{cursor:pointer;}#mermaid-svg-meQYxNKYQJXbRI1q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-meQYxNKYQJXbRI1q .arrowheadPath{fill:#333333;}#mermaid-svg-meQYxNKYQJXbRI1q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-meQYxNKYQJXbRI1q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-meQYxNKYQJXbRI1q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-meQYxNKYQJXbRI1q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-meQYxNKYQJXbRI1q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-meQYxNKYQJXbRI1q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-meQYxNKYQJXbRI1q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-meQYxNKYQJXbRI1q .cluster text{fill:#333;}#mermaid-svg-meQYxNKYQJXbRI1q .cluster span{color:#333;}#mermaid-svg-meQYxNKYQJXbRI1q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-meQYxNKYQJXbRI1q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-meQYxNKYQJXbRI1q rect.text{fill:none;stroke-width:0;}#mermaid-svg-meQYxNKYQJXbRI1q .icon-shape,#mermaid-svg-meQYxNKYQJXbRI1q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-meQYxNKYQJXbRI1q .icon-shape p,#mermaid-svg-meQYxNKYQJXbRI1q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-meQYxNKYQJXbRI1q .icon-shape .label rect,#mermaid-svg-meQYxNKYQJXbRI1q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-meQYxNKYQJXbRI1q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-meQYxNKYQJXbRI1q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-meQYxNKYQJXbRI1q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 前台用户
1.0 用户认证
2.0 订单数据管理
3.0 多维分析
4.0 时序与对比
5.0 相关预测异常
管理员
6.0 SimpleUI后台
运维/初始化
7.0 CSV导入与启动
User表
OrderRecord表
图表bundle输出

3.1.3 系统功能模块设计

结合侧栏实际菜单与路由划分,系统功能划分为六大模块:(1)用户认证;(2)订单数据管理(列表/导出/后台);(3)多维分析(金额/状态/品类/城市/用户/商家);(4)时序与对比;(5)相关/预测/异常算法分析;(6)SimpleUI 后台。各模块对应独立页面路由与 bundle API;个人信息与改密挂载于认证模块延伸功能。

图3.4 系统功能结构图
#mermaid-svg-H0QyRsKV50VS4K7S{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-H0QyRsKV50VS4K7S .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-H0QyRsKV50VS4K7S .error-icon{fill:#552222;}#mermaid-svg-H0QyRsKV50VS4K7S .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-H0QyRsKV50VS4K7S .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-H0QyRsKV50VS4K7S .marker{fill:#333333;stroke:#333333;}#mermaid-svg-H0QyRsKV50VS4K7S .marker.cross{stroke:#333333;}#mermaid-svg-H0QyRsKV50VS4K7S svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-H0QyRsKV50VS4K7S p{margin:0;}#mermaid-svg-H0QyRsKV50VS4K7S .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-H0QyRsKV50VS4K7S .cluster-label text{fill:#333;}#mermaid-svg-H0QyRsKV50VS4K7S .cluster-label span{color:#333;}#mermaid-svg-H0QyRsKV50VS4K7S .cluster-label span p{background-color:transparent;}#mermaid-svg-H0QyRsKV50VS4K7S .label text,#mermaid-svg-H0QyRsKV50VS4K7S span{fill:#333;color:#333;}#mermaid-svg-H0QyRsKV50VS4K7S .node rect,#mermaid-svg-H0QyRsKV50VS4K7S .node circle,#mermaid-svg-H0QyRsKV50VS4K7S .node ellipse,#mermaid-svg-H0QyRsKV50VS4K7S .node polygon,#mermaid-svg-H0QyRsKV50VS4K7S .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-H0QyRsKV50VS4K7S .rough-node .label text,#mermaid-svg-H0QyRsKV50VS4K7S .node .label text,#mermaid-svg-H0QyRsKV50VS4K7S .image-shape .label,#mermaid-svg-H0QyRsKV50VS4K7S .icon-shape .label{text-anchor:middle;}#mermaid-svg-H0QyRsKV50VS4K7S .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-H0QyRsKV50VS4K7S .rough-node .label,#mermaid-svg-H0QyRsKV50VS4K7S .node .label,#mermaid-svg-H0QyRsKV50VS4K7S .image-shape .label,#mermaid-svg-H0QyRsKV50VS4K7S .icon-shape .label{text-align:center;}#mermaid-svg-H0QyRsKV50VS4K7S .node.clickable{cursor:pointer;}#mermaid-svg-H0QyRsKV50VS4K7S .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-H0QyRsKV50VS4K7S .arrowheadPath{fill:#333333;}#mermaid-svg-H0QyRsKV50VS4K7S .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-H0QyRsKV50VS4K7S .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-H0QyRsKV50VS4K7S .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-H0QyRsKV50VS4K7S .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-H0QyRsKV50VS4K7S .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-H0QyRsKV50VS4K7S .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-H0QyRsKV50VS4K7S .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-H0QyRsKV50VS4K7S .cluster text{fill:#333;}#mermaid-svg-H0QyRsKV50VS4K7S .cluster span{color:#333;}#mermaid-svg-H0QyRsKV50VS4K7S div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-H0QyRsKV50VS4K7S .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-H0QyRsKV50VS4K7S rect.text{fill:none;stroke-width:0;}#mermaid-svg-H0QyRsKV50VS4K7S .icon-shape,#mermaid-svg-H0QyRsKV50VS4K7S .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-H0QyRsKV50VS4K7S .icon-shape p,#mermaid-svg-H0QyRsKV50VS4K7S .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-H0QyRsKV50VS4K7S .icon-shape .label rect,#mermaid-svg-H0QyRsKV50VS4K7S .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-H0QyRsKV50VS4K7S .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-H0QyRsKV50VS4K7S .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-H0QyRsKV50VS4K7S :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外卖订单数据分析可视化系统
用户认证
订单数据管理
多维分析
时序与对比
相关预测异常分析
SimpleUI后台
登录注册退出
个人资料与改密
订单列表分页筛选
CSV导出
后台订单维护
金额/状态/品类
城市/用户/商家
日订单量与热力图
月度对比与环比
Pearson相关矩阵
WMA预测与线性趋势
IQR异常检测
订单CRUD
用户CRUD

3.2 系统详细设计

3.2.1 用户认证模块设计

模块负责前台注册、登录、退出及会话保持,以及个人资料、头像与密码修改。用户信息存储于自定义 User 表;登录成功后将 username 写入 request.session。中间件 AuthMiddleware 对未登录访问重定向至 /app/login/,豁免 /app/login//app/register/ 与以 /admin/ 开头的后台路径。前台演示账号为 admin/123456(明文密码校验);Django 超管通过 seed_admin 创建,密码 admin123,与前台用户表分离。

3.2.1.1 用户认证流程设计

图3.5 用户认证模块流程图
#mermaid-svg-zye85TrsPvrKcv3Q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zye85TrsPvrKcv3Q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zye85TrsPvrKcv3Q .error-icon{fill:#552222;}#mermaid-svg-zye85TrsPvrKcv3Q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zye85TrsPvrKcv3Q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zye85TrsPvrKcv3Q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zye85TrsPvrKcv3Q .marker.cross{stroke:#333333;}#mermaid-svg-zye85TrsPvrKcv3Q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zye85TrsPvrKcv3Q p{margin:0;}#mermaid-svg-zye85TrsPvrKcv3Q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zye85TrsPvrKcv3Q .cluster-label text{fill:#333;}#mermaid-svg-zye85TrsPvrKcv3Q .cluster-label span{color:#333;}#mermaid-svg-zye85TrsPvrKcv3Q .cluster-label span p{background-color:transparent;}#mermaid-svg-zye85TrsPvrKcv3Q .label text,#mermaid-svg-zye85TrsPvrKcv3Q span{fill:#333;color:#333;}#mermaid-svg-zye85TrsPvrKcv3Q .node rect,#mermaid-svg-zye85TrsPvrKcv3Q .node circle,#mermaid-svg-zye85TrsPvrKcv3Q .node ellipse,#mermaid-svg-zye85TrsPvrKcv3Q .node polygon,#mermaid-svg-zye85TrsPvrKcv3Q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zye85TrsPvrKcv3Q .rough-node .label text,#mermaid-svg-zye85TrsPvrKcv3Q .node .label text,#mermaid-svg-zye85TrsPvrKcv3Q .image-shape .label,#mermaid-svg-zye85TrsPvrKcv3Q .icon-shape .label{text-anchor:middle;}#mermaid-svg-zye85TrsPvrKcv3Q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zye85TrsPvrKcv3Q .rough-node .label,#mermaid-svg-zye85TrsPvrKcv3Q .node .label,#mermaid-svg-zye85TrsPvrKcv3Q .image-shape .label,#mermaid-svg-zye85TrsPvrKcv3Q .icon-shape .label{text-align:center;}#mermaid-svg-zye85TrsPvrKcv3Q .node.clickable{cursor:pointer;}#mermaid-svg-zye85TrsPvrKcv3Q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zye85TrsPvrKcv3Q .arrowheadPath{fill:#333333;}#mermaid-svg-zye85TrsPvrKcv3Q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zye85TrsPvrKcv3Q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zye85TrsPvrKcv3Q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zye85TrsPvrKcv3Q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zye85TrsPvrKcv3Q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zye85TrsPvrKcv3Q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zye85TrsPvrKcv3Q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zye85TrsPvrKcv3Q .cluster text{fill:#333;}#mermaid-svg-zye85TrsPvrKcv3Q .cluster span{color:#333;}#mermaid-svg-zye85TrsPvrKcv3Q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zye85TrsPvrKcv3Q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zye85TrsPvrKcv3Q rect.text{fill:none;stroke-width:0;}#mermaid-svg-zye85TrsPvrKcv3Q .icon-shape,#mermaid-svg-zye85TrsPvrKcv3Q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zye85TrsPvrKcv3Q .icon-shape p,#mermaid-svg-zye85TrsPvrKcv3Q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zye85TrsPvrKcv3Q .icon-shape .label rect,#mermaid-svg-zye85TrsPvrKcv3Q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zye85TrsPvrKcv3Q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zye85TrsPvrKcv3Q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zye85TrsPvrKcv3Q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GET
POST
匹配
不匹配
通过
失败


访问 /app/login/
请求方法
渲染 login.html
读取用户名密码
User表明文校验
写入 session username
跳转 /app/home/
提示用户名或密码错误
访问 /app/register/
注册校验
创建 User 记录
跳转登录页
提示空值/重复/密码不一致
访问受保护页
session.username?
放行视图

3.2.2 订单数据管理模块设计

模块提供订单列表 /app/order_list/、CSV 导出 /app/api/export/ 与 SimpleUI 后台订单维护。列表支持按月份、城市、品类筛选,分页 20/50/100 条,展示总订单、完成单、平均客单价与 GMV 统计卡片。导出按当前筛选条件输出 UTF-8 BOM CSV,字段与 EXPORT_FIELDS 一致。

3.2.2.1 订单数据管理流程设计

图3.6 订单数据管理模块流程图
#mermaid-svg-MK9sJdznlKxGYxLM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MK9sJdznlKxGYxLM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MK9sJdznlKxGYxLM .error-icon{fill:#552222;}#mermaid-svg-MK9sJdznlKxGYxLM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MK9sJdznlKxGYxLM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MK9sJdznlKxGYxLM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MK9sJdznlKxGYxLM .marker.cross{stroke:#333333;}#mermaid-svg-MK9sJdznlKxGYxLM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MK9sJdznlKxGYxLM p{margin:0;}#mermaid-svg-MK9sJdznlKxGYxLM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MK9sJdznlKxGYxLM .cluster-label text{fill:#333;}#mermaid-svg-MK9sJdznlKxGYxLM .cluster-label span{color:#333;}#mermaid-svg-MK9sJdznlKxGYxLM .cluster-label span p{background-color:transparent;}#mermaid-svg-MK9sJdznlKxGYxLM .label text,#mermaid-svg-MK9sJdznlKxGYxLM span{fill:#333;color:#333;}#mermaid-svg-MK9sJdznlKxGYxLM .node rect,#mermaid-svg-MK9sJdznlKxGYxLM .node circle,#mermaid-svg-MK9sJdznlKxGYxLM .node ellipse,#mermaid-svg-MK9sJdznlKxGYxLM .node polygon,#mermaid-svg-MK9sJdznlKxGYxLM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MK9sJdznlKxGYxLM .rough-node .label text,#mermaid-svg-MK9sJdznlKxGYxLM .node .label text,#mermaid-svg-MK9sJdznlKxGYxLM .image-shape .label,#mermaid-svg-MK9sJdznlKxGYxLM .icon-shape .label{text-anchor:middle;}#mermaid-svg-MK9sJdznlKxGYxLM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MK9sJdznlKxGYxLM .rough-node .label,#mermaid-svg-MK9sJdznlKxGYxLM .node .label,#mermaid-svg-MK9sJdznlKxGYxLM .image-shape .label,#mermaid-svg-MK9sJdznlKxGYxLM .icon-shape .label{text-align:center;}#mermaid-svg-MK9sJdznlKxGYxLM .node.clickable{cursor:pointer;}#mermaid-svg-MK9sJdznlKxGYxLM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MK9sJdznlKxGYxLM .arrowheadPath{fill:#333333;}#mermaid-svg-MK9sJdznlKxGYxLM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MK9sJdznlKxGYxLM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MK9sJdznlKxGYxLM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MK9sJdznlKxGYxLM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MK9sJdznlKxGYxLM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MK9sJdznlKxGYxLM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MK9sJdznlKxGYxLM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MK9sJdznlKxGYxLM .cluster text{fill:#333;}#mermaid-svg-MK9sJdznlKxGYxLM .cluster span{color:#333;}#mermaid-svg-MK9sJdznlKxGYxLM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MK9sJdznlKxGYxLM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MK9sJdznlKxGYxLM rect.text{fill:none;stroke-width:0;}#mermaid-svg-MK9sJdznlKxGYxLM .icon-shape,#mermaid-svg-MK9sJdznlKxGYxLM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MK9sJdznlKxGYxLM .icon-shape p,#mermaid-svg-MK9sJdznlKxGYxLM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MK9sJdznlKxGYxLM .icon-shape .label rect,#mermaid-svg-MK9sJdznlKxGYxLM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MK9sJdznlKxGYxLM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MK9sJdznlKxGYxLM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MK9sJdznlKxGYxLM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

进入 /app/order_list/
读取 month/city/category 筛选
filtered_queryset ORM 过滤
聚合 list_stats 统计卡片
Paginator 分页渲染
导出 CSV?
api_export 写 CSV
仅浏览列表
/admin/ 维护
OrderRecordAdmin CRUD
post_save 触发 invalidate_takeaway_cache

3.2.3 多维分析模块设计

分析页路由包括 /app/order/(金额)、/app/status/(状态)、/app/category/(品类)、/app/city/(城市)、/app/user/(用户)、/app/merchant/(商家)。对应 bundle API 为 api/order-bundle/api/status-bundle/ 等,由 takeaway_sql_bundles.py 内 SQL 聚合后返回 ECharts 系列数据。各页共享月份/城市/品类筛选参数,与 filtered_queryset 逻辑一致。

3.2.3.1 多维分析流程设计

图3.7 多维分析模块流程图
#mermaid-svg-vSWjnTviHU3OcJjH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vSWjnTviHU3OcJjH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vSWjnTviHU3OcJjH .error-icon{fill:#552222;}#mermaid-svg-vSWjnTviHU3OcJjH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vSWjnTviHU3OcJjH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vSWjnTviHU3OcJjH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vSWjnTviHU3OcJjH .marker.cross{stroke:#333333;}#mermaid-svg-vSWjnTviHU3OcJjH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vSWjnTviHU3OcJjH p{margin:0;}#mermaid-svg-vSWjnTviHU3OcJjH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vSWjnTviHU3OcJjH .cluster-label text{fill:#333;}#mermaid-svg-vSWjnTviHU3OcJjH .cluster-label span{color:#333;}#mermaid-svg-vSWjnTviHU3OcJjH .cluster-label span p{background-color:transparent;}#mermaid-svg-vSWjnTviHU3OcJjH .label text,#mermaid-svg-vSWjnTviHU3OcJjH span{fill:#333;color:#333;}#mermaid-svg-vSWjnTviHU3OcJjH .node rect,#mermaid-svg-vSWjnTviHU3OcJjH .node circle,#mermaid-svg-vSWjnTviHU3OcJjH .node ellipse,#mermaid-svg-vSWjnTviHU3OcJjH .node polygon,#mermaid-svg-vSWjnTviHU3OcJjH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vSWjnTviHU3OcJjH .rough-node .label text,#mermaid-svg-vSWjnTviHU3OcJjH .node .label text,#mermaid-svg-vSWjnTviHU3OcJjH .image-shape .label,#mermaid-svg-vSWjnTviHU3OcJjH .icon-shape .label{text-anchor:middle;}#mermaid-svg-vSWjnTviHU3OcJjH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vSWjnTviHU3OcJjH .rough-node .label,#mermaid-svg-vSWjnTviHU3OcJjH .node .label,#mermaid-svg-vSWjnTviHU3OcJjH .image-shape .label,#mermaid-svg-vSWjnTviHU3OcJjH .icon-shape .label{text-align:center;}#mermaid-svg-vSWjnTviHU3OcJjH .node.clickable{cursor:pointer;}#mermaid-svg-vSWjnTviHU3OcJjH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vSWjnTviHU3OcJjH .arrowheadPath{fill:#333333;}#mermaid-svg-vSWjnTviHU3OcJjH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vSWjnTviHU3OcJjH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vSWjnTviHU3OcJjH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vSWjnTviHU3OcJjH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vSWjnTviHU3OcJjH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vSWjnTviHU3OcJjH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vSWjnTviHU3OcJjH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vSWjnTviHU3OcJjH .cluster text{fill:#333;}#mermaid-svg-vSWjnTviHU3OcJjH .cluster span{color:#333;}#mermaid-svg-vSWjnTviHU3OcJjH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vSWjnTviHU3OcJjH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vSWjnTviHU3OcJjH rect.text{fill:none;stroke-width:0;}#mermaid-svg-vSWjnTviHU3OcJjH .icon-shape,#mermaid-svg-vSWjnTviHU3OcJjH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vSWjnTviHU3OcJjH .icon-shape p,#mermaid-svg-vSWjnTviHU3OcJjH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vSWjnTviHU3OcJjH .icon-shape .label rect,#mermaid-svg-vSWjnTviHU3OcJjH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vSWjnTviHU3OcJjH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vSWjnTviHU3OcJjH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vSWjnTviHU3OcJjH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 未命中
命中
进入某分析页
渲染模块模板
前端请求对应 bundle API
takeaway_charts 读缓存
takeaway_sql_bundles SQL聚合
写入 LocMemCache
返回 JSON charts
ECharts 渲染图表

3.2.4 时序与对比模块设计

时序页 /app/time_series/ 展示最近 90 日订单量折线、小时×星期热力图与 1~14 日自相关系数。对比页 /app/comparison/ 按月聚合订单量、GMV 与环比变化率。对应 API 为 api/time-series-bundle/api/comparison-bundle/

3.2.4.1 时序与对比流程设计

图3.8 时序与对比模块流程图
#mermaid-svg-0w3TN1LTvwRNPhFG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0w3TN1LTvwRNPhFG .error-icon{fill:#552222;}#mermaid-svg-0w3TN1LTvwRNPhFG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0w3TN1LTvwRNPhFG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0w3TN1LTvwRNPhFG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0w3TN1LTvwRNPhFG .marker.cross{stroke:#333333;}#mermaid-svg-0w3TN1LTvwRNPhFG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0w3TN1LTvwRNPhFG p{margin:0;}#mermaid-svg-0w3TN1LTvwRNPhFG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG .cluster-label text{fill:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG .cluster-label span{color:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG .cluster-label span p{background-color:transparent;}#mermaid-svg-0w3TN1LTvwRNPhFG .label text,#mermaid-svg-0w3TN1LTvwRNPhFG span{fill:#333;color:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG .node rect,#mermaid-svg-0w3TN1LTvwRNPhFG .node circle,#mermaid-svg-0w3TN1LTvwRNPhFG .node ellipse,#mermaid-svg-0w3TN1LTvwRNPhFG .node polygon,#mermaid-svg-0w3TN1LTvwRNPhFG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0w3TN1LTvwRNPhFG .rough-node .label text,#mermaid-svg-0w3TN1LTvwRNPhFG .node .label text,#mermaid-svg-0w3TN1LTvwRNPhFG .image-shape .label,#mermaid-svg-0w3TN1LTvwRNPhFG .icon-shape .label{text-anchor:middle;}#mermaid-svg-0w3TN1LTvwRNPhFG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0w3TN1LTvwRNPhFG .rough-node .label,#mermaid-svg-0w3TN1LTvwRNPhFG .node .label,#mermaid-svg-0w3TN1LTvwRNPhFG .image-shape .label,#mermaid-svg-0w3TN1LTvwRNPhFG .icon-shape .label{text-align:center;}#mermaid-svg-0w3TN1LTvwRNPhFG .node.clickable{cursor:pointer;}#mermaid-svg-0w3TN1LTvwRNPhFG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0w3TN1LTvwRNPhFG .arrowheadPath{fill:#333333;}#mermaid-svg-0w3TN1LTvwRNPhFG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0w3TN1LTvwRNPhFG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0w3TN1LTvwRNPhFG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0w3TN1LTvwRNPhFG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0w3TN1LTvwRNPhFG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0w3TN1LTvwRNPhFG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0w3TN1LTvwRNPhFG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0w3TN1LTvwRNPhFG .cluster text{fill:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG .cluster span{color:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0w3TN1LTvwRNPhFG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0w3TN1LTvwRNPhFG rect.text{fill:none;stroke-width:0;}#mermaid-svg-0w3TN1LTvwRNPhFG .icon-shape,#mermaid-svg-0w3TN1LTvwRNPhFG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0w3TN1LTvwRNPhFG .icon-shape p,#mermaid-svg-0w3TN1LTvwRNPhFG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0w3TN1LTvwRNPhFG .icon-shape .label rect,#mermaid-svg-0w3TN1LTvwRNPhFG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0w3TN1LTvwRNPhFG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0w3TN1LTvwRNPhFG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0w3TN1LTvwRNPhFG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} /app/time_series/
请求 time-series-bundle
SQL 按日 GROUP BY 订单量
计算滞后 1~14 自相关
SQL 小时×星期热力聚合
返回 daily/autocorr/heatmap
/app/comparison/
请求 comparison-bundle
SQL 按月 COUNT 与 SUM GMV
计算环比 mom_change
返回月度对比 charts

3.2.5 相关预测异常分析模块设计

相关页 /app/correlation/ 展示 Pearson 矩阵与散点图;预测页 /app/prediction/ 展示日 GMV 历史与 WMA 未来 7 日预测、客单价线性趋势;异常页 /app/anomaly/ 展示 IQR/P99 阈值、异常订单时间线与金额分布。三页均附带 partials/algo_explain.html 算法说明。

3.2.5.1 相关预测异常分析流程设计

图3.9 相关预测异常分析模块流程图
#mermaid-svg-zlRhPEKYTtJQ2a9C{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-zlRhPEKYTtJQ2a9C .error-icon{fill:#552222;}#mermaid-svg-zlRhPEKYTtJQ2a9C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-zlRhPEKYTtJQ2a9C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .marker.cross{stroke:#333333;}#mermaid-svg-zlRhPEKYTtJQ2a9C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-zlRhPEKYTtJQ2a9C p{margin:0;}#mermaid-svg-zlRhPEKYTtJQ2a9C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .cluster-label text{fill:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .cluster-label span{color:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .cluster-label span p{background-color:transparent;}#mermaid-svg-zlRhPEKYTtJQ2a9C .label text,#mermaid-svg-zlRhPEKYTtJQ2a9C span{fill:#333;color:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .node rect,#mermaid-svg-zlRhPEKYTtJQ2a9C .node circle,#mermaid-svg-zlRhPEKYTtJQ2a9C .node ellipse,#mermaid-svg-zlRhPEKYTtJQ2a9C .node polygon,#mermaid-svg-zlRhPEKYTtJQ2a9C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .rough-node .label text,#mermaid-svg-zlRhPEKYTtJQ2a9C .node .label text,#mermaid-svg-zlRhPEKYTtJQ2a9C .image-shape .label,#mermaid-svg-zlRhPEKYTtJQ2a9C .icon-shape .label{text-anchor:middle;}#mermaid-svg-zlRhPEKYTtJQ2a9C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .rough-node .label,#mermaid-svg-zlRhPEKYTtJQ2a9C .node .label,#mermaid-svg-zlRhPEKYTtJQ2a9C .image-shape .label,#mermaid-svg-zlRhPEKYTtJQ2a9C .icon-shape .label{text-align:center;}#mermaid-svg-zlRhPEKYTtJQ2a9C .node.clickable{cursor:pointer;}#mermaid-svg-zlRhPEKYTtJQ2a9C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .arrowheadPath{fill:#333333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zlRhPEKYTtJQ2a9C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-zlRhPEKYTtJQ2a9C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zlRhPEKYTtJQ2a9C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-zlRhPEKYTtJQ2a9C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .cluster text{fill:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C .cluster span{color:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-zlRhPEKYTtJQ2a9C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-zlRhPEKYTtJQ2a9C rect.text{fill:none;stroke-width:0;}#mermaid-svg-zlRhPEKYTtJQ2a9C .icon-shape,#mermaid-svg-zlRhPEKYTtJQ2a9C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-zlRhPEKYTtJQ2a9C .icon-shape p,#mermaid-svg-zlRhPEKYTtJQ2a9C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-zlRhPEKYTtJQ2a9C .icon-shape .label rect,#mermaid-svg-zlRhPEKYTtJQ2a9C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-zlRhPEKYTtJQ2a9C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-zlRhPEKYTtJQ2a9C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-zlRhPEKYTtJQ2a9C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 相关
预测
异常


进入算法分析页
页面类型
correlation_bundle_sql 抽样
np.corrcoef 相关矩阵
prediction_bundle_sql 日GMV
WMA 0.5/0.3/0.2 预测7天
np.polyfit 客单价趋势
anomaly_bundle_sql 抽样金额
IQR k=1.5 有点?
k=1.0 或 P99 降级
统计超阈值订单
返回 charts + algorithms 文案

3.2.6 SimpleUI后台模块设计

后台入口 /admin/,使用 django-simpleui 皮肤。OrderRecordAdmin 提供列表展示、按月 SimpleListFilter、状态/品类/城市筛选与订单号/用户/商家搜索;UserAdmin 维护前台用户。订单变更通过 Django signal 调用 invalidate_takeaway_cache 刷新分析缓存。

3.2.6.1 SimpleUI后台流程设计

图3.10 SimpleUI后台模块流程图
#mermaid-svg-6nicXe6YS8MG2jiu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6nicXe6YS8MG2jiu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6nicXe6YS8MG2jiu .error-icon{fill:#552222;}#mermaid-svg-6nicXe6YS8MG2jiu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6nicXe6YS8MG2jiu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6nicXe6YS8MG2jiu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6nicXe6YS8MG2jiu .marker.cross{stroke:#333333;}#mermaid-svg-6nicXe6YS8MG2jiu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6nicXe6YS8MG2jiu p{margin:0;}#mermaid-svg-6nicXe6YS8MG2jiu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6nicXe6YS8MG2jiu .cluster-label text{fill:#333;}#mermaid-svg-6nicXe6YS8MG2jiu .cluster-label span{color:#333;}#mermaid-svg-6nicXe6YS8MG2jiu .cluster-label span p{background-color:transparent;}#mermaid-svg-6nicXe6YS8MG2jiu .label text,#mermaid-svg-6nicXe6YS8MG2jiu span{fill:#333;color:#333;}#mermaid-svg-6nicXe6YS8MG2jiu .node rect,#mermaid-svg-6nicXe6YS8MG2jiu .node circle,#mermaid-svg-6nicXe6YS8MG2jiu .node ellipse,#mermaid-svg-6nicXe6YS8MG2jiu .node polygon,#mermaid-svg-6nicXe6YS8MG2jiu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6nicXe6YS8MG2jiu .rough-node .label text,#mermaid-svg-6nicXe6YS8MG2jiu .node .label text,#mermaid-svg-6nicXe6YS8MG2jiu .image-shape .label,#mermaid-svg-6nicXe6YS8MG2jiu .icon-shape .label{text-anchor:middle;}#mermaid-svg-6nicXe6YS8MG2jiu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6nicXe6YS8MG2jiu .rough-node .label,#mermaid-svg-6nicXe6YS8MG2jiu .node .label,#mermaid-svg-6nicXe6YS8MG2jiu .image-shape .label,#mermaid-svg-6nicXe6YS8MG2jiu .icon-shape .label{text-align:center;}#mermaid-svg-6nicXe6YS8MG2jiu .node.clickable{cursor:pointer;}#mermaid-svg-6nicXe6YS8MG2jiu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6nicXe6YS8MG2jiu .arrowheadPath{fill:#333333;}#mermaid-svg-6nicXe6YS8MG2jiu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6nicXe6YS8MG2jiu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6nicXe6YS8MG2jiu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6nicXe6YS8MG2jiu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6nicXe6YS8MG2jiu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6nicXe6YS8MG2jiu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6nicXe6YS8MG2jiu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6nicXe6YS8MG2jiu .cluster text{fill:#333;}#mermaid-svg-6nicXe6YS8MG2jiu .cluster span{color:#333;}#mermaid-svg-6nicXe6YS8MG2jiu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6nicXe6YS8MG2jiu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6nicXe6YS8MG2jiu rect.text{fill:none;stroke-width:0;}#mermaid-svg-6nicXe6YS8MG2jiu .icon-shape,#mermaid-svg-6nicXe6YS8MG2jiu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6nicXe6YS8MG2jiu .icon-shape p,#mermaid-svg-6nicXe6YS8MG2jiu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6nicXe6YS8MG2jiu .icon-shape .label rect,#mermaid-svg-6nicXe6YS8MG2jiu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6nicXe6YS8MG2jiu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6nicXe6YS8MG2jiu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6nicXe6YS8MG2jiu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

OrderRecord
User
访问 /admin/
Django 超管已登录?
SimpleUI 登录页
auth_user 校验 admin/admin123
进入后台首页
选择模型
列表/筛选/搜索/编辑
前台用户维护
post_save/post_delete 信号
invalidate_takeaway_cache

3.3 数据库设计

3.3.1 数据库关系设计

系统业务库为 MySQL 中的 takeaway。核心实体为前台用户表 app_user(模型 User)与外卖订单表 app_orderrecord(模型 OrderRecord)。订单表通过 user_idmerchant_id 字符串字段记录业务标识, 建立外键至用户表;登录态通过 Session 绑定前台用户名;Django 超管使用独立 auth_user 表。模型文件中虽存在历史遗留 WindRecord,本系统论文与功能均涉及风电数据。

图3.11 数据库表关系图
#mermaid-svg-YHiviaJOzPHpQOXY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YHiviaJOzPHpQOXY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YHiviaJOzPHpQOXY .error-icon{fill:#552222;}#mermaid-svg-YHiviaJOzPHpQOXY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YHiviaJOzPHpQOXY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YHiviaJOzPHpQOXY .marker.cross{stroke:#333333;}#mermaid-svg-YHiviaJOzPHpQOXY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YHiviaJOzPHpQOXY p{margin:0;}#mermaid-svg-YHiviaJOzPHpQOXY .entityBox{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-YHiviaJOzPHpQOXY .relationshipLabelBox{fill:hsl(80, 100%, 96.2745098039%);opacity:0.7;background-color:hsl(80, 100%, 96.2745098039%);}#mermaid-svg-YHiviaJOzPHpQOXY .relationshipLabelBox rect{opacity:0.5;}#mermaid-svg-YHiviaJOzPHpQOXY .labelBkg{background-color:rgba(248.6666666666, 255, 235.9999999999, 0.5);}#mermaid-svg-YHiviaJOzPHpQOXY .edgeLabel .label{fill:#9370DB;font-size:14px;}#mermaid-svg-YHiviaJOzPHpQOXY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YHiviaJOzPHpQOXY .edge-pattern-dashed{stroke-dasharray:8,8;}#mermaid-svg-YHiviaJOzPHpQOXY .node rect,#mermaid-svg-YHiviaJOzPHpQOXY .node circle,#mermaid-svg-YHiviaJOzPHpQOXY .node ellipse,#mermaid-svg-YHiviaJOzPHpQOXY .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YHiviaJOzPHpQOXY .relationshipLine{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-YHiviaJOzPHpQOXY .marker{fill:none!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-YHiviaJOzPHpQOXY .edgeLabel{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YHiviaJOzPHpQOXY .edgeLabel .label rect{fill:rgba(232,232,232, 0.8);}#mermaid-svg-YHiviaJOzPHpQOXY .edgeLabel .label text{fill:#333;}#mermaid-svg-YHiviaJOzPHpQOXY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 逻辑关联user_id
后台维护
USER
int
id
PK
主键
varchar
username
用户名
varchar
password
密码明文
varchar
sex
性别
varchar
address
地址
file
avatar
头像
varchar
textarea
个人简介
datetime
createTime
创建时间
ORDERRECORD
int
id
PK
主键
varchar
order_id
UK
订单号唯一
varchar
user_id
用户ID
varchar
merchant_id
商家ID
varchar
food_category
菜品品类
float
order_amount
订单金额
varchar
order_status
订单状态
varchar
city
城市
datetime
order_time
下单时间
AUTH_USER
int
id
PK
Django超管
varchar
username
管理员用户名
varchar
password
哈希密码

表3.1 User 表结构

字段名称 字段类型 字段说明 是否主键 是否为空
id int 唯一标识符
username varchar(255) 用户名
password varchar(255) 密码(明文存储)
sex varchar(255) 性别
address varchar(255) 地址
avatar varchar(100) 头像路径
textarea varchar(255) 个人简介
createTime datetime 创建时间

表3.2 OrderRecord 表结构

字段名称 字段类型 字段说明 是否主键 是否为空
id int 唯一标识符
order_id varchar(32) 订单号(唯一)
user_id varchar(32) 用户 ID
merchant_id varchar(32) 商家 ID
food_category varchar(32) 菜品品类
order_amount float 订单金额(元)
order_status varchar(16) 订单状态(完成/取消)
city varchar(32) 城市
order_time datetime 下单时间

4 系统实现

4.0 系统整体实现流程

系统实现遵循模块化开发原则,按环境准备、库表迁移、数据导入、认证与列表页、分析 bundle、算法页、SimpleUI 后台到联调测试的顺序推进,最终以 8040 端口完成本地演示部署。

图4.0 系统整体实现流程图
#mermaid-svg-qmKueoeSV58MbP2W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qmKueoeSV58MbP2W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qmKueoeSV58MbP2W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qmKueoeSV58MbP2W .error-icon{fill:#552222;}#mermaid-svg-qmKueoeSV58MbP2W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qmKueoeSV58MbP2W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qmKueoeSV58MbP2W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qmKueoeSV58MbP2W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qmKueoeSV58MbP2W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qmKueoeSV58MbP2W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qmKueoeSV58MbP2W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qmKueoeSV58MbP2W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qmKueoeSV58MbP2W .marker.cross{stroke:#333333;}#mermaid-svg-qmKueoeSV58MbP2W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qmKueoeSV58MbP2W p{margin:0;}#mermaid-svg-qmKueoeSV58MbP2W .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qmKueoeSV58MbP2W .cluster-label text{fill:#333;}#mermaid-svg-qmKueoeSV58MbP2W .cluster-label span{color:#333;}#mermaid-svg-qmKueoeSV58MbP2W .cluster-label span p{background-color:transparent;}#mermaid-svg-qmKueoeSV58MbP2W .label text,#mermaid-svg-qmKueoeSV58MbP2W span{fill:#333;color:#333;}#mermaid-svg-qmKueoeSV58MbP2W .node rect,#mermaid-svg-qmKueoeSV58MbP2W .node circle,#mermaid-svg-qmKueoeSV58MbP2W .node ellipse,#mermaid-svg-qmKueoeSV58MbP2W .node polygon,#mermaid-svg-qmKueoeSV58MbP2W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qmKueoeSV58MbP2W .rough-node .label text,#mermaid-svg-qmKueoeSV58MbP2W .node .label text,#mermaid-svg-qmKueoeSV58MbP2W .image-shape .label,#mermaid-svg-qmKueoeSV58MbP2W .icon-shape .label{text-anchor:middle;}#mermaid-svg-qmKueoeSV58MbP2W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qmKueoeSV58MbP2W .rough-node .label,#mermaid-svg-qmKueoeSV58MbP2W .node .label,#mermaid-svg-qmKueoeSV58MbP2W .image-shape .label,#mermaid-svg-qmKueoeSV58MbP2W .icon-shape .label{text-align:center;}#mermaid-svg-qmKueoeSV58MbP2W .node.clickable{cursor:pointer;}#mermaid-svg-qmKueoeSV58MbP2W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qmKueoeSV58MbP2W .arrowheadPath{fill:#333333;}#mermaid-svg-qmKueoeSV58MbP2W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qmKueoeSV58MbP2W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qmKueoeSV58MbP2W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qmKueoeSV58MbP2W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qmKueoeSV58MbP2W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qmKueoeSV58MbP2W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qmKueoeSV58MbP2W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qmKueoeSV58MbP2W .cluster text{fill:#333;}#mermaid-svg-qmKueoeSV58MbP2W .cluster span{color:#333;}#mermaid-svg-qmKueoeSV58MbP2W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qmKueoeSV58MbP2W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qmKueoeSV58MbP2W rect.text{fill:none;stroke-width:0;}#mermaid-svg-qmKueoeSV58MbP2W .icon-shape,#mermaid-svg-qmKueoeSV58MbP2W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qmKueoeSV58MbP2W .icon-shape p,#mermaid-svg-qmKueoeSV58MbP2W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qmKueoeSV58MbP2W .icon-shape .label rect,#mermaid-svg-qmKueoeSV58MbP2W .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qmKueoeSV58MbP2W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qmKueoeSV58MbP2W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qmKueoeSV58MbP2W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

项目初始化
安装 requirements 依赖
创建 MySQL 库 takeaway
导入订单 CSV
实现用户认证与订单列表
实现多维分析与算法页
实现 SimpleUI 后台
功能联调测试
测试通过?
问题修复
runserver 8040 部署演示
系统完成

4.1 用户认证模块的实现

登录视图校验 User 表用户名与密码,成功后写入 Session 并跳转首页;注册校验空值、重复与两次密码一致性。AuthMiddleware 统一拦截未登录请求。个人中心与改密分别对应 changeSelfInfochangePassword 路由。

功能实现流程设计

图4.1 用户认证模块实现流程图
#mermaid-svg-yUHZR3pxIxk3CqxS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yUHZR3pxIxk3CqxS .error-icon{fill:#552222;}#mermaid-svg-yUHZR3pxIxk3CqxS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yUHZR3pxIxk3CqxS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yUHZR3pxIxk3CqxS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yUHZR3pxIxk3CqxS .marker.cross{stroke:#333333;}#mermaid-svg-yUHZR3pxIxk3CqxS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yUHZR3pxIxk3CqxS p{margin:0;}#mermaid-svg-yUHZR3pxIxk3CqxS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS .cluster-label text{fill:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS .cluster-label span{color:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS .cluster-label span p{background-color:transparent;}#mermaid-svg-yUHZR3pxIxk3CqxS .label text,#mermaid-svg-yUHZR3pxIxk3CqxS span{fill:#333;color:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS .node rect,#mermaid-svg-yUHZR3pxIxk3CqxS .node circle,#mermaid-svg-yUHZR3pxIxk3CqxS .node ellipse,#mermaid-svg-yUHZR3pxIxk3CqxS .node polygon,#mermaid-svg-yUHZR3pxIxk3CqxS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yUHZR3pxIxk3CqxS .rough-node .label text,#mermaid-svg-yUHZR3pxIxk3CqxS .node .label text,#mermaid-svg-yUHZR3pxIxk3CqxS .image-shape .label,#mermaid-svg-yUHZR3pxIxk3CqxS .icon-shape .label{text-anchor:middle;}#mermaid-svg-yUHZR3pxIxk3CqxS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yUHZR3pxIxk3CqxS .rough-node .label,#mermaid-svg-yUHZR3pxIxk3CqxS .node .label,#mermaid-svg-yUHZR3pxIxk3CqxS .image-shape .label,#mermaid-svg-yUHZR3pxIxk3CqxS .icon-shape .label{text-align:center;}#mermaid-svg-yUHZR3pxIxk3CqxS .node.clickable{cursor:pointer;}#mermaid-svg-yUHZR3pxIxk3CqxS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yUHZR3pxIxk3CqxS .arrowheadPath{fill:#333333;}#mermaid-svg-yUHZR3pxIxk3CqxS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yUHZR3pxIxk3CqxS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yUHZR3pxIxk3CqxS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yUHZR3pxIxk3CqxS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yUHZR3pxIxk3CqxS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yUHZR3pxIxk3CqxS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yUHZR3pxIxk3CqxS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yUHZR3pxIxk3CqxS .cluster text{fill:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS .cluster span{color:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yUHZR3pxIxk3CqxS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yUHZR3pxIxk3CqxS rect.text{fill:none;stroke-width:0;}#mermaid-svg-yUHZR3pxIxk3CqxS .icon-shape,#mermaid-svg-yUHZR3pxIxk3CqxS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yUHZR3pxIxk3CqxS .icon-shape p,#mermaid-svg-yUHZR3pxIxk3CqxS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yUHZR3pxIxk3CqxS .icon-shape .label rect,#mermaid-svg-yUHZR3pxIxk3CqxS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yUHZR3pxIxk3CqxS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yUHZR3pxIxk3CqxS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yUHZR3pxIxk3CqxS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GET
POST


否且无Session

访问 /app/login/
GET或POST
渲染 login.html
查询 User 匹配
匹配成功?
session'username'=uname
redirect /app/home/
返回错误提示
AuthMiddleware 拦截
豁免路径?
放行

核心代码实现
python 复制代码
# 来源:app/views.py --- 登录(节选)
def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    username = request.POST.get("username")
    password = request.POST.get("password")
    try:
        User.objects.get(username=username, password=password)
        request.session["username"] = username
        return redirect("/app/home/")
    except User.DoesNotExist:
        return errorResponse.errorResponse(request, "用户名或密码错误")
python 复制代码
# 来源:middleware/auth.py --- 登录拦截
class AuthMiddleware(MiddlewareMixin):
    def process_request(self, request):
        exempt_paths = ['/app/login/', '/app/register/', '/admin/']
        for path in exempt_paths:
            if request.path_info.startswith(path):
                return None
        if not request.session.get('username'):
            return redirect('/app/login/')
        return None
实现效果展示

访问路径http://127.0.0.1:8040/app/login/(默认账号 admin / 123456

界面截图见第 4.7 节图4.7。

4.2 订单数据管理模块的实现

order_list 读取筛选参数并调用 filtered_queryset 过滤,聚合 list_stats 统计卡片,使用 Paginator 分页渲染。api_export 按筛选条件输出 CSV。后台 OrderRecordAdmin 提供检索与按月筛选;信号处理器在订单变更时调用 invalidate_takeaway_cache

功能实现流程设计

图4.2 订单数据管理模块实现流程图
#mermaid-svg-CWIQ5DqsvT4KiWfO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CWIQ5DqsvT4KiWfO .error-icon{fill:#552222;}#mermaid-svg-CWIQ5DqsvT4KiWfO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CWIQ5DqsvT4KiWfO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .marker.cross{stroke:#333333;}#mermaid-svg-CWIQ5DqsvT4KiWfO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CWIQ5DqsvT4KiWfO p{margin:0;}#mermaid-svg-CWIQ5DqsvT4KiWfO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .cluster-label text{fill:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .cluster-label span{color:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .cluster-label span p{background-color:transparent;}#mermaid-svg-CWIQ5DqsvT4KiWfO .label text,#mermaid-svg-CWIQ5DqsvT4KiWfO span{fill:#333;color:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .node rect,#mermaid-svg-CWIQ5DqsvT4KiWfO .node circle,#mermaid-svg-CWIQ5DqsvT4KiWfO .node ellipse,#mermaid-svg-CWIQ5DqsvT4KiWfO .node polygon,#mermaid-svg-CWIQ5DqsvT4KiWfO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .rough-node .label text,#mermaid-svg-CWIQ5DqsvT4KiWfO .node .label text,#mermaid-svg-CWIQ5DqsvT4KiWfO .image-shape .label,#mermaid-svg-CWIQ5DqsvT4KiWfO .icon-shape .label{text-anchor:middle;}#mermaid-svg-CWIQ5DqsvT4KiWfO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .rough-node .label,#mermaid-svg-CWIQ5DqsvT4KiWfO .node .label,#mermaid-svg-CWIQ5DqsvT4KiWfO .image-shape .label,#mermaid-svg-CWIQ5DqsvT4KiWfO .icon-shape .label{text-align:center;}#mermaid-svg-CWIQ5DqsvT4KiWfO .node.clickable{cursor:pointer;}#mermaid-svg-CWIQ5DqsvT4KiWfO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .arrowheadPath{fill:#333333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CWIQ5DqsvT4KiWfO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CWIQ5DqsvT4KiWfO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CWIQ5DqsvT4KiWfO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CWIQ5DqsvT4KiWfO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .cluster text{fill:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO .cluster span{color:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CWIQ5DqsvT4KiWfO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CWIQ5DqsvT4KiWfO rect.text{fill:none;stroke-width:0;}#mermaid-svg-CWIQ5DqsvT4KiWfO .icon-shape,#mermaid-svg-CWIQ5DqsvT4KiWfO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CWIQ5DqsvT4KiWfO .icon-shape p,#mermaid-svg-CWIQ5DqsvT4KiWfO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CWIQ5DqsvT4KiWfO .icon-shape .label rect,#mermaid-svg-CWIQ5DqsvT4KiWfO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CWIQ5DqsvT4KiWfO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CWIQ5DqsvT4KiWfO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CWIQ5DqsvT4KiWfO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

访问 /app/order_list/
读取筛选参数
filtered_queryset 过滤
聚合 list_stats
Paginator 分页
渲染 order_list.html
导出?
api/export 输出 CSV
结束

核心代码实现
python 复制代码
# 来源:app/views.py --- 订单列表(节选)
def order_list(request):
    userInfo = _user(request)
    base_qs = filtered_queryset(request)
    completed_qs = base_qs.filter(order_status=STATUS_COMPLETED)
    list_stats = {
        "total": base_qs.count(),
        "completed": completed_qs.count(),
        "avg_amount": completed_qs.aggregate(a=Avg("order_amount"))["a"] or 0,
        "total_gmv": completed_qs.aggregate(s=Sum("order_amount"))["s"] or 0,
    }
    paginator = Paginator(base_qs.order_by("-order_time"), page_size)
    page_obj = paginator.get_page(request.GET.get("page"))
    return render(request, "order_list.html", {...})
python 复制代码
# 来源:app/signals.py --- 缓存失效
@receiver(post_save, sender=OrderRecord)
@receiver(post_delete, sender=OrderRecord)
def order_record_changed(sender, **kwargs):
    invalidate_takeaway_cache()
实现效果展示

访问路径http://127.0.0.1:8040/app/order_list/http://127.0.0.1:8040/app/api/export/

界面截图见第 4.7 节图4.9。

4.3 多维分析模块的实现

各分析视图通过 _module_page 渲染模板,前端 takeaway_pages.js 并行请求 api/order-bundle/api/status-bundle/ 等接口。聚合逻辑位于 takeaway_sql_bundles.py,例如 order_bundle_sql 用 SQL 计算金额分桶与分时订单量,category_bundle_sql 统计品类饼图与客单价。

功能实现流程设计

图4.3 多维分析模块实现流程图
#mermaid-svg-vy97t6xg2WjAGRVm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vy97t6xg2WjAGRVm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vy97t6xg2WjAGRVm .error-icon{fill:#552222;}#mermaid-svg-vy97t6xg2WjAGRVm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vy97t6xg2WjAGRVm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vy97t6xg2WjAGRVm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vy97t6xg2WjAGRVm .marker.cross{stroke:#333333;}#mermaid-svg-vy97t6xg2WjAGRVm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vy97t6xg2WjAGRVm p{margin:0;}#mermaid-svg-vy97t6xg2WjAGRVm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vy97t6xg2WjAGRVm .cluster-label text{fill:#333;}#mermaid-svg-vy97t6xg2WjAGRVm .cluster-label span{color:#333;}#mermaid-svg-vy97t6xg2WjAGRVm .cluster-label span p{background-color:transparent;}#mermaid-svg-vy97t6xg2WjAGRVm .label text,#mermaid-svg-vy97t6xg2WjAGRVm span{fill:#333;color:#333;}#mermaid-svg-vy97t6xg2WjAGRVm .node rect,#mermaid-svg-vy97t6xg2WjAGRVm .node circle,#mermaid-svg-vy97t6xg2WjAGRVm .node ellipse,#mermaid-svg-vy97t6xg2WjAGRVm .node polygon,#mermaid-svg-vy97t6xg2WjAGRVm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vy97t6xg2WjAGRVm .rough-node .label text,#mermaid-svg-vy97t6xg2WjAGRVm .node .label text,#mermaid-svg-vy97t6xg2WjAGRVm .image-shape .label,#mermaid-svg-vy97t6xg2WjAGRVm .icon-shape .label{text-anchor:middle;}#mermaid-svg-vy97t6xg2WjAGRVm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vy97t6xg2WjAGRVm .rough-node .label,#mermaid-svg-vy97t6xg2WjAGRVm .node .label,#mermaid-svg-vy97t6xg2WjAGRVm .image-shape .label,#mermaid-svg-vy97t6xg2WjAGRVm .icon-shape .label{text-align:center;}#mermaid-svg-vy97t6xg2WjAGRVm .node.clickable{cursor:pointer;}#mermaid-svg-vy97t6xg2WjAGRVm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vy97t6xg2WjAGRVm .arrowheadPath{fill:#333333;}#mermaid-svg-vy97t6xg2WjAGRVm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vy97t6xg2WjAGRVm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vy97t6xg2WjAGRVm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vy97t6xg2WjAGRVm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vy97t6xg2WjAGRVm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vy97t6xg2WjAGRVm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vy97t6xg2WjAGRVm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vy97t6xg2WjAGRVm .cluster text{fill:#333;}#mermaid-svg-vy97t6xg2WjAGRVm .cluster span{color:#333;}#mermaid-svg-vy97t6xg2WjAGRVm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vy97t6xg2WjAGRVm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vy97t6xg2WjAGRVm rect.text{fill:none;stroke-width:0;}#mermaid-svg-vy97t6xg2WjAGRVm .icon-shape,#mermaid-svg-vy97t6xg2WjAGRVm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vy97t6xg2WjAGRVm .icon-shape p,#mermaid-svg-vy97t6xg2WjAGRVm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vy97t6xg2WjAGRVm .icon-shape .label rect,#mermaid-svg-vy97t6xg2WjAGRVm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vy97t6xg2WjAGRVm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vy97t6xg2WjAGRVm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vy97t6xg2WjAGRVm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 访问 /app/order/ 等分析页
_module_page 渲染模板
前端请求 api/*-bundle/
takeaway_sql_bundles SQL 聚合
返回系列 JSON
ECharts 绘制图表

核心代码实现
python 复制代码
# 来源:app/urls.py --- 分析路由与 API(节选)
path("order/", views.module_order, name="module_order"),
path("status/", views.module_status, name="module_status"),
path("category/", views.module_category, name="module_category"),
path("api/order-bundle/", api_views.api_order_bundle, name="api_order_bundle"),
path("api/status-bundle/", api_views.api_status_bundle, name="api_status_bundle"),
python 复制代码
# 来源:app/utils/takeaway_sql_bundles.py --- 金额分桶聚合(节选)
hist_rows = _fetch(
    f"""
    SELECT FLOOR(LEAST(order_amount, 199.99) / 10) * 10 AS bucket, COUNT(*) AS cnt
    FROM `{table}`
    WHERE {where_sql} AND order_status = %s AND order_amount IS NOT NULL
    GROUP BY bucket ORDER BY bucket
    """,
    [*params, STATUS_COMPLETED],
)
实现效果展示

访问路径

  • 订单金额:http://127.0.0.1:8040/app/order/
  • 订单状态:http://127.0.0.1:8040/app/status/
  • 品类分析:http://127.0.0.1:8040/app/category/
  • 城市分析:http://127.0.0.1:8040/app/city/
  • 用户分析:http://127.0.0.1:8040/app/user/
  • 商家分析:http://127.0.0.1:8040/app/merchant/

界面截图见第 4.7 节图4.10~图4.15。

4.4 时序与对比模块的实现

time_series_bundle_sql 按日统计订单量并计算 1~14 日自相关;按小时与星期聚合热力数据。comparison_bundle_sql 按月统计订单量与 GMV,并计算环比变化率。

功能实现流程设计

图4.4 时序与对比模块实现流程图
#mermaid-svg-TGnOizZB609wBSmy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TGnOizZB609wBSmy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TGnOizZB609wBSmy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TGnOizZB609wBSmy .error-icon{fill:#552222;}#mermaid-svg-TGnOizZB609wBSmy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TGnOizZB609wBSmy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TGnOizZB609wBSmy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TGnOizZB609wBSmy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TGnOizZB609wBSmy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TGnOizZB609wBSmy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TGnOizZB609wBSmy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TGnOizZB609wBSmy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TGnOizZB609wBSmy .marker.cross{stroke:#333333;}#mermaid-svg-TGnOizZB609wBSmy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TGnOizZB609wBSmy p{margin:0;}#mermaid-svg-TGnOizZB609wBSmy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TGnOizZB609wBSmy .cluster-label text{fill:#333;}#mermaid-svg-TGnOizZB609wBSmy .cluster-label span{color:#333;}#mermaid-svg-TGnOizZB609wBSmy .cluster-label span p{background-color:transparent;}#mermaid-svg-TGnOizZB609wBSmy .label text,#mermaid-svg-TGnOizZB609wBSmy span{fill:#333;color:#333;}#mermaid-svg-TGnOizZB609wBSmy .node rect,#mermaid-svg-TGnOizZB609wBSmy .node circle,#mermaid-svg-TGnOizZB609wBSmy .node ellipse,#mermaid-svg-TGnOizZB609wBSmy .node polygon,#mermaid-svg-TGnOizZB609wBSmy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TGnOizZB609wBSmy .rough-node .label text,#mermaid-svg-TGnOizZB609wBSmy .node .label text,#mermaid-svg-TGnOizZB609wBSmy .image-shape .label,#mermaid-svg-TGnOizZB609wBSmy .icon-shape .label{text-anchor:middle;}#mermaid-svg-TGnOizZB609wBSmy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TGnOizZB609wBSmy .rough-node .label,#mermaid-svg-TGnOizZB609wBSmy .node .label,#mermaid-svg-TGnOizZB609wBSmy .image-shape .label,#mermaid-svg-TGnOizZB609wBSmy .icon-shape .label{text-align:center;}#mermaid-svg-TGnOizZB609wBSmy .node.clickable{cursor:pointer;}#mermaid-svg-TGnOizZB609wBSmy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TGnOizZB609wBSmy .arrowheadPath{fill:#333333;}#mermaid-svg-TGnOizZB609wBSmy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TGnOizZB609wBSmy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TGnOizZB609wBSmy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TGnOizZB609wBSmy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TGnOizZB609wBSmy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TGnOizZB609wBSmy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TGnOizZB609wBSmy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TGnOizZB609wBSmy .cluster text{fill:#333;}#mermaid-svg-TGnOizZB609wBSmy .cluster span{color:#333;}#mermaid-svg-TGnOizZB609wBSmy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TGnOizZB609wBSmy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TGnOizZB609wBSmy rect.text{fill:none;stroke-width:0;}#mermaid-svg-TGnOizZB609wBSmy .icon-shape,#mermaid-svg-TGnOizZB609wBSmy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TGnOizZB609wBSmy .icon-shape p,#mermaid-svg-TGnOizZB609wBSmy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TGnOizZB609wBSmy .icon-shape .label rect,#mermaid-svg-TGnOizZB609wBSmy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TGnOizZB609wBSmy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TGnOizZB609wBSmy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TGnOizZB609wBSmy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 时序
对比
访问 time_series 或 comparison
按日或按月 GROUP BY
时序或对比
计算 1~14 日自相关
计算月度环比
返回 bundle JSON
前端渲染图表

核心代码实现
python 复制代码
# 来源:app/utils/takeaway_sql_bundles.py --- 自相关(节选)
arr = np.asarray(values, dtype=float)
autocorr = {}
for lag in range(1, 15):
    if len(arr) > lag + 5:
        c = np.corrcoef(arr[:-lag], arr[lag:])[0, 1]
        autocorr[str(lag)] = _num(float(c) if c == c else 0, 3)
python 复制代码
# 来源:app/utils/takeaway_sql_bundles.py --- 月度环比(节选)
mom = []
for i, cur in enumerate(gmv_vals):
    if i == 0 or not gmv_vals[i - 1]:
        mom.append(0)
    else:
        mom.append(_num((cur - gmv_vals[i - 1]) / gmv_vals[i - 1] * 100, 1))
实现效果展示

访问路径

  • 时序分析:http://127.0.0.1:8040/app/time_series/
  • 月度对比:http://127.0.0.1:8040/app/comparison/

界面截图见第 4.7 节图4.16、图4.20。

4.5 相关预测异常分析模块的实现

correlation_bundle_sqlMOD(id,25)=0 抽样并计算 Pearson 矩阵;prediction_bundle_sql 用 WMA 权重 0.5/0.3/0.2 预测未来 7 日 GMV,并用 np.polyfit 拟合客单价趋势;anomaly_bundle_sql 依次尝试 IQR k=1.5k=1.0P99 阈值策略。

功能实现流程设计

图4.5 相关预测异常分析模块实现流程图
#mermaid-svg-ZKA9DG2dOCThTZig{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZKA9DG2dOCThTZig .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZKA9DG2dOCThTZig .error-icon{fill:#552222;}#mermaid-svg-ZKA9DG2dOCThTZig .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZKA9DG2dOCThTZig .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZKA9DG2dOCThTZig .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZKA9DG2dOCThTZig .marker.cross{stroke:#333333;}#mermaid-svg-ZKA9DG2dOCThTZig svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZKA9DG2dOCThTZig p{margin:0;}#mermaid-svg-ZKA9DG2dOCThTZig .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZKA9DG2dOCThTZig .cluster-label text{fill:#333;}#mermaid-svg-ZKA9DG2dOCThTZig .cluster-label span{color:#333;}#mermaid-svg-ZKA9DG2dOCThTZig .cluster-label span p{background-color:transparent;}#mermaid-svg-ZKA9DG2dOCThTZig .label text,#mermaid-svg-ZKA9DG2dOCThTZig span{fill:#333;color:#333;}#mermaid-svg-ZKA9DG2dOCThTZig .node rect,#mermaid-svg-ZKA9DG2dOCThTZig .node circle,#mermaid-svg-ZKA9DG2dOCThTZig .node ellipse,#mermaid-svg-ZKA9DG2dOCThTZig .node polygon,#mermaid-svg-ZKA9DG2dOCThTZig .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZKA9DG2dOCThTZig .rough-node .label text,#mermaid-svg-ZKA9DG2dOCThTZig .node .label text,#mermaid-svg-ZKA9DG2dOCThTZig .image-shape .label,#mermaid-svg-ZKA9DG2dOCThTZig .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZKA9DG2dOCThTZig .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZKA9DG2dOCThTZig .rough-node .label,#mermaid-svg-ZKA9DG2dOCThTZig .node .label,#mermaid-svg-ZKA9DG2dOCThTZig .image-shape .label,#mermaid-svg-ZKA9DG2dOCThTZig .icon-shape .label{text-align:center;}#mermaid-svg-ZKA9DG2dOCThTZig .node.clickable{cursor:pointer;}#mermaid-svg-ZKA9DG2dOCThTZig .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZKA9DG2dOCThTZig .arrowheadPath{fill:#333333;}#mermaid-svg-ZKA9DG2dOCThTZig .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZKA9DG2dOCThTZig .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZKA9DG2dOCThTZig .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZKA9DG2dOCThTZig .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZKA9DG2dOCThTZig .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZKA9DG2dOCThTZig .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZKA9DG2dOCThTZig .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZKA9DG2dOCThTZig .cluster text{fill:#333;}#mermaid-svg-ZKA9DG2dOCThTZig .cluster span{color:#333;}#mermaid-svg-ZKA9DG2dOCThTZig div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZKA9DG2dOCThTZig .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZKA9DG2dOCThTZig rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZKA9DG2dOCThTZig .icon-shape,#mermaid-svg-ZKA9DG2dOCThTZig .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZKA9DG2dOCThTZig .icon-shape p,#mermaid-svg-ZKA9DG2dOCThTZig .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZKA9DG2dOCThTZig .icon-shape .label rect,#mermaid-svg-ZKA9DG2dOCThTZig .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZKA9DG2dOCThTZig .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZKA9DG2dOCThTZig .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZKA9DG2dOCThTZig :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 相关
预测
异常
访问 correlation/prediction/anomaly
抽样或按日聚合
算法类型
Pearson 相关矩阵
WMA 0.5/0.3/0.2
IQR / P99 阈值
返回结果 JSON
前端展示

核心代码实现
python 复制代码
# 来源:app/utils/takeaway_sql_bundles.py --- WMA 预测(节选)
if len(gmv_vals) >= 14:
    recent = gmv_vals[-14:]
    for i in range(7):
        if len(recent) >= 3:
            pred = 0.5 * recent[-1] + 0.3 * recent[-2] + 0.2 * recent[-3]
        else:
            pred = recent[-1]
        forecast_values.append(_num(pred))
        recent = recent[1:] + [pred]
python 复制代码
# 来源:app/utils/takeaway_sql_bundles.py --- IQR 异常检测(节选)
stats, low, high = _iqr_bounds(sample_amounts, k=1.5)
outlier_count = _count_above(high) if stats else 0
if outlier_count == 0 and sample_amounts:
    stats, low, high = _iqr_bounds(sample_amounts, k=1.0)
    mode = "iqr_tight"
if outlier_count == 0 and sample_amounts:
    p99 = float(np.percentile(np.asarray(sample_amounts, dtype=float), 99))
    high = p99
    mode = "p99"
实现效果展示

访问路径

  • 相关分析:http://127.0.0.1:8040/app/correlation/
  • 预测分析:http://127.0.0.1:8040/app/prediction/
  • 异常检测:http://127.0.0.1:8040/app/anomaly/

界面截图见第 4.7 节图4.17~图4.19。

4.6 SimpleUI后台模块的实现

app/admin.py 注册 UserAdminOrderRecordAdmin,配置 SimpleUI 菜单分组「外卖订单数据」「用户与权限」。OrderMonthFilter 使用 DATE_FORMAT 按月筛选,规避 USE_TZdate_hierarchy 兼容问题。后台首页链接可跳转前台 /app/home/

功能实现流程设计

图4.6 SimpleUI后台模块实现流程图
#mermaid-svg-DqOY5f7E7u3Air4h{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DqOY5f7E7u3Air4h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DqOY5f7E7u3Air4h .error-icon{fill:#552222;}#mermaid-svg-DqOY5f7E7u3Air4h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DqOY5f7E7u3Air4h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DqOY5f7E7u3Air4h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DqOY5f7E7u3Air4h .marker.cross{stroke:#333333;}#mermaid-svg-DqOY5f7E7u3Air4h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DqOY5f7E7u3Air4h p{margin:0;}#mermaid-svg-DqOY5f7E7u3Air4h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DqOY5f7E7u3Air4h .cluster-label text{fill:#333;}#mermaid-svg-DqOY5f7E7u3Air4h .cluster-label span{color:#333;}#mermaid-svg-DqOY5f7E7u3Air4h .cluster-label span p{background-color:transparent;}#mermaid-svg-DqOY5f7E7u3Air4h .label text,#mermaid-svg-DqOY5f7E7u3Air4h span{fill:#333;color:#333;}#mermaid-svg-DqOY5f7E7u3Air4h .node rect,#mermaid-svg-DqOY5f7E7u3Air4h .node circle,#mermaid-svg-DqOY5f7E7u3Air4h .node ellipse,#mermaid-svg-DqOY5f7E7u3Air4h .node polygon,#mermaid-svg-DqOY5f7E7u3Air4h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DqOY5f7E7u3Air4h .rough-node .label text,#mermaid-svg-DqOY5f7E7u3Air4h .node .label text,#mermaid-svg-DqOY5f7E7u3Air4h .image-shape .label,#mermaid-svg-DqOY5f7E7u3Air4h .icon-shape .label{text-anchor:middle;}#mermaid-svg-DqOY5f7E7u3Air4h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DqOY5f7E7u3Air4h .rough-node .label,#mermaid-svg-DqOY5f7E7u3Air4h .node .label,#mermaid-svg-DqOY5f7E7u3Air4h .image-shape .label,#mermaid-svg-DqOY5f7E7u3Air4h .icon-shape .label{text-align:center;}#mermaid-svg-DqOY5f7E7u3Air4h .node.clickable{cursor:pointer;}#mermaid-svg-DqOY5f7E7u3Air4h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DqOY5f7E7u3Air4h .arrowheadPath{fill:#333333;}#mermaid-svg-DqOY5f7E7u3Air4h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DqOY5f7E7u3Air4h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DqOY5f7E7u3Air4h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DqOY5f7E7u3Air4h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DqOY5f7E7u3Air4h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DqOY5f7E7u3Air4h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DqOY5f7E7u3Air4h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DqOY5f7E7u3Air4h .cluster text{fill:#333;}#mermaid-svg-DqOY5f7E7u3Air4h .cluster span{color:#333;}#mermaid-svg-DqOY5f7E7u3Air4h div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DqOY5f7E7u3Air4h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DqOY5f7E7u3Air4h rect.text{fill:none;stroke-width:0;}#mermaid-svg-DqOY5f7E7u3Air4h .icon-shape,#mermaid-svg-DqOY5f7E7u3Air4h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DqOY5f7E7u3Air4h .icon-shape p,#mermaid-svg-DqOY5f7E7u3Air4h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DqOY5f7E7u3Air4h .icon-shape .label rect,#mermaid-svg-DqOY5f7E7u3Air4h .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DqOY5f7E7u3Air4h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DqOY5f7E7u3Air4h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DqOY5f7E7u3Air4h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 打开 /admin/
管理员登录
OrderRecord / User CRUD
信号触发 invalidate_takeaway_cache
分析页读取新缓存版本
结束

核心代码实现
python 复制代码
# 来源:app/admin.py --- 订单后台(节选)
@admin.register(OrderRecord)
class OrderRecordAdmin(admin.ModelAdmin):
    list_display = (
        "id", "order_id", "user_id", "merchant_id",
        "food_category", "order_amount", "order_status", "city", "order_time",
    )
    list_filter = (OrderMonthFilter, "order_status", "food_category", "city")
    search_fields = ("order_id", "user_id", "merchant_id")
    ordering = ("-order_time",)
python 复制代码
# 来源:app/utils/takeaway_core.py --- 缓存版本失效
def invalidate_takeaway_cache():
    version = cache.get("takeaway:cache_version", 0) + 1
    cache.set("takeaway:cache_version", version, None)
实现效果展示

访问路径http://127.0.0.1:8040/admin/admin / admin123

界面截图见第 4.7 节图4.21。

4.7 用户界面实现

界面采用 Bootstrap 布局、takeaway-theme.css 外卖橙主题与统一侧栏导航,覆盖首页、订单列表、六维分析、时序对比、算法三页与个人中心。顶栏展示当前用户与退出入口。SimpleUI 后台使用独立管理皮肤。本节集中给出主要界面效果,第 4.1~4.6 节不再重复界面图题。

用户登录界面

访问路径http://127.0.0.1:8040/app/login/

图4.7 用户登录界面

系统首页界面

访问路径http://127.0.0.1:8040/app/home/

图4.8 系统首页界面

订单列表界面

访问路径http://127.0.0.1:8040/app/order_list/

图4.9 订单列表界面

订单金额分析界面

访问路径http://127.0.0.1:8040/app/order/

图4.10 订单金额分析界面

订单状态分析界面

访问路径http://127.0.0.1:8040/app/status/

图4.11 订单状态分析界面

品类分析界面

访问路径http://127.0.0.1:8040/app/category/

图4.12 品类分析界面

城市分析界面

访问路径http://127.0.0.1:8040/app/city/

图4.13 城市分析界面

用户分析界面

访问路径http://127.0.0.1:8040/app/user/

图4.14 用户分析界面

商家分析界面

访问路径http://127.0.0.1:8040/app/merchant/

图4.15 商家分析界面

时序分析界面

访问路径http://127.0.0.1:8040/app/time_series/

图4.16 时序分析界面

相关性分析界面

访问路径http://127.0.0.1:8040/app/correlation/

图4.17 相关性分析界面

预测分析界面

访问路径http://127.0.0.1:8040/app/prediction/

图4.18 预测分析界面

异常检测界面

访问路径http://127.0.0.1:8040/app/anomaly/

图4.19 异常检测界面

月度对比界面

访问路径http://127.0.0.1:8040/app/comparison/

图4.20 月度对比界面

SimpleUI后台管理界面

访问路径http://127.0.0.1:8040/admin/

图4.21 SimpleUI后台管理界面

4.8 系统集成与部署

部署步骤概要如下:

  1. 安装 requirements.txt 依赖(Django 3.2、pandas、numpy、PyMySQL、mysqlclient、django-simpleui 等)。
  2. 创建 MySQL 库 takeaway(字符集建议 utf8mb4),并在 基于Django系统/settings.py 核对连接账号(默认 root / 123456,主机 localhost,端口 3306)。
  3. 执行 python manage.py migrate 完成表结构迁移。
  4. 同步并导入数据:python scripts/fetch_takeaway_data.py,再执行 python manage.py import_orders --path food_order_data.csv --truncate(Heywhale 原始约 152000 条)。
  5. 初始化账号:python manage.py seed_admin(前台 admin/123456,后台 admin/admin123)。
  6. 启动服务:在项目根目录执行 python manage.py runserver 0.0.0.0:8040
  7. 浏览器访问 http://127.0.0.1:8040/app/login/;后台 http://127.0.0.1:8040/admin/

根 URL 将业务挂在 /app/ 下,静态与媒体资源在 DEBUG=True 时由 Django 开发服务器挂载。若通过 SQL 直接改库,需执行 python manage.py refresh_cache 刷新 bundle 缓存。课题演示以 8040 端口本地运行为准。


5 系统测试

5.1 系统功能测试

测试环境:Windows 10,Python 3.11,Django 3.2,MySQL(库 takeaway),浏览器访问 http://127.0.0.1:8040。前台默认账号 admin/123456,后台 admin/admin123

5.1.1 用户认证功能测试用例
编号 说明 条件 操作步骤 预期结果 实际结果
TC-A01 正确登录 已 seed_admin 打开 /app/login/,输入 admin/123456 提交 跳转 /app/home/,侧栏可见 通过
TC-A02 错误密码 账号存在 输入错误密码提交 提示用户名或密码错误 通过
TC-A03 注册新用户 用户名未占用 /app/register/ 填写一致密码 注册成功并回到登录页 通过
TC-A04 退出登录 已登录 执行 /app/logOut/ Session 清空,需重新登录 通过
TC-A05 未登录拦截 无 Session 直接访问 /app/order_list/ 重定向至登录 通过
TC-A06 修改资料 已登录 /app/changeSelfInfo/ 更新资料 资料可保存 通过
5.1.2 数据管理功能测试用例
编号 说明 条件 操作步骤 预期结果 实际结果
TC-B01 首页 KPI 库有订单数据 打开 /app/home/ 总订单/完成/取消/GMV 正常 通过
TC-B02 列表分页 记录数>20 打开 /app/order_list/ 默认每页 20 条,可翻页 通过
TC-B03 条件筛选 已登录 按月份与城市筛选 列表结果符合条件 通过
TC-B04 CSV 导出 已登录 访问 /app/api/export/?city=北京 下载 UTF-8 BOM CSV 通过
TC-B05 后台订单检索 超管已登录 /admin/app/orderrecord/ 搜索订单号 列表过滤正确 通过
TC-B06 缓存刷新 后台修改订单 编辑 OrderRecord 保存 刷新分析页数据更新 通过
5.1.3 核心业务功能测试用例
编号 说明 条件 操作步骤 预期结果 实际结果
TC-C01 概览 API 库有约 15 万单 请求 /app/api/overview/ 返回 kpis 与 charts JSON 通过
TC-C02 金额 bundle 已登录 请求 /app/api/order-bundle/ 金额分布与分时图有数据 通过
TC-C03 WMA 预测 日 GMV≥14 天 打开 /app/prediction/ 展示历史与 +1d~+7d 预测 通过
TC-C04 IQR 异常 完成单有样本 打开 /app/anomaly/ 展示阈值说明与偏高样本 通过
TC-C05 Pearson 相关 抽样有数据 打开 /app/correlation/ 相关矩阵与散点可见 通过
TC-C06 SimpleUI 登录 seed_admin 已执行 /admin/ 输入 admin/admin123 进入后台首页 通过
TC-C07 自相关 日序列足够长 打开 /app/time_series/ 滞后 1~14 系数可见 通过
5.1.4 数据分析功能测试用例
编号 说明 条件 操作步骤 预期结果 实际结果
TC-D01 状态分析 有完成/取消 /app/status/ 饼图与取消率图有数据 通过
TC-D02 品类分析 有多品类 /app/category/ 饼图与客单价柱图正常 通过
TC-D03 城市分析 有多城市 /app/city/ 订单量与 GMV 对比可见 通过
TC-D04 用户分析 有 user_id /app/user/ Top 用户排行可见 通过
TC-D05 商家分析 有 merchant_id /app/merchant/ Top 商家 GMV 可见 通过
TC-D06 月度对比 跨月数据 /app/comparison/ 环比折线/柱图可见 通过
TC-D07 筛选联动 库约 152000 单 各页选择月份筛选 各 bundle 数据随筛选变化 通过
5.1.5 系统集成功能测试用例
编号 说明 条件 操作步骤 预期结果 实际结果
TC-E01 端口启动 依赖已安装 python manage.py runserver 8040 8040 可访问登录页 通过
TC-E02 数据导入 已建库 import_orders --truncate OrderRecord 约 152000 条 通过
TC-E03 路由前缀 服务已启动 访问 /app/home/ 业务页正常 通过
TC-E04 后台豁免 无前台 Session 打开 /admin/ 可进入 SimpleUI 登录 通过
TC-E05 fetch 脚本 CSV 已放置 运行 fetch_takeaway_data.py 同步至 data/raw 通过
TC-E06 前后台共库 库有数据 前台 KPI 与后台订单数对照 数量级一致 通过
TC-E07 refresh_cache SQL 直改库 manage.py refresh_cache 分析页缓存刷新 通过
5.1.6 用户界面功能测试用例
编号 说明 条件 操作步骤 预期结果 实际结果
TC-F01 侧栏导航 已登录 点击各分析菜单 对应页面可打开 通过
TC-F02 登录页展示 服务已启动 打开 /app/login/ 表单与外卖橙主题正常 通过
TC-F03 筛选条 分析页 切换月份/城市/品类 图表重新加载 通过
TC-F04 图表容器 分析页有数据 打开金额/城市等页 ECharts 容器有图 通过
TC-F05 算法说明 已登录 打开相关/预测/异常页 可见 algo_explain 文案 通过
TC-F06 热力图 已登录 打开 time_series 小时×星期热力可见 通过
TC-F07 SimpleUI 皮肤 管理员登录 打开 /admin/ 中文菜单与订单列表可用 通过

5.2 系统性能测试

5.2.1 响应时间性能分析

152000 条订单规模下,登录页与静态资源加载通常较快;首页 KPI 采用单次 SQL 条件聚合,首屏可在数秒内完成。各分析 bundle 优先 MySQL GROUP BY,配合 TAKEAWAY_CACHE_TTL=120 秒缓存,冷启动后重复请求显著加快。相关与异常页对 id 取模抽样,避免全表进 pandas;预测页基于日粒度聚合序列,开销可控。

5.2.2 并发性能测试

课题演示场景以单机少量并发为主。Session 认证与开发服务器适合本地验收;若并发显著增大,可改用生产级 WSGI 与反向代理。当前实现满足课程验收与本机演示需求。

5.2.3 数据库性能测试

order_id 唯一索引与 order_timecityfood_category 等索引有利于筛选与分组;订单列表分页限制每页 20/50/100 条,控制单次返回行数。全表 GROUP BY 在 15 万行规模下仍可接受,并通过 bundle 缓存降低重复计算。复合索引 idx_order_time_city 支撑按月与城市联合分析。

5.2.4 性能优化措施
  1. 分析 bundle 优先 SQL 聚合,每次全表加载 pandas。
  2. takeaway:cache_version 版本号缓存,订单变更时统一失效。
  3. 相关/异常 bundle 对 id 取模抽样(1/25、1/20),控制内存与计算量。
  4. 首页月度 GMV 最多展示最近 18 个月,避免图表过长。
  5. 前端并行请求多 bundle,缩短单页总等待时间。
  6. import_orders 批量写入与索引设计,保证导入后查询可用。

6 总结与展望

6.1 总结

本文完成了基于 Python 的外卖订单数据分析可视化系统的设计与实现。系统以 Django 3.2 为 Web 框架,MySQL 库名 takeaway,服务端口 8040 ,入口 manage.py,前台 /app/login/admin/123456)→ /app/home/,SimpleUI 后台 /admin/admin/admin123)。围绕 UserOrderRecord(约 152000 条)核心表,按 6 大模块组织了用户认证、订单数据管理、多维分析、时序与对比、相关/预测/异常算法分析、SimpleUI 后台。数据以 Heywhale 外卖订单 CSV 为主,经 fetch_takeaway_data.pyimport_orders 入库;聚合逻辑集中在 takeaway_sql_bundles.py;算法包括 Pearson 相关(抽样)、WMA 日 GMV 预测、IQR 异常检测(可降级 P99)与日订单自相关;不包含 协同过滤或 KNN 推荐。中间件豁免登录、注册与 /admin/ 前缀。经功能测试,主要模块运行符合预期,能够支撑教学演示与课题分析。

从模块规模看,论文第 3、4 章按上述 6 个功能模块展开;前台共 12 个分析/列表页面(含首页),13 个 bundle API;路由前缀统一为 /app/,形成可复现的本地化外卖订单数据分析方案。

6.2 展望

后续可从以下方向继续完善:对前台密码改为哈希存储并细化角色权限;将 LocMemCache 替换为 Redis 以支持多进程部署;在具备真实业务标签的前提下引入更稳健的时序模型并明确误差指标;优化移动端图表交互;将开发服务器替换为 Nginx + uWSGI/Gunicorn 以提升并发能力;增强 CSV 增量导入与数据质量校验流程。


参考文献

1 Django Software Foundation. Django documentation (3.2.x)EB/OL. https://docs.djangoproject.com/, 2021.

2 Apache ECharts. ECharts 官方文档EB/OL. https://echarts.apache.org/zh/index.html.

3 王珊, 萨师煊. 数据库系统概论M. 5版. 北京: 高等教育出版社, 2014.

4 McKinney W. Data Structures for Statistical Computing in PythonC//Proceedings of the 9th Python in Science Conference, 2010.

5 李刚. 疯狂 Python 讲义M. 北京: 电子工业出版社, 2019.

6 Bootstrap Team. Bootstrap DocumentationEB/OL. https://getbootstrap.com/.

7 周志华. 机器学习M. 北京: 清华大学出版社, 2016.

8 MySQL AB. MySQL 8.0 Reference ManualEB/OL. https://dev.mysql.com/doc/refman/8.0/en/.

9 刘十八. 数据可视化分析与应用M. 北京: 机械工业出版社, 2022.

10 Pearson K. Notes on regression and inheritance in the case of two parentsJ. Proceedings of the Royal Society of London, 1895, 58: 240-242.

11 Tukey J W. Exploratory Data AnalysisM. Reading: Addison-Wesley, 1977.

12 和鲸社区. Heywhale 外卖订单数据集EB/OL. https://www.heywhale.com/mw/dataset/694a40b12e539d577f2f4bf0.

13 孙十二, 钱十三. Bootstrap响应式Web开发实战M. 北京: 人民邮电出版社, 2022.

14 Box G E P, Jenkins G M. Time Series Analysis: Forecasting and ControlM. San Francisco: Holden-Day, 1970.

15 项亮. 推荐系统实践M. 北京: 人民邮电出版社, 2012.


致谢

感谢指导教师在课题选题、系统架构与论文撰写方面的悉心指导;感谢学院提供的实验环境与学习资源;感谢 Heywhale 社区公开外卖订单数据集为本课题提供真实样本;感谢开源社区维护的 Django、ECharts、SimpleUI 等项目,使本系统得以高效实现与演示。

相关推荐
MC皮蛋侠客1 小时前
SQLAlchemy 系列(三):声明式映射与 Schema——让类型、默认值和约束一致
数据库·python
会博通·代码搬运工1 小时前
会博通API对接实战:工程企业文档分布式采集系统的技术实现与Python SDK详解
开发语言·分布式·python·线性代数·矩阵·架构·电子档案合规
空堂与归1 小时前
大模型再火,也得先过 class 这一关
python
Muselit1 小时前
Python 泛型:把 list[User] 讲明白
python·fastapi
2501_916007471 小时前
Python实现HTTPS爬虫的完整指南:使用requests、BeautifulSoup、Selenium和Scrapy
爬虫·python·ios·小程序·https·uni-app·iphone
gptAI_plus1 小时前
别把整个仓库塞给 AI:用 Python 生成安全的代码上下文清单
python·chatgpt
吃饱了得干活1 小时前
Agent 记忆系统:从短期记忆到长期记忆
python·langchain·agent
大鱼>2 小时前
DSPy:LLM程序自动编译与提示词优化
开发语言·人工智能·python·深度学习
2401_843253702 小时前
金融智能:AI如何重构银行业未来
人工智能·python·金融