GitHub Actions 的每次运行都是全新的虚拟环境,运行结束后所有数据都会被销毁。那么如何保证电费数据的连续性?
不存储原始数据,只存储聚合后的 summary 数据,并保留所有历史
database/{校区}/):不提交到仓库,节省空间database/summaries/):提交到仓库,保留历史┌─────────────────────────────────────────────────────────────┐
│ GitHub Actions 数据流(完整历史版) │
└─────────────────────────────────────────────────────────────┘
第一次运行 (Day 1)
┌──────────────┐
│ 1. Checkout │ ← 检出仓库(summaries/ 为空)
│ 空仓库 │
└──────┬───────┘
│
▼
┌──────────────┐
│ 2. Query │ ← 查询电费数据,写入原始 database/
│ 原始数据 │ database/仙林校区/19幢/.../20260515.json
└──────┬───────┘
│
▼
┌──────────────┐
│ 3. Aggregate│ ← 生成 summaries(第一次,无历史数据)
│ 新 summary │ database/summaries/campuses/.../53463.json
└──────┬───────┘ {
│ "balance_history": {
▼ "20260515": 135.20
┌──────────────┐ }
│ 4. Commit │ }
│ summaries/ │ ← 只提交 summaries/,原始数据丢弃
│ + Push │
└──────────────┘
═══════════════════════════════════════════════════════════
第二次运行 (Day 2) - 全新环境
┌──────────────┐
│ 1. Checkout │ ← 检出仓库(包含 Day 1 的 summary!)
│ Day 1 │ database/summaries/campuses/.../53463.json
│ summaries │ "balance_history": {"20260515": 135.20}
└──────┬───────┘
│
▼
┌──────────────┐
│ 2. Query │ ← 查询电费数据,写入原始 database/
│ 新原始数据 │ database/仙林校区/19幢/.../20260516.json
└──────┬───────┘
│
▼
┌──────────────┐
│ 3. Merge │ ← 加载旧 summary + 新数据,合并
│ 旧+新 │ database/summaries/campuses/.../53463.json
│ │ "balance_history": {
│ │ "20260515": 135.20, ← Day 1
│ │ "20260516": 132.40 ← Day 2
│ │ }
└──────┬───────┘
│
▼
┌──────────────┐
│ 4. Commit │ ← 提交合并后的 summaries/
│ summaries/ │
│ + Push │
└──────────────┘
═══════════════════════════════════════════════════════════
第 365 天运行 - 全新环境
┌──────────────┐
│ 1. Checkout │ ← 检出仓库(包含 364 天的 summary!)
│ Day 1-364 │ "balance_history": {
│ summaries │ "20260515": 135.20,
│ │ "20260516": 132.40,
│ │ ...
│ │ "20260513": 118.30 ← 完整364天数据
│ │ }
└──────┬───────┘
│
▼
┌──────────────┐
│ 2. Query │ ← 查询 Day 365 数据
└──────┬───────┘
│
▼
┌──────────────┐
│ 3. Merge │ ← 合并 365 天数据(保留所有历史)
│ 完整历史 │ "balance_history": {
│ │ "20260515": 135.20,
│ │ ...
│ │ "20260514": 118.30,
│ │ "20260515": 116.50 ← Day 365
│ │ }
└──────┬───────┘
│
▼
┌──────────────┐
│ 4. Commit │
│ summaries/ │
│ + Push │
└──────────────┘
# 忽略原始数据(节省空间)
database/仙林校区/
database/鼓楼校区/
database/浦口校区/
database/苏州校区/
database/archives/
# 保留聚合数据(提交到仓库)
!database/summaries/
# 保留日志
!logs/query_runs/
scripts/aggregate_data.py 的合并逻辑:
def merge_room_data(existing: Dict, new: Dict) -> Dict:
"""合并新旧数据,保留所有历史"""
# 合并 balance_history(保留所有日期)
merged_history = existing.get('balance_history', {}).copy()
merged_history.update(new.get('balance_history', {}))
# 不删除旧数据,保留所有历史
return {
'room_id': new['room_id'],
'balance_history': merged_history, # 完整历史
...
}
.github/workflows/daily-query.yml 关键步骤:
steps:
# Step 1: 检出仓库(包含历史 summary 数据)
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
# Step 2: 查询新数据(写入原始 database/)
- name: Query electricity data
run: |
python nju_electric_query.py \
--cookie-file /tmp/cookie.json \
-d ./database \
$(cat config/room_ids.txt)
# Step 3: 合并新数据与旧 summary(保留所有历史)
- name: Generate summaries
run: |
python scripts/aggregate_data.py \
--database ./database \
--output ./database/summaries
# 自动加载 database/summaries/ 中的旧数据并合并
# Step 4: 只提交 summaries(不提交原始数据)
- name: Commit and push summaries only
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# 只添加 summaries 和 logs
git add database/summaries/ logs/ || true
if ! git diff --staged --quiet; then
git commit -m "chore: update electricity summaries for $(date +%Y-%m-%d)"
git push
fi
每个房间 JSON 包含所有历史数据:
| 时间跨度 | 天数 | 键值对数 | 文件大小 | 说明 |
|---|---|---|---|---|
| 1 个月 | 30 | 30 | ~1KB | 初始阶段 |
| 6 个月 | 180 | 180 | ~6KB | 半年数据 |
| 1 年 | 365 | 365 | ~11KB | 一年数据 |
| 2 年 | 730 | 730 | ~22KB | 两年数据 |
| 5 年 | 1825 | 1825 | ~55KB | 五年数据 |
计算方式:
"20260515": 135.20 ≈ 25-30 字节| 时间跨度 | 总体积 | 说明 |
|---|---|---|
| 1 个月 | ~500KB | 初始阶段 |
| 1 年 | ~5.5MB | 一年数据 |
| 2 年 | ~11MB | 两年数据 |
| 5 年 | ~27.5MB | 五年数据 |
对比原始数据:
database/summaries/campuses/仙林校区/buildings/19幢/rooms/53463.json:
{
"room_id": "53463",
"room_name": "19栋第16层1613",
"campus": "仙林校区",
"building": "19幢",
"current_balance": 146.99,
"balance_history": {
"20260515": 135.20,
"20260516": 132.40,
"20260517": 130.10,
"20260518": 128.50,
...
"20270415": 150.30,
"20270416": 148.20,
"20270417": 146.99
},
"last_updated": "20270417"
}
关键点:
balance_history 包含所有历史余额数据(无时间限制)// 加载单个房间的完整历史数据
const room = await fetch(
'database/summaries/campuses/仙林校区/buildings/19幢/rooms/53463.json'
).then(r => r.json());
// balance_history 包含所有历史数据
console.log(room.balance_history);
// {
// "20260515": 135.20,
// "20260516": 132.40,
// ... 完整历史
// }
const dates = Object.keys(room.balance_history).sort();
console.log(`数据时间范围: ${dates[0]} ~ ${dates[dates.length-1]}`);
console.log(`总天数: ${dates.length}`);
// 计算任意时间范围的统计
function calculateStats(room, startDate, endDate) {
const history = room.balance_history;
const balances = [];
for (const [date, balance] of Object.entries(history)) {
if (date >= startDate && date <= endDate) {
balances.push({ date, balance });
}
}
const values = balances.map(b => b.balance);
return {
count: balances.length,
avg: values.reduce((a, b) => a + b, 0) / values.length,
min: Math.min(...values),
max: Math.max(...values),
current: values[values.length - 1]
};
}
// 最近7天
const stats7d = calculateStats(room, '20270410', '20270417');
// 最近30天
const stats30d = calculateStats(room, '20270318', '20270417');
// 完整历史
const allDates = Object.keys(room.balance_history).sort();
const statsAll = calculateStats(room, allDates[0], allDates[allDates.length - 1]);
console.log(`完整历史统计:`);
console.log(` 总天数: ${statsAll.count}`);
console.log(` 平均余额: ${statsAll.avg.toFixed(2)}度`);
console.log(` 最高: ${statsAll.max}度`);
console.log(` 最低: ${statsAll.min}度`);
// 绘制完整历史趋势
function renderTrendChart(room) {
const history = room.balance_history;
const dates = Object.keys(history).sort();
const balances = dates.map(d => history[d]);
// 使用 Chart.js 或类似库
new Chart(ctx, {
type: 'line',
data: {
labels: dates.map(d => formatDate(d)),
datasets: [{
label: '电费余额',
data: balances,
borderColor: 'rgb(75, 192, 192)'
}]
}
});
}
# 查看仓库中的 summary 文件
git log --oneline --all -- database/summaries/
# 查看特定房间的历史提交
git log --follow database/summaries/campuses/仙林校区/buildings/19幢/rooms/53463.json
# 查看最新 summary 包含多少天数据
cat database/summaries/campuses/仙林校区/buildings/19幢/rooms/53463.json | \
jq '.balance_history | length'
# 查看数据时间范围
cat database/summaries/campuses/仙林校区/buildings/19幢/rooms/53463.json | \
jq '.balance_history | keys | {first: .[0], last: .[-1], total: length}'
如果未来数据过多,可以选择:
53463_2026.json, 53463_2027.json✅ 数据持久化机制(完整历史版):
balance_history 保留所有历史数据(无时间限制)✅ 核心要点:
database/{校区}/ 被 .gitignore 忽略database/summaries/ 不被忽略,会提交git commit summariesbalance_history 字段累积所有历史数据