# 写入路径：doUpdateWf / updateLastTime

## 改写范围

| HTTP | Controller 方法 | Service | 行为 |
|------|-----------------|---------|------|
| `POST .../doUpdateWf` | `update` | `doUpdateWf` | 选择性字段合并后 `replace`，标记 dirty |
| `POST .../doUpdateWfBySpotNo` | `updateLastTime` | `updateLastTime` | 更新 `LAST_TIME` + dirty，**不查 Oracle** |

`PUT /{id}` → `modify` **不改**。

## doUpdateWf

1. 校验 `pointNo`。
2. `get` Tarantool 现有行；若不存在且开启 fallback，可从 Oracle 加载后写入。
3. 将请求中非空字段合并到现有模型。
4. `replace` 全行（含 `_dirty=true`, `_updated_at=now`）。
5. 成功返回 `pointNo`；失败返回 `""`（避免 Controller `isEmpty()` NPE）。

## updateLastTime

1. 校验 `pointNo`。
2. 优先 `update` 字段 30=`LAST_TIME`(epoch ms)、66=`_dirty`、67=`_updated_at`。
3. 若行不存在：尝试 Oracle 读入灌一条再 update，或 fallback Oracle。
4. 成功返回 `pointNo`；失败返回 `""`。

## 编解码

- 由 `MeterWorkMonitorTupleCodec` 负责 `MeterWorkMonitor` ↔ `List<?>`（**67** 元组：65 业务 + `_dirty` + `_updated_at`）。
- 线上 format：`66=_dirty(boolean)`，`67=_updated_at(number)`。
- `LAST_TIME`：`Date.getTime()` ↔ Long。
- null 字符串在 tuple 中写空串或 box.NULL 策略：业务 string 字段用 `""` / number 用 `0` / date 用 `null`（box.NULL）。

## 失败策略

| 配置 | 行为 |
|------|------|
| `rtdb.monitor.fallback-oracle=false`（默认） | 仅写 Tarantool；失败打日志并返回 `""` |
| `rtdb.monitor.fallback-oracle=true` | Tarantool 失败时回退 `mapper.updateByPrimaryKeySelective` |

实现类：

- `com.steerinfo.meterwork.meterworkmonitor.rtdb.MeterWorkMonitorTupleCodec`
- `com.steerinfo.meterwork.meterworkmonitor.rtdb.MeterWorkMonitorRtdbRepository`
- `MeterWorkMonitorServiceImpl`（注入 Repository）
