骑行装备销售订单数据可视化分析系统 --- 技术文档
1. 项目概述
1.1 项目简介
本系统是一个基于 Flask 框架的骑行装备销售订单数据可视化分析平台(Ride BI),提供多维度销售数据分析、交互式图表展示、用户权限管理等功能。系统从 CSV 数据文件导入订单数据到 MySQL 数据库,通过 RESTful API 提供数据查询,前端使用 ECharts 渲染各类可视化图表。
1.2 技术栈
| 层级 | 技术 |
|---|---|
| 后端框架 | Flask 3.x(Python) |
| 数据库 | MySQL(通过 PyMySQL 驱动) |
| 前端渲染 | Jinja2 模板 + 原生 JavaScript |
| 图表库 | ECharts 5.5.1 |
| UI 框架 | Bootstrap 5.3.3 |
| 图标库 | Lucide Icons |
| 密码加密 | werkzeug(PBKDF2) |
| 会话管理 | Flask Session(服务端) |
1.3 系统特点
- 零前端框架依赖:纯原生 JavaScript 实现,无 React/Vue/Angular,加载快、兼容性好
- 角色权限分离:管理员与普通用户功能严格隔离
- 全量筛选联动:所有图表支持日期、省份、产品、销售员四维筛选
- 操作审计:所有数据变更自动记录操作日志
- 响应式布局:CSS Grid 自适应桌面和平板设备























