feat: 核心电费查询 Go 服务

- FasterLZU 登录 + xiaofubao 6 步电费链路

- shiroJID 会话缓存 + 每日记录存储

- 定时任务: 每天 00:00 记录, 失败重试 5 次

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:29:33 +08:00
commit b2ad61e3fe
6 changed files with 1122 additions and 0 deletions

82
go/crypto.go Normal file
View File

@@ -0,0 +1,82 @@
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"encoding/hex"
"fmt"
"strings"
)
// crypto.go - AES-CBC 加密/解密 + MD5 签名(与 FasterLZU APP 行为一致)
// 密钥必须通过环境变量 LZU_AES_KEY / LZU_MD5_KEY 提供16 字节)
var (
aesKey = envOr("LZU_AES_KEY", "") // 16 字节,由部署环境注入
md5Key = envOr("LZU_MD5_KEY", "")
)
// aesEncrypt 使用 AES-CBCIV=key零填充输出小写 hex
func aesEncrypt(plaintext string) (string, error) {
key := []byte(aesKey)
if len(key) != 16 {
return "", fmt.Errorf("AES key 长度不是 16 字节")
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
data := []byte(plaintext)
// 零填充到 blockSize 的整数倍
blockSize := aes.BlockSize
paddedLen := len(data)
if paddedLen%blockSize != 0 {
paddedLen = len(data) + (blockSize - len(data)%blockSize)
}
padded := make([]byte, paddedLen)
copy(padded, data)
// 剩余部分保持 0x00零填充
iv := key
mode := cipher.NewCBCEncrypter(block, iv)
ciphertext := make([]byte, paddedLen)
mode.CryptBlocks(ciphertext, padded)
return hex.EncodeToString(ciphertext), nil
}
// aesDecrypt AES-CBC 解密,去除尾部零填充
func aesDecrypt(hexStr string) (string, error) {
key := []byte(aesKey)
if len(key) != 16 {
return "", fmt.Errorf("AES key 长度不是 16 字节")
}
encrypted, err := hex.DecodeString(hexStr)
if err != nil {
return "", err
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
iv := key
mode := cipher.NewCBCDecrypter(block, iv)
decrypted := make([]byte, len(encrypted))
mode.CryptBlocks(decrypted, encrypted)
// 去除尾部零填充
validLen := len(decrypted)
for i := len(decrypted) - 1; i >= 0 && i >= len(decrypted)-16; i-- {
if decrypted[i] != 0 {
break
}
validLen--
}
return string(decrypted[:validLen]), nil
}
// md5Encrypt 签名: 参数值以 | 连接,末尾拼接 key返回 MD5 hex
func md5Encrypt(params []string, key string) string {
data := strings.Join(params, "|") + "|" + key
sum := md5.Sum([]byte(data))
return hex.EncodeToString(sum[:])
}