Files
dorm-power-monitor/go/buy_records.go
fallensigh 57b63316f3 feat: 精确耗电计算(接入充值记录接口)
- 新增 queryISIMSRoomBuyRecord 充值记录获取 + 10分钟缓存

- 日耗电 = 剩余变化 + 当天充值电量(电价0.535元/度)

- 月度汇总增加充值次数/电量/金额

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-08-23 21:43:30 +08:00

119 lines
3.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"encoding/json"
"fmt"
"strings"
"time"
)
// buy_records.go - 充值/缴费记录获取与缓存
// 接口: POST /app/electric/queryISIMSRoomBuyRecord
// 返回每笔充值记录(金额/类型/时间),用于精确计算日耗电
// buyRecord 单笔充值记录
type buyRecord struct {
Date string `json:"date"` // 充值日期 YYYY-MM-DD
Money float64 `json:"money"` // 充值金额(元)
Kwh float64 `json:"kwh"` // 换算电量(度) = money / price
Type string `json:"type"` // 现金充值 / 下发补助
Detail string `json:"detail"` // 完整时间 YYYY-M-D H:M:S
}
// 充值记录缓存
var (
buyRecordsCache []buyRecord
buyRecordsTime int64
)
// queryBuyRecords 获取某房间的充值记录(需 application 域 shiroJID
func queryBuyRecords(shiroJID string, room *roomInfo) ([]buyRecord, error) {
price := unitPrice()
s := newXfSession(shiroJID)
body := fmt.Sprintf("areaId=%s&buildingCode=%s&floorCode=%s&roomCode=%s&mdtype=room&platform=%s",
room.AreaID, room.BuildingCode, room.FloorCode, room.RoomCode, xfPlatform)
b, _, err := s.postForm("https://application.xiaofubao.com/app/electric/queryISIMSRoomBuyRecord",
body, "https://application.xiaofubao.com/")
if err != nil {
return nil, err
}
var res struct {
StatusCode flexInt `json:"statusCode"`
Rows []struct {
DateTime string `json:"datetime"` // "2026-8-23 0:45:49"
BuyType string `json:"buytype"` // 现金充值 / 下发补助
BuyUsing string `json:"buyusingtpe"` // 照明用电
Money string `json:"money"` // "40.00"
} `json:"rows"`
}
if err := json.Unmarshal(b, &res); err != nil {
return nil, err
}
if res.StatusCode != 0 {
return nil, fmt.Errorf("queryISIMSRoomBuyRecord 失败: %s", string(b))
}
var records []buyRecord
for _, r := range res.Rows {
var money float64
fmt.Sscanf(r.Money, "%f", &money)
rec := buyRecord{
Date: datePart(r.DateTime),
Money: round2(money),
Kwh: round2(money / price),
Type: r.BuyType,
Detail: r.DateTime,
}
records = append(records, rec)
}
return records, nil
}
// getBuyRecordsCached 获取充值记录带缓存10 分钟有效)
func getBuyRecordsCached(shiroJID string, room *roomInfo) ([]buyRecord, error) {
if buyRecordsCache != nil && time.Now().Unix()-buyRecordsTime < 600 {
return buyRecordsCache, nil
}
recs, err := queryBuyRecords(shiroJID, room)
if err != nil {
return nil, err
}
buyRecordsCache = recs
buyRecordsTime = time.Now().Unix()
return recs, nil
}
// rechargeKwhByDate 生成 map[date] -> 当天充值电量(度)
func rechargeKwhByDate(records []buyRecord) map[string]float64 {
m := map[string]float64{}
for _, r := range records {
m[r.Date] = round2(m[r.Date] + r.Kwh)
}
return m
}
// unitPrice 电价(元/度),默认 0.535,可用 XF_PRICE 环境变量覆盖
func unitPrice() float64 {
if v := envOr("XF_PRICE", ""); v != "" {
var p float64
if _, err := fmt.Sscanf(v, "%f", &p); err == nil && p > 0 {
return p
}
}
return 0.535
}
// datePart 从 "2026-8-23 0:45:49" 提取 "2026-08-23"
func datePart(dt string) string {
parts := strings.SplitN(dt, " ", 2)
if len(parts) == 0 {
return dt
}
// 规范化月份/日补零
ymd := strings.Split(parts[0], "-")
if len(ymd) != 3 {
return parts[0]
}
return fmt.Sprintf("%s-%02s-%02s", ymd[0], ymd[1], ymd[2])
}