2. 项目结构
ride/
├── app.py # 主应用文件(Flask 工厂函数 + 53 个路由)
├── config.py # 配置文件(数据库、密钥、分页参数)
├── db.py # 数据库层(连接管理、DDL、CRUD 辅助函数)
├── auth.py # 认证工具(密码哈希、用户查询、操作日志)
├── decorators.py # 装饰器(@login_required, @admin_required)
├── data_utils.py # 数据工具(筛选构建、校验、JSON 序列化)
├── importer.py # CSV 数据导入器
├── requirements.txt # Python 依赖
├── 骑行装备销售订单数据.csv # 原始数据文件(78,262 行)
│
├── blueprints/
│ ├── __init__.py
│ └── auth_bp.py # 认证蓝图(登录、注册、个人中心)
│
├── scripts/
│ └── init_db.py # 数据库初始化脚本
│
├── static/
│ ├── css/
│ │ └── app.css # 全局样式(22KB)
│ └── js/
│ ├── common.js # 公共工具库(RideBI 对象)
│ ├── dashboard.js # 经营总览图表
│ ├── region.js # 区域分析图表
│ ├── product.js # 产品结构图表
│ ├── salesperson.js # 销售员表现图表
│ ├── customer.js # 客户价值图表
│ ├── delivery.js # 履约时效图表
│ ├── manage.js # 数据管理(订单 CRUD)
│ └── admin.js # 管理员用户管理
│
└── templates/
├── base.html # 基础布局模板
├── dashboard.html # 经营总览
├── region.html # 区域分析
├── product.html # 产品结构
├── salesperson.html # 销售员表现
├── customer.html # 客户价值
├── delivery.html # 履约时效
├── manage.html # 数据管理
├── bookmarks.html # 我的收藏
├── announcements.html # 系统公告
├── auth/
│ ├── login.html # 登录页
│ ├── register.html # 注册页
│ └── profile.html # 个人中心
└── admin/
├── users.html # 用户管理
├── logs.html # 操作日志
└── announcements.html # 公告管理
3. 数据库设计
3.1 连接配置
| 参数 | 值 |
|---|---|
| 主机 | localhost |
| 端口 | 3306 |
| 用户 | root |
| 密码 | 123456 |
| 数据库 | design_141_ride |
| 字符集 | utf8mb4 |
| 排序规则 | utf8mb4_unicode_ci |
3.2 数据表概览
系统共 7 张数据表:
| 表名 | 用途 | 数据量 |
|---|---|---|
order_items |
订单明细(核心业务表) | 78,262 行 |
users |
用户账号 | 系统生成 |
operation_logs |
操作审计日志 | 系统生成 |
bookmarks |
用户收藏 | 用户创建 |
announcements |
系统公告 | 管理员创建 |
favorites |
图表收藏 | 用户创建 |
alerts |
预警阈值 | 管理员配置 |
3.3 表结构详情
3.3.1 order_items(订单明细表)
sql
CREATE TABLE IF NOT EXISTS order_items (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_id VARCHAR(32) NOT NULL COMMENT '订单编号',
customer_name VARCHAR(160) NOT NULL COMMENT '客户名称',
customer_id VARCHAR(40) NOT NULL COMMENT '客户编号',
province VARCHAR(32) NOT NULL COMMENT '省份',
salesperson_id INT NOT NULL COMMENT '销售员编号',
order_date DATE NOT NULL COMMENT '订单日期',
est_delivery DATE NOT NULL COMMENT '预计送达日期',
act_delivery_date DATE NOT NULL COMMENT '实际送达日期',
product_id INT NOT NULL COMMENT '产品编号',
product_name VARCHAR(120) NOT NULL COMMENT '产品名称',
quantity INT NOT NULL COMMENT '数量',
price DECIMAL(14,4) NOT NULL COMMENT '单价',
amount DECIMAL(16,4) NOT NULL COMMENT '金额',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_order_date (order_date),
KEY idx_order_id (order_id),
KEY idx_customer_id (customer_id),
KEY idx_province (province),
KEY idx_product_name (product_name),
KEY idx_salesperson (salesperson_id),
KEY idx_amount (amount)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT | 自增主键 |
| order_id | VARCHAR(32) | 订单编号,如 ORD-00001 |
| customer_name | VARCHAR(160) | 客户全称 |
| customer_id | VARCHAR(40) | 客户编号,如 CUS-001 |
| province | VARCHAR(32) | 省份名称 |
| salesperson_id | INT | 销售员编号 |
| order_date | DATE | 下单日期(2024-01-01 至 2025-12-31) |
| est_delivery | DATE | 预计送达日期 |
| act_delivery_date | DATE | 实际送达日期 |
| product_id | INT | 产品编号 |
| product_name | VARCHAR(120) | 产品名称 |
| quantity | INT | 购买数量 |
| price | DECIMAL(14,4) | 单价(元) |
| amount | DECIMAL(16,4) | 金额 = 数量 × 单价 |
3.3.2 users(用户表)
sql
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
username VARCHAR(64) NOT NULL,
password_hash VARCHAR(256) NOT NULL,
nickname VARCHAR(64) NOT NULL DEFAULT '',
email VARCHAR(120) DEFAULT NULL,
phone VARCHAR(20) DEFAULT NULL,
role ENUM('admin', 'user') NOT NULL DEFAULT 'user',
status TINYINT NOT NULL DEFAULT 1 COMMENT '1=active 0=disabled',
last_login_at DATETIME DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
3.3.3 operation_logs(操作日志表)
sql
CREATE TABLE IF NOT EXISTS operation_logs (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
username VARCHAR(64) NOT NULL,
action VARCHAR(32) NOT NULL COMMENT 'login/logout/create/update/delete/export',
target_type VARCHAR(32) DEFAULT NULL COMMENT 'order/user/announcement/bookmark',
target_id VARCHAR(32) DEFAULT NULL,
detail TEXT DEFAULT NULL,
ip VARCHAR(45) DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_user_id (user_id),
KEY idx_action (action),
KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
3.3.4 bookmarks(收藏表)
sql
CREATE TABLE IF NOT EXISTS bookmarks (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
title VARCHAR(200) NOT NULL,
bookmark_type VARCHAR(20) NOT NULL DEFAULT 'page' COMMENT 'page/chart/report',
target_url VARCHAR(500) NOT NULL,
description TEXT DEFAULT NULL,
sort_order INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
3.3.5 announcements(公告表)
sql
CREATE TABLE IF NOT EXISTS announcements (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
content TEXT NOT NULL,
author_id BIGINT UNSIGNED NOT NULL,
status TINYINT NOT NULL DEFAULT 1 COMMENT '1=published 0=draft',
pinned TINYINT NOT NULL DEFAULT 0,
view_count INT NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_status (status),
KEY idx_pinned (pinned),
KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
3.3.6 favorites(图表收藏表)
sql
CREATE TABLE IF NOT EXISTS favorites (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
target_type VARCHAR(32) NOT NULL COMMENT 'chart/report',
target_key VARCHAR(128) NOT NULL,
filter_snapshot TEXT DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_user_target (user_id, target_type, target_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
3.3.7 alerts(预警表)
sql
CREATE TABLE IF NOT EXISTS alerts (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
alert_type VARCHAR(32) NOT NULL COMMENT 'delivery_delay/sales_drop',
metric_name VARCHAR(64) NOT NULL,
threshold_value DECIMAL(14,4) NOT NULL,
threshold_unit VARCHAR(16) DEFAULT NULL COMMENT 'days/percent/yuan',
current_value DECIMAL(14,4) DEFAULT NULL,
status ENUM('normal','warning','critical') NOT NULL DEFAULT 'normal',
province VARCHAR(32) DEFAULT NULL,
last_checked_at DATETIME DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
3.4 数据导入
数据通过 importer.py 从 CSV 文件批量导入:
python
from importer import import_csv
import_csv(csv_path="骑行装备销售订单数据.csv", reset=True, batch_size=1000)
- 编码:utf-8-sig(处理 BOM 头)
- 批量大小:1000 行/批次
- 使用
executemany提高插入效率 reset=True会先清空order_items表再导入
4. 后端架构
4.1 应用工厂模式
app.py 使用 create_app() 工厂函数创建 Flask 应用:
python
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
# 注册蓝图
from blueprints.auth_bp import auth_bp
app.register_blueprint(auth_bp, url_prefix="/auth")
# 初始化数据库
init_database()
seed_admin()
# 注册所有路由
register_routes(app)
return app
4.2 模块职责
| 模块 | 文件 | 职责 |
|---|---|---|
| 配置 | config.py |
数据库连接、密钥、分页参数、默认管理员 |
| 数据库 | db.py |
连接管理、表 DDL、CRUD 辅助函数 |
| 认证 | auth.py |
密码哈希、用户 CRUD、登录日志 |
| 装饰器 | decorators.py |
登录/管理员权限校验 |
| 数据工具 | data_utils.py |
筛选条件构建、表单校验、JSON 序列化 |
| 导入器 | importer.py |
CSV 批量导入 |
| 认证蓝图 | auth_bp.py |
登录、注册、个人中心路由 |
| 主应用 | app.py |
所有业务路由(53 个端点) |
4.3 数据库访问层
db.py 提供统一的数据库访问接口:
python
# 查询多条
rows = fetch_all("SELECT * FROM order_items WHERE province = %s", ["北京"])
# 查询单条
row = fetch_one("SELECT * FROM users WHERE id = %s", [1])
# 执行写操作
result = execute("INSERT INTO announcements (...) VALUES (...)", [...])
# 返回 {"affected": 1, "lastrowid": 42}
# 批量插入
execute_many("INSERT INTO order_items (...) VALUES (...)", [row1, row2, ...])
连接管理 :每次请求创建新连接,使用 contextmanager 管理事务:
- 正常退出 →
conn.commit() - 异常退出 →
conn.rollback() - 最终 →
conn.close()
4.4 权限控制
通过两个装饰器实现:
python
@login_required # 需要登录(session 中有 user_id)
@admin_required # 需要管理员角色(session 中 role == "admin")
行为差异:
/api/*路径:返回 JSON 错误(401/403)- 页面路径:重定向到登录页或首页
4.5 筛选条件构建
data_utils.py 中的 build_filters() 函数统一处理所有图表 API 的筛选参数:
python
def build_filters(args, include_keyword=False):
conditions = []
params = []
start = args.get("start")
end = args.get("end")
province = args.get("province")
product = args.get("product")
salesperson = args.get("salesperson")
if start:
conditions.append("order_date >= %s")
params.append(start)
if end:
conditions.append("order_date <= %s")
params.append(end)
if province:
conditions.append("province = %s")
params.append(province)
if product:
conditions.append("product_name = %s")
params.append(product)
if salesperson:
conditions.append("salesperson_id = %s")
params.append(int(salesperson))
where = "WHERE " + " AND ".join(conditions) if conditions else ""
return where, params
所有图表 API 接受以下通用查询参数:
| 参数 | 说明 | 示例 |
|---|---|---|
start |
开始日期 | 2024-01-01 |
end |
结束日期 | 2025-12-31 |
province |
省份筛选 | 北京 |
product |
产品筛选 | 碳纤维公路车架 |
salesperson |
销售员筛选 | 3 |
4.6 JSON 序列化
data_utils.py 中的 jsonable() 函数处理 MySQL 返回的特殊类型:
python
def jsonable(value):
if isinstance(value, Decimal):
return float(value)
if isinstance(value, (datetime, date)):
return value.isoformat()
if isinstance(value, dict):
return {k: jsonable(v) for k, v in value.items()}
if isinstance(value, (list, tuple)):
return [jsonable(v) for v in value]
return value
4.7 产品分类逻辑
通过 SQL CASE WHEN 表达式将产品名称映射到品类:
python
PRODUCT_CATEGORY_SQL = """
CASE
WHEN product_name LIKE '%车架%' THEN '车架'
WHEN product_name LIKE '%轮组%' OR product_name LIKE '%轮%' THEN '轮组'
WHEN product_name LIKE '%头盔%' OR product_name LIKE '%眼镜%'
OR product_name LIKE '%手套%' OR product_name LIKE '%骑行服%'
OR product_name LIKE '%锁鞋%' OR product_name LIKE '%鞋%' THEN '骑行穿戴'
WHEN product_name LIKE '%链条%' OR product_name LIKE '%飞轮%'
OR product_name LIKE '%刹车%' OR product_name LIKE '%变速%'
OR product_name LIKE '%坐垫%' OR product_name LIKE '%把带%' THEN '零部件'
WHEN product_name LIKE '%码表%' OR product_name LIKE '%车灯%'
OR product_name LIKE '%水壶%' OR product_name LIKE '%包%'
OR product_name LIKE '%打气筒%' OR product_name LIKE '%工具%' THEN '配件'
WHEN product_name LIKE '%公路车%' OR product_name LIKE '%山地车%'
OR product_name LIKE '%整车%' THEN '整车'
ELSE '其他'
END
"""
5. API 接口文档
5.1 公共 API
5.1.1 健康检查
GET /api/health
认证:无需登录
响应:
json
{
"success": true,
"database": "design_141_ride",
"rows": 78262
}
5.1.2 筛选选项
GET /api/filters
认证:需要登录
响应:
json
{
"success": true,
"provinces": [{"name": "北京"}, {"name": "上海"}, ...],
"products": [{"name": "碳纤维公路车架"}, ...],
"salespeople": [{"id": 1}, {"id": 2}, ...]
}
5.1.3 核心指标
GET /api/summary
认证:需要登录
响应:
json
{
"success": true,
"data": {
"total_revenue": 12345678.00,
"total_orders": 5432,
"total_customers": 1234,
"on_time_rate": 85.6,
"avg_order_amount": 2273.50,
"avg_delivery_days": 8.3
}
}
5.1.4 经营洞察
GET /api/insights
认证:需要登录
响应:
json
{
"success": true,
"data": {
"top_province": {"province": "广东", "revenue": 1234567},
"top_product": {"product_name": "碳纤维公路车架", "revenue": 987654},
"best_month": {"month": "2025-06", "revenue": 234567},
"delay_risk": [{"province": "西藏", "delay_rate": 35.2}]
}
}
5.2 图表 API
所有图表 API 均需登录,支持通用筛选参数。
5.2.1 销售趋势
GET /api/charts/trend
响应:
json
{
"success": true,
"data": [
{"month": "2024-01", "revenue": 123456, "orders": 345, "moving_avg": 120000},
...
]
}
5.2.2 省份排名
GET /api/charts/province
响应:
json
{
"success": true,
"data": [
{"province": "广东", "revenue": 1234567, "orders": 890, "customers": 456},
...
]
}
5.2.3 产品帕累托
GET /api/charts/product
响应:
json
{
"success": true,
"data": [
{"name": "碳纤维公路车架", "revenue": 987654, "quantity": 345, "cum_pct": 15.3},
...
]
}
5.2.4 品类结构
GET /api/charts/category
响应:
json
{
"success": true,
"data": [
{"name": "车架", "revenue": 3456789, "quantity": 1234, "orders": 987},
...
]
}
5.2.5 销售员表现
GET /api/charts/salesperson
响应:
json
{
"success": true,
"data": [
{"id": 1, "revenue": 987654, "orders": 234, "customers": 123, "on_time_rate": 88.5},
...
]
}
5.2.6 省份履约
GET /api/charts/delivery
响应:
json
{
"success": true,
"data": [
{"province": "北京", "total": 500, "on_time": 430, "on_time_rate": 86.0, "avg_days": 7.2},
...
]
}
5.2.7 客户排名
GET /api/charts/customer
响应:
json
{
"success": true,
"data": [
{"customer_id": "CUS-001", "customer_name": "张三", "revenue": 98765, "orders": 23},
...
]
}
5.2.8 价格-销量散点
GET /api/charts/price-quantity
响应:
json
{
"success": true,
"data": [
{"name": "碳纤维公路车架", "price": 3999, "quantity": 156, "revenue": 623844},
...
]
}
5.2.9 月度热力图
GET /api/charts/heatmap
响应:
json
{
"success": true,
"data": [
{"month": 1, "weekday": 0, "revenue": 123456},
...
],
"months": ["2024-01", "2024-02", ...],
"weekdays": ["周一", "周二", ...]
}
5.2.10 同比收入
GET /api/charts/yoy-revenue
响应:
json
{
"success": true,
"data": [
{"month": "01", "revenue_2024": 987654, "revenue_2025": 1234567, "growth_rate": 25.0},
...
]
}
5.2.11 省份地图
GET /api/charts/province-map
响应:
json
{
"success": true,
"data": [
{"name": "广东", "revenue": 1234567, "orders": 890, "customers": 456},
...
]
}
5.2.12 销售员月度趋势
GET /api/charts/salesperson-trend
响应:
json
{
"success": true,
"data": [
{"salesperson_id": 1, "month": "2024-01", "revenue": 98765, "orders": 23},
...
]
}
5.2.13 销售员雷达图
GET /api/charts/salesperson-radar
响应:
json
{
"success": true,
"data": [
{
"id": 1,
"revenue": 987654,
"orders": 234,
"customers": 123,
"on_time_rate": 88.5,
"avg_order_amount": 4220.0,
"total_revenue": 987654
},
...
],
"indicators": [
{"name": "销售额", "max": 1500000},
{"name": "订单数", "max": 400},
...
]
}
5.2.14 履约趋势
GET /api/charts/delivery-trend
响应:
json
{
"success": true,
"data": [
{"month": "2024-01", "total": 450, "delayed": 67, "on_time_rate": 85.1, "avg_days": 8.2},
...
]
}
5.2.15 延迟分布
GET /api/charts/delivery-distribution
响应:
json
{
"success": true,
"data": [
{"bucket": "提前3天以上", "count": 1234},
{"bucket": "准时(±1天)", "count": 3456},
{"bucket": "延迟1-3天", "count": 890},
...
]
}
5.2.16 RFM 客户分析
GET /api/charts/customer-rfm
响应:
json
{
"success": true,
"data": [
{"customer_id": "CUS-001", "customer_name": "张三", "recency": 15, "frequency": 8, "monetary": 45678, "segment": "Champions"},
...
],
"summary": {
"Champions": 123,
"Loyal": 234,
"New": 345,
"At Risk": 167,
"Lost": 89,
"Potential": 145
}
}
RFM 分段规则:
- Champions:R≥75%, F≥75%, M≥75%
- Loyal:F≥75%, M≥75%
- New:R≥75%, F<25%
- At Risk:R<25%, M≥50%
- Lost:R<25%, F<25%, M<25%
- Potential:其余客户
5.2.17 购买频次分布
GET /api/charts/customer-frequency
响应:
json
{
"success": true,
"data": [
{"frequency": "1次", "count": 456},
{"frequency": "2-3次", "count": 234},
...
]
}
5.2.18 新老客户趋势
GET /api/charts/customer-retention
响应:
json
{
"success": true,
"data": [
{"month_label": "2024-01", "new_customers": 123, "returning_customers": 456},
...
]
}
5.2.19 客户省份分布
GET /api/charts/customer-province
响应:
json
{
"success": true,
"data": [
{"name": "广东", "customers": 234, "revenue": 1234567},
...
]
}
5.2.20 客户产品偏好
GET /api/charts/customer-product
响应:
json
{
"success": true,
"data": [
{"name": "碳纤维公路车架", "customers": 123, "quantity": 345, "revenue": 987654},
...
]
}
5.3 订单 CRUD API(管理员)
5.3.1 查询订单列表
GET /api/orders?page=1&per_page=15&keyword=&start=&end=&province=&product=&salesperson=
响应:
json
{
"success": true,
"data": [...],
"pagination": {
"page": 1,
"per_page": 15,
"total": 78262,
"pages": 5218
}
}
5.3.2 获取单条订单
GET /api/orders/<id>
5.3.3 创建订单
POST /api/orders
Content-Type: application/json
{
"order_id": "ORD-78263",
"customer_name": "新客户",
"customer_id": "CUS-999",
"province": "北京",
"salesperson_id": "1",
"order_date": "2025-06-01",
"est_delivery": "2025-06-08",
"act_delivery_date": "2025-06-07",
"product_id": "1",
"product_name": "碳纤维公路车架",
"quantity": "2",
"price": "3999",
"amount": "7998"
}
5.3.4 更新订单
PUT /api/orders/<id>
Content-Type: application/json
{ ... 同创建 ... }
5.3.5 删除订单
DELETE /api/orders/<id>
5.4 收藏 API
GET /api/bookmarks # 获取当前用户收藏列表
POST /api/bookmarks # 创建收藏
DELETE /api/bookmarks/<id> # 删除收藏
5.5 公告 API
GET /api/announcements # 获取已发布公告列表(分页)
GET /api/announcements/<id> # 获取公告详情(自动增加浏览量)
5.6 管理员 API
GET /api/admin/users # 用户列表
POST /api/admin/users # 创建用户
PUT /api/admin/users/<id> # 更新用户
PUT /api/admin/users/<id>/reset-password # 重置密码
DELETE /api/admin/users/<id> # 删除用户
GET /api/admin/logs # 操作日志
GET /api/admin/announcements # 公告列表(含草稿)
POST /api/admin/announcements # 创建公告
PUT /api/admin/announcements/<id> # 更新公告
DELETE /api/admin/announcements/<id> # 删除公告
GET /api/export/orders # 导出订单 CSV
6. 前端架构
6.1 页面结构
系统共 11 个功能页面,通过顶部导航栏组织:
[Ride BI] [经营总览] [维度分析 ▼] [数据管理] [公告] [...] [用户 ▼]
├── 区域分析
├── 产品结构
├── 销售员表现
├── 履约时效
└── 客户价值
用户菜单(登录后可见):
- 个人中心
- 我的收藏
- 用户管理(管理员)
- 操作日志(管理员)
- 公告管理(管理员)
- 退出登录
6.2 公共工具库(common.js)
RideBI 全局对象提供以下工具:
| 方法 | 说明 |
|---|---|
RideBI.api(path, params) |
GET 请求封装,自动处理 401 跳转 |
RideBI.postApi(path, data) |
POST 请求封装 |
RideBI.byId(id) |
document.getElementById 简写 |
RideBI.fmtNumber(v) |
数字格式化(千分位) |
RideBI.fmtMoney(v) |
金额格式化(人民币) |
RideBI.fmtMoneyShort(v) |
金额简写(万/亿) |
RideBI.fmtPercent(v) |
百分比格式化 |
RideBI.baseOption() |
ECharts 基础配置(配色、网格、提示框) |
RideBI.currentParams() |
从筛选栏收集当前筛选参数 |
RideBI.loadSavedFilters() |
从 sessionStorage 恢复筛选状态 |
RideBI.fillSelect(sel, rows, getVal, getLabel) |
填充下拉选项 |
RideBI.chart(id) |
获取/创建 ECharts 实例 |
RideBI.resizeCharts() |
自适应所有图表尺寸 |
RideBI.showToast(msg, type) |
显示提示消息 |
RideBI.initBookmark(btnId, title, url) |
初始化收藏按钮 |
6.3 页面 JS 文件职责
| 文件 | 行数 | 职责 |
|---|---|---|
common.js |
240 | 公共工具库(RideBI 对象) |
dashboard.js |
133 | KPI 指标、趋势图、同比图、经营洞察 |
region.js |
132 | 省份排名柱状图、热力地图、履约风险图 |
product.js |
147 | 产品帕累托图、品类饼图、价格散点图、热力图 |
salesperson.js |
163 | 销售员贡献图、月度趋势图、雷达图 |
customer.js |
214 | 客户排名、RFM 气泡图、频次分布、留存趋势 |
delivery.js |
147 | 准时率仪表盘、省份履约、月度趋势、延迟分布 |
manage.js |
243 | 订单表格、筛选、分页、新增/编辑/删除弹窗 |
admin.js |
128 | 用户管理表格、新增/编辑弹窗 |
6.4 图表类型分布
| 图表类型 | 数量 | 使用页面 |
|---|---|---|
| 柱状图(Bar) | 8 | 区域、产品、销售员、客户、履约 |
| 折线图(Line) | 5 | 总览趋势、履约趋势、销售员趋势 |
| 饼图(Pie) | 2 | 品类结构、履约风险 |
| 散点图(Scatter) | 2 | 价格-销量、RFM |
| 雷达图(Radar) | 1 | 销售员能力 |
| 仪表盘(Gauge) | 1 | 准时率 |
| 热力图(Heatmap) | 2 | 月度热力图、省份地图 |
| 地图(Map) | 1 | 省份销售热力 |
| 帕累托图(Bar+Line) | 1 | 产品累计占比 |
| 堆叠面积图 | 1 | 新老客户趋势 |
6.5 配色方案
javascript
palette = ["#0f766e", "#14b8a6", "#ea580c", "#f59e0b", "#0284c7", "#16a34a", "#dc2626", "#8b5cf6"]
主色调为青绿色系(#0f766e / #14b8a6),辅以橙色(#ea580c)作为对比色。
6.6 CSS 布局系统
核心类名:
| 类名 | 用途 |
|---|---|
.workbench-head |
页面头部(标题 + 元信息 + 操作按钮) |
.filter-bar |
筛选栏(Flex 布局,自动换行) |
.kpi-grid |
KPI 指标卡片网格 |
.chart-grid |
图表卡片网格(3 列自适应) |
.chart-grid-2 |
2 列图表网格 |
.chart-card |
单个图表卡片 |
.chart-wide |
跨 2 列 |
.chart-full |
跨满整行 |
.table-shell |
数据表格容器 |
.table-toolbar |
表格工具栏 |
.pagination-bar |
分页栏 |
.announce-overlay |
自定义弹窗遮罩 |
.announce-dialog |
自定义弹窗对话框 |
响应式断点:
| 断点 | 图表列数 |
|---|---|
| >1200px | 3 列 |
| 768-1200px | 2 列 |
| <768px | 1 列 |
7. 功能模块详解
7.1 经营总览(Dashboard)
路由 :GET /
功能:
- 6 个 KPI 指标卡(销售额、订单数、客户数、准时率、客单金额、平均履约天数)
- 销售趋势折线图(含移动平均线)
- 经营洞察面板(最佳省份、最佳产品、最佳月份、延迟风险)
- 同比收入对比柱状图
- 6 个快捷入口卡片
API 调用链:
/api/summary → KPI 指标
/api/charts/trend → 趋势图
/api/insights → 洞察面板
/api/charts/yoy-revenue → 同比图
7.2 区域分析(Region)
路由 :GET /region
图表:
- 省份销售排名:Top 15 省份柱状图 + 折线(订单数双轴)
- 销售热力地图:中国地图着色(按销售额)
- 省份履约风险:双轴图(准时率柱状 + 平均履约天数折线)
API 调用链:
/api/charts/province → 排名图
/api/charts/province-map → 地图
/api/charts/delivery → 履约风险
7.3 产品结构(Product)
路由 :GET /product
图表:
- 产品帕累托图:Top 12 产品柱状(收入)+ 折线(累计占比%)
- 品类结构:饼图(按品类分组)
- 价格-销量关系:散点图(价格 vs 销量,气泡大小 = 收入)
- 月度热力图:月份 × 星期几 收入热力矩阵
API 调用链:
/api/charts/product → 帕累托
/api/charts/category → 品类
/api/charts/price-quantity → 散点
/api/charts/heatmap → 热力图
7.4 销售员表现(Salesperson)
路由 :GET /salesperson
图表:
- 销售员贡献度:柱状图(收入)+ 折线(准时率)
- 月度销售趋势:多系列折线图(各销售员月收入)
- 绩效雷达图:多维指标对比(销售额、订单数、客户数、准时率、客单金额)
API 调用链:
/api/charts/salesperson → 贡献度
/api/charts/salesperson-trend → 趋势
/api/charts/salesperson-radar → 雷达
7.5 客户价值(Customer)
路由 :GET /customer
图表:
- 客户价值排名:Top 15 客户柱状图
- RFM 客户分群:气泡图(Recency vs Frequency,气泡大小 = Monetary,颜色 = 分群)
- 购买频次分布:柱状图
- 新老客户趋势:堆叠面积图
- 客户省份分布:双轴图(客户数柱状 + 销售额折线)
- 客户产品偏好:柱状图
API 调用链:
/api/charts/customer → 排名
/api/charts/customer-rfm → RFM
/api/charts/customer-frequency → 频次
/api/charts/customer-retention → 留存
/api/charts/customer-province → 省份
/api/charts/customer-product → 产品
7.6 履约时效(Delivery)
路由 :GET /delivery
图表:
- 整体准时率:仪表盘(Gauge)
- 省份履约对比:柱状图(准时率)
- 月度履约趋势:双轴(延迟订单数柱状 + 准时率折线)
- 延迟天数分布:柱状图(延迟区间分布)
API 调用链:
/api/charts/delivery → 省份履约
/api/charts/delivery-trend → 趋势
/api/charts/delivery-distribution → 分布
7.7 数据管理(Manage)
路由 :GET /manage(仅管理员)
功能:
- 订单列表(分页、筛选、排序)
- 关键词搜索(订单号、客户名、产品名)
- 日期范围筛选
- 省份/产品/销售员下拉筛选
- 每页条数切换(10/15/30/50)
- 新增订单(弹窗表单)
- 编辑订单(弹窗表单,自动回填)
- 删除订单(确认后删除)
- 金额自动计算(数量 × 单价)
表单校验:
- 必填:订单号、客户名称、客户编号、省份、销售员、日期、产品、数量、单价
- 类型校验:数量 ≥ 1,单价 ≥ 0
- 金额自动计算:
amount = quantity × price
7.8 认证系统
登录:
- 用户名 + 密码(JSON POST)
- 密码使用 werkzeug PBKDF2 哈希验证
- 登录成功后写入 Session(user_id, username, nickname, role)
- Session 永久有效,24 小时过期
- 登录日志自动记录
注册:
- 用户名 + 密码 + 昵称(JSON POST)
- 用户名唯一性校验
- 默认角色:user
- 注册成功后自动登录
个人中心:
- 查看/修改昵称、邮箱、手机
- 修改密码(需验证原密码)
7.9 权限矩阵
| 功能 | 管理员 | 普通用户 |
|---|---|---|
| 查看总览/分析页面 | ✓ | ✓ |
| 筛选图表 | ✓ | ✓ |
| 收藏页面 | ✓ | ✓ |
| 查看公告 | ✓ | ✓ |
| 订单 CRUD | ✓ | ✗ |
| 用户管理 | ✓ | ✗ |
| 公告管理 | ✓ | ✗ |
| 操作日志 | 全部 | 仅自己 |
| 数据导出 | ✓ | ✗ |
8. 部署与运行
8.1 环境要求
- Python 3.8+
- MySQL 5.7+ 或 8.0+
- pip(Python 包管理)
8.2 安装步骤
bash
# 1. 安装依赖
pip install -r requirements.txt
# 2. 配置数据库(修改 config.py 或设置环境变量)
export DB_HOST=localhost
export DB_PORT=3306
export DB_USER=root
export DB_PASSWORD=123456
export DB_NAME=design_141_ride
# 3. 初始化数据库并导入数据
python scripts/init_db.py --reset
# 4. 启动应用
python app.py
8.3 初始化脚本参数
bash
python scripts/init_db.py [OPTIONS]
--reset 删除并重建所有表
--skip-import 跳过 CSV 数据导入
--csv <path> 指定 CSV 文件路径
8.4 环境变量
| 变量名 | 默认值 | 说明 |
|---|---|---|
SECRET_KEY |
ride-bi-141-fixed-secret-key-2024 | Flask 会话密钥 |
SESSION_LIFETIME |
86400 | 会话过期时间(秒) |
DB_HOST |
localhost | 数据库主机 |
DB_PORT |
3306 | 数据库端口 |
DB_USER |
root | 数据库用户 |
DB_PASSWORD |
123456 | 数据库密码 |
DB_NAME |
design_141_ride | 数据库名 |
8.5 默认账号
| 角色 | 用户名 | 密码 |
|---|---|---|
| 管理员 | admin | admin123 |
8.6 访问地址
http://127.0.0.1:5001
9. 数据字典
9.1 订单状态枚举
| 字段 | 值 | 含义 |
|---|---|---|
| users.role | admin | 管理员 |
| users.role | user | 普通用户 |
| users.status | 1 | 正常 |
| users.status | 0 | 禁用 |
| announcements.status | 1 | 已发布 |
| announcements.status | 0 | 草稿 |
| alerts.status | normal | 正常 |
| alerts.status | warning | 预警 |
| alerts.status | critical | 严重 |
9.2 操作类型枚举
| action 值 | 含义 |
|---|---|
| login | 用户登录 |
| logout | 用户登出 |
| create | 新增记录 |
| update | 修改记录 |
| delete | 删除记录 |
| export | 导出数据 |
9.3 RFM 客户分群
| 分群 | 条件 | 含义 |
|---|---|---|
| Champions | R≥75%, F≥75%, M≥75% | 冠军客户 |
| Loyal | F≥75%, M≥75% | 忠实客户 |
| New | R≥75%, F<25% | 新客户 |
| At Risk | R<25%, M≥50% | 流失风险 |
| Lost | R<25%, F<25%, M<25% | 已流失 |
| Potential | 其余 | 潜力客户 |
10. 扩展指南
10.1 新增图表页面
- 创建模板
templates/xxx.html(继承base.html) - 创建 JS
static/js/xxx.js - 在
app.py中添加页面路由 - 在
app.py中添加数据 API - 在
base.html导航栏中添加入口
10.2 新增 API 端点
python
@app.route("/api/charts/new-chart")
@login_required
def chart_new_chart():
where, params = build_filters(request.args)
rows = fetch_all(f"SELECT ... FROM order_items {where}", params)
return jsonify(jsonable({"success": True, "data": rows}))
10.3 新增筛选维度
- 在
data_utils.py的build_filters()中添加条件 - 在
api/filters端点中添加选项数据 - 在
base.html或页面模板中添加筛选控件 - 在
common.js的currentParams()中添加映射
10.4 数据库迁移
如需新增表,在 db.py 中:
- 定义
CREATE TABLESQL - 添加到
ALL_TABLES列表 - 运行
init_database()自动创建
11. 已知限制
- 无 ORM:全部使用原生 SQL,复杂查询需要手动拼接
- 单进程:Flask 开发服务器不支持并发,生产环境需用 Gunicorn/uWSGI
- 无前端构建:纯原生 JS,无模块化/打包/压缩
- Session 存储:默认内存存储,重启丢失(已通过固定 SECRET_KEY 解决)
- pandas 未使用:requirements.txt 中声明但代码中未引用
- CSV 数据静态:数据更新需重新导入,无实时数据源接入
12. 代码统计
| 类型 | 文件数 | 总行数 |
|---|---|---|
| Python 后端 | 10 | ~2,500 |
| JavaScript 前端 | 9 | ~1,550 |
| HTML 模板 | 16 | ~1,200 |
| CSS 样式 | 1 | ~1,100 |
| 合计 | 36 | ~6,350 |