景点热度数据可视化分析系统 — 技术文档
1. 项目概述
1.1 项目简介
本系统是一个基于 Python 的景点热度数据可视化分析平台,用于对全国各城市旅游景点的热度、游客量、票价、好评率等多维度数据进行采集、存储、分析和可视化展示。系统采用 B/S 架构,后端使用 FastAPI 框架,前端采用 Jinja2 模板引擎 + jQuery + ECharts/ApexCharts 实现交互式数据可视化。
1.2 核心功能
| |
|---|
| 注册、登录、JWT Cookie 鉴权、密码修改、头像上传 |
| 4 个 KPI 卡片 + 20+ 交互式图表(地图、漏斗、桑基、仪表盘、树状图等) |
| DataTable 表格展示、筛选、增删改查(管理员专属) |
| 5 个专题分析页面(概览、城市、价格、游客、季节) |
| |
| |
1.3 技术栈
| | |
|---|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| ECharts 5.4.3 + ApexCharts | |
| | |
| Material Design Icons + Remix Icons | |























2. 项目目录结构
tourism/├── main.py # 应用入口,FastAPI 实例创建与路由注册├── config.py # 全局配置(路径、密钥、过期时间)├── database.py # SQLite 数据库操作(用户 CRUD)├── requirements.txt # Python 依赖├── package.json # Node.js 依赖(仅 ECharts)├── 基于Python的景点热度分析.xlsx # 原始数据源(Excel)├── users.db # SQLite 用户数据库│├── data/│ ├── __init__.py│ ├── loader.py # Excel 数据加载与持久化│ └── processor.py # 30+ 个数据分析/聚合函数│├── models/│ ├── __init__.py│ └── schemas.py # Pydantic 数据模型定义│├── routers/│ ├── __init__.py│ ├── pages.py # 页面路由(HTML 模板渲染)│ ├── api_users.py # 用户 API(登录/注册/个人中心/管理)│ ├── api_attractions.py # 景点 CRUD API│ └── api_charts.py # 图表数据 API(31 个端点)│├── templates/ # Jinja2 HTML 模板│ ├── base.html # 基础布局(侧边栏、顶栏、脚本加载)│ ├── login.html # 登录页│ ├── register.html # 注册页│ ├── dashboard.html # 数据看板(20+ 图表)│ ├── data_table.html # 数据管理页(DataTable)│ ├── profile.html # 个人中心│ ├── admin_users.html # 用户管理(管理员)│ ├── analysis_overview.html # 概览分析│ ├── analysis_city.html # 城市分析│ ├── analysis_price.html # 价格分析│ ├── analysis_visitor.html # 游客分析│ └── analysis_season.html # 季节分析│├── static/│ ├── css/│ │ ├── bootstrap.min.css # Bootstrap 4│ │ ├── app.min.css # Molon 模板样式│ │ ├── icons-local.min.css # 本地化图标字体 CSS│ │ ├── tourism-custom.css # 自定义设计系统(CSS 变量、KPI/图表/表格样式)│ │ ├── tourism-enhance.css # 补充增强样式│ │ └── ... # 其他第三方 CSS│ ││ ├── js/│ │ ├── common.js # TourismApp 全局工具对象(API 封装、通知、格式化)│ │ ├── app.js # Molon 模板初始化脚本(侧边栏、主题切换)│ │ ├── dashboard-tourism.js # 看板图表渲染(ECharts + ApexCharts,~750 行)│ │ ├── datatable-tourism.js # DataTable 配置与 CRUD 交互│ │ ├── analysis-overview.js # 概览分析图表│ │ ├── analysis-city.js # 城市分析图表│ │ ├── analysis-price.js # 价格分析图表│ │ ├── analysis-visitor.js # 游客分析图表│ │ ├── analysis-season.js # 季节分析图表│ │ ├── echarts.min.js # ECharts 5.4.3(本地)│ │ ├── apexcharts.min.js # ApexCharts(本地)│ │ ├── jquery.min.js # jQuery│ │ ├── bootstrap.bundle.min.js # Bootstrap Bundle│ │ ├── jquery.dataTables.min.js # DataTables 核心│ │ └── ... # 其他第三方 JS│ ││ ├── font/ # 本地字体文件(MDI、Remix、Inter 等)│ ├── picture/ # Logo、头像占位图│ └── uploads/ # 用户上传的头像文件│└── moban6127/ # Molon 模板原始文件(参考用,不参与运行)
3. 系统架构
3.1 架构图
┌─────────────────────────────────────────────────────────────┐│ 浏览器 (Client) ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────────┐ ││ │ 登录页面 │ │ 数据看板 │ │ 数据管理 │ │ 分析页面 ×5 │ ││ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬────────┘ ││ │ │ │ │ ││ jQuery + Bootstrap + ECharts + ApexCharts + DataTables │└───────┼────────────┼────────────┼───────────────┼───────────┘ │ │ │ │ ▼ ▼ ▼ ▼┌─────────────────────────────────────────────────────────────┐│ FastAPI (Uvicorn) ││ ││ ┌──────────────┐ ┌───────────────┐ ┌──────────────────┐ ││ │ pages.py │ │ api_users.py │ │ api_charts.py │ ││ │ (HTML 路由) │ │ (用户 API) │ │ (图表数据 API) │ ││ └──────┬───────┘ └───────┬───────┘ └────────┬─────────┘ ││ │ │ │ ││ ┌──────┴───────┐ ┌──────┴───────┐ ┌────────┴─────────┐ ││ │ Jinja2 模板 │ │ database.py │ │ processor.py │ ││ │ (templates/) │ │ (SQLite) │ │ (Pandas 聚合) │ ││ └──────────────┘ └──────────────┘ └────────┬─────────┘ ││ │ ││ ┌───────┴─────────┐ ││ │ loader.py │ ││ │ (Excel 读写) │ ││ └─────────────────┘ │└─────────────────────────────────────────────────────────────┘ │ │ ▼ ▼┌──────────────┐ ┌─────────────────────┐│ users.db │ │ 基于Python的景点 ││ (SQLite) │ │ 热度分析.xlsx │└──────────────┘ └─────────────────────┘
3.2 数据流
- 启动阶段:
main.py 启动时调用 init_db() 初始化 SQLite 数据库,调用 load_data() 将 Excel 数据加载到内存中的 Pandas DataFrame。 - 页面请求:浏览器请求页面路由 →
pages.py 验证 JWT Cookie → 渲染 Jinja2 模板返回 HTML。 - API 请求:前端 JS 通过
TourismApp.apiGet/apiPost 发起 fetch 请求 → 路由处理函数从 DataFrame 或 SQLite 读取数据 → 返回 JSON。 - 数据持久化:景点数据的增删改操作直接修改内存中的 DataFrame 并调用
save_to_excel() 写回 Excel 文件。
4. 后端详细设计
4.1 入口文件 main.py
app = FastAPI(title="景点热度数据可视化分析系统")
- 注册 4 个路由模块:
pages、api_users、api_charts、api_attractions
4.2 配置文件 config.py
| | |
|---|
DATA_FILE_PATH | ./基于Python的景点热度分析.xlsx | |
DATABASE_PATH | ./users.db | |
SECRET_KEY | tourism-analysis-secret-key-2024 | |
TOKEN_ALGORITHM | HS256 | |
TOKEN_EXPIRE_HOURS | 24 | |
4.3 数据库模块 database.py
使用 SQLite3 存储用户信息,提供以下操作函数:
| |
|---|
init_db() | 创建 users 表,自动迁移 is_admin 列,首用户设为管理员 |
create_user() | |
verify_password() | |
get_user_by_id() | |
update_user_profile() | |
update_password() | |
update_avatar() | |
is_user_admin() | |
get_all_users() | |
delete_user() | |
set_user_admin() | |
users 表结构:
4.4 数据加载模块 data/loader.py
- 使用
pd.read_excel() 读取 Excel 文件到全局 _df 变量 get_dataframe():返回 DataFrame 副本(用于分析,不影响原始数据)get_raw_dataframe():返回原始 DataFrame 引用(用于 CRUD 操作)save_to_excel():将当前 DataFrame 写回 Excel 文件refresh_data()
4.5 数据分析模块 data/processor.py
包含 30+ 个 Pandas 聚合分析函数,全部接受 DataFrame 参数,返回字典格式数据。按功能分类:
基础统计:
| |
|---|
get_summary_stats() | KPI 数据(景点总数、月均游客、好评率、门票均价) |
get_city_distribution() | |
get_type_distribution() | |
get_heat_level_distribution() | |
get_season_distribution() | |
get_top_attractions() | |
get_star_distribution() | |
关联分析:
| |
|---|
get_price_visitor_data() | |
get_city_avg_metrics() | |
get_influencer_vs_review() | |
get_age_group_data() | |
get_city_map_data() | |
get_radar_data() | |
深度分析:
| |
|---|
get_city_total_visitors() | |
get_manager_attraction_count() | |
get_open_days_vs_visitors() | |
get_guide_mentions_top() | |
get_influencer_distribution() | |
get_price_distribution() | |
get_daily_tickets_top() | |
get_facilities_analysis() | |
get_review_influencer_bubble() | |
get_city_type_stacked() | |
get_heat_star_cross() | |
get_age_group_city() | |
get_season_heat_heatmap() | |
get_price_by_type() | |
get_composite_score_top() | |
新增图表:
| |
|---|
get_city_attraction_count() | |
get_price_review_scatter() | |
get_visitors_by_type() | |
get_hotel_restaurant_scatter() | |
get_sankey_data() | |
get_type_visitor_treemap() | |
get_price_box_data() | |
4.6 Pydantic 模型 models/schemas.py
| |
|---|
UserLogin | 登录请求体(username, password) |
UserRegister | 注册请求体(username, password, email) |
UserResponse | |
UserProfileUpdate | |
PasswordChange | 密码修改(old_password, new_password) |
AttractionCreate | |
AttractionUpdate | |
4.7 路由模块
4.7.1 页面路由 routers/pages.py
| | | |
|---|
GET / | dashboard.html / login.html | | |
GET /login | | | |
GET /register | | | |
GET /dashboard | | | |
GET /data | | | |
GET /profile | | | |
GET /admin/users | | | |
GET /analysis/overview | | | |
GET /analysis/city | | | |
GET /analysis/price | | | |
GET /analysis/visitor | | | |
GET /analysis/season | | | |
鉴权逻辑:读取 Cookie 中的 access_token,JWT 解析获取 user_id,查询数据库获取用户信息。未登录返回登录页,非管理员访问管理页面重定向到看板。
4.7.2 用户 API routers/api_users.py
| | |
|---|
/api/login | | |
/api/register | | |
/api/user/profile | | |
/api/user/profile | | |
/api/user/password | | |
/api/user/avatar | | 上传头像(multipart/form-data) |
/api/logout | | |
/api/admin/users | | |
/api/admin/users/{id} | | |
/api/admin/users/{id}/admin | | |
JWT 认证流程:
- 登录成功后,服务端生成 JWT Token(含 user_id、过期时间)
- Token 写入
access_token Cookie(httponly,24 小时过期) - 后续请求通过
get_current_user() 从 Cookie 读取 Token 并解码验证
4.7.3 景点 CRUD API routers/api_attractions.py
| | |
|---|
/api/attractions | | 获取景点列表(支持 city/type/heat_level 筛选) |
/api/attractions/{id} | | |
/api/attractions | | |
/api/attractions/{id} | | |
/api/attractions/{id} | | |
数据存储说明:景点数据存储在 Excel 文件中,通过 Pandas DataFrame 在内存中操作。增删改操作会同步修改内存 DataFrame 并写回 Excel。行索引 index 作为唯一标识。
4.7.4 图表数据 API routers/api_charts.py
31 个 GET 端点,全部返回 JSON 数据:
| |
|---|
/api/charts/summary | |
/api/charts/city-visitors | |
/api/charts/city-map | |
/api/charts/type-distribution | |
/api/charts/heat-level | |
/api/charts/season | |
/api/charts/top-attractions | |
/api/charts/price-visitors | |
/api/charts/city-metrics | |
/api/charts/star-distribution | |
/api/charts/influencer-review | |
/api/charts/age-group | |
/api/charts/radar | |
/api/charts/city-total-visitors | |
/api/charts/manager-count | |
/api/charts/open-days-visitors | |
/api/charts/guide-mentions | |
/api/charts/influencer-distribution | |
/api/charts/price-distribution | |
/api/charts/daily-tickets | |
/api/charts/facilities | |
/api/charts/review-influencer-bubble | |
/api/charts/city-type-stacked | |
/api/charts/heat-star-cross | |
/api/charts/age-group-city | |
/api/charts/season-heatmap | |
/api/charts/price-by-type | |
/api/charts/composite-score | |
/api/charts/city-attraction-count | |
/api/charts/price-review-scatter | |
/api/charts/visitors-by-type | |
/api/charts/hotel-restaurant-scatter | |
/api/charts/sankey | |
/api/charts/type-visitor-treemap | |
/api/charts/price-box | |
5. 前端详细设计
5.1 基础布局 base.html
采用 Molon Admin Template 的垂直布局:
- 顶栏(Topbar):Logo、全屏按钮、用户头像下拉菜单、设置按钮
- 侧边栏(Sidebar):用户头像卡片、导航菜单(数据看板、数据管理、数据分析子菜单、个人中心、用户管理)
- 主内容区(Main Content):通过
{% block content %} 子模板填充 - 右侧面板
脚本加载顺序:
- common.js(TourismApp 全局对象)
5.2 全局工具对象 common.js
const TourismApp = { getCookie(name) // 读取 Cookie async apiGet(url) // GET 请求封装(自动处理 401 跳转) async apiPost(url, data) // POST 请求封装 async apiPut(url, data) // PUT 请求封装 async apiDelete(url) // DELETE 请求封装 notify(type, message) // Toastr 通知 formatNumber(n) // 数字千分位格式化 chartColors // 图表调色板(10 色)}
5.3 登录/注册页面
- 表单验证(前端):用户名至少 3 字符,密码至少 6 字符
- 密码可见性切换:点击眼睛图标在
type="password" 和 type="text" 之间切换
5.4 数据看板 dashboard.html + dashboard-tourism.js
看板包含 13 行共 20+ 个图表:
| | | | |
|---|
| | | | kpi-* |
| | | | chart_city_map |
| | | | chart_heat_level |
| | | | chart_top_attractions |
| | | | chart_type_dist |
| | | | chart_price_visitors |
| | | | chart_city_metrics |
| | | | chart_radar |
| | | | chart_season |
| | | | chart_star |
| | | | chart_age_group |
| | | | chart_city_count |
| | | | chart_visitors_type |
| | | | chart_price_review |
| | | | chart_city_type_stacked |
| | | | chart_season_heatmap |
| | | | chart_composite_score |
| | | | chart_hotel_restaurant |
| | | | chart_funnel |
| | | | chart_gauge |
| | | | chart_sankey |
| | | | chart_treemap |
| | | | chart_price_by_type |
图表配色方案:
const C = { blue: '#0f9cf3', green: '#1cbb8c', gold: '#fcb92c', red: '#f32f53', purple: '#6f42c1', teal: '#17a2b8', orange: '#fd7e14', pink: '#e83e8c', blueL: '#4aa3ff', greenL: '#6fd088'};
热度等级颜色:顶流 = 绿色,热门 = 红色,一般 = 金色
5.5 数据管理页面 data_table.html + datatable-tourism.js
- DataTables 配置:14 列(含隐藏索引列和操作按钮列)
- 分页:每页 15 条,支持 10/15/25/50 切换
- 操作按钮:编辑(弹出 Modal)、删除(SweetAlert2 确认)
- 添加/编辑 Modal:17 个表单字段,包含下拉选择和数字输入
DataTable 列定义:
5.6 分析页面
5 个专题分析页面,每个页面有独立的 JS 文件:
5.7 CSS 设计系统 tourism-custom.css
采用"云山意境"设计语言:
CSS 变量:
- 翡翠色系(Jade):
--jade-900 ~ --jade-100,用于主色调 - 金色系(Gold):
--gold-600 ~ --gold-200,用于强调色 - 奶油色系(Cream):
--cream-100 ~ --cream-400,用于背景 - 墨色系(Ink):
--ink-900 ~ --ink-300,用于文字
组件样式:
- KPI 卡片:玻璃态背景 + 渐变边框动画 + 悬停上浮
- 图表卡片:简洁白底 + 渐变顶栏装饰 + 悬停阴影
6. 数据结构
6.1 景点数据字段(Excel)
| | |
|---|
| | |
| | |
| | 北京/上海/成都/重庆/大理/厦门/青岛/西安/杭州/三亚 |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
6.2 城市坐标映射
city_coords = { "北京": [116.46, 39.92], "上海": [121.48, 31.22], "成都": [104.06, 30.67], "重庆": [106.54, 29.59], "大理": [100.19, 25.69], "厦门": [118.10, 24.46], "青岛": [120.33, 36.07], "西安": [108.95, 34.27], "杭州": [120.19, 30.26], "三亚": [109.51, 18.25],}
7. 安全设计
7.1 认证机制
- JWT Token:登录成功后生成 HS256 签名的 JWT,包含 user_id 和过期时间
- Cookie 存储:Token 存储在 httponly Cookie 中,防止 XSS 窃取
- 过期控制:Token 24 小时过期,过期后自动跳转登录页
- 密码存储:使用 SHA256 哈希(注意:生产环境建议使用 bcrypt)
7.2 权限控制
- 普通用户
- 管理员
- 页面级:
pages.py 中每个路由检查登录状态和管理员权限 - API 级:
require_admin() 函数验证管理员权限
7.3 数据安全
- Excel 文件直接读写(适合小型项目,生产环境建议使用数据库)
- CORS 配置允许所有来源(开发环境,生产环境应限制)
8. 部署与运行
8.1 环境要求
- Node.js(可选,仅用于 ECharts npm 包)
8.2 安装依赖
pip install -r requirements.txt
依赖清单:
fastapi==0.104.1uvicorn[standard]==0.24.0jinja2==3.1.2python-multipart==0.0.6pandas==2.1.4openpyxl==3.1.2python-jose[cryptography]==3.3.0
8.3 启动服务
服务默认运行在 http://localhost:8000,支持热重载(reload=True)。
8.4 访问系统
- 浏览器打开
http://localhost:8000 - 首次使用需注册账号(第一个注册的用户自动成为管理员)
9. 外部资源依赖
9.1 已本地化的资源
| | |
|---|
| Material Design Icons CSS | | /static/css/icons-local.min.css |
| | /static/font/materialdesignicons-webfont.* |
| | /static/js/echarts.min.js |
| | /static/js/apexcharts.min.js |
9.2 本地静态文件
| |
|---|
/static/css/bootstrap.min.css | |
/static/css/app.min.css | |
/static/js/jquery.min.js | |
/static/js/bootstrap.bundle.min.js | |
/static/js/metisMenu.min.js | |
/static/js/simplebar.min.js | |
/static/js/waves.min.js | |
/static/js/toastr.min.js | |
/static/js/sweetalert2.min.js | |
/static/js/jquery.dataTables.min.js | |
10. 已知问题与优化建议
10.1 已知问题
| |
|---|
| |
| 不支持并发写入,建议迁移到 SQLite 或 PostgreSQL |
| |
| |
10.2 优化建议
| |
|---|
| 将 Excel 迁移到 SQLite/PostgreSQL,支持并发和事务 |
| 对图表数据 API 添加 Redis 缓存,减少重复计算 |
| 考虑迁移到 Vue.js/React 实现 SPA |
| 使用 Docker 容器化,配合 Nginx 反向代理 |
| |
| |
11. 关键设计决策记录
| |
|---|
| FastAPI 原生支持 async、自动文档生成、Pydantic 校验 |
| |
| ECharts 擅长地图/桑基/漏斗等复杂图表,ApexCharts 擅长常规统计图表 |
| JWT Cookie 而非 localStorage | httponly Cookie 防止 XSS 攻击 |
| |
| 实现简单,适合演示项目(生产环境应使用 bcrypt) |
| |