feat: 前端充值信息展示 + 文档更新

- 表格新增充值电量列, 汇总卡新增充值统计

- README 更新精确计算说明与 XF_PRICE 配置

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-08-23 21:43:36 +08:00
parent 57b63316f3
commit f2cb2e796d
3 changed files with 24 additions and 4 deletions

View File

@@ -124,6 +124,7 @@ LZU_USERNAME=学号 LZU_PASSWORD=密码 ./electric-monitor
| `XF_YM_APPID` | `1810181825222034` | 一码通应用 ID | | `XF_YM_APPID` | `1810181825222034` | 一码通应用 ID |
| `LZU_AES_KEY` | — | AES 加密密钥16字节由环境注入**必填** | | `LZU_AES_KEY` | — | AES 加密密钥16字节由环境注入**必填** |
| `LZU_MD5_KEY` | — | MD5 签名密钥(由环境注入)**必填** | | `LZU_MD5_KEY` | — | MD5 签名密钥(由环境注入)**必填** |
| `XF_PRICE` | `0.535` | 电价(元/度),用于充值金额→电量换算 |
| `CACHE_FILE` | `data/electric_cache.json` | 会话缓存路径 | | `CACHE_FILE` | `data/electric_cache.json` | 会话缓存路径 |
| `HISTORY_FILE` | `data/electric_history.json` | 历史记录路径 | | `HISTORY_FILE` | `data/electric_history.json` | 历史记录路径 |
| `XF_CACHE_TTL` | `518400` (6天) | 会话缓存有效期(秒)| | `XF_CACHE_TTL` | `518400` (6天) | 会话缓存有效期(秒)|
@@ -196,6 +197,15 @@ GET /api/history?month=2026-08 # 指定月份 (YYYY-MM)
**跨月基线**:指定月份时,月内第一天的耗电以上月末记录为基线计算(跨月衔接正确)。 **跨月基线**:指定月份时,月内第一天的耗电以上月末记录为基线计算(跨月衔接正确)。
### 精确耗电计算(含充值修正)
- **电价**:默认 `0.535 元/度`(由余额接口反推,可用 `XF_PRICE` 覆盖)
- **充值记录接口**`POST /app/electric/queryISIMSRoomBuyRecord`areaId/buildingCode/floorCode/roomCode/mdtype=room
- **日耗电公式**`日耗电 = (昨天剩余 - 今天剩余) + 当天充值电量`(充值金额 × 1/电价)
- 充值当天不再归零:如充值 ¥30 → 56.07 度,该日耗电 = 剩余变化 + 56.07
- 月度汇总新增:`recharge_days`(充值次数)、`recharge_kwh`(充值电量)、`recharge_money`(充值金额)
- 充值记录 10 分钟缓存,接口失败时降级为无充值计算(余额回升归零)
## 定时任务说明 ## 定时任务说明
- **触发时机**:每天 00:00-00:05 窗口内Asia/Shanghai 时区) - **触发时机**:每天 00:00-00:05 窗口内Asia/Shanghai 时区)
@@ -303,3 +313,4 @@ A: 自动重试 5 次 + 次日补记。若持续失败,检查账号是否到
- **v1.0** (2026-08-23): Go 版完整实现Docker 部署,实时监控 + 每日耗电统计 - **v1.0** (2026-08-23): Go 版完整实现Docker 部署,实时监控 + 每日耗电统计
- **v1.0.1** (2026-08-23): 内存/稳定性优化——HTTP Server 超时、出站请求 context 超时、修复 debugMain 编译问题;补充内存分析文档 - **v1.0.1** (2026-08-23): 内存/稳定性优化——HTTP Server 超时、出站请求 context 超时、修复 debugMain 编译问题;补充内存分析文档
- **v1.1** (2026-08-23): **按月查询**——月份选择器导航、月度汇总(总耗电/日均/疑似充值/月初月末电量)、跨月耗电基线衔接 - **v1.1** (2026-08-23): **按月查询**——月份选择器导航、月度汇总(总耗电/日均/疑似充值/月初月末电量)、跨月耗电基线衔接
- **v1.2** (2026-08-23): **精确耗电计算**——接入充值记录接口 `queryISIMSRoomBuyRecord`、电价 0.535 元/度换算、充值当天耗电精确修正、月度汇总增加充值字段

View File

