diff --git a/aucma-base/src/main/resources/board4_database_troubleshooting_and_rebuild_guide.md b/aucma-base/src/main/resources/board4_database_troubleshooting_and_rebuild_guide.md
index a4b4ff0..f830bc1 100644
--- a/aucma-base/src/main/resources/board4_database_troubleshooting_and_rebuild_guide.md
+++ b/aucma-base/src/main/resources/board4_database_troubleshooting_and_rebuild_guide.md
@@ -1,358 +1,159 @@
-# Board4 数据库排查与自动化重建说明
+# Board4 数据库排查与重建说明
## 1. 文档目的
-本文档用于沉淀本次 `Board4` 产量统计方案在数据库侧的完整排查过程、最终状态和自动化 SQL 备份,避免未来数据库被重置后无法快速恢复。
+本文档用于沉淀本次 `Board4` 产量统计改造在数据库侧的完整排查过程、最终数据库对象状态、重建顺序和 SQL 备份。
-本文档覆盖:
+重点说明:
-1. 本次数据库对象排查过程
-2. 当前数据库对象最终状态
-3. 自动化部署链路说明
-4. 数据库重建顺序
-5. 所有关键 SQL 原文备份
+1. 数据库负责:
+ - `RT_DAILY_PROD_STATE`
+ - `DEVICE_DAILY_PRODUCTION`
+ - 当前月分表触发器
+ - `PKG_DEVICE_PROD_CALC`
+ - `JOB_FLUSH_RT_TO_DAY`
+2. **月分表新建职责不再由数据库自动化完成**
+3. **新建月分表由 C# 采集程序负责实现**
---
-## 2. 背景与目标
+## 2. 当前最终职责边界
-本次数据库侧改造围绕以下目标展开:
+### 2.1 数据库负责的内容
-1. 为 `Board4.dayTotal`、`Board4.monthTotal`、`deviceProductionList` 引入实时表 `RT_DAILY_PROD_STATE`
-2. 为月累计提供权威日汇总表 `DEVICE_DAILY_PRODUCTION`
-3. 让自动采集设备写入月分表 `BASE_DEVICE_PARAM_VAL_YYYYMM`
-4. 让自动采集分表通过触发器自动更新 `RT_DAILY_PROD_STATE`
-5. 让数据库夜间自动执行 `RT -> DAY` 搬迁
-6. 让数据库在月底自动创建下月分表和下月触发器
+1. 保存实时产量状态:`RT_DAILY_PROD_STATE`
+2. 保存日产量汇总:`DEVICE_DAILY_PRODUCTION`
+3. 通过当前月分表触发器把自动采集明细同步到 RT
+4. 通过 `PKG_DEVICE_PROD_CALC` 把昨日 RT 搬迁到 DAY
+5. 通过 `JOB_FLUSH_RT_TO_DAY` 每天 00:10 自动执行 RT -> DAY
-当前分表规则已统一为:
+### 2.2 C# 采集程序负责的内容
-```text
-BASE_DEVICE_PARAM_VAL_YYYYMM
-```
+1. 创建当前月/后续月份的采集分表:
+ - `BASE_DEVICE_PARAM_VAL_202603`
+ - `BASE_DEVICE_PARAM_VAL_202604`
+ - ...
+2. 保证采集入库时目标月分表已经存在
+3. 新建月分表对应触发器的部署流程由业务发布过程配套完成
-例如:
+### 2.3 Java 负责的内容
-```text
-BASE_DEVICE_PARAM_VAL_202603
-BASE_DEVICE_PARAM_VAL_202604
-```
+1. OLD 设备写 `BASE_DEVICE_PARAM_VAL`
+2. OLD 设备在写入成功后,由 Java Service 同步更新 `RT_DAILY_PROD_STATE`
---
-## 3. 本次排查过程
+## 3. 数据库排查过程总结
-### 3.1 第一轮检查
+### 3.1 初始排查
-最开始执行了基于 `USER_TABLES / USER_OBJECTS / USER_SCHEDULER_JOBS` 的检查 SQL,结果全部为空。
+最初通过 `USER_TABLES / USER_OBJECTS / USER_SCHEDULER_JOBS` 查询关键对象时结果为空。
-最初判断有两种可能:
+后续确认:
-1. 当前登录用户不是实际业务 schema
-2. 业务 schema 尚未部署任何对象
+1. 实际业务 schema 是 `HAIWEI`
+2. 需要通过 `ALL_TABLES / ALL_OBJECTS / ALL_TRIGGERS / ALL_SCHEDULER_JOBS` 查询全局对象
-后续用户确认:
+### 3.2 关键对象定位结果
-```text
-BASE_DEVICE_PARAM_VAL_202603 已经建了
-```
+确认在 `HAIWEI` schema 下已存在:
-这说明:
-
-1. 之前查询使用的视角不对
-2. 需要改用 `ALL_TABLES / ALL_OBJECTS / ALL_SCHEDULER_JOBS` 继续排查
-
----
-
-### 3.2 第二轮检查:确认实际对象所在 schema
-
-通过 `ALL_TABLES / ALL_OBJECTS / ALL_TRIGGERS / ALL_SCHEDULER_JOBS` 排查后,确认对象位于:
-
-```text
-HAIWEI
-```
-
-确认已存在对象:
-
-1. `HAIWEI.BASE_DEVICE_PARAM_VAL_202603`
-2. `HAIWEI.RT_DAILY_PROD_STATE`
-3. `HAIWEI.DEVICE_DAILY_PRODUCTION`
-4. `HAIWEI.PKG_DEVICE_PROD_CALC`
-5. `HAIWEI.PKG_DEVICE_PROD_CALC BODY`
-
-当时仍缺失:
-
-1. `TRG_BDPV_202603_RT`
-2. `JOB_FLUSH_RT_TO_DAY`
-3. `PKG_DEVICE_PARAM_PARTITION`
-4. `JOB_ENSURE_DEVICE_PARAM_PARTITION`
-
----
-
-### 3.3 当前月触发器与调度任务部署
-
-后续补部署后,再次查询,确认以下对象已存在:
-
-1. `HAIWEI.TRG_BDPV_202603_RT`
-2. `HAIWEI.JOB_FLUSH_RT_TO_DAY`
-3. `HAIWEI.JOB_ENSURE_DEVICE_PARAM_PARTITION`
-
-当时 `JOB` 状态为:
-
-```text
-JOB_ENSURE_DEVICE_PARAM_PARTITION TRUE SCHEDULED RUN_COUNT=0 FAILURE_COUNT=0
-JOB_FLUSH_RT_TO_DAY TRUE SCHEDULED RUN_COUNT=0 FAILURE_COUNT=0
-```
-
-这说明:
-
-1. 任务已经创建并启用
-2. 但尚未到首次实际执行时间
-3. 仍需继续验证“自动建下月分表+触发器”能力
-
----
-
-### 3.4 手工验证自动建下月分表能力时出现 ORA-01031
-
-执行:
-
-```sql
-BEGIN
- HAIWEI.PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER('202604', '202603');
-END;
-/
-```
-
-返回:
-
-```text
-ORA-01031: 权限不足
-ORA-06512: 在 "HAIWEI.PKG_DEVICE_PARAM_PARTITION", line 52
-ORA-06512: 在 "HAIWEI.PKG_DEVICE_PARAM_PARTITION", line 164
-```
-
-问题定位:
-
-1. 不是 SQL 逻辑问题
-2. 是 Oracle 包内执行 DDL 时的权限问题
-3. `PKG_DEVICE_PARAM_PARTITION` 内部需要执行:
- - `CREATE TABLE`
- - `CREATE INDEX`
- - `CREATE OR REPLACE TRIGGER`
-
-Oracle 特性说明:
-
-1. 包里执行 DDL 依赖的是 **直接授予的系统权限**
-2. 不是依赖角色权限
-3. Oracle 没有 `GRANT CREATE INDEX TO user` 这种系统权限,索引能力依赖表对象和表空间配额
-
----
-
-### 3.5 权限排查与修正
-
-在权限排查过程中发现一个执行细节:
-
-1. 当前 SQL 客户端执行 `GRANT` 语句时,**不能带尾部分号**
-2. 带分号时会报:
-
-```text
-ORA-00933: SQL 命令未正确结束
-```
-
-最终应采用“不带分号、单条执行”的方式,例如:
-
-```sql
-GRANT CREATE TRIGGER TO HAIWEI
-```
-
-本次建议的最小权限模型为:
-
-```sql
-GRANT CREATE TABLE TO HAIWEI
-GRANT CREATE TRIGGER TO HAIWEI
-GRANT CREATE JOB TO HAIWEI
-GRANT CREATE PROCEDURE TO HAIWEI
-```
-
-注意:
-
-1. 不需要 `CREATE ANY TABLE`
-2. 不需要 `CREATE ANY TRIGGER`
-3. 不需要维护其它 schema
-4. 只允许 `HAIWEI` 在自己 schema 下自动维护本系统对象
-
----
-
-### 3.6 表空间配额检查
-
-后续补查了 `HAIWEI` 的表空间信息。
-
-用户反馈的关键结果为:
-
-```text
-HAIWEI_DATA -1 10215620608
-```
-
-可解释为:
-
-1. `HAIWEI` 在业务表空间 `HAIWEI_DATA` 上已有配额
-2. `MAX_BYTES = -1` 一般表示无限配额
-3. `BYTES = 10215620608` 表示已经占用约 9.5GB
-
-结论:
-
-1. 当前自动建表失败的根因不是表空间配额
-2. 核心根因是 DDL 权限不足
-
----
-
-### 3.7 自动建下月分表能力最终验证通过
-
-在权限与环境调整完成后,再次执行:
-
-```sql
-BEGIN
- HAIWEI.PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER('202604', '202603');
-END;
-/
-```
-
-执行成功。
-
-这意味着:
-
-1. `PKG_DEVICE_PARAM_PARTITION` 可以在 `HAIWEI` schema 内自动建下月分表
-2. 也可以自动创建对应下月触发器
-3. 后续不需要每个月手工执行触发器模板
-
----
-
-## 4. 当前数据库最终状态
-
-根据本次排查与执行结果,当前数据库已经具备以下能力。
-
-### 4.1 已存在的关键表
-
-1. `HAIWEI.BASE_DEVICE_PARAM_VAL_202603`
-2. `HAIWEI.RT_DAILY_PROD_STATE`
-3. `HAIWEI.DEVICE_DAILY_PRODUCTION`
-
-### 4.2 已存在的关键包
-
-1. `HAIWEI.PKG_DEVICE_PROD_CALC`
-2. `HAIWEI.PKG_DEVICE_PROD_CALC BODY`
-3. `HAIWEI.PKG_DEVICE_PARAM_PARTITION`
-4. `HAIWEI.PKG_DEVICE_PARAM_PARTITION BODY`
-
-### 4.3 已存在的关键触发器
-
-1. `HAIWEI.TRG_BDPV_202603_RT`
-
-### 4.4 已存在的关键调度任务
-
-1. `HAIWEI.JOB_FLUSH_RT_TO_DAY`
-2. `HAIWEI.JOB_ENSURE_DEVICE_PARAM_PARTITION`
-
----
-
-## 5. 自动化链路说明
-
-### 5.1 当前月自动采集链路
-
-```text
-自动采集数据
- -> BASE_DEVICE_PARAM_VAL_202603
- -> TRG_BDPV_202603_RT
- -> RT_DAILY_PROD_STATE
-```
+1. `BASE_DEVICE_PARAM_VAL_202603`
+2. `RT_DAILY_PROD_STATE`
+3. `DEVICE_DAILY_PRODUCTION`
+4. `PKG_DEVICE_PROD_CALC`
+5. `TRG_BDPV_202603_RT`
+6. `JOB_FLUSH_RT_TO_DAY`
说明:
-1. 仅自动采集设备会走当前月分表
-2. 触发器只处理:
- - `PARAM_NAME = '机台状态-实际产出数量'`
-3. 触发器会执行 delta-sum 累计逻辑
+1. 当前月分表已经存在
+2. 当前月触发器已经存在
+3. RT / DAY 基础表已经存在
+4. 夜间 RT->DAY 搬迁包和 job 已经存在
+
+### 3.3 自动建分表方案废弃
+
+本次过程中曾经设计过数据库自动创建下月分表/触发器的方案,但当前最终决策为:
+
+1. 删除数据库自动建月分表脚本
+2. 取消数据库侧月底自动建分表/触发器方案
+3. 后续月份新建分表改为由 **C# 采集程序** 负责
+
+原因:
+
+1. 分表本身是采集入库前提
+2. 由采集程序自己保障分表存在更符合职责边界
+3. 可避免数据库额外 DDL 权限复杂度
+4. 可避免“数据库月底 job 未执行,但采集程序已切月写入”的风险
+
+---
+
+## 4. 当前数据库最终对象状态
+
+### 4.1 表
+
+1. `HAIWEI.BASE_DEVICE_PARAM_VAL_202603`
+2. `HAIWEI.RT_DAILY_PROD_STATE`
+3. `HAIWEI.DEVICE_DAILY_PRODUCTION`
+
+### 4.2 触发器
+
+1. `HAIWEI.TRG_BDPV_202603_RT`
+
+### 4.3 包
+
+1. `HAIWEI.PKG_DEVICE_PROD_CALC`
+2. `HAIWEI.PKG_DEVICE_PROD_CALC BODY`
+
+### 4.4 调度任务
+
+1. `HAIWEI.JOB_FLUSH_RT_TO_DAY`
+
+---
+
+## 5. 数据库完整链路
+
+### 5.1 自动采集设备链路
+
+```text
+C# 采集程序
+ -> 确保 BASE_DEVICE_PARAM_VAL_202603 已存在
+ -> 插入 BASE_DEVICE_PARAM_VAL_202603
+ -> TRG_BDPV_202603_RT
+ -> RT_DAILY_PROD_STATE
+ -> JOB_FLUSH_RT_TO_DAY
+ -> PKG_DEVICE_PROD_CALC
+ -> DEVICE_DAILY_PRODUCTION
+```
### 5.2 OLD 设备链路
```text
-PDA / Java 写入
+PDA / Java
-> BASE_DEVICE_PARAM_VAL
- -> BaseDeviceParamValServiceImpl.syncRtStateIfNeeded(...)
- -> RT_DAILY_PROD_STATE
+ -> Java Service 同步更新 RT_DAILY_PROD_STATE
```
-说明:
-
-1. OLD 设备不依赖数据库触发器
-2. OLD 的 RT 更新由 Java 服务层同步完成
-
-### 5.3 夜间汇总链路
+### 5.3 Board4 查询链路
```text
-JOB_FLUSH_RT_TO_DAY
- -> PKG_DEVICE_PROD_CALC.FLUSH_RT_TO_DAY
- -> RT_DAILY_PROD_STATE(昨天)
- -> DEVICE_DAILY_PRODUCTION
- -> 删除昨天 RT
+Board4.dayTotal
+ -> RT_DAILY_PROD_STATE(当天)
+
+Board4.monthTotal
+ -> DEVICE_DAILY_PRODUCTION(本月已汇总部分)
+ + RT_DAILY_PROD_STATE(本月未入DAY部分)
+
+Board4.deviceProductionList
+ -> RT_DAILY_PROD_STATE(当天 current_total)
```
-调度时间:
-
-```text
-每天 00:10:00
-```
-
-### 5.4 下月分表自动准备链路
-
-```text
-JOB_ENSURE_DEVICE_PARAM_PARTITION
- -> PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER
- -> 用当前月表克隆下月表
- -> 创建下月索引
- -> 创建下月 RT 触发器
-```
-
-调度时间:
-
-```text
-每月 28/29/30/31 日 23:55:00
-```
-
-这样设计的原因:
-
-1. 不同月份天数不同
-2. 过程本身是幂等的
-3. 重复触发也不会重复建对象
-
---
-## 6. 现在是否已经“完全自动运行”
+## 6. 数据库重建顺序
-从机制角度看,当前已经具备:
-
-1. 当前月自动采集数据自动进 RT
-2. 夜间自动 RT -> DAY
-3. 月底自动准备下月分表与触发器
-
-因此,当前可以认为:
-
-```text
-数据库自动化链路已经部署完成
-```
-
-但仍建议在以下两个时间点做一次运维验证:
-
-1. `JOB_FLUSH_RT_TO_DAY` 首次实际运行后,确认 `RUN_COUNT > 0`
-2. 月底任务首次自动执行后,确认已自动生成:
- - `BASE_DEVICE_PARAM_VAL_202604`
- - `TRG_BDPV_202604_RT`
-
----
-
-## 7. 数据库重建顺序
-
-如果未来数据库被重置,可按以下顺序恢复:
+如果未来数据库被重置,按以下顺序执行。
### 第一步:创建基础表
@@ -365,80 +166,47 @@ JOB_ENSURE_DEVICE_PARAM_PARTITION
### 第三步:确保当前月分表存在
-4. 手工创建当前月分表,例如:
+4. 由 **C# 采集程序** 或其配套建表脚本创建当前月分表,例如:
`BASE_DEVICE_PARAM_VAL_202603`
-说明:
-
-1. 自动建分表包默认是“用当前月分表去克隆下月分表”
-2. 所以首次上线时,当前月分表仍需人工准备
-
-### 第四步:为当前月分表创建触发器
+### 第四步:部署当前月触发器
5. `base_device_param_val_202603_trigger.sql`
-### 第五步:创建夜间调度任务
+### 第五步:创建夜间 RT->DAY job
6. `device_prod_calc_scheduler_job.sql`
-### 第六步:创建自动建下月分表包
-
-7. `device_param_partition_auto_pkg.sql`
-
-### 第七步:创建自动建下月分表调度任务
-
-8. `device_param_partition_auto_scheduler_job.sql`
-
---
-## 8. 脚本执行顺序与作用总表
+## 7. 当前资源目录保留的 SQL 文件
-为避免数据库重置后执行顺序混乱,建议严格按下表顺序执行。
-
-| 顺序 | 文件名 | 文件位置 | 是否必须 | 作用 |
-|------|--------|----------|----------|------|
-| 1 | `rt_daily_prod_state.sql` | `aucma-base/src/main/resources` | 必须 | 创建实时产量状态表 `RT_DAILY_PROD_STATE`,承接自动采集触发器与 OLD 设备 Java 同步累计 |
-| 2 | `device_daily_production.sql` | `aucma-base/src/main/resources` | 必须 | 创建设备日产量权威汇总表 `DEVICE_DAILY_PRODUCTION`,承接夜间 RT->DAY 搬迁 |
-| 3 | `device_prod_calc_pkg.sql` | `aucma-base/src/main/resources` | 必须 | 创建夜间搬迁包 `PKG_DEVICE_PROD_CALC`,负责把昨日 RT 数据搬迁到 DAY |
-| 4 | `base_device_param_val_202603_trigger.sql` | `aucma-base/src/main/resources` | 当前月必须 | 为当前月分表 `BASE_DEVICE_PARAM_VAL_202603` 创建触发器 `TRG_BDPV_202603_RT`,让自动采集写入实时更新 RT |
-| 5 | `device_prod_calc_scheduler_job.sql` | `aucma-base/src/main/resources` | 必须 | 创建夜间调度任务 `JOB_FLUSH_RT_TO_DAY`,每天 00:10 自动执行 `PKG_DEVICE_PROD_CALC.FLUSH_RT_TO_DAY` |
-| 6 | `device_param_partition_auto_pkg.sql` | `aucma-base/src/main/resources` | 推荐且建议必须 | 创建自动建下月分表/触发器包 `PKG_DEVICE_PARAM_PARTITION`,避免每月手工建新分表 |
-| 7 | `device_param_partition_auto_scheduler_job.sql` | `aucma-base/src/main/resources` | 推荐且建议必须 | 创建月底自动建下月分表与触发器的任务 `JOB_ENSURE_DEVICE_PARAM_PARTITION` |
-
-补充说明:
-
-1. 第 4 步之所以仍要“当前月必须手工执行一次”,是因为自动建月分表包默认以“当前月分表”为源克隆“下月分表”,所以首次上线必须先把当前月表和当前月触发器补齐。
-2. 第 6、7 步执行完成后,后续月份无需再手工创建 `BASE_DEVICE_PARAM_VAL_YYYYMM` 和 `TRG_BDPV_YYYYMM_RT`。
-3. 如果数据库不是首次重置,而只是补装夜间任务,可以在确认基础表和当前月触发器都存在后,从第 5 步开始执行。
-
----
-
-## 9. 自动化 SQL 文件清单
-
-本次自动化相关 SQL 一共 7 个:
-
-1. `zs_aucma-mes-back/aucma-base/src/main/resources/rt_daily_prod_state.sql`
-2. `zs_aucma-mes-back/aucma-base/src/main/resources/device_daily_production.sql`
-3. `zs_aucma-mes-back/aucma-base/src/main/resources/device_prod_calc_pkg.sql`
-4. `sql/base_device_param_val_202603_trigger.sql`
-5. `sql/device_prod_calc_scheduler_job.sql`
-6. `sql/device_param_partition_auto_pkg.sql`
-7. `sql/device_param_partition_auto_scheduler_job.sql`
-
-以下为全部 SQL 原文备份。
-
----
-
-## 10. SQL 原文备份
-
-### 9.1 `rt_daily_prod_state.sql`
-
-文件位置:
+当前保留在:
```text
-zs_aucma-mes-back/aucma-base/src/main/resources/rt_daily_prod_state.sql
+zs_aucma-mes-back/aucma-base/src/main/resources
```
+的数据库脚本有 5 个:
+
+1. `rt_daily_prod_state.sql`
+2. `device_daily_production.sql`
+3. `device_prod_calc_pkg.sql`
+4. `base_device_param_val_202603_trigger.sql`
+5. `device_prod_calc_scheduler_job.sql`
+
+说明:
+
+1. 这些脚本是数据库恢复必需的脚本
+2. 不再保留“自动建下月分表/触发器”的数据库脚本
+3. 后续月份分表由 C# 采集程序负责
+
+---
+
+## 8. SQL 原文备份
+
+### 8.1 `rt_daily_prod_state.sql`
+
```sql
-- RT_DAILY_PROD_STATE:设备当日实时产量状态表
-- 兼容:Oracle 11 / Oracle 19
@@ -477,15 +245,7 @@ CREATE INDEX IDX_RT_DAILY_PROD_STATE_DEVICE
ON RT_DAILY_PROD_STATE (DEVICE_CODE, PROD_DATE);
```
----
-
-### 9.2 `device_daily_production.sql`
-
-文件位置:
-
-```text
-zs_aucma-mes-back/aucma-base/src/main/resources/device_daily_production.sql
-```
+### 8.2 `device_daily_production.sql`
```sql
-- DEVICE_DAILY_PRODUCTION:设备日产量权威汇总表
@@ -519,15 +279,7 @@ CREATE INDEX IDX_DEVICE_DAILY_PRODUCTION_DEVICE
ON DEVICE_DAILY_PRODUCTION (DEVICE_CODE, PROD_DATE);
```
----
-
-### 9.3 `device_prod_calc_pkg.sql`
-
-文件位置:
-
-```text
-zs_aucma-mes-back/aucma-base/src/main/resources/device_prod_calc_pkg.sql
-```
+### 8.3 `device_prod_calc_pkg.sql`
```sql
-- 设备产量夜间搬迁过程
@@ -597,22 +349,13 @@ END PKG_DEVICE_PROD_CALC;
/
```
----
-
-### 9.4 `base_device_param_val_202603_trigger.sql`
-
-文件位置:
-
-```text
-sql/base_device_param_val_202603_trigger.sql
-```
+### 8.4 `base_device_param_val_202603_trigger.sql`
```sql
-- 2026年03月自动采集分表触发器
-- 说明:
--- 1. 本脚本是 base_device_param_val_partition_trigger_template.sql 的 202603 实例化版本。
--- 2. 挂载表:BASE_DEVICE_PARAM_VAL_202603
--- 3. 仅处理自动采集设备;OLD 设备 RT 累计由 Java Service 同步完成。
+-- 1. 挂载表:BASE_DEVICE_PARAM_VAL_202603
+-- 2. 仅处理自动采集设备;OLD 设备 RT 累计由 Java Service 同步完成。
CREATE OR REPLACE TRIGGER TRG_BDPV_202603_RT
AFTER INSERT ON BASE_DEVICE_PARAM_VAL_202603
@@ -687,15 +430,7 @@ END;
/
```
----
-
-### 9.5 `device_prod_calc_scheduler_job.sql`
-
-文件位置:
-
-```text
-sql/device_prod_calc_scheduler_job.sql
-```
+### 8.5 `device_prod_calc_scheduler_job.sql`
```sql
-- 夜间 RT -> DAY 搬迁调度任务
@@ -736,283 +471,19 @@ END;
---
-### 9.6 `device_param_partition_auto_pkg.sql`
+## 9. 最终结论
-文件位置:
+当前最终数据库方案是:
+
+1. 当前月分表由 C# 采集程序创建
+2. 当前月分表触发器由数据库脚本部署
+3. 自动采集明细通过触发器实时进入 RT
+4. OLD 数据由 Java 同步进入 RT
+5. 夜间由 `JOB_FLUSH_RT_TO_DAY` 驱动 `PKG_DEVICE_PROD_CALC` 搬迁到 DAY
+
+换句话说:
```text
-sql/device_param_partition_auto_pkg.sql
+数据库侧保留“实时累计 + 夜间汇总”
+分表创建职责回归 C# 采集程序
```
-
-```sql
--- 自动创建设备参数月分表与对应触发器
--- 说明:
--- 1. 仅需部署一次本包,后续可由 DBMS_SCHEDULER 自动调用。
--- 2. 默认使用“当前月分表”克隆出“下月分表”,并自动创建下月 RT 触发器。
--- 3. 本包是幂等的:目标分表或目标触发器已存在时会自动跳过。
--- 4. 首次上线前,仍需人工确保“当前月分表”已经存在,例如 BASE_DEVICE_PARAM_VAL_202603。
-
-CREATE OR REPLACE PACKAGE PKG_DEVICE_PARAM_PARTITION AS
-
- PROCEDURE ENSURE_MONTH_TABLE_AND_TRIGGER(
- P_TARGET_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1), 'YYYYMM'),
- P_SOURCE_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(TRUNC(SYSDATE, 'MM'), 'YYYYMM')
- );
-
-END PKG_DEVICE_PARAM_PARTITION;
-/
-
-CREATE OR REPLACE PACKAGE BODY PKG_DEVICE_PARAM_PARTITION AS
-
- C_TABLE_PREFIX CONSTANT VARCHAR2(64) := 'BASE_DEVICE_PARAM_VAL_';
- C_TRIGGER_PREFIX CONSTANT VARCHAR2(64) := 'TRG_BDPV_';
-
- PROCEDURE VALIDATE_SUFFIX(P_SUFFIX IN VARCHAR2) IS
- BEGIN
- IF P_SUFFIX IS NULL OR NOT REGEXP_LIKE(P_SUFFIX, '^\d{6}$') THEN
- RAISE_APPLICATION_ERROR(-20001, '非法分表后缀: ' || NVL(P_SUFFIX, 'NULL'));
- END IF;
- END VALIDATE_SUFFIX;
-
- FUNCTION OBJECT_EXISTS(P_OBJECT_NAME IN VARCHAR2, P_OBJECT_TYPE IN VARCHAR2) RETURN NUMBER IS
- V_COUNT NUMBER;
- BEGIN
- SELECT COUNT(1)
- INTO V_COUNT
- FROM USER_OBJECTS
- WHERE OBJECT_NAME = UPPER(P_OBJECT_NAME)
- AND OBJECT_TYPE = UPPER(P_OBJECT_TYPE);
- RETURN V_COUNT;
- END OBJECT_EXISTS;
-
- FUNCTION TABLE_EXISTS(P_TABLE_NAME IN VARCHAR2) RETURN NUMBER IS
- V_COUNT NUMBER;
- BEGIN
- SELECT COUNT(1)
- INTO V_COUNT
- FROM USER_TABLES
- WHERE TABLE_NAME = UPPER(P_TABLE_NAME);
- RETURN V_COUNT;
- END TABLE_EXISTS;
-
- PROCEDURE EXECUTE_DDL(P_SQL IN CLOB) IS
- V_CURSOR INTEGER;
- BEGIN
- V_CURSOR := DBMS_SQL.OPEN_CURSOR;
- DBMS_SQL.PARSE(V_CURSOR, P_SQL, DBMS_SQL.NATIVE);
- DBMS_SQL.CLOSE_CURSOR(V_CURSOR);
- EXCEPTION
- WHEN OTHERS THEN
- IF DBMS_SQL.IS_OPEN(V_CURSOR) THEN
- DBMS_SQL.CLOSE_CURSOR(V_CURSOR);
- END IF;
- RAISE;
- END EXECUTE_DDL;
-
- PROCEDURE CREATE_TARGET_TABLE(P_SOURCE_TABLE IN VARCHAR2, P_TARGET_TABLE IN VARCHAR2) IS
- V_SQL CLOB;
- BEGIN
- V_SQL := 'CREATE TABLE ' || P_TARGET_TABLE || ' AS SELECT * FROM ' || P_SOURCE_TABLE || ' WHERE 1 = 0';
- EXECUTE IMMEDIATE V_SQL;
- END CREATE_TARGET_TABLE;
-
- PROCEDURE CREATE_TARGET_INDEXES(P_TARGET_TABLE IN VARCHAR2, P_TARGET_SUFFIX IN VARCHAR2) IS
- V_INDEX_NAME_1 VARCHAR2(128);
- V_INDEX_NAME_2 VARCHAR2(128);
- BEGIN
- V_INDEX_NAME_1 := 'IDX_BDPV_' || P_TARGET_SUFFIX || '_PCD';
- V_INDEX_NAME_2 := 'IDX_BDPV_' || P_TARGET_SUFFIX || '_DCT';
-
- IF OBJECT_EXISTS(V_INDEX_NAME_1, 'INDEX') = 0 THEN
- EXECUTE IMMEDIATE 'CREATE INDEX ' || V_INDEX_NAME_1 ||
- ' ON ' || P_TARGET_TABLE || ' (PARAM_NAME, COLLECT_TIME, DEVICE_CODE, RECORD_ID)';
- END IF;
-
- IF OBJECT_EXISTS(V_INDEX_NAME_2, 'INDEX') = 0 THEN
- EXECUTE IMMEDIATE 'CREATE INDEX ' || V_INDEX_NAME_2 ||
- ' ON ' || P_TARGET_TABLE || ' (DEVICE_CODE, COLLECT_TIME)';
- END IF;
- END CREATE_TARGET_INDEXES;
-
- PROCEDURE CREATE_TARGET_TRIGGER(P_TARGET_TABLE IN VARCHAR2, P_TARGET_SUFFIX IN VARCHAR2) IS
- V_TRIGGER_NAME VARCHAR2(128);
- V_SQL CLOB;
- BEGIN
- V_TRIGGER_NAME := C_TRIGGER_PREFIX || P_TARGET_SUFFIX || '_RT';
- IF OBJECT_EXISTS(V_TRIGGER_NAME, 'TRIGGER') > 0 THEN
- RETURN;
- END IF;
-
- V_SQL := 'CREATE OR REPLACE TRIGGER ' || V_TRIGGER_NAME || CHR(10) ||
- 'AFTER INSERT ON ' || P_TARGET_TABLE || CHR(10) ||
- 'FOR EACH ROW' || CHR(10) ||
- 'DECLARE' || CHR(10) ||
- ' V_PROD_DATE DATE;' || CHR(10) ||
- ' V_NEW_VAL NUMBER(18,4);' || CHR(10) ||
- ' V_LAST_VAL NUMBER(18,4);' || CHR(10) ||
- ' V_CUR_TOTAL NUMBER(18,4);' || CHR(10) ||
- ' V_RESET_COUNT NUMBER(10);' || CHR(10) ||
- ' V_DELTA NUMBER(18,4);' || CHR(10) ||
- 'BEGIN' || CHR(10) ||
- ' IF :NEW.PARAM_NAME <> ''机台状态-实际产出数量'' THEN' || CHR(10) ||
- ' RETURN;' || CHR(10) ||
- ' END IF;' || CHR(10) ||
- ' IF :NEW.DEVICE_CODE LIKE ''OLD-%'' THEN' || CHR(10) ||
- ' RETURN;' || CHR(10) ||
- ' END IF;' || CHR(10) ||
- ' BEGIN' || CHR(10) ||
- ' V_NEW_VAL := TO_NUMBER(:NEW.PARAM_VALUE);' || CHR(10) ||
- ' EXCEPTION' || CHR(10) ||
- ' WHEN OTHERS THEN' || CHR(10) ||
- ' RETURN;' || CHR(10) ||
- ' END;' || CHR(10) ||
- ' V_PROD_DATE := TRUNC(:NEW.COLLECT_TIME);' || CHR(10) ||
- ' BEGIN' || CHR(10) ||
- ' SELECT LAST_PARAM_VAL, CURRENT_TOTAL, RESET_COUNT' || CHR(10) ||
- ' INTO V_LAST_VAL, V_CUR_TOTAL, V_RESET_COUNT' || CHR(10) ||
- ' FROM RT_DAILY_PROD_STATE' || CHR(10) ||
- ' WHERE PROD_DATE = V_PROD_DATE' || CHR(10) ||
- ' AND DEVICE_CODE = :NEW.DEVICE_CODE' || CHR(10) ||
- ' AND PARAM_NAME = :NEW.PARAM_NAME' || CHR(10) ||
- ' FOR UPDATE;' || CHR(10) ||
- ' IF V_NEW_VAL > V_LAST_VAL THEN' || CHR(10) ||
- ' V_DELTA := V_NEW_VAL - V_LAST_VAL;' || CHR(10) ||
- ' ELSIF V_NEW_VAL < V_LAST_VAL THEN' || CHR(10) ||
- ' V_DELTA := V_NEW_VAL;' || CHR(10) ||
- ' V_RESET_COUNT := NVL(V_RESET_COUNT, 0) + 1;' || CHR(10) ||
- ' ELSE' || CHR(10) ||
- ' V_DELTA := 0;' || CHR(10) ||
- ' END IF;' || CHR(10) ||
- ' UPDATE RT_DAILY_PROD_STATE' || CHR(10) ||
- ' SET LAST_PARAM_VAL = V_NEW_VAL,' || CHR(10) ||
- ' CURRENT_TOTAL = NVL(V_CUR_TOTAL, 0) + NVL(V_DELTA, 0),' || CHR(10) ||
- ' RESET_COUNT = NVL(V_RESET_COUNT, 0),' || CHR(10) ||
- ' LAST_COLLECT_TIME = :NEW.COLLECT_TIME,' || CHR(10) ||
- ' UPDATE_TIME = SYSDATE' || CHR(10) ||
- ' WHERE PROD_DATE = V_PROD_DATE' || CHR(10) ||
- ' AND DEVICE_CODE = :NEW.DEVICE_CODE' || CHR(10) ||
- ' AND PARAM_NAME = :NEW.PARAM_NAME;' || CHR(10) ||
- ' EXCEPTION' || CHR(10) ||
- ' WHEN NO_DATA_FOUND THEN' || CHR(10) ||
- ' INSERT INTO RT_DAILY_PROD_STATE (' || CHR(10) ||
- ' PROD_DATE, DEVICE_CODE, PARAM_NAME, LAST_PARAM_VAL,' || CHR(10) ||
- ' CURRENT_TOTAL, RESET_COUNT, DIRTY_FLAG, LAST_COLLECT_TIME, UPDATE_TIME' || CHR(10) ||
- ' ) VALUES (' || CHR(10) ||
- ' V_PROD_DATE, :NEW.DEVICE_CODE, :NEW.PARAM_NAME, V_NEW_VAL,' || CHR(10) ||
- ' 0, 0, 0, :NEW.COLLECT_TIME, SYSDATE' || CHR(10) ||
- ' );' || CHR(10) ||
- ' END;' || CHR(10) ||
- 'END;';
-
- EXECUTE_DDL(V_SQL);
- END CREATE_TARGET_TRIGGER;
-
- PROCEDURE ENSURE_MONTH_TABLE_AND_TRIGGER(
- P_TARGET_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1), 'YYYYMM'),
- P_SOURCE_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(TRUNC(SYSDATE, 'MM'), 'YYYYMM')
- ) IS
- V_SOURCE_TABLE VARCHAR2(128);
- V_TARGET_TABLE VARCHAR2(128);
- BEGIN
- VALIDATE_SUFFIX(P_TARGET_SUFFIX);
- VALIDATE_SUFFIX(P_SOURCE_SUFFIX);
-
- V_SOURCE_TABLE := C_TABLE_PREFIX || P_SOURCE_SUFFIX;
- V_TARGET_TABLE := C_TABLE_PREFIX || P_TARGET_SUFFIX;
-
- IF TABLE_EXISTS(V_SOURCE_TABLE) = 0 THEN
- RAISE_APPLICATION_ERROR(-20002, '源分表不存在: ' || V_SOURCE_TABLE);
- END IF;
-
- IF TABLE_EXISTS(V_TARGET_TABLE) = 0 THEN
- CREATE_TARGET_TABLE(V_SOURCE_TABLE, V_TARGET_TABLE);
- END IF;
-
- CREATE_TARGET_INDEXES(V_TARGET_TABLE, P_TARGET_SUFFIX);
- CREATE_TARGET_TRIGGER(V_TARGET_TABLE, P_TARGET_SUFFIX);
- END ENSURE_MONTH_TABLE_AND_TRIGGER;
-
-END PKG_DEVICE_PARAM_PARTITION;
-/
-```
-
----
-
-### 9.7 `device_param_partition_auto_scheduler_job.sql`
-
-文件位置:
-
-```text
-sql/device_param_partition_auto_scheduler_job.sql
-```
-
-```sql
--- 自动创建下月分表与触发器的数据库任务
--- 说明:
--- 1. 每月 28 号到 31 号 23:55 都会尝试执行一次。
--- 2. 由于 ENSURE_MONTH_TABLE_AND_TRIGGER 是幂等的,所以重复执行不会重复建表。
--- 3. 采用这种写法是为了兼容不同月份天数。
-
-BEGIN
- DBMS_SCHEDULER.DROP_JOB(
- JOB_NAME => 'JOB_ENSURE_DEVICE_PARAM_PARTITION',
- FORCE => TRUE
- );
-EXCEPTION
- WHEN OTHERS THEN
- IF SQLCODE != -27475 THEN
- RAISE;
- END IF;
-END;
-/
-
-BEGIN
- DBMS_SCHEDULER.CREATE_JOB(
- JOB_NAME => 'JOB_ENSURE_DEVICE_PARAM_PARTITION',
- JOB_TYPE => 'PLSQL_BLOCK',
- JOB_ACTION => 'BEGIN PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER; END;',
- START_DATE => SYSTIMESTAMP,
- REPEAT_INTERVAL => 'FREQ=MONTHLY;BYMONTHDAY=28,29,30,31;BYHOUR=23;BYMINUTE=55;BYSECOND=0',
- ENABLED => TRUE,
- AUTO_DROP => FALSE,
- COMMENTS => '自动创建下月设备参数分表与 RT 触发器'
- );
-END;
-/
-```
-
----
-
-## 11. 推荐保留策略
-
-建议:
-
-1. 本文档长期保留在:
- `zs_aucma-mes-back/aucma-base/src/main/resources`
-2. 根目录 `sql/` 中的自动化 SQL 文件不要删除
-3. 数据库重置、迁移、容灾恢复时,以本文档为准恢复对象
-
----
-
-## 12. 最终结论
-
-本次排查后,数据库侧已经完成:
-
-1. 当前月自动采集分表 `BASE_DEVICE_PARAM_VAL_202603`
-2. 当前月触发器 `TRG_BDPV_202603_RT`
-3. 实时表 `RT_DAILY_PROD_STATE`
-4. 日汇总表 `DEVICE_DAILY_PRODUCTION`
-5. 夜间搬迁包 `PKG_DEVICE_PROD_CALC`
-6. 夜间搬迁任务 `JOB_FLUSH_RT_TO_DAY`
-7. 自动建下月分表包 `PKG_DEVICE_PARAM_PARTITION`
-8. 自动建下月分表任务 `JOB_ENSURE_DEVICE_PARAM_PARTITION`
-
-从架构上看,数据库已具备:
-
-```text
-当前月自动采集入 RT
-+ 夜间自动 RT -> DAY
-+ 月底自动准备下月分表与下月触发器
-```
-
-后续只需要常规运维检查 job 实际运行情况,不需要再每个月手工执行触发器模板。最自律帅气聪明的臧辰浩
diff --git a/aucma-base/src/main/resources/board4_end_to_end_full_implementation_guide.md b/aucma-base/src/main/resources/board4_end_to_end_full_implementation_guide.md
index 0e70464..b2e4aa0 100644
--- a/aucma-base/src/main/resources/board4_end_to_end_full_implementation_guide.md
+++ b/aucma-base/src/main/resources/board4_end_to_end_full_implementation_guide.md
@@ -2,335 +2,94 @@
## 1. 文档目的
-本文档是本次 `Board4` 产量统计改造的**完整端到端说明文档**,目标是把以下内容一次性讲清楚:
+本文档用于完整说明当前 `Board4` 产量统计方案从数据库对象到后端查询再到前端展示的端到端实现。
-1. 为什么要改
-2. 改造后的总体架构
-3. 数据库对象分别做什么
-4. 自动采集数据从入库到 `Board4` 展示的完整链路
-5. OLD 设备 PDA 数据从入库到 `Board4` 展示的完整链路
-6. 夜间 `RT -> DAY` 搬迁的完整链路
-7. `board1 / board4 / trace / SPC / 参数监控` 这些查询是怎么走的
-8. 本次所有代码文件、SQL 文件、文件位置、关键代码与关键 SQL
-9. 数据库部署顺序
-10. 常见问题与排查方法
+本文档重点强调的最终职责边界为:
-本文档位置:
-
-```text
-zs_aucma-mes-back/aucma-base/src/main/resources/board4_end_to_end_full_implementation_guide.md
-```
+1. **月分表新建由 C# 采集程序负责**
+2. 数据库负责:
+ - 当前月分表触发器
+ - `RT_DAILY_PROD_STATE`
+ - `DEVICE_DAILY_PRODUCTION`
+ - 夜间 `RT -> DAY`
+3. Java 负责:
+ - OLD 设备写单表
+ - OLD 写入后同步 RT
+ - 双源查询
+4. 前端保持原有接口不变,通过后端新口径取数
---
-## 2. 改造背景
+## 2. 当前理解、需求与业务定义
-### 2.1 原始问题
+### 2.1 当前理解
-`Board4` 原始产量统计存在以下问题:
+当前场景并不是简单改一条 SQL,而是涉及以下多条链路:
-1. 日产量、月产量原本依赖“取最新值”或“按天取最后值”之类逻辑
-2. 这种逻辑无法正确处理:
- - 计数器回退
- - 误操作清零
- - `生产计数-当前日期生产总数` 跨天不清零
-3. 自动采集即将切换到月分表,原来的单表 SQL 无法直接兼容
-4. OLD 设备仍然由 PDA 人工录入,不能走同一条自动采集链路
+1. `Board4` 日产量、月产量、设备总产量
+2. `board1` 设备状态与开机时间
+3. `val/index.vue` 最新参数监控
+4. `trace/index.vue` 参数追溯
+5. SPC 参数历史分析
+6. OLD 设备 PDA 写入
+7. 自动采集设备月分表写入
-### 2.2 这次改造要解决的核心目标
+因此,本次改造必须同时保证:
-1. `Board4.dayTotal` 改为读取实时累计表 `RT_DAILY_PROD_STATE`
-2. `Board4.monthTotal` 改为读取 `DEVICE_DAILY_PRODUCTION + RT_DAILY_PROD_STATE`
-3. `Board4.deviceProductionList` 改为读取 `RT_DAILY_PROD_STATE`
-4. 自动采集设备写月分表 `BASE_DEVICE_PARAM_VAL_YYYYMM`
-5. OLD 设备继续写 `BASE_DEVICE_PARAM_VAL`
-6. OLD 设备写入成功后,Java 同步更新 `RT_DAILY_PROD_STATE`
-7. 自动采集设备写入成功后,由月分表触发器更新 `RT_DAILY_PROD_STATE`
-8. 夜间将 `RT_DAILY_PROD_STATE` 搬迁到 `DEVICE_DAILY_PRODUCTION`
-9. 让 `board1 / board4 / trace / SPC / latest 参数监控` 都兼容“双源查询”
+1. 入库路径正确
+2. 中间层累计正确
+3. 汇总层可沉淀历史
+4. 查询层口径统一
----
+### 2.2 业务事实
-### 2.3 当前理解与代码现状
+当前已确认的业务事实如下:
-本次改造并不是从零开始设计,而是在既有代码和既有业务约束上逐步演进。
-
-改造前的主要代码现状如下:
-
-1. `Board4` 的日累计、月累计、设备产量列表位于:
- `zs_aucma-mes-back/aucma-report/src/main/resources/mapper/report/Board4Mapper.xml`
-2. 原 SQL 仍采用“取最新值”或“按天取最后值”的口径,不具备完整的增量求和能力
-3. `BaseDeviceParamVal` 原本把 `recordId` 当成 `Long` 使用,默认绑定旧单表序列
-4. `BaseDeviceParamValMapper.xml` 中大量查询默认指向旧单表 `BASE_DEVICE_PARAM_VAL`
-5. `DmsMobileController` 是 OLD 设备 PDA 参数写入和三色灯写入的重要入口
-6. `board1/index.vue` 与 `board4/index.vue` 都依赖设备状态统计接口,但原来没有统一双源口径
-7. `val/index.vue`、`trace/index.vue`、SPC 查询都和 `BaseDeviceParamValMapper.xml` 的查询能力直接相关
-
-这意味着:
-
-1. 不能只改 `Board4Mapper.xml`
-2. 必须同时改造数据落库路径、状态统计路径、追溯路径、SPC 路径
-3. 必须保证“OLD 单表 + 自动采集月分表 + RT 实时表 + DAY 日汇总表”四层协同一致
-
-### 2.4 业务事实与已确认定义
-
-业务上已经确认的事实与定义如下:
-
-1. 产量相关参数有两个:
+1. 与产量相关的参数主要有两个:
- `机台状态-实际产出数量`
- `生产计数-当前日期生产总数`
-2. 已知异常包括:
- - `生产计数-当前日期生产总数` 可能跨天不清零
- - `机台状态-实际产出数量` 可能被误操作清零
- - 采样频率不能完全覆盖硬件瞬时变化,可能丢失峰值
- - 不同设备发生异常的频率与时间不一致
-3. 当前 5 台 OLD 设备无法自动采集,只能由 PDA 手工录入
-4. OLD 设备识别规则固定为:
+2. 当前 5 台 OLD 设备不能自动采集,只能人工录入
+3. OLD 设备识别规则固定为:
`device_code` 以 `OLD-` 开头
-5. 设备去掉 `OLD-` 前缀后,视为进入自动采集链路
-6. `Board4.yearTotal` 固定使用 `BASE_ORDERINFO`,统计周期固定为“上一自然年”
-7. `Board4.selectDeviceProductionAnalysis` 固定展示“今日增量”
-8. `BASE_DEVICE_PARAM_VAL` 固定作为 OLD 设备人工录入单表
-9. `BASE_DEVICE_PARAM_VAL_YYYYMM` 固定作为自动采集月分表
-10. 两类表长期并存
-11. OLD 设备转自动采集后:
- - 旧表历史保留
- - 统计查询不再回看旧表历史
-12. C# 采集程序直连数据库,Java 不承接其入库
-13. `board1`、`val/index.vue`、`trace/index.vue`、SPC、设备状态接口都要适配双源
-14. 实时表命名固定为 `RT_DAILY_PROD_STATE`
-15. 日汇总表命名固定为 `DEVICE_DAILY_PRODUCTION`
-16. 有效设备口径固定为 `BASE_DEVICELEDGER.IS_FLAG = 1`
-
-### 2.5 样本数据与实际情况
-
-在 `val.sql` 和相关分析文档中,可以归纳出以下“实际运行情况”:
-
-1. 数据采集存在明显时间序列特征,设备会以近似固定频率写入参数值
-2. 两个产量参数并非语义完全一致:
- - `机台状态-实际产出数量` 更接近累积计数器
- - `生产计数-当前日期生产总数` 更接近理论上的“日计数器”
-3. 在样本区间中,按增量求和计算时,两种参数在局部样本上得到的总量可能相同
-4. 但从长期稳定性看,`机台状态-实际产出数量` 更适合做主口径
-5. 自动采集数据是大批量、持续写入的;PDA/OLD 数据是稀疏、人工驱动的
-6. 因采集频率限制,任何方案都无法完全恢复“采样间隔内瞬时增长又瞬时清零”的峰值
-
-因此,本方案追求的是:
-
-1. 在现有信息条件下给出稳健、可解释、可持续维护的产量统计
-2. 尽量避免高估
-3. 接受极端丢峰导致的少量低估
-
-### 2.6 改造前 SQL 逻辑的问题
-
-改造前以“最新值 / 当天最后值 / 本月每天最后值”为主的统计方式,存在以下问题:
-
-1. 对 `生产计数-当前日期生产总数`:
- - 跨天不清零会直接导致日累计虚高
-2. 对 `机台状态-实际产出数量`:
- - 中途清零会导致之前已产出部分被覆盖掉
-3. 如果一天内多次清零,只取最终值会丢失中间已产出量
-4. 按月分表后,旧 SQL 直接查 `BASE_DEVICE_PARAM_VAL`,无法兼容 `BASE_DEVICE_PARAM_VAL_YYYYMM`
-5. 原 `Board4` 高刷大屏如果每次都扫明细做窗口计算,性能与维护成本都不可接受
-6. 原 `board1` 和 `board4` 状态统计没有统一到双源三色灯口径,存在页面口径漂移风险
-
-### 2.7 详细需求清单
-
-本次从业务到技术,实际要满足的需求可以拆成 6 组:
-
-#### 2.7.1 Board4 统计需求
-
-1. `yearTotal` 继续来自 `BASE_ORDERINFO`
-2. `monthTotal` 输出当月累计产量
-3. `dayTotal` 输出当日累计产量
-4. `selectDeviceProductionAnalysis` 输出“今日增量”
-5. `deviceProductionList` 的合计要与 `dayTotal` 口径一致
-
-#### 2.7.2 双源数据需求
-
-1. 自动采集数据进入 `BASE_DEVICE_PARAM_VAL_YYYYMM`
-2. OLD 人工录入数据进入 `BASE_DEVICE_PARAM_VAL`
-3. 查询层必须同时兼容:
- - OLD 单表
- - 自动采集月分表
-4. `board1` 的状态、开机时间、设备列表必须兼容双源
-
-#### 2.7.3 主键与记录标识需求
-
-1. 自动采集月分表 `record_id` 是字符串业务唯一标识
-2. 自动采集月分表 `record_id` 不承担数值主键语义
-3. OLD 单表继续保留数值序列能力
-4. Java 领域模型需要统一主键语义,避免双字段歧义
-
-#### 2.7.4 跨月查询需求
-
-1. `trace` 允许自定义时间范围,可能跨 2~N 个月
-2. SPC 允许自定义时间范围,也可能跨月
-3. 月初、跨月边界必须能正确拿到边界前值
-
-#### 2.7.5 自动化需求
-
-1. 当前月自动采集写入时,能够自动维护 RT
-2. 夜间自动完成 RT -> DAY 搬迁
-3. 后续月份自动创建下月分表与下月触发器
-4. 尽量避免每个月手工运维
-
-#### 2.7.6 可维护性需求
-
-1. 方案要兼容 Oracle 11 / Oracle 19
-2. 要避免动态表名注入风险
-3. 要尽量复用 SQL 片段,减少重复维护
-4. 要让排障时能清晰区分:
- - 明细层问题
- - 触发器/Java RT 问题
- - 夜间汇总问题
- - 查询层问题
-
-### 2.8 需求分析与技术细节
-
-#### 2.8.1 为什么主口径选 `机台状态-实际产出数量`
-
-原因:
-
-1. 它更接近累积计数器语义
-2. 对“跨天不清零”问题天然免疫
-3. 只要处理回退/清零,就能稳定用 delta-sum 计算
-4. 比 `生产计数-当前日期生产总数` 少一层“理论应清零但现实未清零”的业务解释负担
-
-#### 2.8.2 为什么算法选 delta-sum
-
-delta-sum 是监控系统处理 counter 类时间序列的通用做法。
-
-规则:
-
-1. `prev_val IS NULL`:记 0
-2. `val >= prev_val`:记差值
-3. `val < prev_val`:视为重置,记 `val`
-
-优点:
-
-1. 能处理正常递增
-2. 能处理异常清零
-3. 能处理一天内多次回退
-4. 逻辑简单,可在 Java、触发器、存储过程三处统一实现
-
-#### 2.8.3 边界记录问题
-
-无论是日累计还是月累计,delta-sum 都依赖前一条记录。
-
-这带来一个客观问题:
-
-1. 时间窗口内第一条记录没有前值时,默认 delta = 0
-2. 如果窗口开始前到第一条记录之间设备已有产量,这部分会丢失
-
-处理方式分两类:
-
-1. 精确计算场景:
- - 在明细层引入边界前一条记录
- - 用 `LAG()` 做跨边界求差
-2. 实时累计场景:
- - RT 表只从当天首条开始做实时累计
- - 历史日的最终权威值由夜间搬迁/汇总沉淀
-
-这也是为什么当前最终方案选择:
-
-1. Board4 实时查询读 RT/DAY
-2. 不让高刷页面直接扫明细做全量窗口计算
-
-#### 2.8.4 按月分表的跨月查询问题
-
-手动月分表会带来两个直接后果:
-
-1. 表名不能用 `#{}` 绑定,只能用 `${}` 拼接
-2. 跨月查询必须先在 Java 层计算涉及的月份后缀列表
-
-这就是 `DeviceParamTableRouter.resolveReadTableSuffixes(...)` 存在的原因。
-
-关键点:
-
-1. 后缀由服务端计算
-2. 后缀必须强校验为 6 位数字
-3. 不能接受前端直接传表名
-
-#### 2.8.5 Oracle 11 / 19 兼容性
-
-本方案默认兼容:
-
-1. 测试环境 Oracle 11
-2. 生产环境 Oracle 19
-
-因此设计上遵循:
-
-1. MyBatis XML 和触发器尽量使用 Oracle 11 能运行的语法
-2. 动态 SQL 与夜间包不要依赖过新的语法糖
-3. `DBMS_SCHEDULER` 用标准方式创建 job
-
-#### 2.8.6 `recordId` 为什么统一改成 `String`
-
-原因:
-
-1. 自动采集月分表的 `record_id` 本身就是字符串业务标识
-2. 如果 OLD 单表继续在 Java 里保留 `Long recordId`,会出现双语义主键
-3. 为了统一领域模型,Java 侧改成 `String recordId`
-4. OLD 单表仍可在数据库层用数值序列,Java 侧通过 `TO_CHAR` 接收
-
-#### 2.8.7 为什么 Board4 读 RT/DAY 而不是读明细
-
-原因:
-
-1. `board4` 是高刷大屏
-2. 频繁刷明细 + 窗口函数对数据库压力大
-3. 读 RT/DAY 的复杂度更低
-4. 把计算压力前移到写入时和夜间批处理,更符合这个场景
-
-#### 2.8.8 为什么 Board4 状态统计要复用 board1
-
-原因:
-
-1. `board4/index.vue` 的 `runningData` 实际来自 `aucma-base` 的状态接口
-2. `board1/index.vue` 也依赖同一组状态接口
-3. 如果两边口径不统一,会出现:
- - `board1` 显示运行
- - `board4` 显示为空/停机
-4. 所以状态统计必须统一复用双源三色灯查询
-
-#### 2.8.9 为什么切换后的 OLD 历史不再回看
-
-这是业务定义,不是技术限制。
-
-确定规则后,系统才有清晰的一致性边界:
-
-1. OLD 阶段查旧表
-2. 自动采集阶段查月分表
-3. 切换后不做跨源拼接历史补算
-
-好处:
-
-1. 实现简单
-2. 口径清晰
-3. 性能与维护压力更低
-
-代价:
-
-1. 某些跨切换周期报表不连续
-
-业务已经接受这个代价,所以方案按此执行。
-
-### 2.9 方案的边界与局限
-
-即使当前方案已经是可运行的最优实现,仍要明确几个边界:
-
-1. 触发器只对新插入数据生效,不会自动回补历史数据
-2. 如果当日数据是在触发器建立前写入的,RT 当天会出现缺口
-3. 夜间 job 没跑之前,DAY 表不会有历史沉淀
-4. 当前“后台普通 CRUD 按 recordId 精准反查月分表”的能力并未作为本轮主闭环重点
-5. 自动建分表包依赖数据库具备对应 DDL 权限与表空间配额
+4. 设备去掉 `OLD-` 前缀后,视为已进入自动采集链路
+5. `yearTotal` 固定沿用 `BASE_ORDERINFO` 工单口径
+6. `selectDeviceProductionAnalysis` 固定展示“今日增量”
+7. `BASE_DEVICE_PARAM_VAL` 固定承载 OLD 设备人工录入
+8. `BASE_DEVICE_PARAM_VAL_YYYYMM` 固定承载自动采集数据
+9. 两类表长期并存
+10. 切换后的 OLD 历史数据保留,但后续查询不再回看
+11. 有效设备口径固定为 `BASE_DEVICELEDGER.IS_FLAG = 1`
+
+### 2.3 实际情况与异常
+
+真实运行时存在以下异常与边界:
+
+1. `生产计数-当前日期生产总数` 可能跨天不清零
+2. `机台状态-实际产出数量` 可能被误操作清零
+3. 采样频率不能完整捕捉“瞬时+1后又清零”的峰值
+4. 不同设备的异常发生频率和时间点不同
+5. 自动采集是高频写入
+6. PDA/OLD 写入是低频、人工驱动
+
+### 2.4 本次需求
+
+本次最终需求可归纳为:
+
+1. `Board4.dayTotal` 输出当日累计产量
+2. `Board4.monthTotal` 输出当月累计产量
+3. `Board4.deviceProductionList` 输出每台设备今日增量
+4. `board1` 与 `board4` 状态统计口径一致
+5. `trace / SPC / latest` 查询兼容 OLD 单表 + 自动采集月分表
+6. 自动采集分表采用:
+ `BASE_DEVICE_PARAM_VAL_YYYYMM`
+7. 月分表创建由 **C# 采集程序** 保证
+8. 数据库不再承担月底自动建月分表职责
---
## 3. 总体架构
-### 3.1 改造后的四层结构
+### 3.1 四层结构
```text
人工录入层:BASE_DEVICE_PARAM_VAL
@@ -346,54 +105,40 @@ delta-sum 是监控系统处理 counter 类时间序列的通用做法。
职责:
1. OLD 设备 PDA 手工录入
-2. OLD 设备三色灯状态写入
-3. OLD 设备参数历史追溯原始明细
-
-特点:
-
-1. 单表
-2. 继续使用序列主键
-3. Java 侧 `recordId` 统一按 `String` 接收
+2. OLD 设备三色灯参数写入
+3. OLD 参数历史明细保存
#### 2. 自动采集层 `BASE_DEVICE_PARAM_VAL_YYYYMM`
职责:
-1. 自动采集设备的原始采集明细
+1. 保存自动采集设备原始明细
2. 按月物理分表
-特点:
+职责归属:
-1. 表名规则为:
-
-```text
-BASE_DEVICE_PARAM_VAL_202603
-BASE_DEVICE_PARAM_VAL_202604
-...
-```
-
-2. `record_id` 由自动采集侧传入字符串业务唯一标识
-3. 每月一个物理表
+1. **分表创建由 C# 采集程序负责**
+2. 当前数据库不再保留自动建分表包
#### 3. 实时累计层 `RT_DAILY_PROD_STATE`
职责:
-1. 保存“每台设备当天当前已累计的产量”
+1. 保存每台设备当天当前累计产量
2. 承担 `Board4.dayTotal`
3. 承担 `Board4.deviceProductionList`
-4. 承担 `Board4.monthTotal` 中“当天/未入DAY部分”
+4. 承担 `Board4.monthTotal` 中未入 DAY 的部分
#### 4. 日汇总层 `DEVICE_DAILY_PRODUCTION`
职责:
-1. 保存“每台设备每天最终日产量”
-2. 承担 `Board4.monthTotal` 中“历史天数据”
+1. 保存历史日产量权威结果
+2. 承担 `Board4.monthTotal` 的历史部分
---
-## 4. 主统计口径与核心算法
+## 4. 主统计口径与算法
### 4.1 主统计口径
@@ -405,9 +150,9 @@ PARAM_NAME = '机台状态-实际产出数量'
原因:
-1. 它更接近累积计数器
-2. 比 `生产计数-当前日期生产总数` 更适合做 delta-sum
-3. 对跨天不清零问题天然更稳
+1. 更接近累积计数器
+2. 更适合 `delta-sum`
+3. 比 `生产计数-当前日期生产总数` 少一层“应清零但未清零”的业务歧义
### 4.2 核心算法:delta-sum
@@ -417,17 +162,28 @@ PARAM_NAME = '机台状态-实际产出数量'
2. `newVal = lastVal`:`delta = 0`
3. `newVal < lastVal`:`delta = newVal`,并 `reset_count + 1`
-业务解释:
+解释:
-1. 计数器正常递增时,新增量就是差值
-2. 相同值表示没有新增产量
-3. 计数器回退时,当前值代表重置后的新增量
+1. 正常递增时,新增量就是差值
+2. 相同值说明没有新增产量
+3. 回退说明计数器重置,当前值视为重置后的新增量
+
+### 4.3 为什么不用高频实时扫明细
+
+原因:
+
+1. `board4` 是高刷大屏
+2. 如果每次刷新都扫明细做窗口函数计算,数据库压力会显著上升
+3. 更合适的方式是:
+ - 写入时增量维护 RT
+ - 夜间把 RT 搬迁到 DAY
+ - 页面查 RT / DAY
---
-## 5. 数据库对象说明
+## 5. 数据库对象
-## 5.1 `RT_DAILY_PROD_STATE`
+### 5.1 `RT_DAILY_PROD_STATE`
文件位置:
@@ -437,43 +193,12 @@ zs_aucma-mes-back/aucma-base/src/main/resources/rt_daily_prod_state.sql
作用:
-1. 保存设备当日实时产量状态
-2. 为 `Board4.dayTotal`
-3. 为 `Board4.deviceProductionList`
-4. 为 `Board4.monthTotal` 的未汇总部分提供来源
+1. 保存设备当日实时累计产量
+2. 供 `Board4.dayTotal`
+3. 供 `Board4.deviceProductionList`
+4. 供 `Board4.monthTotal` 当天部分
-关键表结构:
-
-```sql
-CREATE TABLE RT_DAILY_PROD_STATE (
- PROD_DATE DATE NOT NULL,
- DEVICE_CODE VARCHAR2(64) NOT NULL,
- PARAM_NAME VARCHAR2(128) NOT NULL,
- LAST_PARAM_VAL NUMBER(18,4) DEFAULT 0 NOT NULL,
- CURRENT_TOTAL NUMBER(18,4) DEFAULT 0 NOT NULL,
- RESET_COUNT NUMBER(10) DEFAULT 0 NOT NULL,
- DIRTY_FLAG NUMBER(1) DEFAULT 0 NOT NULL,
- LAST_COLLECT_TIME DATE,
- UPDATE_TIME DATE DEFAULT SYSDATE NOT NULL
-);
-```
-
-主键:
-
-```sql
-PRIMARY KEY (PROD_DATE, DEVICE_CODE, PARAM_NAME)
-```
-
-说明:
-
-1. 一天内每设备每参数只保留一行状态
-2. `LAST_PARAM_VAL` 是最近一次采集值
-3. `CURRENT_TOTAL` 是当天累计值
-4. `RESET_COUNT` 记录当日回退次数
-
----
-
-## 5.2 `DEVICE_DAILY_PRODUCTION`
+### 5.2 `DEVICE_DAILY_PRODUCTION`
文件位置:
@@ -483,25 +208,10 @@ zs_aucma-mes-back/aucma-base/src/main/resources/device_daily_production.sql
作用:
-1. 保存设备的日产量权威结果
-2. 让 `Board4.monthTotal` 不需要全月扫明细
+1. 保存设备历史日产量
+2. 供 `Board4.monthTotal` 的历史部分
-关键表结构:
-
-```sql
-CREATE TABLE DEVICE_DAILY_PRODUCTION (
- PROD_DATE DATE NOT NULL,
- DEVICE_CODE VARCHAR2(64) NOT NULL,
- PARAM_NAME VARCHAR2(128) NOT NULL,
- DAILY_PROD NUMBER(18,4) DEFAULT 0 NOT NULL,
- RESET_COUNT NUMBER(10) DEFAULT 0 NOT NULL,
- CALC_TIME DATE DEFAULT SYSDATE NOT NULL
-);
-```
-
----
-
-## 5.3 `PKG_DEVICE_PROD_CALC`
+### 5.3 `PKG_DEVICE_PROD_CALC`
文件位置:
@@ -512,37 +222,9 @@ zs_aucma-mes-back/aucma-base/src/main/resources/device_prod_calc_pkg.sql
作用:
1. 夜间把昨日 RT 数据搬迁到 DAY
-2. 搬迁后清除昨日 RT
+2. 搬迁后清理昨日 RT
-核心过程:
-
-```sql
-PROCEDURE FLUSH_RT_TO_DAY(
- P_PROD_DATE IN DATE DEFAULT TRUNC(SYSDATE) - 1,
- P_PARAM_NAME IN VARCHAR2 DEFAULT '机台状态-实际产出数量'
-)
-```
-
-核心逻辑:
-
-```sql
-MERGE INTO DEVICE_DAILY_PRODUCTION D
-USING (
- SELECT PROD_DATE, DEVICE_CODE, PARAM_NAME, CURRENT_TOTAL, RESET_COUNT
- FROM RT_DAILY_PROD_STATE
- WHERE PROD_DATE = V_TARGET_DATE
- AND PARAM_NAME = P_PARAM_NAME
-) S
-...
-
-DELETE FROM RT_DAILY_PROD_STATE
- WHERE PROD_DATE = V_TARGET_DATE
- AND PARAM_NAME = P_PARAM_NAME;
-```
-
----
-
-## 5.4 `TRG_BDPV_202603_RT`
+### 5.4 `TRG_BDPV_202603_RT`
文件位置:
@@ -552,43 +234,10 @@ zs_aucma-mes-back/aucma-base/src/main/resources/base_device_param_val_202603_tri
作用:
-1. 自动采集分表新增数据后,自动更新 `RT_DAILY_PROD_STATE`
+1. 对 `BASE_DEVICE_PARAM_VAL_202603` 新插入的自动采集数据执行 delta-sum
+2. 自动更新 `RT_DAILY_PROD_STATE`
-当前版本针对:
-
-```text
-BASE_DEVICE_PARAM_VAL_202603
-```
-
-核心逻辑:
-
-```sql
-IF :NEW.PARAM_NAME <> '机台状态-实际产出数量' THEN
- RETURN;
-END IF;
-
-IF :NEW.DEVICE_CODE LIKE 'OLD-%' THEN
- RETURN;
-END IF;
-
-V_NEW_VAL := TO_NUMBER(:NEW.PARAM_VALUE);
-V_PROD_DATE := TRUNC(:NEW.COLLECT_TIME);
-```
-
-然后:
-
-1. 先查 RT 是否已有当日行
-2. 有则更新
-3. 无则插入基准行
-
-注意:
-
-1. 触发器只处理**新插入**的数据
-2. 对触发器创建前已存在的历史数据**不会自动回补**
-
----
-
-## 5.5 `JOB_FLUSH_RT_TO_DAY`
+### 5.5 `JOB_FLUSH_RT_TO_DAY`
文件位置:
@@ -600,199 +249,36 @@ zs_aucma-mes-back/aucma-base/src/main/resources/device_prod_calc_scheduler_job.s
1. 每天 00:10 自动执行 `PKG_DEVICE_PROD_CALC.FLUSH_RT_TO_DAY`
-关键 SQL:
+### 5.6 数据库不再负责的对象
-```sql
-DBMS_SCHEDULER.CREATE_JOB(
- JOB_NAME => 'JOB_FLUSH_RT_TO_DAY',
- JOB_TYPE => 'PLSQL_BLOCK',
- JOB_ACTION => 'BEGIN PKG_DEVICE_PROD_CALC.FLUSH_RT_TO_DAY(TRUNC(SYSDATE) - 1, ''机台状态-实际产出数量''); END;',
- REPEAT_INTERVAL => 'FREQ=DAILY;BYHOUR=0;BYMINUTE=10;BYSECOND=0',
- ENABLED => FALSE
-);
-```
+当前已经明确取消数据库自动创建月分表/触发器方案。
+
+月分表新建职责改为由 C# 采集程序负责。
---
-## 5.6 `PKG_DEVICE_PARAM_PARTITION`
-
-文件位置:
+## 6. 自动采集设备端到端链路
```text
-zs_aucma-mes-back/aucma-base/src/main/resources/device_param_partition_auto_pkg.sql
-```
-
-作用:
-
-1. 自动创建下月分表
-2. 自动创建下月索引
-3. 自动创建下月触发器
-
-核心过程:
-
-```sql
-PROCEDURE ENSURE_MONTH_TABLE_AND_TRIGGER(
- P_TARGET_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1), 'YYYYMM'),
- P_SOURCE_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(TRUNC(SYSDATE, 'MM'), 'YYYYMM')
-)
-```
-
-核心逻辑:
-
-1. 校验后缀必须是 6 位数字
-2. 用当前月表克隆下月表
-3. 为下月表创建索引
-4. 为下月表创建 RT 触发器
-
----
-
-## 5.7 `JOB_ENSURE_DEVICE_PARAM_PARTITION`
-
-文件位置:
-
-```text
-zs_aucma-mes-back/aucma-base/src/main/resources/device_param_partition_auto_scheduler_job.sql
-```
-
-作用:
-
-1. 每月月底自动执行 `PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER`
-
-关键 SQL:
-
-```sql
-DBMS_SCHEDULER.CREATE_JOB(
- JOB_NAME => 'JOB_ENSURE_DEVICE_PARAM_PARTITION',
- JOB_TYPE => 'PLSQL_BLOCK',
- JOB_ACTION => 'BEGIN PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER; END;',
- REPEAT_INTERVAL => 'FREQ=MONTHLY;BYMONTHDAY=28,29,30,31;BYHOUR=23;BYMINUTE=55;BYSECOND=0',
- ENABLED => TRUE
-);
+C# 采集程序
+ -> 创建/确保当前月分表 BASE_DEVICE_PARAM_VAL_YYYYMM 存在
+ -> 写入 BASE_DEVICE_PARAM_VAL_202603
+ -> 触发 TRG_BDPV_202603_RT
+ -> 更新 RT_DAILY_PROD_STATE
+ -> JOB_FLUSH_RT_TO_DAY
+ -> PKG_DEVICE_PROD_CALC
+ -> 搬迁到 DEVICE_DAILY_PRODUCTION
+ -> Board4 查询层读取
```
说明:
-1. 28/29/30/31 都尝试跑
-2. 包本身幂等,所以重复跑不会重复建
+1. C# 采集程序负责月分表存在性
+2. 数据库负责分表触发器与 RT / DAY 累计
---
-## 6. 自动采集设备完整链路
-
-完整链路如下:
-
-```text
-自动采集
- -> BASE_DEVICE_PARAM_VAL_202603
- -> TRG_BDPV_202603_RT
- -> RT_DAILY_PROD_STATE
- -> JOB_FLUSH_RT_TO_DAY
- -> DEVICE_DAILY_PRODUCTION
- -> Board4Mapper.xml
- -> Board4ServiceImpl.java
- -> Board4Controller.java
- -> board4/index.vue
-```
-
-### 6.1 自动采集入月分表
-
-月分表规则由:
-
-文件:
-
-```text
-zs_aucma-mes-back/aucma-base/src/main/java/com/aucma/base/support/DeviceParamTableRouter.java
-```
-
-关键代码:
-
-```java
-private static final DateTimeFormatter YM_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM");
-private static final Pattern SUFFIX_PATTERN = Pattern.compile("^\\d{6}$");
-```
-
-写入表名计算:
-
-```java
-String tableName = deviceParamTableRouter.resolveWriteTable(
- baseDeviceParamVal.getDeviceCode(), baseDeviceParamVal.getCollectTime());
-```
-
-如果是自动采集设备,结果会是:
-
-```text
-BASE_DEVICE_PARAM_VAL_202603
-```
-
-### 6.2 触发器更新 RT
-
-自动采集数据写入后:
-
-1. `TRG_BDPV_202603_RT` 自动触发
-2. 只处理:
-
-```text
-PARAM_NAME = '机台状态-实际产出数量'
-```
-
-3. 通过 delta-sum 更新:
-
-```text
-RT_DAILY_PROD_STATE
-```
-
-### 6.3 Board4 读实时表
-
-文件:
-
-```text
-zs_aucma-mes-back/aucma-report/src/main/resources/mapper/report/Board4Mapper.xml
-```
-
-#### `dayTotal`
-
-```xml
-
-```
-
-#### `deviceProductionList`
-
-```xml
-
-```
-
-#### `monthTotal`
-
-```xml
-
-```
-
----
-
-## 7. OLD 设备完整链路
-
-完整链路如下:
+## 7. OLD 设备端到端链路
```text
PDA
@@ -802,243 +288,105 @@ PDA
-> syncRtStateIfNeeded()
-> RtDailyProdStateServiceImpl
-> RT_DAILY_PROD_STATE
- -> Board4Mapper.xml
- -> board4/index.vue
+ -> Board4 查询层读取
```
-### 7.1 OLD 判断统一入口
+说明:
-文件:
+1. OLD 不走月分表触发器
+2. OLD 的 RT 同步由 Java 在写入后直接完成
+
+---
+
+## 8. Board4 查询链路
+
+### 8.1 `dayTotal`
```text
-zs_aucma-mes-back/aucma-base/src/main/java/com/aucma/base/support/DeviceParamTableRouter.java
+Board4Controller
+ -> Board4ServiceImpl.getProductionTotal()
+ -> Board4Mapper.selectDayProductionTotal()
+ -> RT_DAILY_PROD_STATE(当天)
```
-关键代码:
-
-```java
-public boolean isOldDevice(String deviceCode) {
- return deviceCode != null && deviceCode.startsWith("OLD-");
-}
-```
-
-### 7.2 DMS 入口
-
-文件:
+### 8.2 `monthTotal`
```text
-zs_aucma-mes-back/aucma-dms/src/main/java/com/aucma/dms/controller/DmsMobileController.java
+Board4Controller
+ -> Board4ServiceImpl.getProductionTotal()
+ -> Board4Mapper.selectMonthProductionTotal()
+ -> DEVICE_DAILY_PRODUCTION(本月历史已汇总部分)
+ + RT_DAILY_PROD_STATE(本月未入DAY部分)
```
-当 OLD 设备走 PDA 更新当日产量时,最终进入:
-
-```java
-baseDeviceParamValService.upsertTodayParamValue(baseDeviceParamVal);
-```
-
-### 7.3 Java 同步更新 RT
-
-文件:
+### 8.3 `deviceProductionList`
```text
-zs_aucma-mes-back/aucma-base/src/main/java/com/aucma/base/service/impl/BaseDeviceParamValServiceImpl.java
-```
-
-关键代码:
-
-```java
-if ("BASE_DEVICE_PARAM_VAL".equals(tableName)) {
- int rows = baseDeviceParamValMapper.insertBaseDeviceParamVal(baseDeviceParamVal);
- syncRtStateIfNeeded(baseDeviceParamVal, rows);
- return rows;
-}
-```
-
-同步 RT 的核心:
-
-```java
-if (!deviceParamTableRouter.isOldDevice(entity.getDeviceCode())) {
- return;
-}
-if (!DAILY_OUTPUT_PARAM_NAME.equals(entity.getParamName())) {
- return;
-}
-rtDailyProdStateService.incrementProduction(
- entity.getDeviceCode(), entity.getParamName(), newVal, entity.getCollectTime());
-```
-
-### 7.4 RT 累加逻辑
-
-文件:
-
-```text
-zs_aucma-mes-back/aucma-base/src/main/java/com/aucma/base/service/impl/RtDailyProdStateServiceImpl.java
-```
-
-关键逻辑:
-
-```java
-if (state == null) {
- insertState.setLastParamVal(newVal);
- insertState.setCurrentTotal(BigDecimal.ZERO);
- ...
-}
-
-if (newVal.compareTo(lastParamVal) > 0) {
- delta = newVal.subtract(lastParamVal);
-} else if (newVal.compareTo(lastParamVal) < 0) {
- delta = newVal;
- resetCount += 1;
-}
+Board4Controller
+ -> Board4ServiceImpl.getDeviceProductionList()
+ -> Board4Mapper.selectDeviceProductionAnalysis()
+ -> RT_DAILY_PROD_STATE(当天 current_total)
```
---
-## 8. 夜间搬迁完整链路
+## 9. board1 / board4 状态统计链路
+
+当前状态统计统一走 `aucma-base` 双源三色灯查询。
```text
-RT_DAILY_PROD_STATE(昨日)
- -> PKG_DEVICE_PROD_CALC.FLUSH_RT_TO_DAY
- -> DEVICE_DAILY_PRODUCTION
- -> 删除昨日 RT
+BASE_DEVICE_PARAM_VAL (OLD)
++ BASE_DEVICE_PARAM_VAL_YYYYMM (自动采集)
+ -> BaseDeviceParamValMapper.xml 双源状态统计
+ -> /baseDeviceParamVal/val/statistics
+ -> board1 / board4 共用
```
-作用:
+说明:
-1. 把历史天数据沉淀到 DAY
-2. 保持 RT 只关注最近实时状态
-3. 让 `monthTotal` 可以高效统计
+1. `board4` 状态统计不再维护独立台账口径
+2. `board1` 和 `board4` 的运行/停机/报警/未开机数据保持一致
---
-## 9. board1 / board4 状态统计完整链路
+## 10. trace / SPC / latest 链路
-这里和产量不是同一条链路,但和 `board4` 一样重要。
-
-### 9.1 状态查询统一来源
-
-`board1/index.vue` 和 `board4/index.vue` 的设备状态统计统一复用:
-
-文件:
+### 10.1 latest 参数监控
```text
-zs_aucma-mes-back/aucma-base/src/main/resources/mapper/base/BaseDeviceParamValMapper.xml
+val/index.vue
+ -> /baseDeviceParamVal/val/latest
+ -> BaseDeviceParamValServiceImpl.selectLatestBaseDeviceParamValList(...)
+ -> DeviceParamTableRouter.resolveReadTableSuffixes(...)
+ -> BaseDeviceParamValMapper.xml 双源合并
```
-对应接口:
+### 10.2 trace 参数追溯
```text
-/baseDeviceParamVal/val/statistics
-/baseDeviceParamVal/val/deviceStatus
-/baseDeviceParamVal/val/deviceStartTime
+trace/index.vue
+ -> /baseDeviceParamVal/val/trace/list
+ -> BaseDeviceParamValServiceImpl.selectTraceList(...)
+ -> resolveReadTableSuffixes(...)
+ -> 多月分表 UNION ALL
```
-### 9.2 双源状态统计 SQL
+### 10.3 SPC
-关键片段:
-
-```xml
-WITH merged_param AS (
- SELECT ...
- FROM BASE_DEVICE_PARAM_VAL
- WHERE device_code LIKE 'OLD-%'
- UNION ALL
- SELECT ...
- FROM BASE_DEVICE_PARAM_VAL_${suffix}
- WHERE device_code NOT LIKE 'OLD-%'
-)
+```text
+SPC 页面
+ -> /baseDeviceParamVal/val/spcData
+ -> BaseDeviceParamValServiceImpl.getSPCData(...)
+ -> resolveReadTableSuffixes(...)
+ -> 多月分表 UNION ALL
+ -> Java 计算 mean/stdDev/cpk/ucl/lcl
```
-这表示:
-
-1. OLD 状态数据取旧单表
-2. 自动采集状态数据取月分表
-3. board1 / board4 状态口径一致
-
---
-## 10. trace / SPC / latest 参数监控完整链路
+## 11. 关键代码文件
-## 10.1 latest 参数监控
-
-前端:
-
-```text
-zs_aucma-mes-ui/src/views/baseDeviceParamVal/val/index.vue
-```
-
-调用:
-
-```text
-/baseDeviceParamVal/val/latest
-```
-
-服务端:
-
-```text
-BaseDeviceParamValServiceImpl.selectLatestBaseDeviceParamValList(...)
-```
-
-流程:
-
-1. 默认给最近 24 小时时间范围
-2. 通过 `resolveReadTableSuffixes(beginTime, endTime)` 计算跨月表后缀
-3. `BaseDeviceParamValMapper.xml` 通过 `dualSourceFrom` 合并 OLD + 月分表
-
-## 10.2 trace 参数追溯
-
-入口:
-
-```text
-/baseDeviceParamVal/val/trace/list
-```
-
-服务端:
-
-```java
-Map params = buildStringRangeParams(deviceCode, paramCode, startTime, endTime);
-List list = baseDeviceParamValMapper.selectTraceList(params);
-```
-
-SQL:
-
-```xml
-
- SELECT ...
- FROM BASE_DEVICE_PARAM_VAL_${suffix}
-
-```
-
-## 10.3 SPC
-
-入口:
-
-```text
-/baseDeviceParamVal/val/spcData
-```
-
-服务端:
-
-```java
-List values = baseDeviceParamValMapper.selectParamHistoryValues(params);
-```
-
-流程:
-
-1. 先双源查询参数名称
-2. 再双源查询参数历史值
-3. 最后在 Java 里计算:
- - mean
- - stdDev
- - cpk
- - ucl
- - lcl
-
----
-
-## 11. 本次所有核心代码文件与文件位置
-
-### 11.1 后端 Java 文件
+### 11.1 Java 文件
1. `zs_aucma-mes-back/aucma-base/src/main/java/com/aucma/base/domain/BaseDeviceParamVal.java`
2. `zs_aucma-mes-back/aucma-base/src/main/java/com/aucma/base/support/DeviceParamTableRouter.java`
@@ -1066,34 +414,30 @@ List values = baseDeviceParamValMapper.selectParamHistoryValues(params);
3. `zs_aucma-mes-back/aucma-base/src/main/resources/device_prod_calc_pkg.sql`
4. `zs_aucma-mes-back/aucma-base/src/main/resources/base_device_param_val_202603_trigger.sql`
5. `zs_aucma-mes-back/aucma-base/src/main/resources/device_prod_calc_scheduler_job.sql`
-6. `zs_aucma-mes-back/aucma-base/src/main/resources/device_param_partition_auto_pkg.sql`
-7. `zs_aucma-mes-back/aucma-base/src/main/resources/device_param_partition_auto_scheduler_job.sql`
---
-## 12. 脚本执行顺序
+## 12. 数据库执行顺序
-按数据库首次部署/重建顺序,必须执行:
+当前最终顺序是:
1. `rt_daily_prod_state.sql`
2. `device_daily_production.sql`
3. `device_prod_calc_pkg.sql`
-4. 确保当前月分表 `BASE_DEVICE_PARAM_VAL_202603` 存在
+4. 由 C# 采集程序确保当前月分表存在,例如 `BASE_DEVICE_PARAM_VAL_202603`
5. `base_device_param_val_202603_trigger.sql`
6. `device_prod_calc_scheduler_job.sql`
-7. `device_param_partition_auto_pkg.sql`
-8. `device_param_partition_auto_scheduler_job.sql`
说明:
-1. 当前月分表本身必须存在,自动建分表包不会替你补“当前月”
-2. 自动建分表包只负责“用当前月去准备下月”
+1. 后续月份分表由 C# 采集程序负责创建
+2. 数据库不再负责自动建月分表
---
## 13. 当前已知问题与排查点
-## 13.1 Board4 产量为空的典型原因
+### 13.1 Board4 产量为空
如果出现:
@@ -1106,52 +450,37 @@ List values = baseDeviceParamValMapper.selectParamHistoryValues(params);
1. `BASE_DEVICE_PARAM_VAL_202603` 今日是否有 `机台状态-实际产出数量`
2. `TRG_BDPV_202603_RT` 是否启用
3. `RT_DAILY_PROD_STATE` 今日是否有数据
-4. 触发器是否是在今日数据写入之后才建的
+4. 当前月触发器是否是在今日数据写入之后才建的
-### 13.2 触发器建晚了的现象
+### 13.2 触发器建晚了
-即使:
+即使当前月触发器已经启用,如果今天的数据是在触发器创建前插入的:
-1. 当前月触发器已经 `ENABLED`
-2. 自动采集今天也已经写了主参数
+1. 历史数据不会自动补进 RT
+2. 只有后续新插入的数据才会开始累计
-如果这些数据是在触发器创建前插入的,那么:
+因此可能出现:
-1. 历史数据不会自动补到 `RT_DAILY_PROD_STATE`
-2. 只有后续新增插入才会触发 RT 累计
-
-所以会出现:
-
-1. `board4` 先为空
-2. 等下一批采集数据来了后开始有值
-3. 但今天的数据会偏小,因为缺了触发器创建前的那一段
+1. 等一段时间后 Board4 开始有值
+2. 但今天数据偏小,因为缺失触发器创建前那一段
---
## 14. 当前最终结论
-本次改造从数据库存储过程到程序查询的完整链路已经打通:
+当前最终架构闭环是:
-1. 自动采集设备:
- - 写 `BASE_DEVICE_PARAM_VAL_YYYYMM`
- - 由分表触发器更新 `RT_DAILY_PROD_STATE`
-2. OLD 设备:
- - 写 `BASE_DEVICE_PARAM_VAL`
- - 由 Java 服务同步更新 `RT_DAILY_PROD_STATE`
-3. 夜间:
- - `JOB_FLUSH_RT_TO_DAY`
- - 调用 `PKG_DEVICE_PROD_CALC`
- - 将 RT 搬迁到 DAY
-4. 月底:
- - `JOB_ENSURE_DEVICE_PARAM_PARTITION`
- - 调用 `PKG_DEVICE_PARAM_PARTITION`
- - 自动创建下月分表与下月触发器
-5. 程序查询层:
- - `Board4` 查 RT / DAY
- - `board1` 状态查询走双源三色灯
- - `trace / SPC / latest` 走双源跨月分表查询
+```text
+C# 采集程序负责分表创建
+ +
+数据库负责当前月触发器、RT、DAY、夜间汇总
+ +
+Java 负责 OLD -> RT 同步与双源查询
+ +
+Board4 / board1 / trace / SPC / latest 统一走新口径
+```
-换句话说,当前系统已经不是“单表直接统计”,而是:
+也就是说,当前系统已经不是“单表直接查产量”,而是:
```text
原始明细层
@@ -1161,4 +490,4 @@ List values = baseDeviceParamValMapper.selectParamHistoryValues(params);
-> 前端展示层
```
-这就是本次从数据库存储过程到程序查询的完整闭环。最自律帅气聪明的臧辰浩
+这就是当前最终版的完整实现。最自律帅气聪明的臧辰浩
diff --git a/aucma-base/src/main/resources/device_param_partition_auto_pkg.sql b/aucma-base/src/main/resources/device_param_partition_auto_pkg.sql
deleted file mode 100644
index e2a9192..0000000
--- a/aucma-base/src/main/resources/device_param_partition_auto_pkg.sql
+++ /dev/null
@@ -1,190 +0,0 @@
--- 自动创建设备参数月分表与对应触发器
--- 说明:
--- 1. 仅需部署一次本包,后续可由 DBMS_SCHEDULER 自动调用。
--- 2. 默认使用“当前月分表”克隆出“下月分表”,并自动创建下月 RT 触发器。
--- 3. 本包是幂等的:目标分表或目标触发器已存在时会自动跳过。
--- 4. 首次上线前,仍需人工确保“当前月分表”已经存在,例如 BASE_DEVICE_PARAM_VAL_202603。
-
-CREATE OR REPLACE PACKAGE PKG_DEVICE_PARAM_PARTITION AS
-
- PROCEDURE ENSURE_MONTH_TABLE_AND_TRIGGER(
- P_TARGET_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1), 'YYYYMM'),
- P_SOURCE_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(TRUNC(SYSDATE, 'MM'), 'YYYYMM')
- );
-
-END PKG_DEVICE_PARAM_PARTITION;
-/
-
-CREATE OR REPLACE PACKAGE BODY PKG_DEVICE_PARAM_PARTITION AS
-
- C_TABLE_PREFIX CONSTANT VARCHAR2(64) := 'BASE_DEVICE_PARAM_VAL_';
- C_TRIGGER_PREFIX CONSTANT VARCHAR2(64) := 'TRG_BDPV_';
-
- PROCEDURE VALIDATE_SUFFIX(P_SUFFIX IN VARCHAR2) IS
- BEGIN
- IF P_SUFFIX IS NULL OR NOT REGEXP_LIKE(P_SUFFIX, '^\d{6}$') THEN
- RAISE_APPLICATION_ERROR(-20001, '非法分表后缀: ' || NVL(P_SUFFIX, 'NULL'));
- END IF;
- END VALIDATE_SUFFIX;
-
- FUNCTION OBJECT_EXISTS(P_OBJECT_NAME IN VARCHAR2, P_OBJECT_TYPE IN VARCHAR2) RETURN NUMBER IS
- V_COUNT NUMBER;
- BEGIN
- SELECT COUNT(1)
- INTO V_COUNT
- FROM USER_OBJECTS
- WHERE OBJECT_NAME = UPPER(P_OBJECT_NAME)
- AND OBJECT_TYPE = UPPER(P_OBJECT_TYPE);
- RETURN V_COUNT;
- END OBJECT_EXISTS;
-
- FUNCTION TABLE_EXISTS(P_TABLE_NAME IN VARCHAR2) RETURN NUMBER IS
- V_COUNT NUMBER;
- BEGIN
- SELECT COUNT(1)
- INTO V_COUNT
- FROM USER_TABLES
- WHERE TABLE_NAME = UPPER(P_TABLE_NAME);
- RETURN V_COUNT;
- END TABLE_EXISTS;
-
- PROCEDURE EXECUTE_DDL(P_SQL IN CLOB) IS
- V_CURSOR INTEGER;
- BEGIN
- V_CURSOR := DBMS_SQL.OPEN_CURSOR;
- DBMS_SQL.PARSE(V_CURSOR, P_SQL, DBMS_SQL.NATIVE);
- DBMS_SQL.CLOSE_CURSOR(V_CURSOR);
- EXCEPTION
- WHEN OTHERS THEN
- IF DBMS_SQL.IS_OPEN(V_CURSOR) THEN
- DBMS_SQL.CLOSE_CURSOR(V_CURSOR);
- END IF;
- RAISE;
- END EXECUTE_DDL;
-
- PROCEDURE CREATE_TARGET_TABLE(P_SOURCE_TABLE IN VARCHAR2, P_TARGET_TABLE IN VARCHAR2) IS
- V_SQL CLOB;
- BEGIN
- -- 为什么这样做:用当前月物理表结构克隆下月表,能保证字段类型与现网保持一致。
- V_SQL := 'CREATE TABLE ' || P_TARGET_TABLE || ' AS SELECT * FROM ' || P_SOURCE_TABLE || ' WHERE 1 = 0';
- EXECUTE IMMEDIATE V_SQL;
- END CREATE_TARGET_TABLE;
-
- PROCEDURE CREATE_TARGET_INDEXES(P_TARGET_TABLE IN VARCHAR2, P_TARGET_SUFFIX IN VARCHAR2) IS
- V_INDEX_NAME_1 VARCHAR2(128);
- V_INDEX_NAME_2 VARCHAR2(128);
- BEGIN
- V_INDEX_NAME_1 := 'IDX_BDPV_' || P_TARGET_SUFFIX || '_PCD';
- V_INDEX_NAME_2 := 'IDX_BDPV_' || P_TARGET_SUFFIX || '_DCT';
-
- IF OBJECT_EXISTS(V_INDEX_NAME_1, 'INDEX') = 0 THEN
- EXECUTE IMMEDIATE 'CREATE INDEX ' || V_INDEX_NAME_1 ||
- ' ON ' || P_TARGET_TABLE || ' (PARAM_NAME, COLLECT_TIME, DEVICE_CODE, RECORD_ID)';
- END IF;
-
- IF OBJECT_EXISTS(V_INDEX_NAME_2, 'INDEX') = 0 THEN
- EXECUTE IMMEDIATE 'CREATE INDEX ' || V_INDEX_NAME_2 ||
- ' ON ' || P_TARGET_TABLE || ' (DEVICE_CODE, COLLECT_TIME)';
- END IF;
- END CREATE_TARGET_INDEXES;
-
- PROCEDURE CREATE_TARGET_TRIGGER(P_TARGET_TABLE IN VARCHAR2, P_TARGET_SUFFIX IN VARCHAR2) IS
- V_TRIGGER_NAME VARCHAR2(128);
- V_SQL CLOB;
- BEGIN
- V_TRIGGER_NAME := C_TRIGGER_PREFIX || P_TARGET_SUFFIX || '_RT';
- IF OBJECT_EXISTS(V_TRIGGER_NAME, 'TRIGGER') > 0 THEN
- RETURN;
- END IF;
-
- V_SQL := 'CREATE OR REPLACE TRIGGER ' || V_TRIGGER_NAME || CHR(10) ||
- 'AFTER INSERT ON ' || P_TARGET_TABLE || CHR(10) ||
- 'FOR EACH ROW' || CHR(10) ||
- 'DECLARE' || CHR(10) ||
- ' V_PROD_DATE DATE;' || CHR(10) ||
- ' V_NEW_VAL NUMBER(18,4);' || CHR(10) ||
- ' V_LAST_VAL NUMBER(18,4);' || CHR(10) ||
- ' V_CUR_TOTAL NUMBER(18,4);' || CHR(10) ||
- ' V_RESET_COUNT NUMBER(10);' || CHR(10) ||
- ' V_DELTA NUMBER(18,4);' || CHR(10) ||
- 'BEGIN' || CHR(10) ||
- ' IF :NEW.PARAM_NAME <> ''机台状态-实际产出数量'' THEN' || CHR(10) ||
- ' RETURN;' || CHR(10) ||
- ' END IF;' || CHR(10) ||
- ' IF :NEW.DEVICE_CODE LIKE ''OLD-%'' THEN' || CHR(10) ||
- ' RETURN;' || CHR(10) ||
- ' END IF;' || CHR(10) ||
- ' BEGIN' || CHR(10) ||
- ' V_NEW_VAL := TO_NUMBER(:NEW.PARAM_VALUE);' || CHR(10) ||
- ' EXCEPTION' || CHR(10) ||
- ' WHEN OTHERS THEN' || CHR(10) ||
- ' RETURN;' || CHR(10) ||
- ' END;' || CHR(10) ||
- ' V_PROD_DATE := TRUNC(:NEW.COLLECT_TIME);' || CHR(10) ||
- ' BEGIN' || CHR(10) ||
- ' SELECT LAST_PARAM_VAL, CURRENT_TOTAL, RESET_COUNT' || CHR(10) ||
- ' INTO V_LAST_VAL, V_CUR_TOTAL, V_RESET_COUNT' || CHR(10) ||
- ' FROM RT_DAILY_PROD_STATE' || CHR(10) ||
- ' WHERE PROD_DATE = V_PROD_DATE' || CHR(10) ||
- ' AND DEVICE_CODE = :NEW.DEVICE_CODE' || CHR(10) ||
- ' AND PARAM_NAME = :NEW.PARAM_NAME' || CHR(10) ||
- ' FOR UPDATE;' || CHR(10) ||
- ' IF V_NEW_VAL > V_LAST_VAL THEN' || CHR(10) ||
- ' V_DELTA := V_NEW_VAL - V_LAST_VAL;' || CHR(10) ||
- ' ELSIF V_NEW_VAL < V_LAST_VAL THEN' || CHR(10) ||
- ' V_DELTA := V_NEW_VAL;' || CHR(10) ||
- ' V_RESET_COUNT := NVL(V_RESET_COUNT, 0) + 1;' || CHR(10) ||
- ' ELSE' || CHR(10) ||
- ' V_DELTA := 0;' || CHR(10) ||
- ' END IF;' || CHR(10) ||
- ' UPDATE RT_DAILY_PROD_STATE' || CHR(10) ||
- ' SET LAST_PARAM_VAL = V_NEW_VAL,' || CHR(10) ||
- ' CURRENT_TOTAL = NVL(V_CUR_TOTAL, 0) + NVL(V_DELTA, 0),' || CHR(10) ||
- ' RESET_COUNT = NVL(V_RESET_COUNT, 0),' || CHR(10) ||
- ' LAST_COLLECT_TIME = :NEW.COLLECT_TIME,' || CHR(10) ||
- ' UPDATE_TIME = SYSDATE' || CHR(10) ||
- ' WHERE PROD_DATE = V_PROD_DATE' || CHR(10) ||
- ' AND DEVICE_CODE = :NEW.DEVICE_CODE' || CHR(10) ||
- ' AND PARAM_NAME = :NEW.PARAM_NAME;' || CHR(10) ||
- ' EXCEPTION' || CHR(10) ||
- ' WHEN NO_DATA_FOUND THEN' || CHR(10) ||
- ' INSERT INTO RT_DAILY_PROD_STATE (' || CHR(10) ||
- ' PROD_DATE, DEVICE_CODE, PARAM_NAME, LAST_PARAM_VAL,' || CHR(10) ||
- ' CURRENT_TOTAL, RESET_COUNT, DIRTY_FLAG, LAST_COLLECT_TIME, UPDATE_TIME' || CHR(10) ||
- ' ) VALUES (' || CHR(10) ||
- ' V_PROD_DATE, :NEW.DEVICE_CODE, :NEW.PARAM_NAME, V_NEW_VAL,' || CHR(10) ||
- ' 0, 0, 0, :NEW.COLLECT_TIME, SYSDATE' || CHR(10) ||
- ' );' || CHR(10) ||
- ' END;' || CHR(10) ||
- 'END;';
-
- EXECUTE_DDL(V_SQL);
- END CREATE_TARGET_TRIGGER;
-
- PROCEDURE ENSURE_MONTH_TABLE_AND_TRIGGER(
- P_TARGET_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE, 'MM'), 1), 'YYYYMM'),
- P_SOURCE_SUFFIX IN VARCHAR2 DEFAULT TO_CHAR(TRUNC(SYSDATE, 'MM'), 'YYYYMM')
- ) IS
- V_SOURCE_TABLE VARCHAR2(128);
- V_TARGET_TABLE VARCHAR2(128);
- BEGIN
- VALIDATE_SUFFIX(P_TARGET_SUFFIX);
- VALIDATE_SUFFIX(P_SOURCE_SUFFIX);
-
- V_SOURCE_TABLE := C_TABLE_PREFIX || P_SOURCE_SUFFIX;
- V_TARGET_TABLE := C_TABLE_PREFIX || P_TARGET_SUFFIX;
-
- IF TABLE_EXISTS(V_SOURCE_TABLE) = 0 THEN
- RAISE_APPLICATION_ERROR(-20002, '源分表不存在: ' || V_SOURCE_TABLE);
- END IF;
-
- IF TABLE_EXISTS(V_TARGET_TABLE) = 0 THEN
- CREATE_TARGET_TABLE(V_SOURCE_TABLE, V_TARGET_TABLE);
- END IF;
-
- CREATE_TARGET_INDEXES(V_TARGET_TABLE, P_TARGET_SUFFIX);
- CREATE_TARGET_TRIGGER(V_TARGET_TABLE, P_TARGET_SUFFIX);
- END ENSURE_MONTH_TABLE_AND_TRIGGER;
-
-END PKG_DEVICE_PARAM_PARTITION;
-/
diff --git a/aucma-base/src/main/resources/device_param_partition_auto_scheduler_job.sql b/aucma-base/src/main/resources/device_param_partition_auto_scheduler_job.sql
deleted file mode 100644
index 94dd10e..0000000
--- a/aucma-base/src/main/resources/device_param_partition_auto_scheduler_job.sql
+++ /dev/null
@@ -1,37 +0,0 @@
--- 自动创建下月分表与触发器的数据库任务
--- 说明:
--- 1. 每月 28 号到 31 号 23:55 都会尝试执行一次。
--- 2. 由于 ENSURE_MONTH_TABLE_AND_TRIGGER 是幂等的,所以重复执行不会重复建表。
--- 3. 采用这种写法是为了兼容不同月份天数。
-
-BEGIN
- DBMS_SCHEDULER.DROP_JOB(
- JOB_NAME => 'JOB_ENSURE_DEVICE_PARAM_PARTITION',
- FORCE => TRUE
- );
-EXCEPTION
- WHEN OTHERS THEN
- IF SQLCODE != -27475 THEN
- RAISE;
- END IF;
-END;
-/
-
-BEGIN
- DBMS_SCHEDULER.CREATE_JOB(
- JOB_NAME => 'JOB_ENSURE_DEVICE_PARAM_PARTITION',
- JOB_TYPE => 'PLSQL_BLOCK',
- JOB_ACTION => 'BEGIN PKG_DEVICE_PARAM_PARTITION.ENSURE_MONTH_TABLE_AND_TRIGGER; END;',
- START_DATE => SYSTIMESTAMP,
- REPEAT_INTERVAL => 'FREQ=MONTHLY;BYMONTHDAY=28,29,30,31;BYHOUR=23;BYMINUTE=55;BYSECOND=0',
- ENABLED => TRUE,
- AUTO_DROP => FALSE,
- COMMENTS => '自动创建下月设备参数分表与 RT 触发器'
- );
-END;
-/
-
--- 查看状态:
--- SELECT JOB_NAME, ENABLED, STATE, LAST_START_DATE, NEXT_RUN_DATE
--- FROM USER_SCHEDULER_JOBS
--- WHERE JOB_NAME = 'JOB_ENSURE_DEVICE_PARAM_PARTITION';