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:
130
go/lzuapi.go
Normal file
130
go/lzuapi.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// lzuapi.go - FasterLZU (appservice.lzu.edu.cn) 登录客户端
|
||||
// 对应 Python lzuapi.py 中的 login() 与 get_st()
|
||||
|
||||
const (
|
||||
appServiceBase = "https://appservice.lzu.edu.cn"
|
||||
lzuUserAgent = "Mozilla/5.0 (Linux; Android 12; SM-S7110 Build/V417IR; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 lzdx_ua JHZF_LZDXAPP"
|
||||
)
|
||||
|
||||
// lzuClient FasterLZU API 客户端
|
||||
type lzuClient struct {
|
||||
http *http.Client
|
||||
username string
|
||||
password string
|
||||
loginToken string
|
||||
gatewayToken string
|
||||
}
|
||||
|
||||
func newLzuClient(username, password string) *lzuClient {
|
||||
return &lzuClient{
|
||||
http: &http.Client{},
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
}
|
||||
|
||||
// encryptedHeaders 加密请求头
|
||||
func encryptedHeaders() http.Header {
|
||||
h := http.Header{}
|
||||
h.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
h.Set("Transfer-Encrypt", "true")
|
||||
h.Set("User-Agent", lzuUserAgent)
|
||||
return h
|
||||
}
|
||||
|
||||
// Login 登录,返回 (loginToken, gatewayToken)
|
||||
func (c *lzuClient) Login() (string, string, error) {
|
||||
url := appServiceBase + "/api/eusp-unify-terminal/app-user/login"
|
||||
data := map[string]any{
|
||||
"app_os": 2,
|
||||
"name": c.username,
|
||||
"pwd": c.password,
|
||||
}
|
||||
plaintext, _ := json.Marshal(data) // 无空格,与 Python separators=(",",":") 一致
|
||||
ciphertext, err := aesEncrypt(string(plaintext))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBufferString(ciphertext))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
req.Header = encryptedHeaders()
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
decrypted, err := aesDecrypt(strings.TrimSpace(string(body)))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("登录响应解密失败: %w", err)
|
||||
}
|
||||
var res struct {
|
||||
Code int `json:"code"`
|
||||
Data struct {
|
||||
LoginToken string `json:"login_token"`
|
||||
GatewayToken string `json:"gateway_token"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(decrypted), &res); err != nil {
|
||||
return "", "", fmt.Errorf("登录响应解析失败: %w", err)
|
||||
}
|
||||
if res.Code != 1 {
|
||||
return "", "", fmt.Errorf("登录失败 code=%d", res.Code)
|
||||
}
|
||||
c.loginToken = res.Data.LoginToken
|
||||
c.gatewayToken = res.Data.GatewayToken
|
||||
return c.loginToken, c.gatewayToken, nil
|
||||
}
|
||||
|
||||
// GetSt 获取服务令牌 ST
|
||||
func (c *lzuClient) GetSt(serviceID string) (string, error) {
|
||||
url := fmt.Sprintf("%s/api/eusp-unify-terminal/app-user/getSt?loginToken=%s&serviceId=%s&service=",
|
||||
appServiceBase, c.loginToken, serviceID)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header = encryptedHeaders()
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
|
||||
decrypted, err := aesDecrypt(strings.TrimSpace(string(body)))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getSt 响应解密失败: %w", err)
|
||||
}
|
||||
var res struct {
|
||||
Code int `json:"code"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(decrypted), &res); err != nil {
|
||||
return "", fmt.Errorf("getSt 响应解析失败: %w", err)
|
||||
}
|
||||
if res.Code != 1 {
|
||||
return "", fmt.Errorf("getSt 失败 code=%d", res.Code)
|
||||
}
|
||||
return res.Data, nil
|
||||
}
|
||||
Reference in New Issue
Block a user