@@ -49,7 +49,7 @@ async function fetchCurrent() {
function renderSummary(s) { function renderSummary(s) {
if (!s || !s.record_count) { if (!s || !s.record_count) {
const empty = "--"; const empty = "--";
["sRecordCount", "sTotalUsage", "sAvgUsage", "sRecharge", "sStartSurplus", "sEndSurplus"].forEach((id) => { ["sRecordCount", "sTotalUsage", "sAvgUsage", "sRecharge", "sRechargeKwh", "sRechargeMoney", "sStartSurplus", "sEndSurplus"].forEach((id) => {
$(id).textContent = empty; $(id).textContent = empty;
}); });
return; return;
@@ -57,7 +57,9 @@ function renderSummary(s) {
$("sRecordCount").textContent = s.record_count + " 天"; $("sRecordCount").textContent = s.record_count + " 天";
$("sTotalUsage").textContent = s.total_usage != null ? s.total_usage.toFixed(2) : "--"; $("sTotalUsage").textContent = s.total_usage != null ? s.total_usage.toFixed(2) : "--";
$("sAvgUsage").textContent = s.avg_usage != null ? s.avg_usage.toFixed(2) : "--"; $("sAvgUsage").textContent = s.avg_usage != null ? s.avg_usage.toFixed(2) : "--";
$("sRecharge").textContent = s.recharge_days; $("sRecharge").textContent = s.recharge_days != null ? s.recharge_days + " 次" : "--";
$("sRechargeKwh").textContent = s.recharge_kwh != null ? s.recharge_kwh.toFixed(2) : "--";
$("sRechargeMoney").textContent = s.recharge_money != null ? s.recharge_money.toFixed(2) : "--";
$("sStartSurplus").textContent = s.start_surplus != null ? s.start_surplus.toFixed(2) : "--"; $("sStartSurplus").textContent = s.start_surplus != null ? s.start_surplus.toFixed(2) : "--";
$("sEndSurplus").textContent = s.end_surplus != null ? s.end_surplus.toFixed(2) : "--"; $("sEndSurplus").textContent = s.end_surplus != null ? s.end_surplus.toFixed(2) : "--";
} }
@@ -127,7 +129,7 @@ function renderChart(daily) {
function renderHistory(daily) { function renderHistory(daily) {
const body = $("historyBody"); const body = $("historyBody");
if (!daily.length) { if (!daily.length) {
body.innerHTML = '<tr><td colspan="4" class="empty-td">暂无记录</td></tr>'; body.innerHTML = '<tr><td colspan="5" class="empty-td">暂无记录</td></tr>';
return; return;
} }
// daily 是升序,表格显示降序(新→旧) // daily 是升序,表格显示降序(新→旧)
@@ -135,10 +137,14 @@ function renderHistory(daily) {
body.innerHTML = rows body.innerHTML = rows
.map((d) => { .map((d) => {
const usageTxt = d.usage != null && d.usage >= 0 ? d.usage.toFixed(2) : "--"; const usageTxt = d.usage != null && d.usage >= 0 ? d.usage.toFixed(2) : "--";
const rechargeTxt = d.recharge != null && d.recharge > 0
? `<span class="tag auto">+${d.recharge.toFixed(2)}</span>`
: "--";
return `<tr> return `<tr>
<td>${formatDateStr(d.date)}</td> <td>${formatDateStr(d.date)}</td>
<td>${d.surplus.toFixed(2)}</td> <td>${d.surplus.toFixed(2)}</td>
<td>${usageTxt}</td> <td>${usageTxt}</td>
<td>${rechargeTxt}</td>
<td><span class="tag auto">自动</span></td> <td><span class="tag auto">自动</span></td>
</tr>`; </tr>`;
}) })

View File

@@ -42,7 +42,9 @@
<div class="summary-item"><div class="summary-value" id="sRecordCount">--</div><div class="summary-label">记录天数</div></div> <div class="summary-item"><div class="summary-value" id="sRecordCount">--</div><div class="summary-label">记录天数</div></div>
<div class="summary-item"><div class="summary-value" id="sTotalUsage">--</div><div class="summary-label">月总耗电(度)</div></div> <div class="summary-item"><div class="summary-value" id="sTotalUsage">--</div><div class="summary-label">月总耗电(度)</div></div>
<div class="summary-item"><div class="summary-value" id="sAvgUsage">--</div><div class="summary-label">日均耗电(度)</div></div> <div class="summary-item"><div class="summary-value" id="sAvgUsage">--</div><div class="summary-label">日均耗电(度)</div></div>
<div class="summary-item"><div class="summary-value" id="sRecharge">--</div><div class="summary-label">疑似充值()</div></div> <div class="summary-item"><div class="summary-value" id="sRecharge">--</div><div class="summary-label">充值()</div></div>
<div class="summary-item"><div class="summary-value" id="sRechargeKwh">--</div><div class="summary-label">充值电量(度)</div></div>
<div class="summary-item"><div class="summary-value" id="sRechargeMoney">--</div><div class="summary-label">充值金额(元)</div></div>
<div class="summary-item"><div class="summary-value" id="sStartSurplus">--</div><div class="summary-label">月初电量(度)</div></div> <div class="summary-item"><div class="summary-value" id="sStartSurplus">--</div><div class="summary-label">月初电量(度)</div></div>
<div class="summary-item"><div class="summary-value" id="sEndSurplus">--</div><div class="summary-label">月末电量(度)</div></div> <div class="summary-item"><div class="summary-value" id="sEndSurplus">--</div><div class="summary-label">月末电量(度)</div></div>
</div> </div>
@@ -60,6 +62,7 @@
<th>日期</th> <th>日期</th>
<th>剩余电量(度)</th> <th>剩余电量(度)</th>
<th>日耗电(度)</th> <th>日耗电(度)</th>
<th>充值电量(度)</th>
<th>记录方式</th> <th>记录方式</th>
</tr> </tr>
</thead> </thead>