23 KiB
FastBee IoT 设备监测报告引擎开发 Skill
领域背景:公司主营业务为桥梁、建筑、边坡、隧道等结构老化与安全监测,核心监测对象为裂缝、深部位移、轴力/应力、沉降、倾角、应变、振动等工程物理量。
本 Skill 目标:基于已落库的 IoT 物模型监测数据,为单个监测设备按「设备 + 时间范围」生成专业分析报告(Word
.docx),支持历史报表下载,保留期 1 个月。前端入口(2 处):① 大屏分析模式;② 数据中心分析页。两处均通过「选设备 + 选时间」即可生成并导出分析报告;另有历史报表列表可下载。
存储决策:已确认未购买云 OSS 服务 → 统一使用自建 MinIO(S3 协议兼容,复用现有
fastbee-oss插件,不改上传链路)。第一阶段范围:单设备报告。不在范围:项目综合报告、多设备联合分析、AI 自动工程安全判定、在线 Word 模板编辑器。
1. 现状盘点(已代码确认,先读后写)
1.1 存储结论:OSS 未购买 → MinIO
| 检查项 | 结论 |
|---|---|
oss_config 初始化数据(oss.sql / sql/yack-init.sql) |
仅有 aliyun/qiniu 两条记录,access_key/secret_key 均为占位符 XXXXXXXXXXXXXXX,无真实云 OSS 配置 |
fastbee-plugs/fastbee-oss |
基于 aws-java-sdk-s3(S3 协议);OssClient 显式兼容 MinIO:endpoint 非云服务商关键字时自动启用 path-style,getUrl() 有 minio 单独分支 |
| 结论 | 未购买 OSS → 使用自建 MinIO。复用 OssFactory/OssClient,禁止新写 MinIO/S3 Service。只需:PlatformType 增加 MINIO、oss_config 插入 minio 默认记录、部署 MinIO 并配置 30 天生命周期规则 |
1.2 可复用能力
| 能力 | 位置 | 复用方式 |
|---|---|---|
| 设备/产品/物模型实体 | Device.java / Product.java / ThingsModel.java |
直接复用,禁止新建重复 DTO |
| 时序数据查询 | DeviceLogMapper.listHistory(MySQL)/ TDDeviceLogMapper(TDengine) |
报告模块通过 Provider 委托,禁止自写 SQL |
| 数据中心分析能力 | DataCenterController(/data/center/deviceStats、deviceDataQuality、deviceHistogram) |
报告统计/质量可复用其 Service 层逻辑 |
| 文件上传 | OssFactory.instance(redis).upload(byte[], path, contentType) → UploadResult{url, filename} |
上传到 MinIO |
| 文件读取/删除 | OssClient.getObjectContent(path) / delete(path) |
下载代理、过期清理兜底 |
| Word/POI | poi-ooxml 4.1.2(已有) |
新增 poi-tl 做模板渲染 |
| Excel | easyexcel 3.3.1(已有) |
如需导出原始数据复用 |
| MQ | fastbee-mq(RocketMQ) |
未来异步报告任务复用 |
1.3 关键缺失(本 Skill 补齐)
fastbee-report模块:报告引擎、Analyzer、report_record表(均不存在)。- 设备监测分类路由:
DeviceType是连接拓扑(直连/网关/摄像头),iot_category是租户商务分类树,均不能作为分析路由键 → 需iot_product.monitor_category_code+MonitorCategory枚举。 - 报告周期/时间窗口:
ReportPeriod枚举 +ReportPeriodResolver。 - 结构化报告阈值:告警阈值藏在 LiteFlow
iot_scene脚本里,报告不可直接读 → 新增iot_things_threshold。 - Java 端图表(JFreeChart)与 Word 模板引擎(poi-tl)。
- MinIO 默认存储配置。
- 前端两处「生成并导出分析报告」按钮 + 历史报表页(列表/下载/过期)。
1.4 前端现状
| 入口 | 页面 | 现状 |
|---|---|---|
| 大屏分析模式 | yack-web/src/views/bigScreen/home.vue(activeMode === 'analysis') |
已有「设备选择 + 时间范围(预设 5m~7d / 自定义)+ 查询数据和曲线」,无报表按钮 |
| 数据中心 | yack-web/src/views/dataCenter/analysis.vue |
已有「设备 + 时间范围」查询(统计/数据质量/直方图/趋势),无报表按钮 |
| 历史报表 | 无 | 需新增列表 + 下载 |
2. 整体逻辑链路
前端(大屏分析模式 / 数据中心):选设备 + 时间范围
↓ POST /iot/report/generate {deviceId, startTime, endTime}
ReportRequest
↓
ReportPeriodResolver → TimeWindow(周期预设或自定义)
MonitorCategoryResolver → MonitorCategory(读 iot_product.monitor_category_code)
↓
ReportContextBuilder → ReportContext(设备/产品/物模型/时序数据/阈值一次性装配)
↓
ReportAnalyzerFactory.get(category) → 专项 Analyzer(V1:CrackMeterAnalyzer)
↓
AnalysisResult(统计/趋势/质量/预警/结论/图表数据)
↓
ReportModel → WordRenderer(poi-tl {{}} + JFreeChart PNG)→ byte[]
↓
OssFactory → MinIO 上传 → fileUrl
↓
report_record 落库(status=SUCCESS,expire_time = create_time + 30 天)
↓
返回 reportNo / fileUrl → 前端直接下载,或进历史报表
历史报表:
GET /iot/report/list → 分页返回未过期记录
GET /iot/report/download/{reportNo} → 校验 expire_time → OssClient 流式下载
过期双保险:MinIO 生命周期 30 天删除兜底 + 下载接口返回“报告已过期”
3. 后端实现方案
3.1 新增模块 fastbee-report
依赖方向(禁止反向依赖、禁止循环依赖):
fastbee-admin / fastbee-open-api(REST 入口)
↓
fastbee-report(报告引擎核心)
↓
fastbee-iot-service + fastbee-iot-data + fastbee-oss + fastbee-common
↓
MySQL / TDengine / MinIO
核心目录:
fastbee-report/
├── api/ ReportEngine, ReportRequest, ReportResult
├── core/ DefaultReportEngine, ReportContext, ReportContextBuilder, ReportAssembler
├── category/ MonitorCategoryResolver(+Default)
├── period/ ReportPeriodResolver(+Default), TimeWindow
├── analyzer/ ReportAnalyzer, ReportAnalyzerFactory(+Default), crack/CrackMeterAnalyzer
├── analysis/ StatisticsCalculator, TrendAnalyzer, RateCalculator, QualityAnalyzer, WarningAnalyzer
├── threshold/ ThingsThresholdProvider(+Db 实现), ThingsThreshold
├── data/ ReportDataProvider, MetricData
├── model/ ReportModel, AnalysisResult, Statistics, TrendAnalysis, QualityAnalysis, WarningAnalysis, ChartData
├── chart/ ChartGenerator(JFreeChart 生成 PNG)
├── renderer/ ReportRenderer, WordRenderer(poi-tl), WordTemplateResolver
├── record/ ReportRecord, ReportRecordMapper, ReportRecordService
└── exception/ ReportException
3.2 核心流程(DefaultReportEngine)
1. 参数校验:deviceId 必填;startTime/endTime 与 period 二选一(都传时以自定义为准);endTime > startTime
2. 时间窗口:period != CUSTOM 时按自然日历计算
日报=当日 0-24;周报=周一~周日;半月报=1~15 / 16~月末;月报=1~月末(时区 GMT+8)
3. 分类路由:MonitorCategoryResolver.resolveByDeviceId → MonitorCategory
4. 构建上下文:设备/产品/物模型(isMonitor=1)/时序数据/阈值 一次性装入 ReportContext
5. analyzerFactory.get(category).analyze(context, request) → AnalysisResult
6. reportAssembler.assemble(...) → ReportModel
7. reportRenderer.render(...) → byte[] → MinIO 上传 → ReportResult{reportNo, fileUrl, expireTime}
8. reportRecordService 落库(SUCCESS / FAILED + expire_time)
禁止:ReportEngine 直接操作 Mapper;Analyzer 自己 @Autowired 业务 Service 查询;WordRenderer 查库。
3.3 设备监测分类(MonitorCategory)
- 现有
DeviceType(连接拓扑)与iot_category(商务分类树)均不能作为分析路由键。 iot_product新增monitor_category_code VARCHAR(64)(可空),值如CRACK_METER、DEEP_DISPLACEMENT、AXIAL_FORCE、STATIC_LEVEL、VIBRATING_WIRE;存量产品按产品名 SQL 回填。MonitorCategory枚举放fastbee-common;解析不到返回UNKNOWN并报「未配置监测分类」,不启发式猜测。- 新增设备类型 = 新增一个 Analyzer(实现
supports(category)),不改 ReportEngine、不写if(category==xxx)巨型判断。
3.4 时间范围(ReportPeriod)
- 枚举:
DAY / WEEK / HALF_MONTH / MONTH / CUSTOM,放fastbee-common。 - 前端两入口当前均传自定义时间范围(
startTime/endTime);周期预设(日/周/半月/月)留给后台与后续使用。 ReportPeriodResolver计算自然日历窗口;必须单测边界:周一/月末/闰年二月/半月报 15 日分界。
3.5 阈值(WarningAnalyzer)
- 现状:告警阈值藏在 LiteFlow
iot_scene脚本里,报告引擎直接解析成本高、易错。 - V1:新增
iot_things_threshold表(字段:product_id、device_id、monitor_category_code、metric_identifier、warning_value、alarm_value、control_value、unit)。 - 查询优先级:
device_id > product_id > 全局默认(product_id=0)。Analyzer 只读结构化阈值,禁止硬编码阈值。
3.6 分析能力(通用 + 专项)
- 通用:统计(当前/初始/极值/均值/中位/变化量/累计变化)、趋势(线性回归 UP/DOWN/STABLE/FLUCTUATION + 斜率 + R²)、数据质量(缺失/重复/越界/完整率)、预警(NORMAL/WARNING/ALARM)。
- 专项:V1 只实现裂缝计
CrackMeterAnalyzer(当前/初始宽度、变化量、速率、趋势、预警、时程图),跑通后扩展深部位移/轴力/静力水准/振弦。 - 图表:后端 JFreeChart 生成 PNG 嵌入 Word;禁止前端生成图回传后端。
- 结论:基于结构化分析结果用规则+模板生成文字,不用 AI 直接判断结构安全。
3.7 Word 渲染
- 新增依赖:
poi-tl 1.10.4(兼容 POI 4.1.2)、jfreechart 1.5.4;禁止再引入 docx4j。 - 模板:
DEVICE_CRACK_METER_V2.docx等,按template_code(DEVICE_${CATEGORY}_V2)从 classpath 的templates/目录加载(DefaultWordTemplateResolver)。模板与渲染逻辑强耦合,不入库,随代码版本发布。 - 变量:
{{report.no}}、{{report.periodDesc}}、{{device.name}}、{{device.serialNumber}}、{{analysis.statistics.max}}、{{analysis.statistics.delta}}、{{analysis.trend.description}}、{{analysis.warning.status}}、循环表格{{conclusions}}、图片{{charts.crackTrend}}等;模板不直接绑定数据库字段。
3.8 主监测指标(模板里的"监测指标")
{{device.metricName}} 的值来自 AnalysisResult.primaryIdentifier → Statistics.modelName
(即 iot_things_model.model_name 的中文名),由 PrimaryMetricResolver.resolve 判定,
优先级从高到低:
- 用户显式指定 ——
iot_things_threshold.is_primary为真的物模型直接当选, 不受辅助量黑名单与关键词限制。级别口径与阈值数值同一套(逐标识符device > product): 设备级配了该物模型就以设备行为准(设备行没勾、产品行勾了也不作数),设备没配才继承产品行的勾选。 多个标记项同时有数据时,取物模型列表顺序最靠前的(与关键词命中同口径); - 名称/标识符命中
Analyzer#primaryMetricKeywords()的物模型,同类中优先该时段有数据的; - 第一个有数据的非辅助指标;
- 第一个非辅助指标;最后兜底物模型列表首项。
为什么需要第 1 条:关键词表硬编码在 Java 里,物模型命名不规范时第 2 条整体落空、静默退化为第 3/4 条
——等于把 ORDER BY model_order DESC 的排序结果当主指标。现场实例:轴力计产品因物模型叫"锚力"/"压力"、
关键词只有"轴力/应力",把 GPS"经度"(model_order 更靠前)选成了监测指标。
配置入口:yack-web 阈值配置页 views/iot/components/threshold-config.vue 的「主监测指标」开关,
与阈值同表同行落库,无需额外表。
读取方式:不单独查询,直接复用 ThingsThresholdProvider#find(selectByPriority)逐物模型查回的
阈值行里的 is_primary —— 级别口径天然与阈值数值一致,不会出现"标记说它是主指标、阈值数值却取设备行"
的自相矛盾状态。提取逻辑在 ReportContextBuilder#resolveExplicitPrimary(包级静态,可单测)。
设备页可见性:本设备未配置该物模型 → 开关旁显示「继承」;本设备已配置但未勾选、而产品级勾了 → 显示「覆盖产品」并提示报告不会用它作主指标(设备优先口径下该勾选确实失效,必须显式告知)。
注意此处不是并集:同一个物模型两级都配置时,设备行的
is_primary单独决定结果; 不同物模型各自独立判断(互不影响)。
4. 存储方案:MinIO(关键决策)
4.1 结论与理由
- 仓库内
oss_config无真实云 OSS 密钥(占位符),确认未购买 OSS 服务。 - MinIO 自建、S3 协议,现有
OssClient已兼容:endpoint 不含aliyun/qcloud/qiniu/obs关键字时自动启用 path-style;getUrl()对 minio 单独处理。 - 因此不改上传代码,只做:部署 MinIO +
oss_config配置 +PlatformType增加枚举 + 过期策略。
4.2 部署(Docker Compose 单节点示例)
services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
ports:
- "9000:9000" # S3 API
- "9001:9001" # 控制台
environment:
MINIO_ROOT_USER: fastbee-minio
MINIO_ROOT_PASSWORD: <强密码>
volumes:
- minio-data:/data
启动后:创建 bucket fastbee,并在控制台生成 AccessKey/SecretKey。
4.3 系统配置(oss_config 插入默认记录)
-- 先停用云 OSS 配置(存量占位符)
UPDATE `oss_config` SET `status` = '1' WHERE `config_key` IN ('aliyun', 'qiniu');
-- 插入 MinIO 默认配置(endpoint 为云服务器地址,供后端 S3 SDK 连接)
INSERT IGNORE INTO `oss_config`
(tenant_id, tenant_name, config_key, access_key, secret_key, bucket_name, prefix,
endpoint, domain, is_https, region, access_policy, status, del_flag, create_by, create_time, update_by, update_time, remark)
VALUES
(1, 'admin', 'minio', 'supermeminio', 'minio20260909', 'fastbee', 'report',
'http://47.1.28.77:9000', '', 'N', '', '0', '0', '0', 'admin', NOW(), 'admin', NOW(), 'MinIO默认存储');
要点:
status='0'设为默认,启动时OssApplicationRunner自动装载进 Redis(sys_oss:default_config)。endpoint为后端连接 MinIO 的 S3 API 地址,需确保端口 9000 在安全组/防火墙放行;若后端与 MinIO 同机部署,可改为http://127.0.0.1:9000。domain为空时,getUrl()返回http://<endpoint>/<bucketName>;若配置了 CDN 或公网域名,可填入以生成更友好的 URL。PlatformType增加MINIO(5, "MinIO")(仅用于管理页展示;实际路由走config_key)。access_policy=0(私有),报告下载统一走后端代理接口(GET /iot/report/download/{reportNo}),可校验权限和过期状态。bucket_name='fastbee':需在 MinIO 中提前创建此 bucket。endpoint需根据实际 MinIO 部署地址修改(如http://192.168.1.100:9000或生产域名)。bucket_name='fastbee':需在 MinIO 中提前创建此 bucket。
4.4 报告文件路径
/report/yyyy/MM/dd/device/{deviceId}/{reportNo}.docx
例:/report/2026/08/16/device/10001/REPORT-202608160001.docx
禁止把 Word 永久存服务器本地。
4.5 历史报表过期 1 个月(双保险)
- 业务层:
report_record.expire_time = create_time + 30 天;列表只返回未过期记录;下载接口校验过期并提示「报告已过期」。 - 存储层兜底:MinIO 生命周期规则删除
report/前缀下 30 天前的对象:
mc ilm rule add local/fastbee --prefix report/ --expire-days 30
- 下载实现(推荐后端代理流式下载,可校验权限 + 过期):
// 上传(复用现有插件)
UploadResult r = OssFactory.instance(redis)
.upload(docBytes, path, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
// 下载:按 reportNo 查 report_record → 校验 expire_time → getObjectContent(path) 流式输出
5. 前端实现方案
5.1 入口一:大屏分析模式(bigScreen/home.vue)
- 在「分析条件」卡片“查询数据和曲线”旁新增按钮 「生成并导出分析报告」。
- 参数直接取已有
analysisQuery.deviceId+analysisQuery.timeRange([startTime, endTime])。 - 交互:点击 →
POST /iot/report/generate(loading 防重复)→ 成功拿reportNo/fileUrl直接触发下载,并提示可在历史报表查看。 - 未选设备或时间时按钮禁用。
// 参考实现(新增到 home.vue methods)
async handleGenerateReport() {
const [startTime, endTime] = this.analysisQuery.timeRange || [];
if (!this.analysisQuery.deviceId || !startTime || !endTime) {
this.$message.warning('请先选择设备和时间范围');
return;
}
const res = await generateDeviceReport({
deviceId: this.analysisQuery.deviceId,
startTime,
endTime,
templateCode: 'DEVICE_CRACK_METER_V1',
format: 'WORD',
});
// res.data.fileUrl → window.open 或 a[download] 下载
}
5.2 入口二:数据中心(dataCenter/analysis.vue)
- 在筛选栏“查询/重置”旁新增 「生成并导出分析报告」。
- 参数取已有
queryParams.deviceId+queryParams.dayDaterange。 - 交互与 5.1 一致,复用同一 API 封装。
5.3 历史报表(新页面/面板)
- 入口建议:数据中心新增 tab「历史报表」(或大屏个人中心),接口权限
iot:report:list。 - 列表字段:报告编号、设备名称、监测分类、周期、时间范围、生成时间、过期时间、状态、操作(下载)。
- 只展示未过期记录;已过期记录不展示(或置灰提示「已过期」)。
- 下载:
GET /iot/report/download/{reportNo},走浏览器下载。
6. API 设计
POST /iot/report/generate 生成报告(同步,V1)
GET /iot/report/list 历史报表(分页,仅未过期)
GET /iot/report/download/{reportNo} 下载(校验过期,代理下载)
生成请求(前端两入口的通用格式):
POST /iot/report/generate
{
"deviceId": 10001,
"startTime": "2026-08-01 00:00:00",
"endTime": "2026-08-08 23:59:59",
"templateCode": "DEVICE_CRACK_METER_V1",
"format": "WORD"
}
可选周期预设(后台/后续使用):
{
"deviceId": 10001,
"period": "WEEK",
"anchorDate": "2026-08-09",
"templateCode": "DEVICE_CRACK_METER_V1"
}
响应:
{
"code": 200,
"data": {
"reportNo": "REPORT-202608160001",
"status": "SUCCESS",
"fileName": "304259363251-周报-监测分析报告.docx",
"fileUrl": "http://minio-host:9000/fastbee/report/2026/08/16/device/10001/REPORT-202608160001.docx",
"expireTime": "2026-09-15 23:59:59"
}
}
权限:
@PreAuthorize("@ss.hasPermi('iot:report:generate')") // 生成(写)
@PreAuthorize("@ss.hasAnyPermi('iot:report:list,iot:device:list')") // 列表/下载(读,跨页面复用)
7. 数据库改动
| 对象 | 说明 |
|---|---|
iot_product 加列 |
monitor_category_code VARCHAR(64) 可空;存量按产品名回填(如 UPDATE iot_product SET monitor_category_code='CRACK_METER' WHERE product_name LIKE '%裂缝%') |
iot_things_threshold |
物模型阈值表(product/device 级:预警/报警/控制值 + 速率双控) |
report_record |
报告记录表(含 expire_time) |
oss_config 插入 |
minio 默认配置记录(见 4.3) |
report_record 核心字段:
CREATE TABLE report_record (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
report_no VARCHAR(64) NOT NULL COMMENT 'REPORT-yyyyMMddHHmmss-xxxx',
device_id BIGINT NOT NULL,
monitor_category_code VARCHAR(64),
period VARCHAR(32) COMMENT 'DAY/WEEK/HALF_MONTH/MONTH/CUSTOM',
start_time DATETIME,
end_time DATETIME,
status VARCHAR(16) COMMENT 'CREATING/SUCCESS/FAILED',
file_url VARCHAR(512),
expire_time DATETIME COMMENT '过期时间 = create_time + 30 天',
error_message VARCHAR(1024),
tenant_id BIGINT,
create_by VARCHAR(64),
create_time DATETIME,
KEY idx_device (device_id, create_time),
KEY idx_expire (expire_time)
) COMMENT '报告生成记录';
8. 开发顺序
- 基础设施:根 pom 声明 poi-tl/jfreechart 版本;建 3 张新表 +
iot_product加列;MonitorCategory/ReportPeriod枚举;MinIO 部署 +oss_config默认记录 +PlatformType.MINIO。 - fastbee-report 骨架:模块注册进根 pom;
ReportRequest/ReportEngine/ReportContext。 - 分类与周期:
MonitorCategoryResolver、ReportPeriodResolver+ 边界单测。 - Analyzer 框架 + 裂缝计:先只实现
CrackMeterAnalyzer。 - 通用分析:
StatisticsCalculator/TrendAnalyzer/RateCalculator/QualityAnalyzer/WarningAnalyzer。 - Provider:
ReportDataProvider(委托DeviceLogMapper)、ThingsThresholdProvider。 - 渲染:
ChartGenerator(JFreeChart)+WordRenderer(poi-tl)+ 模板DEVICE_CRACK_METER_V1.docx。 - 落库与下载:
report_record+expire_time;MinIO 生命周期规则;下载接口(校验过期)。 - 前端:大屏分析模式 + 数据中心加「生成并导出分析报告」;历史报表页 + 下载。
- 闭环验证:真实设备 + 时间范围 → 生成 → 下载 → 列表可见 → 过期处理验证。
9. Definition of Done
[ ] MinIO 部署完成,oss_config 默认 minio 记录生效(OssFactory 可上传/下载)
[ ] fastbee-report 模块创建完成,Maven 无循环依赖
[ ] iot_product.monitor_category_code + 3 张新表建好
[ ] ReportPeriodResolver 边界单测通过
[ ] CrackMeterAnalyzer 跑通(设备 + 时间 → AnalysisResult)
[ ] WordRenderer(poi-tl) + ChartGenerator(JFreeChart) 生成 DOCX
[ ] 生成接口返回 reportNo / fileUrl / expireTime
[ ] report_record 落库,expire_time = +30 天
[ ] 列表接口只返回未过期记录
[ ] 下载接口可下载;过期返回明确提示
[ ] 大屏分析模式与数据中心均可「生成并导出分析报告」
[ ] 历史报表页面可查看/下载
[ ] 设备 304259363251 完整闭环验证通过
10. 不允许的设计
❌ 新写 MinIO/S3 Service(必须复用 OssFactory/OssClient)
❌ 报告文件永久存服务器本地
❌ ReportEngine / WordRenderer 直接查库
❌ Analyzer 硬编码阈值 / 所有设备写一个 Analyzer / if(category==xxx)
❌ 复用 DeviceType 或 iot_category 作为监测分类路由键
❌ 前端生成图表回传后端
❌ 报告模块反向依赖 fastbee-iot-service 之外、造成循环依赖
❌ 引入 docx4j、新 OSS SDK 等重复依赖
11. 第一阶段范围外(预留)
项目综合报告、多设备联合分析、AI 自动工程安全判定、在线 Word 模板编辑器、异步生成(后续可走 fastbee-mq RocketMQ)。