42 KiB
FastBee IoT 设备监测报告引擎开发 Skill
领域背景:公司主营业务为桥梁、建筑、边坡、隧道等结构老化与安全监测。核心监测对象为裂缝、深部位移、轴力/应力、沉降、倾角、应变、振动等工程物理量。报告引擎的目标是:基于已落库的 IoT 物模型监测数据,为单个监测设备生成专业分析报告,并支持日报 / 周报 / 半月报 / 月报四种周期,最终通过 Word 模板导出
.docx。第一阶段范围:根据所选设备 + 报告周期(或自定义时间范围)生成单设备分析报告。
不在第一阶段实现:项目综合报告、多设备联合分析、AI 自动工程安全判定、在线 Word 模板编辑器。
0. 现状盘点(先读后写,禁止重复造轮子)
执行本 Skill 前,必须先认清 FastBee 当前已有什么、缺什么。下列结论已通过代码调研确认。
0.1 已有可复用基础设施
| 能力 | 位置 | 复用方式 |
|---|---|---|
| 设备实体 | iot_device / Device.java |
Device 含 deviceId, serialNumber, productName, projectId, thingsModelValue |
| 产品实体 | iot_product / Product.java |
Product 含 categoryId, protocolCode, thingsModelsJson |
| 物模型 | iot_things_model / ThingsModel.java |
字段:identifier, unit, datatype, type(1属性/2功能/3事件), isMonitor, isHistory, isChart, isCalculated(0上报/1横向计算/2纵向计算), formula, specs |
| 项目实体 | iot_project / Project.java |
projectId, projectCode, projectName, address, startTime, endTime, projectStatus |
| 设备日志(时序数据) | iot_device_log / DeviceLog.java |
字段:ts, logType(1属性上报/7运算数据), logValue, deviceId, serialNumber, identity, isMonitor, mode |
| 日志查询(MySQL) | DeviceLogMapper.java |
listHistory(DeviceLog) / selectHistoryList / selectMonitorList / selectLastReport |
| 日志查询(TDengine) | TDDeviceLogMapper.java |
TDengine 启用时走此 Mapper |
| 告警定义 | iot_alert / Alert.java |
alertLevel(1提醒/2轻微/3严重), status,阈值规则在 iot_scene(LiteFlow) |
| OSS 上传 | fastbee-plugs/oss / OssFactory.java |
OssFactory.instance(redis).upload(byte[], path, contentType) → UploadResult{url, filename} |
| Word/POI | fastbee-common/pom.xml |
已含 poi-ooxml 4.1.2;未含 poi-tl / docx4j |
| Excel | easyexcel-core 3.3.1 |
已含 |
| MQ | fastbee-mq |
RocketMQ,未来异步报告任务复用 |
| Maven 父 POM | pom.xml |
revision=3.8.5, Java 1.8, Spring Boot 2.5.14, poi.version=4.1.2 |
0.2 关键缺失(本 Skill 要补齐的)
- 设备分类(最重要):
iot_device.device_type/DeviceType.java枚举 = 连接拓扑{UNKNOWN, DIRECT_DEVICE, GATEWAY, CAMERA, SUB_GATEWAY},不区分裂缝计/深部位移/轴力计/静力水准/振弦。iot_category是租户私有的产品分类树,只有categoryName文本、无稳定机器码,不能作为 Analyzer 路由键。 - 报告周期:当前无日报/周报/半月报/月报概念。
- 报告引擎:无
fastbee-report模块、无ReportEngine、无ReportAnalyzer、无ReportRecord表。 - 监测点阈值:
iot_alert无阈值数值列,阈值藏在 LiteFlowiot_scene规则里,报告难以直接读取结构化阈值。 - Java 端图表:仅前端有 echarts;后端无 JFreeChart/XChart,无法生成 PNG 嵌入 Word。
- Word 模板引擎:仅有裸 POI,无
{{var}}占位符语法支持(需引入 poi-tl)。
1. Skill 目标
补齐报告能力链路:
IoT 数据
↓
ReportRequest(设备 + 周期/时间范围 + 模板)
↓
ReportContext(设备/产品/物模型/监测数据/阈值)
↓
ReportAnalyzer(按 MonitorCategory 路由)
↓
AnalysisResult(统计/趋势/质量/预警/结论)
↓
ReportModel
↓
WordRenderer(poi-tl + JFreeChart)
↓
DOCX → OSS → ReportRecord
最终形成:
IoT 数据 → Report Engine → 专业监测分析报告 (.docx)
2. 核心设计原则
2.1 Report Engine 不直接理解数据库
禁止 ReportEngine → 直接操作 Mapper → 查询数据库 → 分析 → Word。
推荐:
数据访问层(DeviceLogMapper / ThingsModelMapper / OssFactory)
↓
ReportContext(已装配好的上下文)
↓
Analyzer(纯计算)
↓
AnalysisResult
↓
ReportModel
↓
WordRenderer
Report Engine 只负责报告流程编排,Analyzer 不允许自己 @Autowired DeviceService/ProjectService 到处查询。
2.2 复用优先
- OSS → 复用
OssFactory,禁止新写MinIOService/S3Service - 数据查询 → 复用
DeviceLogMapper.listHistory,禁止报告模块自己写 TDengine SQL - Word → 基于
poi-ooxml(已含)+ 引入poi-tl,禁止再引入 docx4j - 图表 → 引入
JFreeChart(成熟、纯 Java、生成 PNG) - 物模型/设备/产品领域对象 → 复用
fastbee-iot-service现有实体,禁止新建ReportDevice/ReportThingsModel重复 DTO(除非报告场景确实需要独立 DTO)
2.3 单一职责与可扩展
新增设备分类只新增一个 Analyzer,不修改 ReportEngine、不写 if(category==xxx) 巨型判断。
3. 设备分类深度设计(MonitorCategory)
这是本 Skill 最核心的设计。公司做桥梁/建筑老化检测,设备分类是报告分析的入口——不同传感器(裂缝计 vs 深部位移 vs 静力水准)的分析维度、图表、结论模板完全不同,必须有稳定、机器可读的分类路由键。
3.1 为什么不能复用现有分类
| 现有分类 | 为什么不行 |
|---|---|
iot_device.device_type(DeviceType 枚举) |
是连接拓扑(直连/网关/摄像头/子设备),与传感器物理量无关 |
iot_product.category_id → iot_category |
租户私有的产品目录树,仅 categoryName 文本;同一类传感器在不同租户下分类名/层级可能不同;无稳定 code,不能做路由键 |
ThingsModel identifier 启发式推断 |
命名不规范时误判,专业工程报告不能依赖猜测 |
3.2 方案:iot_product 加列 + MonitorCategoryEnum
3.2.1 数据库改动
-- iot_product 新增监测分类编码列(可空,兼容存量数据)
ALTER TABLE iot_product
ADD COLUMN monitor_category_code VARCHAR(64) NULL COMMENT '监测分类编码(报告引擎路由键,如 CRACK_METER)'
AFTER category_id;
-- 存量产品由后台运维或 SQL 一次性回填
-- 例:UPDATE iot_product SET monitor_category_code='CRACK_METER' WHERE product_name LIKE '%裂缝%';
不在
iot_device落库:设备分类随产品走,避免冗余与不一致。设备查询时 JOINiot_product取monitor_category_code即可(DeviceMapper 已有产品关联)。
3.2.2 MonitorCategoryEnum
位置:fastbee-common/src/main/java/com/fastbee/common/enums/MonitorCategory.java
@Getter
@AllArgsConstructor
public enum MonitorCategory {
CRACK_METER("CRACK_METER", "裂缝计"),
DEEP_DISPLACEMENT("DEEP_DISPLACEMENT", "深部位移"),
AXIAL_FORCE("AXIAL_FORCE", "轴力/压力计"),
STATIC_LEVEL("STATIC_LEVEL", "静力水准仪"),
VIBRATING_WIRE("VIBRATING_WIRE", "振弦式采集仪"),
TILT_METER("TILT_METER", "倾角仪"),
STRAIN_GAUGE("STRAIN_GAUGE", "应变计"),
TEMPERATURE_HUMIDITY("TEMPERATURE_HUMIDITY", "温湿度"),
VIBRATION("VIBRATION", "振动/加速度"),
CUSTOM("CUSTOM", "自定义"),
UNKNOWN("UNKNOWN", "未知");
private final String code;
private final String desc;
public static MonitorCategory of(String code) {
for (MonitorCategory v : values()) {
if (v.code.equalsIgnoreCase(code)) return v;
}
return MonitorCategory.UNKNOWN;
}
}
3.2.3 Product 实体加字段
Product.java 增加:
@ApiModelProperty("监测分类编码(报告引擎路由键)")
private String monitorCategoryCode;
3.2.4 MonitorCategoryResolver
位置:fastbee-report/src/main/java/com/fastbee/report/category/MonitorCategoryResolver.java
public interface MonitorCategoryResolver {
MonitorCategory resolve(Long productId);
MonitorCategory resolveByDeviceId(Long deviceId);
}
实现:先查 iot_product.monitor_category_code;为空时返回 UNKNOWN(第一阶段不实现启发式回退,避免误判;后台需维护分类)。
3.2.5 AnalyzerFactory 按 MonitorCategory 路由
public interface ReportAnalyzerFactory {
ReportAnalyzer get(MonitorCategory category);
}
@Component
public class DefaultReportAnalyzerFactory implements ReportAnalyzerFactory {
private final List<ReportAnalyzer> analyzers;
@Override
public ReportAnalyzer get(MonitorCategory category) {
return analyzers.stream()
.filter(a -> a.supports(category))
.findFirst()
.orElseThrow(() -> new ReportException("不支持的监测分类: " + category));
}
}
3.3 与现有 iot_category 的关系
两者正交,互不替代:
iot_category → 产品商务分类(客户买什么、目录树展示)
iot_product.monitor_category_code → 监测语义分类(如何分析数据、走哪个 Analyzer)
同一个产品可以:category_id = "桥梁监测传感器/裂缝计"(商务树)+ monitor_category_code = CRACK_METER(分析路由键)。
3.4 命名冲突说明
不要复用 DeviceType 这个类名——项目中已存在 com.fastbee.iot.enums.DeviceType(连接拓扑)。本 Skill 的分类统一叫 MonitorCategory,避免歧义。
4. 报告周期设计(ReportPeriod)
支持:日报 / 周报 / 半月报 / 月报,统一使用自然日历窗口(符合工程监测报告习惯)。
4.1 ReportPeriod 枚举
位置:fastbee-common/src/main/java/com/fastbee/common/enums/ReportPeriod.java
@Getter
@AllArgsConstructor
public enum ReportPeriod {
DAY("DAY", "日报"),
WEEK("WEEK", "周报"),
HALF_MONTH("HALF_MONTH", "半月报"),
MONTH("MONTH", "月报"),
CUSTOM("CUSTOM", "自定义");
private final String code;
private final String desc;
}
4.2 自然日历窗口规则
锚点日期 = anchorDate(默认为报告生成当天)。
| 周期 | startTime | endTime |
|---|---|---|
| 日报 | anchorDate 00:00:00 | anchorDate 23:59:59 |
| 周报 | anchorDate 所在周的周一 00:00:00 | 周日 23:59:59(按周一为一周起点) |
| 半月报 | anchorDate 日期 ≤15 → 当月1日 00:00:00 ~ 当月15日 23:59:59;否则 16日 00:00:00 ~ 当月最后一天 23:59:59 | |
| 月报 | anchorDate 所在月 1日 00:00:00 ~ 当月最后一天 23:59:59 | |
| 自定义 | request.startTime | request.endTime |
4.3 ReportPeriodResolver
public interface ReportPeriodResolver {
/** 根据周期与锚点日期,计算 [startTime, endTime] */
TimeWindow resolve(ReportPeriod period, LocalDate anchorDate);
}
public class TimeWindow {
private LocalDateTime startTime;
private LocalDateTime endTime;
}
实现要点:
- 周一为一周起点(
DayOfWeek.MONDAY) - 月末用
YearMonth.atEndOfMonth() - 半月报以 15 日为界
- 时区固定
GMT+8(与DeviceLog.ts的@JsonFormat timezone=GMT+8一致)
4.4 ReportRequest 同时支持周期与自定义
public class ReportRequest {
private Long deviceId;
/** 报告周期;为 CUSTOM 时使用 startTime/endTime */
private ReportPeriod period;
/** 锚点日期(period != CUSTOM 时生效,默认今天) */
private LocalDate anchorDate;
/** 自定义开始时间(period == CUSTOM 时生效) */
private LocalDateTime startTime;
/** 自定义结束时间(period == CUSTOM 时生效) */
private LocalDateTime endTime;
private String templateCode;
private ReportFormat format;
}
校验规则:
period == null且startTime/endTime都给 → 按 CUSTOM 处理period != CUSTOM时忽略startTime/endTime,由ReportPeriodResolver计算endTime必须 >startTime
5. 阈值来源设计(WarningAnalyzer 现实方案)
5.1 现状
iot_alert 表无阈值数值列,阈值规则藏在 LiteFlow iot_scene 规则链里(脚本化、动态、非结构化)。报告引擎直接解析 LiteFlow 脚本取阈值成本高、易错。
5.2 V1 方案:iot_report_threshold(报告专用阈值表)
报告场景需要结构化、可读、可回溯的阈值,与告警规则解耦:
CREATE TABLE iot_report_threshold (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
product_id BIGINT NOT NULL COMMENT '产品ID(0=全局默认)',
device_id BIGINT NOT NULL DEFAULT 0 COMMENT '设备ID(0=产品级)',
monitor_category_code VARCHAR(64) NOT NULL COMMENT '监测分类编码',
metric_identifier VARCHAR(128) NOT NULL COMMENT '物模型标识符',
warning_value DECIMAL(18,6) COMMENT '预警阈值',
alarm_value DECIMAL(18,6) COMMENT '报警阈值',
control_value DECIMAL(18,6) COMMENT '控制值(设计值,可选)',
unit VARCHAR(32) COMMENT '单位',
tenant_id BIGINT,
del_flag CHAR(1) DEFAULT '0',
create_by VARCHAR(64),
create_time DATETIME,
update_by VARCHAR(64),
update_time DATETIME,
KEY idx_device_metric (device_id, metric_identifier),
KEY idx_product_category (product_id, monitor_category_code)
) COMMENT '报告预警阈值配置';
查询优先级:device_id > product_id > 全局默认(product_id=0)。
5.3 WarningAnalyzer 只读结构化阈值
禁止 Analyzer 硬编码 if(value>2)。必须通过 ReportThresholdProvider 查询:
public interface ReportThresholdProvider {
ReportThreshold get(MonitorCategory category, Long productId, Long deviceId, String metricIdentifier);
}
ReportContext 构建时一次性装入该设备所有相关阈值,Analyzer 直接读 Context。
5.4 与 iot_alert 的关系
iot_alert+iot_scene→ 实时告警推送(保持现状,不改)iot_report_threshold→ 报告分析时的预警状态判定(新增)
两者独立,避免报告引擎侵入告警规则引擎。未来可考虑用 iot_alert 反推阈值,但 V1 不做。
6. 物模型与监测数据复用
6.1 监测指标筛选
ThingsModel 字段复用约定:
| 字段 | 报告用途 |
|---|---|
isMonitor=1 |
纳入监测分析的主指标(裂缝宽度、位移、轴力等) |
isHistory=1 |
查询历史时序数据(DeviceLogMapper.listHistory) |
isChart=1 |
生成趋势图 |
isCalculated=2 |
纵向计算(累积值),直接取缓存值作为"累计变化量",报告不重新计算 |
isCalculated=1 |
横向计算(公式含 {}),按 formula 计算 |
isCalculated=0 |
设备原始上报值 |
unit |
报告数值单位 |
datatype |
integer/decimal/string/bool/array/enum |
formula |
计算公式(%s 占位符) |
重要:
isCalculated=2的累积值由现有缓存机制维护(见项目既有约定:isCalculated=2 从缓存模型值累积,无新状态表)。报告 Analyzer 直接读取该物模型的时序值作为"累计变化量",不要在报告里再实现一套累积逻辑。
6.2 数据查询入口
报告模块通过 ReportDataProvider 抽象调用现有 DeviceLogMapper:
public interface ReportDataProvider {
/** 查询设备某标识符在时间窗口内的时序数据 */
List<MetricData> queryMetricData(Long deviceId, String identity,
LocalDateTime start, LocalDateTime end);
/** 查询设备最后一条上报值 */
MetricData queryLast(Long deviceId, String identity);
}
实现:内部委托 DeviceLogMapper.listHistory(MySQL)或 TDDeviceLogMapper(TDengine),按 iot_device_log.identity + ts 范围 + logType IN (1,7) 过滤。
6.3 MetricData(报告内部数据模型)
public class MetricData {
private LocalDateTime timestamp;
private String identity;
private BigDecimal value;
private String unit;
private Integer isCalculated; // 0上报/1横向/2纵向
}
7. 当前架构中的模块职责
报告能力必须遵守现有模块边界:
fastbee-admin ← REST 入口
fastbee-open-api ← 报告 Controller 放这里
fastbee-report ← 新增模块,报告引擎核心
fastbee-iot-service ← 设备/产品/物模型/日志领域(被复用)
fastbee-iot-data ← 时序数据访问(被复用)
fastbee-plugs/oss ← OSS 上传(被复用)
fastbee-common ← MonitorCategory / ReportPeriod 枚举
禁止:
- 把报告逻辑全塞进
fastbee-admin - 把 Word 代码塞进
fastbee-iot-service - 让
fastbee-iot-service反向依赖fastbee-report
8. 推荐新增模块:fastbee-report
fastbee-report/
├── pom.xml
└── src/main/java/com/fastbee/report/
├── api/
│ ├── ReportEngine.java
│ ├── ReportRequest.java
│ └── ReportResult.java
├── core/
│ ├── DefaultReportEngine.java
│ ├── ReportContext.java
│ ├── ReportContextBuilder.java
│ └── ReportAssembler.java
├── category/
│ ├── MonitorCategoryResolver.java
│ └── DefaultMonitorCategoryResolver.java
├── period/
│ ├── ReportPeriodResolver.java
│ └── DefaultReportPeriodResolver.java
├── analyzer/
│ ├── ReportAnalyzer.java
│ ├── ReportAnalyzerFactory.java
│ └── DefaultReportAnalyzerFactory.java
├── analyzer/crack/
│ └── CrackMeterAnalyzer.java
├── analyzer/displacement/
│ └── DeepDisplacementAnalyzer.java
├── analyzer/axialforce/
│ └── AxialForceAnalyzer.java
├── analyzer/staticlevel/
│ └── StaticLevelAnalyzer.java
├── analyzer/vibratingwire/
│ └── VibratingWireAnalyzer.java
├── analysis/
│ ├── StatisticsCalculator.java
│ ├── TrendAnalyzer.java
│ ├── RateCalculator.java
│ ├── QualityAnalyzer.java
│ └── WarningAnalyzer.java
├── threshold/
│ ├── ReportThresholdProvider.java
│ ├── ReportThreshold.java
│ └── DbReportThresholdProvider.java
├── data/
│ ├── ReportDataProvider.java
│ ├── MetricData.java
│ └── DeviceDataSnapshot.java
├── model/
│ ├── ReportModel.java
│ ├── AnalysisResult.java
│ ├── Statistics.java
│ ├── TrendAnalysis.java
│ ├── QualityAnalysis.java
│ ├── WarningAnalysis.java
│ ├── AnalysisConclusion.java
│ └── ChartData.java
├── chart/
│ ├── ChartGenerator.java
│ ├── LineChartGenerator.java // JFreeChart
│ └── ProfileChartGenerator.java // 位移-深度剖面
├── renderer/
│ ├── ReportRenderer.java
│ ├── WordRenderer.java // poi-tl
│ └── WordTemplateResolver.java
├── record/
│ ├── ReportRecord.java
│ ├── ReportRecordMapper.java
│ └── ReportRecordService.java
└── exception/
└── ReportException.java
9. Maven 模块依赖方向
fastbee-admin
↓
fastbee-open-api
↓
fastbee-report
↓
fastbee-iot-service + fastbee-iot-data + fastbee-oss
↓
MySQL / TDengine / Object Storage
禁止 fastbee-iot-service → fastbee-report 反向依赖。报告是上层业务能力。
10. 新增 Maven 依赖
在 fastbee-report/pom.xml 引入(项目根 pom.xml 的 dependencyManagement 同步声明版本):
<!-- Word 模板引擎,基于 poi-ooxml,支持 {{var}} 占位符/循环表格/图片 -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.4</version>
</dependency>
<!-- Java 端图表生成(PNG 嵌入 Word) -->
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.4</version>
</dependency>
poi-tl 1.10.x 兼容 POI 4.1.2;jfreechart 1.5.x 纯 Java、轻量。禁止再引入 docx4j 或 echarts 的 Java 包装。
11. ReportEngine
public interface ReportEngine {
ReportResult generate(ReportRequest request);
}
默认实现:
public class DefaultReportEngine implements ReportEngine {
private final ReportContextBuilder contextBuilder;
private final MonitorCategoryResolver categoryResolver;
private final ReportAnalyzerFactory analyzerFactory;
private final ReportAssembler reportAssembler;
private final ReportRenderer reportRenderer;
private final ReportPeriodResolver periodResolver;
private final ReportRecordService recordService;
@Override
public ReportResult generate(ReportRequest request) {
// 1. 参数校验 + 解析时间窗口
TimeWindow window = periodResolver.resolve(
request.getPeriod(), request.getAnchorDate());
if (request.getPeriod() == ReportPeriod.CUSTOM) {
window = new TimeWindow(request.getStartTime(), request.getEndTime());
}
// 2. 解析设备监测分类
MonitorCategory category = categoryResolver.resolveByDeviceId(request.getDeviceId());
// 3. 构建上下文(设备/产品/物模型/数据/阈值)
ReportContext context = contextBuilder.build(request, window, category);
// 4. 路由 Analyzer
ReportAnalyzer analyzer = analyzerFactory.get(category);
// 5. 执行分析
AnalysisResult analysis = analyzer.analyze(context, request);
// 6. 组装报告模型
ReportModel model = reportAssembler.assemble(context, analysis, request);
// 7. 渲染 Word
ReportResult result = reportRenderer.render(model, request);
// 8. 落库 ReportRecord
recordService.saveSuccess(request, window, category, result);
return result;
}
}
ReportEngine 不得包含具体设备分析逻辑。
12. ReportContext
Analyzer 的输入上下文,构建时一次性装配所有依赖数据:
public class ReportContext {
private Project project;
private Device device;
private Product product;
private MonitorCategory category;
private TimeWindow window;
private List<ThingsModel> metrics; // isMonitor=1 的物模型
private Map<String, List<MetricData>> data; // identity -> 时序数据
private Map<String, ReportThreshold> thresholds; // identity -> 阈值
private ReportRequest request;
private Map<String, Object> attributes; // 扩展
}
ReportContextBuilder 统一完成:
查设备 → 查产品 → 解析 MonitorCategory
→ 查物模型(isMonitor=1)
→ 查阈值(iot_report_threshold)
→ 查时序数据(DeviceLogMapper.listHistory)
→ 装入 ReportContext
Analyzer 不允许自己 @Autowired 业务 Service 查询。
13. ReportAnalyzer
public interface ReportAnalyzer {
boolean supports(MonitorCategory category);
AnalysisResult analyze(ReportContext context, ReportRequest request);
}
14. 通用分析能力
所有设备共享:
StatisticsCalculator 统计(当前/初始/最小/最大/平均/中位/变化量/累计变化)
RateCalculator 变化速率(mm/day, kN/day)—— 需考虑时间间隔/数据缺失/异常值
TrendAnalyzer 趋势(UP/DOWN/STABLE/FLUCTUATION/UNKNOWN + 斜率 + 置信度)
QualityAnalyzer 数据质量(缺失/重复/越界/突变/平线/完整率)
WarningAnalyzer 预警(读 ReportThreshold,输出 NORMAL/WARNING/ALARM)
Statistics
public class Statistics {
private BigDecimal current; // 当前值
private BigDecimal baseline; // 初始值(窗口首条或设备激活首条)
private BigDecimal min;
private BigDecimal max;
private BigDecimal average;
private BigDecimal median;
private BigDecimal delta; // 本期变化量 = current - baseline
private BigDecimal cumulative; // 累计变化量(优先取 isCalculated=2 物模型值)
private String unit;
}
RateCalculator 禁止简单 (last-first)
必须考虑:时间间隔、数据缺失段、异常值剔除(3σ 或 IQR)、采样周期不均。
TrendAnalysis
public class TrendAnalysis {
private TrendType trend; // UP/DOWN/STABLE/FLUCTUATION/UNKNOWN
private BigDecimal slope; // 线性回归斜率
private BigDecimal confidence; // R²
private String description;
}
V1 用简单线性回归;未来可升级移动平均/指数平滑/异常点剔除/季节性分析。
QualityAnalysis
public class QualityAnalysis {
private int total;
private int valid;
private int missing;
private int duplicate;
private int abnormal;
private BigDecimal integrityRate; // 完整率 = valid/total
private String status;
}
WarningAnalysis
public class WarningAnalysis {
private WarningStatus status; // NORMAL/WARNING/ALARM/UNKNOWN
private BigDecimal current;
private BigDecimal warningThreshold;
private BigDecimal alarmThreshold;
private BigDecimal controlValue; // 设计控制值(可选)
private String description;
}
15. AnalysisResult
Analyzer 最终输出契约:
public class AnalysisResult {
private Statistics statistics;
private TrendAnalysis trend;
private QualityAnalysis quality;
private WarningAnalysis warning;
private List<ChartData> charts;
private List<AnalysisConclusion> conclusions;
private Map<String, Object> extensions; // 设备专属分析结果
}
16. 设备专属分析
16.1 裂缝计(CRACK_METER)
当前裂缝宽度、初始裂缝宽度、本期变化量、累计变化量
最大值、最小值、平均值、变化速率、趋势、预警状态
extensions.put("crack", crackExtension)
图表:CRACK_TREND(裂缝宽度时程图)
16.2 深部位移(DEEP_DISPLACEMENT)
不能当单值指标处理,需按深度维度组织:
设备 → 深度 → 不同深度测点 → 位移值
输出:最大位移、最大位移深度、累计位移、位移速率、位移趋势、位移-深度剖面图。
图表:
DEEP_DISPLACEMENT_TREND(位移时程图)DEEP_DISPLACEMENT_PROFILE(位移-深度剖面图)
16.3 轴力/压力计(AXIAL_FORCE)
当前轴力、初始轴力、本期变化、累计变化、最大轴力、平均轴力
变化速率、趋势、控制值占比、预警状态
仅在阈值配置存在设计值/控制值/预警值/报警值时才评价。
16.4 静力水准仪(STATIC_LEVEL)
当前相对高程、初始值、本期变化、累计沉降、最大沉降
沉降速率、趋势、差异沉降、预警状态
图表:STATIC_LEVEL_TREND(沉降时程)、STATIC_LEVEL_COMPARE(测点沉降对比)
16.5 振弦式采集仪(VIBRATING_WIRE)
区分原始物理量(频率/温度)与工程换算量:
当前频率、平均频率、最大频率、最小频率、温度、工程换算值、趋势、数据质量
17. ReportAssembler
public interface ReportAssembler {
ReportModel assemble(ReportContext context, AnalysisResult analysis, ReportRequest request);
}
职责:ReportContext + AnalysisResult + ReportRequest → ReportModel。
public class ReportModel {
private ReportInfo report; // 报告编号、周期、时间窗口、生成时间
private Project project;
private Device device;
private Product product;
private MonitorCategory category;
private MonitoringSummary summary;
private AnalysisResult analysis;
private List<ChartData> charts;
private List<TableData> tables;
private Map<String, Object> variables; // Word 模板变量
}
WordRenderer 只依赖 ReportModel,禁止查询数据库。
18. WordRenderer(poi-tl)
public interface ReportRenderer {
ReportResult render(ReportModel model, ReportRequest request);
}
18.1 渲染流程
加载 Word 模板(WordTemplateResolver 按 templateCode + category 定位)
↓
poi-tl 渲染 {{}} 占位符
↓
插入图表 PNG(ChartData.image)
↓
生成 byte[]
↓
OssFactory.instance(redis).upload(byte[], path, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
↓
返回 fileUrl
18.2 模板命名规范
DEVICE_CRACK_METER_V1.docx
DEVICE_DEEP_DISPLACEMENT_V1.docx
DEVICE_AXIAL_FORCE_V1.docx
DEVICE_STATIC_LEVEL_V1.docx
DEVICE_VIBRATING_WIRE_V1.docx
18.3 Word 变量规范(poi-tl {{}} 语法)
{{report.no}}
{{report.periodDesc}} 日报/周报/半月报/月报
{{report.startTime}}
{{report.endTime}}
{{project.name}}
{{project.code}}
{{device.name}}
{{device.serialNumber}}
{{product.monitorCategoryDesc}}
{{analysis.statistics.current}}
{{analysis.statistics.min}}
{{analysis.statistics.max}}
{{analysis.statistics.average}}
{{analysis.statistics.delta}}
{{analysis.statistics.cumulative}}
{{analysis.rate}}
{{analysis.trend.description}}
{{analysis.quality.integrityRate}}
{{analysis.warning.status}}
{{analysis.warning.description}}
{{conclusions}} 循环列表(poi-tl 循环表格)
{{charts.crackTrend}} 图片占位符
禁止 Word 模板直接引用数据库字段。
18.4 模板表
CREATE TABLE report_template (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
template_code VARCHAR(128) NOT NULL,
template_name VARCHAR(128),
monitor_category_code VARCHAR(64) NOT NULL,
version VARCHAR(32),
file_path VARCHAR(512),
status TINYINT DEFAULT 1,
tenant_id BIGINT,
create_time DATETIME,
update_time DATETIME,
UNIQUE KEY uk_code_version (template_code, version)
) COMMENT '报告模板';
19. 图表规范
public class ChartData {
private String chartCode;
private String title;
private ChartType type; // LINE / BAR / PROFILE / SCATTER
private byte[] image; // PNG 字节
}
图表生成链路:
MetricData → AnalysisResult → ChartData(由 ChartGenerator 用 JFreeChart 生成 PNG) → WordRenderer → DOCX
Java 端用 JFreeChart 生成 PNG,Word 只负责放置图片。禁止在前端生成图回传后端。
20. ReportRecord(报告生命周期)
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) NOT NULL,
period VARCHAR(32) COMMENT 'DAY/WEEK/HALF_MONTH/MONTH/CUSTOM',
template_code VARCHAR(128),
template_version VARCHAR(32),
start_time DATETIME,
end_time DATETIME,
status VARCHAR(16) COMMENT 'CREATING/SUCCESS/FAILED',
file_url VARCHAR(512),
error_message VARCHAR(1024),
tenant_id BIGINT,
create_by VARCHAR(64),
create_time DATETIME,
KEY idx_device (device_id, create_time),
KEY idx_period (period, create_time)
) COMMENT '报告生成记录';
状态:CREATING / SUCCESS / FAILED。
21. API 设计
POST /iot/report/device/generate 生成报告(同步)
GET /iot/report/list 报告列表
GET /iot/report/{reportNo} 报告详情
GET /iot/report/download/{reportNo} 下载(重定向 OSS URL)
生成请求
POST /iot/report/device/generate
{
"deviceId": 10001,
"period": "WEEK",
"anchorDate": "2026-08-09",
"templateCode": "DEVICE_CRACK_METER_V1",
"format": "WORD"
}
或自定义时间范围:
{
"deviceId": 10001,
"period": "CUSTOM",
"startTime": "2026-08-01 00:00:00",
"endTime": "2026-08-08 23:59:59",
"templateCode": "DEVICE_CRACK_METER_V1"
}
响应
{
"reportNo": "REPORT-202608090001",
"status": "SUCCESS",
"fileName": "304259363251-周报-监测分析报告.docx",
"fileUrl": "https://..."
}
权限
按 CLAUDE.md 权限策略,报告接口跨页面复用,读接口用 hasAnyPermi:
// 生成(写)
@PreAuthorize("@ss.hasPermi('iot:report:generate')")
// 列表/详情/下载(读,跨页面复用)
@PreAuthorize("@ss.hasAnyPermi('iot:report:list,iot:device:list')")
22. OSS 文件路径规范
/report/yyyy/MM/dd/device/{deviceId}/{reportNo}.docx
例:/report/2026/08/09/device/10001/REPORT-202608090001.docx
复用 OssFactory.instance(redis).upload(byte[], path, contentType),禁止把 Word 永久存服务器本地。
23. 异步化建议
V1 单设备 + 数据量 < 100 万 → 同步生成即可。
后续若数据量大/图表多/Word 耗时:
API → fastbee-mq(RocketMQ) → ReportConsumer → ReportEngine → OSS → ReportRecord
复用现有 fastbee-mq。V1 不要为异步而异步。
24. 报告结论生成原则
结论必须基于 Statistics + Trend + Quality + Warning 结构化结果,不是让 AI 直接读原始数据。
V1 用规则引擎 + 模板文字:
if (warning == WARNING) {
conclusion = "当前监测值达到项目配置的预警阈值,建议持续关注。";
}
未来可接 LLM 做自然语言润色,但 LLM 不负责工程安全判定。
25. 第一阶段报告内容(单设备固定结构)
1. 监测结论摘要
2. 设备基本信息(含监测分类)
3. 数据质量分析
4. 监测数据统计
5. 趋势分析
6. 预警分析
7. 设备专项分析(按 MonitorCategory)
8. 趋势图
9. 综合分析结论
暂不包含:项目总体分析、多设备关联、施工阶段关联、工程风险评价。
26. 不允许的设计
❌ ReportEngine 直接操作 Mapper
❌ WordRenderer 查询数据库
❌ Word 模板写 SQL
❌ Analyzer 硬编码工程阈值
❌ 所有设备写在一个 Analyzer
❌ if(category==xxx) 巨型判断
❌ AI 直接判断结构是否安全
❌ 把所有报告逻辑放 fastbee-admin
❌ Word 模板和数据库字段直接绑定
❌ 复用 DeviceType 作为分类路由键(应使用 MonitorCategory)
❌ 重复实现 OSS / 数据查询 / 图表能力
❌ 把 iot_category 当作监测分类路由键
❌ 报告模块反向依赖 fastbee-iot-service 之外的循环依赖
27. 测试要求
每个 Analyzer 必须有单元测试,覆盖:
正常数据 / 数据为空 / 数据不足 / 数据缺失 / 数据异常
持续增长 / 持续下降 / 波动 / 达到预警 / 达到报警
测试数据放 src/test/resources/report/:
crack/normal.json, warning.json, alarm.json, abnormal.json, missing.json
displacement/normal.json, warning.json, profile.json
不依赖线上真实数据。
ReportPeriodResolver 必须测试边界:周末/月末/闰年二月/半月报 15 日分界。
28. 第一阶段开发顺序
Step 0:基础设施
- 根
pom.xml声明 poi-tl、jfreechart 版本 iot_product加monitor_category_code列 + 存量数据回填- 新建
iot_report_threshold、report_template、report_record三张表 MonitorCategory、ReportPeriod枚举放fastbee-commonProduct.java加monitorCategoryCode字段
Step 1:fastbee-report 模块骨架
- 创建
fastbee-report模块,加入根 pom<modules> ReportRequest/ReportResult/ReportEngine/ReportContext
Step 2:分类与周期
MonitorCategoryResolver/DefaultMonitorCategoryResolverReportPeriodResolver/DefaultReportPeriodResolver+ 单测
Step 3:Analyzer 框架
ReportAnalyzer/ReportAnalyzerFactory/DefaultReportAnalyzerFactory
Step 4:先只实现 CrackMeterAnalyzer
- 不同时写 5 种
Step 5:通用分析能力
StatisticsCalculator/TrendAnalyzer/RateCalculator/QualityAnalyzer/WarningAnalyzer
Step 6:数据与阈值 Provider
ReportDataProvider(委托DeviceLogMapper)ReportThresholdProvider(查iot_report_threshold)
Step 7:AnalysisResult + ReportModel + ReportAssembler
Step 8:ChartGenerator(JFreeChart 生成 PNG)
Step 9:WordRenderer(poi-tl)
Step 10:制作 DEVICE_CRACK_METER_V1.docx 模板
Step 11:闭环验证
- 设备
304259363251+ 周报 + Word 完整跑通 - OSS 上传 + ReportRecord 落库 + 下载
Step 12:扩展其他分类
DeepDisplacementAnalyzerAxialForceAnalyzerStaticLevelAnalyzerVibratingWireAnalyzer
29. Definition of Done
[ ] fastbee-report 模块创建完成,根 pom 已注册
[ ] Maven 依赖无循环
[ ] iot_product.monitor_category_code 列已加 + 存量回填
[ ] iot_report_threshold / report_template / report_record 三张表已建
[ ] MonitorCategory / ReportPeriod 枚举已放 fastbee-common
[ ] ReportEngine / ReportRequest / ReportContext 创建完成
[ ] MonitorCategoryResolver 创建完成
[ ] ReportPeriodResolver 创建完成 + 边界单测
[ ] ReportAnalyzer / AnalyzerFactory 创建完成
[ ] CrackMeterAnalyzer 创建完成
[ ] StatisticsCalculator / TrendAnalyzer / RateCalculator / QualityAnalyzer / WarningAnalyzer 创建完成
[ ] ReportDataProvider / ReportThresholdProvider 创建完成
[ ] AnalysisResult / ReportModel / ReportAssembler 创建完成
[ ] ChartGenerator(JFreeChart)创建完成
[ ] WordRenderer(poi-tl)创建完成
[ ] Word 模板 DEVICE_CRACK_METER_V1.docx 完成
[ ] OSS 上传完成(复用 OssFactory)
[ ] ReportRecord 落库完成
[ ] POST /iot/report/device/generate 等 API 完成
[ ] 单元测试完成(含 ReportPeriodResolver 边界)
[ ] 设备 304259363251 周报可成功生成 DOCX
[ ] 异常情况返回 FAILED 并落库
[ ] 生成后的报告可重新下载
[ ] 日报/周报/半月报/月报四种周期均可生成
30. 最终架构目标
第一阶段:
304259363251 (deviceId)
│
ReportRequest(period=WEEK)
│
ReportPeriodResolver → [周一00:00, 周日23:59]
│
MonitorCategoryResolver → CRACK_METER
│
ReportEngine
│
CrackMeterAnalyzer
│
AnalysisResult
│
ReportModel
│
WordRenderer(poi-tl) + ChartGenerator(JFreeChart)
│
byte[] → OssFactory → OSS
│
ReportRecord
│
返回下载地址
未来:
Project
│
┌───────────┼────────────┐
▼ ▼ ▼
CRACK AXIAL DISPLACEMENT
│ │ │
▼ ▼ ▼
Analyzer Analyzer Analyzer
│ │ │
└───────────┼────────────┘
▼
ProjectReportEngine(聚合层)
│
▼
ProjectReportModel
│
▼
Word/PDF
单设备 Report Engine 是底层能力,项目报告只是未来的聚合层。
31. AI Agent 执行规则
当 AI Agent 执行本 Skill 时:
第一原则:先读后写
重点搜索:Device / Product / ThingsModel / DeviceLogMapper / TDDeviceLogMapper / OssFactory / Alert / Project / poi-ooxml。禁止重复实现已有能力。
第二原则:先确认模块依赖
禁止循环依赖。fastbee-report 只能向下依赖 fastbee-iot-service / fastbee-iot-data / fastbee-oss / fastbee-common。
第三原则:先跑通裂缝计
验证完整链路后再扩展其他分类。不要一次性实现全部设备。
第四原则:复用优先
OSS / 数据查询 / Word / 图表,能复用就复用,不能复用才引入新依赖(poi-tl、jfreechart)。
第五原则:单一职责、低耦合、可测试、可扩展
32. 最终指令
你现在是 FastBee 项目的高级 Java 架构工程师。请严格按照本 Skill 实施。
执行前:
- 阅读当前项目目录结构。
- 搜索已有设备、物模型、DeviceLogMapper、TDDeviceLogMapper、OssFactory、Alert、Project、poi-ooxml 相关代码。
- 判断哪些能力可以复用。
- 不允许重复实现已有基础能力。
- 不允许修改无关业务。
第一阶段只实现:
单设备 + 报告周期(日/周/半月/月) + 裂缝计(CRACK_METER) + Word
必须先完成完整闭环。完成后再扩展:深部位移 / 轴力 / 静力水准 / 振弦。
代码必须遵循:
ReportRequest(含 period)
↓
ReportPeriodResolver → TimeWindow
↓
MonitorCategoryResolver → MonitorCategory
↓
ReportContextBuilder → ReportContext
↓
ReportAnalyzerFactory.get(category)
↓
AnalysisResult
↓
ReportModel
↓
WordRenderer(poi-tl) + ChartGenerator(JFreeChart)
↓
OssFactory → OSS
↓
ReportRecord
不得破坏 FastBee 当前模块边界。
实现过程中优先保证:
架构正确
数据正确
分析可测试
模板可替换
设备分类可扩展(MonitorCategory)
报告周期可扩展(ReportPeriod)
而不是追求一次实现所有功能。
最终输出:
1. 修改文件列表
2. 新增文件列表
3. Maven 依赖变化
4. 数据库变化(iot_product 加列 + 3 张新表)
5. API 说明
6. Word 模板变量说明
7. 测试结果
8. 使用说明
9. 后续扩展建议