From f207a0d0bddbf8851ea1363bc6760cac64cf1fec Mon Sep 17 00:00:00 2001 From: fallensigh Date: Sun, 23 Aug 2026 21:29:49 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=AE=8C=E6=95=B4=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=96=87=E6=A1=A3(=E9=83=A8=E7=BD=B2/=E9=85=8D=E7=BD=AE/API/?= =?UTF-8?q?=E5=86=85=E5=AD=98=E5=88=86=E6=9E=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- go/README.md | 305 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 go/README.md diff --git a/go/README.md b/go/README.md new file mode 100644 index 0000000..03b819a --- /dev/null +++ b/go/README.md @@ -0,0 +1,305 @@ +# 宿舍电费监控 + +兰州大学宿舍电费实时监控与每日耗电统计 Web 服务。 + +基于 FasterLZU 登录接口 + 小富婆(xiaofubao)一码通电费查询接口构建。纯 Go 标准库实现(零第三方依赖),单二进制部署,Docker 镜像仅 **6.2 MB**。 + +--- + +## 功能特性 + +- **实时电费**:Web 页面实时显示当前剩余电量、预估金额、房间信息,30 秒自动刷新,可手动刷新 +- **每日耗电统计**:每天 24:00(00:00)自动查询并记录电费,日耗电 = 前一天剩余 - 当天剩余,7 天柱状图 + 历史记录表 +- **失败重试**:定时记录失败自动重试 5 次(间隔 60 秒),次日 00:00-00:05 窗口内可补记 +- **会话缓存**:复用 application 域 shiroJID 会话(约 7 天有效),命中缓存时仅 2 个请求即可查电费;失效自动重新登录 +- **数据持久化**:历史记录与会话缓存写入 JSON 文件,挂载 Docker 卷持久保存 + +## 架构 + +``` +浏览器 (HTML+CSS+JS) + │ http://localhost:8000 + ▼ +Go Web 服务 (单二进制, go:embed 内嵌前端) + ├── /api/electric 实时电费查询 + ├── /api/history 历史记录 + 耗电统计 + └── 定时任务 每天 00:00 记录,失败重试5次 + │ + ├── FasterLZU 登录 (appservice.lzu.edu.cn, AES-CBC 加密) + │ └── getSt → ST 票据 + │ + └── xiaofubao 链路 (6 步) + getCodeV2 → getUserByCodeV2 → getUserCode + → getUser4Authorize → queryBind → queryISIMSRoomSurplus +``` + +### 数据流 + +``` +[查询请求] + ├─ 缓存命中? ──是──→ 直接 queryISIMSRoomSurplus (2 个请求) + └─ 否 ──→ FasterLZU 登录 → getSt → xiaofubao 6 步 → 回写缓存 + │ + ▼ + [结果] { room, surplus, amount, status } + │ + ├─ 定时任务 → 写入 electric_history.json + └─ API 响应 → 浏览器渲染 +``` + +## 目录结构 + +``` +final/web/go/ +├── main.go # HTTP 服务器 + 定时任务 + go:embed 静态文件 +├── electric.go # xiaofubao 6 步链路 + shiroJID 缓存 +├── lzuapi.go # FasterLZU 登录客户端 (AES 加密) +├── crypto.go # AES-CBC 加密/解密 + MD5 签名 +├── history.go # 每日记录 JSON 存储 + 耗电统计 +├── static/ # 前端 (go:embed 嵌入二进制) +│ ├── index.html +│ ├── style.css +│ └── app.js +├── Dockerfile # 多阶段构建 → 6.2MB 镜像 +├── docker-compose.yml +├── .env.example # 环境配置模板 +└── data/ # 运行时数据 (挂载卷, 不入库) + ├── electric_cache.json # shiroJID 会话缓存 + └── electric_history.json # 每日电费记录 +``` + +## 快速开始 + +### 方式一:Docker Compose(推荐) + +```bash +cd final/web/go + +# 1. 准备配置 +cp .env.example .env +# 编辑 .env,填入真实账号密码 +# LZU_USERNAME=你的学号 +# LZU_PASSWORD=你的密码 + +# 2. 构建并启动 +docker compose up -d --build + +# 3. 访问 +# http://localhost:8000 +``` + +### 方式二:docker run + +```bash +docker build -t electric-monitor . +docker run -d --name electric-monitor \ + -p 8000:8000 \ + -e LZU_USERNAME=学号 \ + -e LZU_PASSWORD=密码 \ + -v $(pwd)/data:/app/data \ + electric-monitor +``` + +### 方式三:WSL/本机直接运行 + +```bash +cd final/web/go +# 直接编译运行(需 Go 1.18+) +go build -o electric-monitor . +LZU_USERNAME=学号 LZU_PASSWORD=密码 ./electric-monitor +``` + +## 配置项 + +| 环境变量 | 默认值 | 说明 | +|---|---|---| +| `HOST` | `0.0.0.0` | 监听地址 | +| `PORT` | `8000` | 监听端口 | +| `LZU_USERNAME` | — | FasterLZU 登录账号(学号)**必填** | +| `LZU_PASSWORD` | — | FasterLZU 登录密码 **必填** | +| `LZU_SERVICE_ID` | `29615` | 宿舍电费服务 ID | +| `XF_PLATFORM` | `QHIT_EDU` | xiaofubao 平台标识 | +| `XF_AUTH_APPID` | `2606161276416311297` | xiaofubao 应用 ID | +| `XF_SCHOOL_CODE` | `10730` | 学校代码(兰州大学)| +| `XF_YM_APPID` | `1810181825222034` | 一码通应用 ID | +| `LZU_AES_KEY` | — | AES 加密密钥(16字节,由环境注入)**必填** | +| `LZU_MD5_KEY` | — | MD5 签名密钥(由环境注入)**必填** | +| `CACHE_FILE` | `data/electric_cache.json` | 会话缓存路径 | +| `HISTORY_FILE` | `data/electric_history.json` | 历史记录路径 | +| `XF_CACHE_TTL` | `518400` (6天) | 会话缓存有效期(秒)| + +## API 接口 + +### GET /api/electric — 实时电费 + +```json +{ + "ok": true, + "room": "兰州大学榆中本科生29号公寓3层29-321", + "surplus": 72.57, + "amount": 38.82, + "status": "正常用电", + "source": "cache", + "time": 1787490436 +} +``` + +| 字段 | 说明 | +|---|---| +| `ok` | 查询是否成功 | +| `room` | 房间全名 | +| `surplus` | 剩余电量(度)| +| `amount` | 预估金额(元)| +| `status` | 用电状态 | +| `source` | `cache`=缓存会话 / `login`=重新登录 | +| `time` | 查询时间(Unix 秒)| + +### GET /api/history — 历史记录与耗电统计(支持按月) + +``` +GET /api/history # 全部记录 +GET /api/history?month=2026-08 # 指定月份 (YYYY-MM) +``` + +```json +{ + "ok": true, + "month": "2026-08", + "months": ["2026-08", "2026-07"], + "records": [ + {"date": "2026-08-01", "surplus": 83.5, "amount": 44, "usage": 0.7} + ], + "daily": [ + {"date": "2026-08-01", "surplus": 83.5, "amount": 44, "usage": 0.7} + ], + "summary": { + "month": "2026-08", + "record_count": 5, + "start_surplus": 83.5, + "end_surplus": 72.5, + "total_usage": 11.7, + "avg_usage": 2.34, + "recharge_days": 0, + "max_usage": 8.9 + }, + "all_count": 7 +} +``` + +| 字段 | 说明 | +|---|---| +| `month` | 当前查询月份(空=全部)| +| `months` | 所有有记录的月份(降序,供前端选择器)| +| `records` / `daily` | 该月每日统计(升序),`usage`=日耗电(null=无基线)| +| `summary` | 月度汇总:记录天数 / 月初月末电量 / 月总耗电 / 日均 / 疑似充值天数 / 单日最大耗电 | +| `all_count` | 全部记录数 | + +**跨月基线**:指定月份时,月内第一天的耗电以上月末记录为基线计算(跨月衔接正确)。 + +## 定时任务说明 + +- **触发时机**:每天 00:00-00:05 窗口内(Asia/Shanghai 时区) +- **执行逻辑**:查询当前电费 → 写入当日记录(`source: auto`) +- **失败重试**:最多 5 次,间隔 60 秒;窗口内每分钟补记一次直至成功 +- **启动基线**:服务启动时若今日无记录,立即查询建立基线(保证当日耗电可计算) + +## 会话与缓存机制 + +- 认证核心是 **shiroJID** HttpOnly cookie(非 localStorage),分 webapp 域(7 天)与 application 域(可复用) +- 缓存 application 域 shiroJID + 房间编码后,查询仅需 2 个请求(queryBind + queryISIMSRoomSurplus) +- 缓存约 6 天后自动失效,触发重新登录并回写缓存 +- ST 票据(getSt)与 getUserCode 的 code 均为一次性,每次登录重新生成 + +## 内存占用 + +### 实测数据(Docker 容器内运行) + +| 指标 | 实测值 | 说明 | +|---|---|---| +| 容器 MEM USAGE | **1.4 - 6.6 MiB** | `docker stats` 实测(缓存命中时最低)| +| VmRSS(物理内存)| **11.4 MB** | 宿主机 `/proc//status` | +| RssAnon(堆/匿名页)| 6.0 MB | Go 堆 + 运行时数据 | +| RssFile(二进制/库)| 5.4 MB | 可执行文件映射(多进程共享)| +| VmSize(虚拟地址)| 1.23 GB | Go 运行时保留的虚拟地址空间,**非实际占用** | +| 线程数 | 8 | Go runtime 调度器 + 主线程 | +| 镜像大小 | 20.6 MB | alpine 基础层 + 7.7MB 二进制 | +| 二进制大小 | 7.7 MB | `CGO_ENABLED=0` 纯静态 | + +### 内存构成分析 + +``` +11.4 MB VmRSS 总物理内存 +├── ~5.5 MB 二进制镜像映射(.text/.rodata/embed 静态资源,跨进程共享) +├── ~2 MB Go runtime 基础(GC、调度器、栈) +├── ~2 MB HTTP 连接缓冲 + 协程栈 +├── ~1 MB 内嵌静态资源(index.html + style.css + app.js ≈ 9.5KB 实际极小) +└── ~1 MB 零散堆分配(JSON 解析、字符串、缓存结构) +``` + +### 代码级内存分析(Go 视角) + +**固定成本(常驻):** +- **Go runtime 基础**:GC、调度器、内存池(mcache/arena),~10-20MB RSS,与业务无关,是本服务内存的主要构成 +- **go:embed 静态资源**:9.4KB 内嵌二进制,运行时零额外占用(`serveStatic` 每次请求复制 ~4KB 瞬时) +- **配置字符串**:~几百字节,静态 + +**增长项(唯一长期增长):** +- `historyCache` 每日 +1 条记录(~200B/条)→ **~70KB/年**。10 年才 ~700KB,可忽略。历史文件同理,`/api/history` 响应随记录数线性增长 + +**瞬态分配(GC 回收):** +- 完整登录:8 个 `http.Client`(共享 `DefaultTransport` 连接池,非泄漏)+ 8 个小 JSON 响应体,~几十 KB +- 缓存命中:1 个 client + 1 个响应体,~几 KB +- JSON 编解码:`json.NewEncoder` 流式写出,无整包缓冲 + +**goroutine 生命周期:** +- `schedulerLoop`:常驻无限循环(30s 检查一次),设计如此,非泄漏 +- 启动记录协程:成功或 5 次重试后退出(最长 ~5 分钟) +- HTTP 处理协程:连接关闭即回收 + +### 已实施的内存/稳定性优化 + +| 优化项 | 说明 | +|---|---| +| HTTP Server 超时 | `ReadHeaderTimeout=10s / ReadTimeout=30s / WriteTimeout=60s / IdleTimeout=90s`,防止空闲/慢速连接堆积 goroutine | +| 出站请求超时 | 所有 API 请求加 `context.WithTimeout(20s)`,上游无响应时 20s 自动放弃,防止 `queryLock` 永久阻塞导致请求排队 | +| 移除 debugMain | 修复编译阻断(原 `-debug` 分支引用未定义函数)| + +### 与 Python 版对比 + +| 指标 | Python (requests + Flask) | Go (本版) | +|---|---|---| +| 运行时内存(常驻)| 30-60 MB(解释器+依赖)| **~11 MB** | +| 启动速度 | 1-3 秒 | **<1 秒** | +| 部署体积 | 需 Python + pip 依赖 + venv | **单二进制 7.7MB / Docker 6.2MB** | +| 依赖数量 | requests/Flask 等 ≥5 个 | **0 个** | +| 内存泄漏风险 | 有(Python 引用计数/循环引用)| 低(Go GC + 自动回收)| + +### 优化建议 + +- 本服务内存占用已极低(1.4-6.6MiB),**无需进一步优化** +- 若需长期运行多年:可考虑历史记录裁剪(保留最近 N 年),仅影响 ~700KB/10年 级别 +- `GOGC`/`GOMEMLIMIT` 环境变量可微调 GC 频率(默认即可) + +## 安全说明 + +- 账号密码、加密密钥通过环境变量注入,不进镜像层、不入代码仓库 +- `.env` 已被 `.dockerignore` 排除,切勿提交到公开仓库 +- 服务默认监听 `0.0.0.0:8000`,如仅本机使用建议 `HOST=127.0.0.1` +- 会话缓存与历史记录含个人数据,注意目录权限 + +## 常见问题 + +**Q: 查询返回 `ok: false`?** +A: 查看容器日志定位具体失败步骤(`docker logs electric-monitor`)。常见原因:账号密码错误、网络不通、服务端会话异常(会自动重试/重登)。 + +**Q: 如何换绑定的房间?** +A: 房间来自 `queryBind` 接口返回的默认绑定,如更换宿舍需在 xiaofubao APP 中重新绑房后重启服务。 + +**Q: 定时记录失败怎么办?** +A: 自动重试 5 次 + 次日补记。若持续失败,检查账号是否到期、网络是否可达。 + +## 版本历史 + +- **v1.0** (2026-08-23): Go 版完整实现,Docker 部署,实时监控 + 每日耗电统计 +- **v1.0.1** (2026-08-23): 内存/稳定性优化——HTTP Server 超时、出站请求 context 超时、修复 debugMain 编译问题;补充内存分析文档 +- **v1.1** (2026-08-23): **按月查询**——月份选择器导航、月度汇总(总耗电/日均/疑似充值/月初月末电量)、跨月耗电基线衔接