修正用纸统计 useCount 口径:仅厕位模式(runl_mode=0)厕位行累计,对齐老代码意图并消除其分支不一致 bug
This commit is contained in:
@@ -755,7 +755,7 @@
|
|||||||
- 入参:`districtId`(必填正整数)、`startDate`/`endDate`(必填 yyyy-MM-dd,startDate≤endDate≤今天)、
|
- 入参:`districtId`(必填正整数)、`startDate`/`endDate`(必填 yyyy-MM-dd,startDate≤endDate≤今天)、
|
||||||
`positionMode`(position=仅厕位/wall=仅厕墙,只作用于 ES 换纸卷数口径)、`deviceType`(0/1)、`buildingId`、`floor`、`toiletId`。
|
`positionMode`(position=仅厕位/wall=仅厕墙,只作用于 ES 换纸卷数口径)、`deviceType`(0/1)、`buildingId`、`floor`、`toiletId`。
|
||||||
- 聚合维度 gender(0男1女2母婴3残卫4第三5其他99未知):
|
- 聚合维度 gender(0男1女2母婴3残卫4第三5其他99未知):
|
||||||
`paperLengthM`=Σpaper_length/100(米,3 位小数)、`useCount`=Σuse_count(厕墙行恒 0 直接求和)、
|
`paperLengthM`=Σpaper_length/100(米,3 位小数)、`useCount`=Σuse_count(仅厕位模式 runl_mode=0 的厕位行累计,厕墙行恒 0)、
|
||||||
`deviceDayCount`=按日 distinct 厕位/厕数之和(老系统 perPositionsCount,输出改名)、
|
`deviceDayCount`=按日 distinct 厕位/厕数之和(老系统 perPositionsCount,输出改名)、
|
||||||
`perUseLengthM`=paperLengthM/useCount(useCount=0 为 null);另输出按日明细(date/gender/paperLengthM/useCount/deviceDayCount)。
|
`perUseLengthM`=paperLengthM/useCount(useCount=0 为 null);另输出按日明细(date/gender/paperLengthM/useCount/deviceDayCount)。
|
||||||
- 换纸卷数 `rfidCount`(ES):读 `district_strategy.rfid_use_flag`(行 id=站点 id)——
|
- 换纸卷数 `rfidCount`(ES):读 `district_strategy.rfid_use_flag`(行 id=站点 id)——
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
(实现为左闭右开 `[start, end+1)` 避免秒精度丢行);可选 `building_id/floor/toilet_id/device_type` 精确匹配。
|
(实现为左闭右开 `[start, end+1)` 避免秒精度丢行);可选 `building_id/floor/toilet_id/device_type` 精确匹配。
|
||||||
- 聚合维度 `gender`(gender 为 null 的行跳过,老 :178-180):
|
- 聚合维度 `gender`(gender 为 null 的行跳过,老 :178-180):
|
||||||
- `paperLengthM` = Σpaper_length / 100(paper_length 单位厘米,输出米,3 位小数);
|
- `paperLengthM` = Σpaper_length / 100(paper_length 单位厘米,输出米,3 位小数);
|
||||||
- `useCount` = Σuse_count(老代码对厕位/厕墙、runl_mode 有多个分支,但厕墙行 use_count 恒 0,
|
- `useCount` = Σuse_count(**仅 position_id>0 且 runl_mode=0 的厕位模式行累计**,厕墙行恒 0;
|
||||||
等价于直接求和——老 :188-203 分支差异不影响结果);
|
这是老 :200-203、:220-222 分支的意图——老 :188 的日分组已存在分支漏判 runlMode,按意图统一);
|
||||||
- `deviceDayCount` = 按日 distinct 厕位/厕数之和(老 :190-192、:249:positionSet 存
|
- `deviceDayCount` = 按日 distinct 厕位/厕数之和(老 :190-192、:249:positionSet 存
|
||||||
`p_{positionId}`(厕位行)与 `t_{toiletId}`(厕墙行),每天取 set size 再累加)。
|
`p_{positionId}`(厕位行)与 `t_{toiletId}`(厕墙行),每天取 set size 再累加)。
|
||||||
老出参字段名 perPositionsCount 注释有误,本工具改名 deviceDayCount);
|
老出参字段名 perPositionsCount 注释有误,本工具改名 deviceDayCount);
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
| rfid 去重实现 | count+分页拉全量文档,内存 Set 去重 | terms 聚合 size 10000 数 bucket | 不拉全量、低内存低耗时;上限 10000 对正常站点足够,超限部分不计(见 §2.2) |
|
| rfid 去重实现 | count+分页拉全量文档,内存 Set 去重 | terms 聚合 size 10000 数 bucket | 不拉全量、低内存低耗时;上限 10000 对正常站点足够,超限部分不计(见 §2.2) |
|
||||||
| district_strategy 不存在 / rfid_use_flag=null | NPE(:324-325) | 按"未使用 rfid"口径走 state=2 count | 该表与站点 1:1,缺失视为未启用 rfid;避免 500 |
|
| district_strategy 不存在 / rfid_use_flag=null | NPE(:324-325) | 按"未使用 rfid"口径走 state=2 count | 该表与站点 1:1,缺失视为未启用 rfid;避免 500 |
|
||||||
| Dictionary 字典缺失 | NPE 被外层 catch 吞掉,继续执行 | 抛 IllegalStateException | fail fast,避免权限/口径配置异常导致错误数据范围 |
|
| Dictionary 字典缺失 | NPE 被外层 catch 吞掉,继续执行 | 抛 IllegalStateException | fail fast,避免权限/口径配置异常导致错误数据范围 |
|
||||||
| use_count 聚合 | 厕位/厕墙/runl_mode 多分支 | 直接求和 | 厕墙行恒 0,分支差异不影响结果,简化口径 |
|
| use_count 聚合 | 厕位/厕墙/runl_mode 多分支,:188 已存在日分组分支漏判 runlMode | 仅 position_id>0 且 runl_mode=0 行累计 | 统一为老代码新分组分支的意图口径(厕位模式使用人次),消除老代码分支不一致的 bug |
|
||||||
| DB 时间过滤 | between 含两端(秒精度) | 左闭右开 [start, end+1) | 等价且避免 23:59:59 之后毫秒级数据丢行 |
|
| DB 时间过滤 | between 含两端(秒精度) | 左闭右开 [start, end+1) | 等价且避免 23:59:59 之后毫秒级数据丢行 |
|
||||||
|
|
||||||
## 6. 行级权限说明
|
## 6. 行级权限说明
|
||||||
|
|||||||
+11
-3
@@ -233,13 +233,21 @@ public class DistrictPaperStatisticsTools implements ToolObject {
|
|||||||
int paperLength = row.getPaperLength() == null ? 0 : row.getPaperLength();
|
int paperLength = row.getPaperLength() == null ? 0 : row.getPaperLength();
|
||||||
int useCount = row.getUseCount() == null ? 0 : row.getUseCount();
|
int useCount = row.getUseCount() == null ? 0 : row.getUseCount();
|
||||||
agg.paperCm += paperLength;
|
agg.paperCm += paperLength;
|
||||||
agg.useCount += useCount;
|
boolean positionRow = row.getPositionId() != null && row.getPositionId() > 0;
|
||||||
|
// useCount 仅厕位模式(runl_mode=0)的厕位行累计,厕墙恒 0(老 :200-203/220-222 意图;
|
||||||
|
// 老代码日分组已存在分支漏判 runlMode,此处按意图统一,见设计文档差异点)
|
||||||
|
boolean countUse = positionRow && row.getRunlMode() != null && row.getRunlMode() == 0;
|
||||||
|
if (countUse) {
|
||||||
|
agg.useCount += useCount;
|
||||||
|
}
|
||||||
String day = DAY_FORMAT.format(row.getStatisticsTime().toInstant().atZone(SHANGHAI));
|
String day = DAY_FORMAT.format(row.getStatisticsTime().toInstant().atZone(SHANGHAI));
|
||||||
DailyAgg dailyAgg = agg.daily.computeIfAbsent(day, key -> new DailyAgg());
|
DailyAgg dailyAgg = agg.daily.computeIfAbsent(day, key -> new DailyAgg());
|
||||||
dailyAgg.paperCm += paperLength;
|
dailyAgg.paperCm += paperLength;
|
||||||
dailyAgg.useCount += useCount;
|
if (countUse) {
|
||||||
|
dailyAgg.useCount += useCount;
|
||||||
|
}
|
||||||
// 厕位行按 positionId、厕墙行按 toiletId 计台(老 "p_"/"t_" positionSet)
|
// 厕位行按 positionId、厕墙行按 toiletId 计台(老 "p_"/"t_" positionSet)
|
||||||
if (row.getPositionId() != null && row.getPositionId() > 0) {
|
if (positionRow) {
|
||||||
dailyAgg.positionKeys.add("p_" + row.getPositionId());
|
dailyAgg.positionKeys.add("p_" + row.getPositionId());
|
||||||
} else {
|
} else {
|
||||||
dailyAgg.positionKeys.add("t_" + row.getToiletId());
|
dailyAgg.positionKeys.add("t_" + row.getToiletId());
|
||||||
|
|||||||
+22
@@ -116,6 +116,7 @@ class DistrictPaperStatisticsToolsTest {
|
|||||||
row.setToiletId(toiletId);
|
row.setToiletId(toiletId);
|
||||||
row.setPaperLength(paperLengthCm);
|
row.setPaperLength(paperLengthCm);
|
||||||
row.setUseCount(useCount);
|
row.setUseCount(useCount);
|
||||||
|
row.setRunlMode(0);
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +286,27 @@ class DistrictPaperStatisticsToolsTest {
|
|||||||
anyLong(), anyLong(), any(), any());
|
anyLong(), anyLong(), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void useCountOnlyAccumulatesForPositionModeRows() {
|
||||||
|
mockDistrictAndDictionary();
|
||||||
|
MonthlyStatistics wallModeRow = row(today.minusDays(1), 11L, 3L, 0, 100, 5);
|
||||||
|
wallModeRow.setRunlMode(1);
|
||||||
|
when(monthlyStatisticsMapper.selectList(any())).thenReturn(List.of(
|
||||||
|
wallModeRow,
|
||||||
|
row(today.minusDays(1), 11L, 3L, 0, 100, 2)));
|
||||||
|
when(districtStrategyMapper.selectOne(any())).thenReturn(null);
|
||||||
|
when(esGateway.countState2(anyString(), anyBoolean(), anyLong(), anyInt(), anyLong(), anyLong(),
|
||||||
|
any(), any())).thenReturn(0L);
|
||||||
|
|
||||||
|
Map<String, Object> result = tools.districtPaperStatisticsQuery(9L, start, end, null, null, null, null, null);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Map<String, Object>> summary = (List<Map<String, Object>>) result.get("summaryByGender");
|
||||||
|
// runl_mode=1 的厕位行 useCount 不计入,仅厕位模式(runl_mode=0)累计
|
||||||
|
assertEquals(2L, summary.get(0).get("useCount"));
|
||||||
|
assertEquals(2.0, summary.get(0).get("paperLengthM"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void useCountZeroYieldsNullPerUseLength() {
|
void useCountZeroYieldsNullPerUseLength() {
|
||||||
mockDistrictAndDictionary();
|
mockDistrictAndDictionary();
|
||||||
|
|||||||
Reference in New Issue
Block a user