1377 lines
42 KiB
Markdown
1377 lines
42 KiB
Markdown
# 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 要补齐的)
|
||
|
||
1. **设备分类(最重要)**:`iot_device.device_type` / `DeviceType.java` 枚举 = 连接拓扑 `{UNKNOWN, DIRECT_DEVICE, GATEWAY, CAMERA, SUB_GATEWAY}`,**不区分裂缝计/深部位移/轴力计/静力水准/振弦**。`iot_category` 是租户私有的产品分类树,只有 `categoryName` 文本、无稳定机器码,不能作为 Analyzer 路由键。
|
||
2. **报告周期**:当前无日报/周报/半月报/月报概念。
|
||
3. **报告引擎**:无 `fastbee-report` 模块、无 `ReportEngine`、无 `ReportAnalyzer`、无 `ReportRecord` 表。
|
||
4. **监测点阈值**:`iot_alert` 无阈值数值列,阈值藏在 LiteFlow `iot_scene` 规则里,报告难以直接读取结构化阈值。
|
||
5. **Java 端图表**:仅前端有 echarts;后端无 JFreeChart/XChart,无法生成 PNG 嵌入 Word。
|
||
6. **Word 模板引擎**:仅有裸 POI,无 `{{var}}` 占位符语法支持(需引入 poi-tl)。
|
||
|
||
---
|
||
|
||
# 1. Skill 目标
|
||
|
||
补齐报告能力链路:
|
||
|
||
```text
|
||
IoT 数据
|
||
↓
|
||
ReportRequest(设备 + 周期/时间范围 + 模板)
|
||
↓
|
||
ReportContext(设备/产品/物模型/监测数据/阈值)
|
||
↓
|
||
ReportAnalyzer(按 MonitorCategory 路由)
|
||
↓
|
||
AnalysisResult(统计/趋势/质量/预警/结论)
|
||
↓
|
||
ReportModel
|
||
↓
|
||
WordRenderer(poi-tl + JFreeChart)
|
||
↓
|
||
DOCX → OSS → ReportRecord
|
||
```
|
||
|
||
最终形成:
|
||
|
||
```text
|
||
IoT 数据 → Report Engine → 专业监测分析报告 (.docx)
|
||
```
|
||
|
||
---
|
||
|
||
# 2. 核心设计原则
|
||
|
||
## 2.1 Report Engine 不直接理解数据库
|
||
|
||
禁止 `ReportEngine → 直接操作 Mapper → 查询数据库 → 分析 → Word`。
|
||
|
||
推荐:
|
||
|
||
```text
|
||
数据访问层(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 数据库改动
|
||
|
||
```sql
|
||
-- 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` 落库:设备分类随产品走,避免冗余与不一致。设备查询时 JOIN `iot_product` 取 `monitor_category_code` 即可(DeviceMapper 已有产品关联)。
|
||
|
||
### 3.2.2 MonitorCategoryEnum
|
||
|
||
位置:`fastbee-common/src/main/java/com/fastbee/common/enums/MonitorCategory.java`
|
||
|
||
```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` 增加:
|
||
|
||
```java
|
||
@ApiModelProperty("监测分类编码(报告引擎路由键)")
|
||
private String monitorCategoryCode;
|
||
```
|
||
|
||
### 3.2.4 MonitorCategoryResolver
|
||
|
||
位置:`fastbee-report/src/main/java/com/fastbee/report/category/MonitorCategoryResolver.java`
|
||
|
||
```java
|
||
public interface MonitorCategoryResolver {
|
||
MonitorCategory resolve(Long productId);
|
||
MonitorCategory resolveByDeviceId(Long deviceId);
|
||
}
|
||
```
|
||
|
||
实现:先查 `iot_product.monitor_category_code`;为空时返回 `UNKNOWN`(第一阶段不实现启发式回退,避免误判;后台需维护分类)。
|
||
|
||
### 3.2.5 AnalyzerFactory 按 MonitorCategory 路由
|
||
|
||
```java
|
||
public interface ReportAnalyzerFactory {
|
||
ReportAnalyzer get(MonitorCategory category);
|
||
}
|
||
```
|
||
|
||
```java
|
||
@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 的关系
|
||
|
||
两者**正交**,互不替代:
|
||
|
||
```text
|
||
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`
|
||
|
||
```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
|
||
|
||
```java
|
||
public interface ReportPeriodResolver {
|
||
/** 根据周期与锚点日期,计算 [startTime, endTime] */
|
||
TimeWindow resolve(ReportPeriod period, LocalDate anchorDate);
|
||
}
|
||
```
|
||
|
||
```java
|
||
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 同时支持周期与自定义
|
||
|
||
```java
|
||
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(报告专用阈值表)
|
||
|
||
报告场景需要**结构化、可读、可回溯**的阈值,与告警规则解耦:
|
||
|
||
```sql
|
||
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` 查询:
|
||
|
||
```java
|
||
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`:
|
||
|
||
```java
|
||
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(报告内部数据模型)
|
||
|
||
```java
|
||
public class MetricData {
|
||
private LocalDateTime timestamp;
|
||
private String identity;
|
||
private BigDecimal value;
|
||
private String unit;
|
||
private Integer isCalculated; // 0上报/1横向/2纵向
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# 7. 当前架构中的模块职责
|
||
|
||
报告能力必须遵守现有模块边界:
|
||
|
||
```text
|
||
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
|
||
|
||
```text
|
||
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 模块依赖方向
|
||
|
||
```text
|
||
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` 同步声明版本):
|
||
|
||
```xml
|
||
<!-- 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
|
||
|
||
```java
|
||
public interface ReportEngine {
|
||
ReportResult generate(ReportRequest request);
|
||
}
|
||
```
|
||
|
||
默认实现:
|
||
|
||
```java
|
||
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 的输入上下文,构建时一次性装配所有依赖数据:
|
||
|
||
```java
|
||
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` 统一完成:
|
||
|
||
```text
|
||
查设备 → 查产品 → 解析 MonitorCategory
|
||
→ 查物模型(isMonitor=1)
|
||
→ 查阈值(iot_report_threshold)
|
||
→ 查时序数据(DeviceLogMapper.listHistory)
|
||
→ 装入 ReportContext
|
||
```
|
||
|
||
Analyzer 不允许自己 `@Autowired` 业务 Service 查询。
|
||
|
||
---
|
||
|
||
# 13. ReportAnalyzer
|
||
|
||
```java
|
||
public interface ReportAnalyzer {
|
||
boolean supports(MonitorCategory category);
|
||
AnalysisResult analyze(ReportContext context, ReportRequest request);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# 14. 通用分析能力
|
||
|
||
所有设备共享:
|
||
|
||
```text
|
||
StatisticsCalculator 统计(当前/初始/最小/最大/平均/中位/变化量/累计变化)
|
||
RateCalculator 变化速率(mm/day, kN/day)—— 需考虑时间间隔/数据缺失/异常值
|
||
TrendAnalyzer 趋势(UP/DOWN/STABLE/FLUCTUATION/UNKNOWN + 斜率 + 置信度)
|
||
QualityAnalyzer 数据质量(缺失/重复/越界/突变/平线/完整率)
|
||
WarningAnalyzer 预警(读 ReportThreshold,输出 NORMAL/WARNING/ALARM)
|
||
```
|
||
|
||
### Statistics
|
||
|
||
```java
|
||
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
|
||
|
||
```java
|
||
public class TrendAnalysis {
|
||
private TrendType trend; // UP/DOWN/STABLE/FLUCTUATION/UNKNOWN
|
||
private BigDecimal slope; // 线性回归斜率
|
||
private BigDecimal confidence; // R²
|
||
private String description;
|
||
}
|
||
```
|
||
|
||
V1 用简单线性回归;未来可升级移动平均/指数平滑/异常点剔除/季节性分析。
|
||
|
||
### QualityAnalysis
|
||
|
||
```java
|
||
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
|
||
|
||
```java
|
||
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 最终输出契约:
|
||
|
||
```java
|
||
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)
|
||
|
||
```text
|
||
当前裂缝宽度、初始裂缝宽度、本期变化量、累计变化量
|
||
最大值、最小值、平均值、变化速率、趋势、预警状态
|
||
```
|
||
|
||
`extensions.put("crack", crackExtension)`
|
||
|
||
图表:`CRACK_TREND`(裂缝宽度时程图)
|
||
|
||
## 16.2 深部位移(DEEP_DISPLACEMENT)
|
||
|
||
不能当单值指标处理,需按深度维度组织:
|
||
|
||
```text
|
||
设备 → 深度 → 不同深度测点 → 位移值
|
||
```
|
||
|
||
输出:最大位移、最大位移深度、累计位移、位移速率、位移趋势、位移-深度剖面图。
|
||
|
||
图表:
|
||
- `DEEP_DISPLACEMENT_TREND`(位移时程图)
|
||
- `DEEP_DISPLACEMENT_PROFILE`(位移-深度剖面图)
|
||
|
||
## 16.3 轴力/压力计(AXIAL_FORCE)
|
||
|
||
```text
|
||
当前轴力、初始轴力、本期变化、累计变化、最大轴力、平均轴力
|
||
变化速率、趋势、控制值占比、预警状态
|
||
```
|
||
|
||
仅在阈值配置存在设计值/控制值/预警值/报警值时才评价。
|
||
|
||
## 16.4 静力水准仪(STATIC_LEVEL)
|
||
|
||
```text
|
||
当前相对高程、初始值、本期变化、累计沉降、最大沉降
|
||
沉降速率、趋势、差异沉降、预警状态
|
||
```
|
||
|
||
图表:`STATIC_LEVEL_TREND`(沉降时程)、`STATIC_LEVEL_COMPARE`(测点沉降对比)
|
||
|
||
## 16.5 振弦式采集仪(VIBRATING_WIRE)
|
||
|
||
区分原始物理量(频率/温度)与工程换算量:
|
||
|
||
```text
|
||
当前频率、平均频率、最大频率、最小频率、温度、工程换算值、趋势、数据质量
|
||
```
|
||
|
||
---
|
||
|
||
# 17. ReportAssembler
|
||
|
||
```java
|
||
public interface ReportAssembler {
|
||
ReportModel assemble(ReportContext context, AnalysisResult analysis, ReportRequest request);
|
||
}
|
||
```
|
||
|
||
职责:`ReportContext + AnalysisResult + ReportRequest → ReportModel`。
|
||
|
||
```java
|
||
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)
|
||
|
||
```java
|
||
public interface ReportRenderer {
|
||
ReportResult render(ReportModel model, ReportRequest request);
|
||
}
|
||
```
|
||
|
||
## 18.1 渲染流程
|
||
|
||
```text
|
||
加载 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 模板命名规范
|
||
|
||
```text
|
||
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 `{{}}` 语法)
|
||
|
||
```text
|
||
{{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 模板表
|
||
|
||
```sql
|
||
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. 图表规范
|
||
|
||
```java
|
||
public class ChartData {
|
||
private String chartCode;
|
||
private String title;
|
||
private ChartType type; // LINE / BAR / PROFILE / SCATTER
|
||
private byte[] image; // PNG 字节
|
||
}
|
||
```
|
||
|
||
图表生成链路:
|
||
|
||
```text
|
||
MetricData → AnalysisResult → ChartData(由 ChartGenerator 用 JFreeChart 生成 PNG) → WordRenderer → DOCX
|
||
```
|
||
|
||
Java 端用 JFreeChart 生成 PNG,Word 只负责放置图片。禁止在前端生成图回传后端。
|
||
|
||
---
|
||
|
||
# 20. ReportRecord(报告生命周期)
|
||
|
||
```sql
|
||
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 设计
|
||
|
||
```text
|
||
POST /iot/report/device/generate 生成报告(同步)
|
||
GET /iot/report/list 报告列表
|
||
GET /iot/report/{reportNo} 报告详情
|
||
GET /iot/report/download/{reportNo} 下载(重定向 OSS URL)
|
||
```
|
||
|
||
### 生成请求
|
||
|
||
```json
|
||
POST /iot/report/device/generate
|
||
{
|
||
"deviceId": 10001,
|
||
"period": "WEEK",
|
||
"anchorDate": "2026-08-09",
|
||
"templateCode": "DEVICE_CRACK_METER_V1",
|
||
"format": "WORD"
|
||
}
|
||
```
|
||
|
||
或自定义时间范围:
|
||
|
||
```json
|
||
{
|
||
"deviceId": 10001,
|
||
"period": "CUSTOM",
|
||
"startTime": "2026-08-01 00:00:00",
|
||
"endTime": "2026-08-08 23:59:59",
|
||
"templateCode": "DEVICE_CRACK_METER_V1"
|
||
}
|
||
```
|
||
|
||
### 响应
|
||
|
||
```json
|
||
{
|
||
"reportNo": "REPORT-202608090001",
|
||
"status": "SUCCESS",
|
||
"fileName": "304259363251-周报-监测分析报告.docx",
|
||
"fileUrl": "https://..."
|
||
}
|
||
```
|
||
|
||
### 权限
|
||
|
||
按 CLAUDE.md 权限策略,报告接口跨页面复用,读接口用 `hasAnyPermi`:
|
||
|
||
```java
|
||
// 生成(写)
|
||
@PreAuthorize("@ss.hasPermi('iot:report:generate')")
|
||
|
||
// 列表/详情/下载(读,跨页面复用)
|
||
@PreAuthorize("@ss.hasAnyPermi('iot:report:list,iot:device:list')")
|
||
```
|
||
|
||
---
|
||
|
||
# 22. OSS 文件路径规范
|
||
|
||
```text
|
||
/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 耗时:
|
||
|
||
```text
|
||
API → fastbee-mq(RocketMQ) → ReportConsumer → ReportEngine → OSS → ReportRecord
|
||
```
|
||
|
||
复用现有 `fastbee-mq`。V1 不要为异步而异步。
|
||
|
||
---
|
||
|
||
# 24. 报告结论生成原则
|
||
|
||
结论必须基于 `Statistics + Trend + Quality + Warning` 结构化结果,**不是**让 AI 直接读原始数据。
|
||
|
||
V1 用规则引擎 + 模板文字:
|
||
|
||
```java
|
||
if (warning == WARNING) {
|
||
conclusion = "当前监测值达到项目配置的预警阈值,建议持续关注。";
|
||
}
|
||
```
|
||
|
||
未来可接 LLM 做自然语言润色,但 **LLM 不负责工程安全判定**。
|
||
|
||
---
|
||
|
||
# 25. 第一阶段报告内容(单设备固定结构)
|
||
|
||
```text
|
||
1. 监测结论摘要
|
||
2. 设备基本信息(含监测分类)
|
||
3. 数据质量分析
|
||
4. 监测数据统计
|
||
5. 趋势分析
|
||
6. 预警分析
|
||
7. 设备专项分析(按 MonitorCategory)
|
||
8. 趋势图
|
||
9. 综合分析结论
|
||
```
|
||
|
||
暂不包含:项目总体分析、多设备关联、施工阶段关联、工程风险评价。
|
||
|
||
---
|
||
|
||
# 26. 不允许的设计
|
||
|
||
```text
|
||
❌ ReportEngine 直接操作 Mapper
|
||
❌ WordRenderer 查询数据库
|
||
❌ Word 模板写 SQL
|
||
❌ Analyzer 硬编码工程阈值
|
||
❌ 所有设备写在一个 Analyzer
|
||
❌ if(category==xxx) 巨型判断
|
||
❌ AI 直接判断结构是否安全
|
||
❌ 把所有报告逻辑放 fastbee-admin
|
||
❌ Word 模板和数据库字段直接绑定
|
||
❌ 复用 DeviceType 作为分类路由键(应使用 MonitorCategory)
|
||
❌ 重复实现 OSS / 数据查询 / 图表能力
|
||
❌ 把 iot_category 当作监测分类路由键
|
||
❌ 报告模块反向依赖 fastbee-iot-service 之外的循环依赖
|
||
```
|
||
|
||
---
|
||
|
||
# 27. 测试要求
|
||
|
||
每个 Analyzer 必须有单元测试,覆盖:
|
||
|
||
```text
|
||
正常数据 / 数据为空 / 数据不足 / 数据缺失 / 数据异常
|
||
持续增长 / 持续下降 / 波动 / 达到预警 / 达到报警
|
||
```
|
||
|
||
测试数据放 `src/test/resources/report/`:
|
||
|
||
```text
|
||
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-common`
|
||
- [ ] `Product.java` 加 `monitorCategoryCode` 字段
|
||
|
||
## Step 1:fastbee-report 模块骨架
|
||
|
||
- [ ] 创建 `fastbee-report` 模块,加入根 pom `<modules>`
|
||
- [ ] `ReportRequest` / `ReportResult` / `ReportEngine` / `ReportContext`
|
||
|
||
## Step 2:分类与周期
|
||
|
||
- [ ] `MonitorCategoryResolver` / `DefaultMonitorCategoryResolver`
|
||
- [ ] `ReportPeriodResolver` / `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:扩展其他分类
|
||
|
||
- [ ] `DeepDisplacementAnalyzer`
|
||
- [ ] `AxialForceAnalyzer`
|
||
- [ ] `StaticLevelAnalyzer`
|
||
- [ ] `VibratingWireAnalyzer`
|
||
|
||
---
|
||
|
||
# 29. Definition of Done
|
||
|
||
```text
|
||
[ ] 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. 最终架构目标
|
||
|
||
第一阶段:
|
||
|
||
```text
|
||
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
|
||
│
|
||
返回下载地址
|
||
```
|
||
|
||
未来:
|
||
|
||
```text
|
||
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 实施。
|
||
|
||
执行前:
|
||
|
||
1. 阅读当前项目目录结构。
|
||
2. 搜索已有设备、物模型、DeviceLogMapper、TDDeviceLogMapper、OssFactory、Alert、Project、poi-ooxml 相关代码。
|
||
3. 判断哪些能力可以复用。
|
||
4. 不允许重复实现已有基础能力。
|
||
5. 不允许修改无关业务。
|
||
|
||
第一阶段只实现:
|
||
|
||
```text
|
||
单设备 + 报告周期(日/周/半月/月) + 裂缝计(CRACK_METER) + Word
|
||
```
|
||
|
||
必须先完成完整闭环。完成后再扩展:深部位移 / 轴力 / 静力水准 / 振弦。
|
||
|
||
代码必须遵循:
|
||
|
||
```text
|
||
ReportRequest(含 period)
|
||
↓
|
||
ReportPeriodResolver → TimeWindow
|
||
↓
|
||
MonitorCategoryResolver → MonitorCategory
|
||
↓
|
||
ReportContextBuilder → ReportContext
|
||
↓
|
||
ReportAnalyzerFactory.get(category)
|
||
↓
|
||
AnalysisResult
|
||
↓
|
||
ReportModel
|
||
↓
|
||
WordRenderer(poi-tl) + ChartGenerator(JFreeChart)
|
||
↓
|
||
OssFactory → OSS
|
||
↓
|
||
ReportRecord
|
||
```
|
||
|
||
不得破坏 FastBee 当前模块边界。
|
||
|
||
实现过程中优先保证:
|
||
|
||
```text
|
||
架构正确
|
||
数据正确
|
||
分析可测试
|
||
模板可替换
|
||
设备分类可扩展(MonitorCategory)
|
||
报告周期可扩展(ReportPeriod)
|
||
```
|
||
|
||
而不是追求一次实现所有功能。
|
||
|
||
最终输出:
|
||
|
||
```text
|
||
1. 修改文件列表
|
||
2. 新增文件列表
|
||
3. Maven 依赖变化
|
||
4. 数据库变化(iot_product 加列 + 3 张新表)
|
||
5. API 说明
|
||
6. Word 模板变量说明
|
||
7. 测试结果
|
||
8. 使用说明
|
||
9. 后续扩展建议
|
||
```
|