
这两年反诈宣传在基层需求非常大社区、学校、企业都在做但大多数还停留在发传单、贴海报的阶段触达效率低数据沉淀为零。我做过几个类似的宣传管理系统这次把一个完整的“基于Python的防诈宣传平台”从零到一做了出来后台用Django管内容Flask单独出可视化数据接口前端用ECharts渲染大屏。这篇文章把整个项目的设计思路、关键模块、可视化实现和踩坑记录完整拆出来适合正在做Web开发课设、毕设或者想在企业内部搭一套反诈宣传工具的同学直接参考。1. 项目整体设计与技术选型1.1 防诈宣传平台到底要解决什么问题先说需求。防诈宣传平台不是一个单纯的内容发布网站它要承接三件事第一是宣传内容的管理与触达也就是文章、视频、海报这些物料要能分类发布并且能追踪到哪些内容被哪些人看过第二是诈骗案例的收集与检索基层工作人员日常能接触到大量真实案例需要把这些案例结构化录入形成可查询、可统计的数据库第三是数据可视化管理者需要一眼看清当前宣传覆盖了多少人、哪类诈骗高发、哪个区域活动最多。这个定位决定了技术选型的方向。如果只做内容发布一个WordPress就够了如果要案例库统计可视化那必须自己写后端逻辑。我最终选择了Django和Flask双框架共存的结构后面会详细说为什么。平台的核心用户是基层反诈宣传员和管理人员不是普通网民。所以界面设计要简洁操作路径要短录入案例不能超过三分钟查看数据大屏要一眼能找到关键指标。这个定位直接影响了我对功能模块的划分和数据模型的设计。1.2 Django和Flask为什么要共存很多人看到“Django和Flask同时使用”会觉得很奇怪其实这是一个很务实的架构选择。Django的优势是“全家桶”自带Admin后台、ORM、认证、表单、分页非常适合做内容管理类的系统。防诈宣传平台的素材管理、案例录入、用户权限、活动记录这些功能用Django的Admin和ORM来做开发效率非常高而且几乎不用自己写底层CRUD。Flask的优势是轻量和灵活非常适合做纯API服务。可视化大屏需要的是标准JSON数据接口不需要模板渲染、不需要表单验证、不需要ORM只要给我一个/api/chart/trend接口返回数据就行。这种情况下Flask比Django更直接代码量少启动快接口逻辑一目了然。所以在实际项目中我是这样分工的Django负责素材管理、案例管理、宣传活动管理、用户体系、审批流、所有面向管理员的功能页面Flask负责可视化大屏的数据API、图表数据聚合、大屏相关的简单查询接口两者共用同一个MySQL数据库Django的ORM负责写入Flask用SQLAlchemy只读查询这样分工之后两种框架都做自己最擅长的事没有重叠也没有互相拖累。提示如果你对Django和Flask都不熟不要在项目里同时上两套框架。这个方案的前提是你已经掌握了其中一个另一个只需要写简单的API。不建议为了炫技把项目搞复杂。1.3 可视化方案选型为什么是ECharts可视化层面我对比过三个方案ECharts、Chart.js和Plotly。Chart.js优点是轻量、上手快但图表类型偏基础做防诈数据大屏需要的中国地图热力图、南丁格尔玫瑰图、双轴混合图这些高级图表类型支持不够好。Plotly适合做科研数据分析交互很强但打包体积大而且在大屏上的视觉效果偏“实验室风”不够大气。ECharts是百度开源的项目现在由Apache维护图表类型最全地图支持好动画效果多主题可定制性强最关键的是它对中文地图场景做了现成支持——做地区维度的诈骗案件分布热力图ECharts直接用China.js或GeoJSON就能搞定不需要自己画地图。另外ECharts的社区案例非常多几乎你能想到的图表效果都能搜到参考代码。对于反诈宣传大屏这种需要“看得懂、记得住”的场景ECharts的视觉冲击力和信息表达能力都是最优选择。从数据接入角度来说ECharts纯粹是前端渲染工具只需要给它喂标准JSON数据它自己处理动画、坐标轴、图例。这和Flask的API天然完美配合。Flask返回{dates: [...], values: [...]}ECharts直接用setOption渲染链路非常干净。2. 数据模型设计与模拟数据生成2.1 核心数据表结构拆解防诈宣传平台的数据模型我按业务域拆成了四块内容域、案例域、活动域、反馈域。每一块包含2~3张核心表表与表之间的关系尽量简单避免过度关联导致后期统计复杂。内容域主要有三张表Article宣传文章/图文素材字段包括标题、封面图、正文内容、发布状态、浏览量、发布时间Category内容分类比如“刷单返利”“冒充客服”“冒充公检法”等ArticleCategoryRel文章与分类的多对多关系表一篇文章可以挂多个分类案例域是三张表FraudCase诈骗案例核心表字段包括案件编号、标题、诈骗类型、涉案金额、案发地区、受害人年龄段、案情描述、防范要点FraudType诈骗类型字典表固定数据比如刷单返利、虚假投资、冒充客服等CaseTag标签表用于快速检索比如“高发”“新手段”“金额巨大”活动域两张表Campaign宣传活动表字段包括活动名称、活动地点、活动时间、参与人数、活动形式CampaignPhoto活动照片表一个活动对应多张照片反馈域一张表UserFeedback用户反馈表记录用户提交的疑似诈骗线索、举报信息字段包括反馈类型、内容描述、联系方式、提交时间、处理状态这套模型覆盖了“宣传前、宣传中、宣传后”完整链路。面试时被问到数据模型设计可以直接用这个思路解释每一张表都有明确的业务意义不存在为了凑字段而设计的表。2.2 为什么需要模拟数据做可视化大屏最怕的是没有数据。真实业务系统的数据往往积累慢、格式乱、涉密不能外泄所以开发阶段几乎必然要用模拟数据。但模拟数据不是随便乱造。我见过很多人用random.randint(1, 100)生成一组数字就放图表里结果趋势图方向混乱饼图占比完全失真大屏一看就是假的。好的模拟数据要满足两个要求第一符合业务逻辑刷单返利类案件数量就是应该比“冒充领导”多第二有趋势和波动能体现时间维度上的变化。我的做法是写了一个fake_data.py脚本用Faker库配合手写规则生成数据。比如涉案金额范围不同诈骗类型要区别对待刷单返利类单案金额通常在2000~50000元之间偶尔有十万以上的“大案”虚假投资类单案金额普遍偏高50000~500000元冒充客服类金额集中在1000~20000元这些取值范围不是拍脑袋定的我参考了公开的反诈宣传材料中披露的案例数据虽然不能保证真实分布完全一致但至少从视觉效果和管理者的认知角度是可信的。生成趋势数据时也要注意诈骗案件数量不能是单调递增或完全随机的。现实中通常是波动的周末可能偏高大家都有空刷手机月底可能有一波高峰诈骗团伙冲业绩大促期间刷单类案件猛增。我写规则时人为叠加了这些周期性因素大屏上展示出来的折线图就特别有“真实感”。2.3 统计口径设计按类型、按地区、按时间可视化大屏说白了就是一个“多维度统计查询系统”。我提前设计了四个核心统计口径每个口径对应一个图表按诈骗类型统计案件数量饼图/玫瑰图一眼看出哪种诈骗最多发按时间统计发案趋势折线图/柱状图展示最近30天或12个月的趋势变化按地区统计案件数量地图热力图直接映射到各省市数据按被骗人群统计年龄段性别双轴图/堆叠柱状图判断宣传重点人群这些统计口径在写模拟数据脚本时就要把字段留好。我建表的时候就预留了案发时间、案发地区、受害人年龄、受害人性别这几个维度字段后面统计起来完全不需要改表结构。很多人做到可视化阶段才发现缺字段回头改表、改数据、改接口非常浪费时间。3. 核心功能模块的详细实现3.1 素材管理模块Django Admin的正确用法宣传素材管理是内容平台的基础功能。我使用了Django自带的Admin后台作为管理入口但做了一些改动让它更贴合业务场景。首先把FraudCase、Article、Campaign这三张核心表注册到Admin然后在FraudCaseAdmin里配置了search_fields和list_filter让管理员可以直接按案件标题、诈骗类型、案发地区来搜索按案件状态来筛选。list_display配了关键字段案件编号、标题、类型、涉案金额、案发时间、发布时间这样列表页就是一张完整的案件登记台账。其次Admin后台默认的富文本编辑器太弱了我改成了Markdown编辑组件在models.py里把正文映射为富文本HTML展示直接嵌入到Admin的变更页中。Markdown格式的好处是宣传员录入案例时不需要学习复杂的排版工具写纯文本即可前端统一渲染成样式一致的页面。实操心得Django Admin里给FraudCase配readonly_fields很重要把“案件编号”设为只读系统自动生成防止管理员录入时手写编号出现重复。编号规则我定为类型缩写年月日三位序号比如TS-20250115-001写入Django的save_model方法里自动生成。3.2 案例库管理结构化录入与多维检索案例库是整个平台的灵魂管理者最常用的功能就是“查同类”最近有没有类似手法的案件某个小区最近有没有发案这个功能用Django的ORM多条件查询就能实现。录入端我做了一个独立的case_add.html页面不使用Django Admin原因是Admin的表单布局太死板基层宣传员用不惯。自定义表单的好处是可以做动态交互比如选择诈骗类型为“刷单返利”后自动在页面右侧展示该类案件的常见套路提示帮助录入人员判断案件归类是否准确。检索端做了三个维度的匹配关键词匹配标题和案情描述用icontains模糊查询类型下拉框选择FraudType精确匹配时间范围案发时间在开始日期和结束日期之间检索结果用列表页展示每一条提供“详情”“编辑”“导出”三个操作。导出功能用的是Django的csv模块直接把查询结果导成CSV文件方便基层单位做台账归档。3.3 举报与反馈入口搭建UGC线索收集通道反诈宣传不能只是单向输出还要接收群众反馈。我在平台里搭建了一个用户反馈模块前端表单提供四个字段“反馈类型疑似诈骗举报/线索提供/意见建议、内容描述、联系方式、附件图片”提交后写入UserFeedback表。Django后台每收到一条新反馈状态自动为“待处理”管理员处理后改为“已处理”并填写处理备注。这里为了提升效率我给UserFeedbackAdmin配置了actions可以批量勾选多条反馈一键标记为“已处理”省去逐条打开编辑的麻烦。考虑到面对的是普通群众这个模块做了两点优化第一联系方式只做前台校验格式对就行不强制实名第二提交成功后页面跳转到“感谢反馈”并且数据即时入库避免群众以为没提交成功反复提交。3.4 宣传活动管理线下活动数字化沉淀线下宣传活动是反诈工作中比例最大的一部分。社区讲座、校园宣讲、广场摆摊这些活动需要记录下来形成工作痕迹否则年底总结时拿不出数据。活动管理模块的核心表是Campaign一条活动记录包括活动名称、地点、时间、参与人数、形式、合作单位、现场描述。此外每个活动可以上传活动照片我做了CampaignPhoto子表一对多挂接上传后自动生成缩略图。统计页面上按月份展示活动数量柱状图按行政区统计活动频次按活动类型统计参与人数占比。这个模块的可视化和案例数据可视化是分离的活动数据偏“亮党建”“亮工作”案例数据偏“警情分析”两个视角不要混在一张图表里。4. 可视化大屏与图表实战4.1 Flask数据接口设计标准JSON返回结构大屏前端全部通过Ajax请求Flask接口拿数据。我把所有接口统一设计成同样的JSON结构前端不管调哪个接口解析逻辑都是一样的{ code: 0, message: success, data: { categories: [刷单返利, 虚假投资, 冒充客服], values: [128, 86, 45] } }code为0表示正常返回非0表示异常。前端拿到数据后先判断code再渲染data。这种结构既简单又可靠避免了每个接口各自定义字段导致前端逻辑混乱。接口路由我用Blueprint做了模块化管理from flask import Blueprint, jsonify chart_bp Blueprint(chart, __name__) chart_bp.route(/api/chart/type_distribution) def type_distribution(): # 按诈骗类型统计案件数量 rows db.session.query( FraudCase.fraud_type, func.count(FraudCase.id) ).group_by(FraudCase.fraud_type).all() data { categories: [r[0] for r in rows], values: [r[1] for r in rows] } return jsonify(code0, messagesuccess, datadata)所有统计查询接口都是“一个接口一个查询逻辑”聚合计算尽量下推到SQL层面用func.count、func.sum、func.date_format这些SQL函数完成而不是把全部数据拉到Python里再循环统计。这样接口响应速度快代码也干净。4.2 ECharts图表实现玫瑰图、折线图、地图热力、双轴图大屏我规划了两行五列的布局第一行放核心KPI卡片和趋势图第二行放类型分布、地区热力、人群分布。ECharts图表的实现用常规的initsetOption方式但有几个细节值得单独说。类型分布用南丁格尔玫瑰图因为诈骗类型之间的数量差距可能很大第一名可能是最后一名的三倍普通饼图小扇区根本看不清。玫瑰图通过半径放大差异视觉效果更直观。代码关键配置option { series: [{ type: pie, roseType: radius, radius: [15%, 70%], center: [50%, 55%], data: chartData }] };趋势图用双轴设计同时展示每月发案数量和涉案金额。发案数量用柱状图涉案金额用折线图。两个指标单位不同所以需要双Y轴。这个图表的信息密度很高一眼能看出“几月份发案多、几月份金额高”对管理者判断宣传资源的投放节奏很有价值。地区热力图我用的GeoJSON地图数据通过ECharts的registerMap注册后配合visualMap组件实现。颜色区块的渐变区间需要根据数据大小自动生成visualMap: { min: 0, max: Math.max(...mapValues), inRange: { color: [#e8f4fd, #1a7ad4] } }这样数据量大时区域颜色深数据量小时颜色浅视觉层次非常清楚。注意加载地图时一定要提前确认GeoJSON文件和ECharts版本兼容性。ECharts 4和ECharts 5的registerMap调用方式不同项目里统一用了ECharts 5代码就没出过兼容性问题。4.3 大屏布局与自动刷新机制大屏是挂在走廊电视或者接待大厅的不可能有人去手动刷新页面所以自动刷新是刚需。我在页面上做了一个setInterval每30秒重新拉取一次数据setInterval(() { fetchDataAndUpdateCharts(); }, 30000);刷新时要注意一个问题直接setOption会保留之前的动画状态导致数据变化时图表闪动。解决方法是每次更新前先调用myChart.clear()清空画布再重新渲染。这样虽然多了一次绘制开销但30秒一次完全不影响。另一个大屏体验细节是标题栏的滚动公告。我在顶部放了一条滚动字幕轮播展示最新的三条防诈提醒。这个功能不需要后端前端定时切换数组内容就行但看起来非常“专业”建议加上。5. 前端页面与交互实现5.1 面向管理员的页面结构管理员端我设计了五个一级页面工作台、素材管理、案例管理、活动管理、数据大屏。工作台是默认首页展示待处理反馈数量、今日新增案件、本月活动场次等KPI卡片点击卡片可以快速跳转到对应模块。导航结构用到的是Django模板的url标签解析路由所以后端加了app_name和name参数保证路由名唯一。模板侧写了base.html作为母版把侧边栏、顶栏、面包屑这些公共部分抽离子页面只需要继承母版并根据{% block content %}填充内容。这个设计的好处是新增页面时不需要改公共布局只需要写业务内容区块后期维护非常省心。5.2 图表与页面的联动交互“只看图”和“能点图”是两个层次。我在数据大屏页面里加了图表联动点击类型分布玫瑰图的某个扇区下方实时显示该类型案件的Top5案例列表点击趋势图的某个月份旁边弹出该月的宣传文章清单。实现思路不复杂ECharts的click事件返回params.name前端拿到名称后用fetch请求对应的过滤接口把返回数据渲染到表格区域myChart.on(click, (params) { const typeName params.name; fetch(/api/cases/by_type?type${encodeURIComponent(typeName)}) .then(res res.json()) .then(data renderCaseList(data.data)); });这个交互让大屏从“展示工具”变成了“分析入口”领导现场看大屏时可以直接问“最近刷单返利案件都有哪些”点一下就能看到详细案例而不是只能看到一串数字。5.3 宣传物料展示页的模板渲染面向普通访客的宣传物料展示页我用Django模板直接渲染不走Flask接口。页面结构是两栏布局主栏是文章列表流新发布的内容排前面侧栏是热门文章排行榜和防诈提示卡片。文章列表的封面图使用thumbnail生成缩略图后展示列表页不加载原图大幅压缩了页面体积。具体方式是利用Pillow在后端对上传图片做压缩处理原图保存缩略图单独存储from PIL import Image def make_thumbnail(source_path, thumb_path, size(400, 250)): with Image.open(source_path) as img: img.thumbnail(size) img.save(thumb_path, quality85, optimizeTrue)这个细节对页面打开速度的提升非常明显一个没有压缩的图可能是3MB压缩后200KB访问体验完全不同。6. 常见问题与排查技巧实录6.1 Flask接口跨域问题开发时前端页面跑在Django的8000端口Flask跑在5000端口浏览器认为这是跨域请求Ajax直接报错。解决办法是给Flask添加跨域支持直接在响应头里加app.after_request def add_cors_headers(response): response.headers[Access-Control-Allow-Origin] * response.headers[Access-Control-Allow-Methods] GET, POST, OPTIONS response.headers[Access-Control-Allow-Headers] Content-Type return response这个方案最简单直接应对本地开发和测试环境完全够用。生产环境如果走Nginx统一代理可以把前后端挂在同一个域名下就不存在跨域问题了。6.2 Django和Flask同时启动时的端口配置Django默认跑8000Flask默认跑5000两个服务都要暴露给前端访问时最简单的办法是开发环境直接开两个终端分别启动。但前后端页面的资源路径比如ECharts库、Django静态文件要注意区分不能互相加载。我建议在Django的base.html模板里用一个script标签显式引入ECharts的CDN链接不要放到Flask渲染的页面里这样两个服务的资源依赖关系彻底解耦。Flask只负责JSON接口Django负责HTMLJSCSS职责划分越清晰越不容易出问题。实操心得如果要在同一台服务器上构建生产环境建议用Nginx做反向代理把/admin、/content路径代理到Django把/api/chart路径代理到Flask。这样对外只开放80端口不需要暴露两个服务端口也更安全。6.3 数据可视化图表不显示或空白大概率是数据格式不匹配。ECharts要求data字段要么是[{name: xx, value: xx}]格式要么是[数值1, 数值2]格式如果你接口返回的是对象数组但字段名不一致图表就会空白。排查三步走第一步浏览器F12看Network确认请求返回的JSON结构第二步Console看有没有报错第三步直接在页面里console.log(data)把数据打印出来核对字段名。还有一个小坑是ECharts容器没有高度或宽度为0图表也会渲染不出来。容器CSS必须显式设置高度比如height: 400px不能依赖内容撑开。6.4 统计数据与页面明细不一致图表统计的是全量数据而列表页往往有分页所以数字对不上很正常。但有一种真正的Bug是后端统计SQL的筛选条件写错了比如统计时忘了加“状态为已发布”这个条件把草稿状态的数据也算进去了。排查思路拿统计接口返回的总数去数据库里跑一条原生SQL看能不能对得上。如果SQL查询结果和接口返回一致说明数据本身没问题只是页面展示口径差异如果不一致就是聚合条件写错了。注意在统计时和列表时统一使用同一套筛选条件。6.5 大屏长时间运行卡顿大屏通常一周七天不关机长时间跑下来浏览器容易变得卡顿。主要原因是ECharts实例没有销毁、定时器叠加、DOM节点不断增长。常规优化措施使用window.addEventListener(beforeunload, () { clearInterval(timer); })在页面关闭时清理定时器每次刷新前先clear()图表实例再重新设置数据避免旧图表的绑定事件残留如果页面不需要交互用myChart.setOption(option, true)加上notMerge参数强制覆盖旧配置。我再多说一句大屏专用浏览器建议用Chromium内核的最新版并且开启disable-featuresTranslate和disable-featuresAutoPlayPolicy这类优化参数能显著减少无意义的后台任务占用。7. 项目部署与上线经验7.1 本地开发环境的依赖管理项目用pip安装依赖我强烈建议用一个requirements.txt把环境固定住。Django版本、Flask版本、SQLAlchemy版本、MySQL驱动、Faker、Pillow这些都要写进去否则换一台电脑跑项目版本不一致就会各种报错。我的requirements.txt核心内容大致长这样Django4.2.5 Flask2.3.3 Flask-SQLAlchemy3.0.5 PyMySQL1.1.0 Faker19.6.1 Pillow10.0.0 requests2.31.0固定版本号的作用是保证项目在任何机器上跑起来行为一致。尤其是Django的大版本之间API差异很大Django 2.x的写法在Django 4.x里可能直接编译不过千万别装“最新版”就当终点。7.2 生产环境部署组合建议生产部署我推荐一套组合Nginx Gunicorn 两个系统服务。Django用Gunicorn启动Flask也用Gunicorn单独启动一个进程Nginx统一接收外部请求按路径转发到对应服务。一个需要注意的点是MySQL的数据表字符集一定要设置为utf8mb4不然后端录入emoji表情或生僻字会直接报错。建库语句CREATE DATABASE fraud_platform DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;7.3 上线前必做的安全检查防诈平台涉及群众反馈信息上线前必须做好基础安全加固。我把这几件事列成清单项目上线前逐项检查关闭Django的DEBUG False避免报错信息暴露代码路径在ALLOWED_HOSTS里只填实际域名或IP不要用*Django管理后台地址不要保留默认/admin/改成一个不易猜测的路径用户反馈里的联系方式做脱敏处理列表页只显示前三位和后两位配置HTTPS证书全站强制跳转加密访问安全这件事不需要做到万无一失但至少不能让敏感信息裸奔。特别是联系方式这类个人信息一旦泄露对平台的影响会非常大这个问题怎么强调都不过分。8. 项目扩展方向与思考8.1 加入智能预警功能数据积累到一定量级后可以做智能预警当某类诈骗在短期内发案数量明显上升系统自动标红并生成预警信息推送给管理员。技术实现不复杂设一个阈值比如“近7天刷单返利案件数量占比超过总量30%”就触发预警需要的无非是一张预警规则表和定时任务。Django可以用django-crontab或Celery做定时任务每天凌晨跑一次统计有异常就写预警表、发通知。这个功能对管理者的价值极大属于“从记录工具变成决策工具”的关键一步。8.2 对接宣传数据大屏与手机端现在大屏是在电脑浏览器上看的但管理员不可能天天坐在大屏前。可以考虑做一个轻量版数据面板直接对接手机浏览器精简展示核心指标方便随时随地查看。开发方式不用重新写一套直接做响应式适配即可ECharts图表在小屏上自动缩小。8.3 批量导入历史案例真实场景中基层单位手头可能有几百条Excel格式的历史案例记录但系统里一条条录入太费时间。增加一个批量导入功能上传Excel模板后端用pandas或openpyxl读取逐行校验后写入FraudCase表。这一步能极大提升系统上线的初期数据积累速度没有数据的系统很难让人信服和持续使用。我在实际项目里就吃了这个亏前期系统案例太少大屏几乎没法看后来花了整整一个周末写导入脚本才把历史数据批量灌进去。现在回头看这个功能一开始就应该做在设计里。