图表那块我是从实用出发做的,选了一个能跑在多端的图表库。后端把结构化的数据丢过来,前端直接把数据绑上去,就能画出折线、柱状、饼图这些常见图形。数据都是异步拉的:前端只要把开始/结束时间和区域ID给后端,后端返回一串结构化数组。渲染时做了两件关键事:一是对数值做平滑处理,别让极端点把坐标轴拉歪了;二是一次性生成两份图片,一张大图用来导出,高分辨率;一张缩略图用于列表预览,加载快不卡顿。交互上支持缩放和悬浮提示,导出就是把画布转成PNG,返回客户端保存,省得大家还得手动截屏,挺方便的。

图片处理和存储这块也按套路来,不管是用户上传的图还是系统自动截图,保存都走服务端。拿到图片流后,我先用GUID当文件名,按日期目录扔到存储里,写盘或对象存储都行。写入流程里加了个临时文件落盘步骤,等写完再做原子重命名,避免并发写导致半成品出现。写完后再生成一张固定宽度的缩略图和一张原尺寸备份,缩略图给列表用,原图留着导出或放大看。图片路径和元数据会记到数据库,包括原始路径、缩略图路径、上传人、上传时间、来源类型这些字段,查起来清清楚楚。
页面上加载图片时要顾及性能。列表里用了懒加载,只有滚动到那行才去拉缩略图。用户点查看时才去请求原图,并在本地做一次缓存。展示时按容器等比缩放,避免跑版。遇到超大图,还会先做简单压缩,防止一次性拉几十兆,体验太差。

区域选择做成了分层的树形控件,支持单选和多选两种模式。区域数据来自一张区域表,表里有ID、父ID、名称、层级这些字段。前端拿到全量区域后先构一棵树,默认只渲染根节点,展开时再按需加载子节点,点完返回一个ID数组。后端拿这些ID做筛选时,常规是拼IN子句,但ID太多时会把这些ID先写到临时表,再用关联查询,这样避免SQL过长变成性能黑洞。界面上还加了“常用区域收藏”,常常查同一组区域的用户就省事了,点一下就选好了。
时间段控件看着简单,但细节不小。我用了两个日期选择控件配合时间选择,既支持整天也支持自定义到时分秒。加了预设快捷项:今天、本周、本月、上月、最近7天、最近30天,点一下自动填好时间并做校验,保证起点不晚于终点。时区统一交给后端处理,前端传UTC时间戳,后端按系统设定转换。为了防误操作,设了最小范围(列如至少1分钟)和最大范围(列如不超过一年),超限就提示并拦截提交。

后端数据访问统一封装了个工具类,负责建连接、参数绑定、执行SQL、结果映射和事务管理。对外暴露的方法比较常用:QueryList(sql, params)、QuerySingle(sql, params)、ExecuteNonQuery(sql, params)、
BeginTransaction/Commit/Rollback等。查询默认走从库,写操作走主库,做到读写分离。遇到异常就统一落到日志系统,并且返回标准错误码,方便上层判断和重试。
数据库最早做起来是先建report_db,表有report_records、areas、images、users四张。report_records里保存报表元信息:id、自定义名、start_time、end_time、area_ids(JSON)、data_blob(JSON)、created_by、created_at这些列。images表存图片路径和对应的report_id。建表脚本给了两套:本地开发用的精简版,生产用的完整版,后者带索引、分区提议和外键。为了测试时间筛选和分页,我还写了初始化脚本,创建库表后插入了几条跨天的测试数据。脚本里也包含在start_time、end_time、area_id等字段上的索引声明,能提升查询速度。

从表建好到页面能看见数据,中间有许多零碎活要做:数据导入脚本、定时任务把老数据汇总到统计表、权限校验、导出接口的频率限制以防滥用等等。开发时踩过不少坑。列如分区后某些查询计划反而变差,这就需要逐条分析执行计划调整分区键或加索引;又列如图片并发写入会把磁盘IO顶起来,解决办法是限流写入、并发队列和异步处理。上线部署那会儿,还在脚本里加了每日备份和图片清理策略,防止磁盘越长越大。














- 最新
- 最热
只看作者