@yorha2b-lab/autodev 2.1.4 → 2.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +80 -28
- package/package.json +1 -1
- package/templates/react/utils/utils.js +54 -1
|
@@ -1,49 +1,53 @@
|
|
|
1
|
-
name: Bunker Native Stats
|
|
1
|
+
name: Bunker Native Stats & Intelligence
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
schedule:
|
|
5
|
-
- cron: '45 19 * * *' # 北京时间凌晨 03:45
|
|
5
|
+
- cron: '45 19 * * *' # 北京时间凌晨 03:45 自动巡航
|
|
6
6
|
workflow_dispatch:
|
|
7
7
|
|
|
8
|
-
# 💡 补丁 1:强制开启 Node.js 24,消灭那个烦人的黄色警告
|
|
9
8
|
env:
|
|
10
9
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
11
10
|
|
|
12
11
|
jobs:
|
|
13
|
-
|
|
12
|
+
recon-and-ledger:
|
|
14
13
|
runs-on: ubuntu-latest
|
|
15
14
|
permissions:
|
|
16
15
|
contents: write
|
|
17
16
|
steps:
|
|
18
|
-
# 1. 检出数据分支
|
|
19
17
|
- name: Checkout Data Branch
|
|
20
18
|
uses: actions/checkout@v4
|
|
21
19
|
with:
|
|
22
20
|
ref: github-repo-stats
|
|
23
21
|
token: ${{ secrets.STATS_TOKEN }}
|
|
24
|
-
fetch-depth: 0
|
|
22
|
+
fetch-depth: 0
|
|
25
23
|
|
|
26
|
-
#
|
|
27
|
-
- name:
|
|
24
|
+
# 1. 环境准备:安装绘图依赖
|
|
25
|
+
- name: Setup Plotting Environment
|
|
28
26
|
run: |
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
pip install matplotlib pandas
|
|
29
|
+
|
|
30
|
+
# 2. 情报采集与账本物理焊接
|
|
31
|
+
- name: Intelligence Gathering & Sovereign Merge
|
|
32
|
+
run: |
|
|
33
|
+
echo "🤖 Pod 042: 启动‘全频道情报采集协议’..."
|
|
34
|
+
curl -s -H "Authorization: token ${{ secrets.STATS_TOKEN }}" \
|
|
35
|
+
-H "Accept: application/vnd.github.v3+json" \
|
|
36
|
+
https://api.github.com/repos/${{ github.repository }}/traffic/clones > raw_clones.json
|
|
37
|
+
|
|
38
|
+
curl -s -H "Authorization: token ${{ secrets.STATS_TOKEN }}" \
|
|
33
39
|
-H "Accept: application/vnd.github.v3+json" \
|
|
34
|
-
https://api.github.com/repos/${{ github.repository }}/traffic/
|
|
40
|
+
https://api.github.com/repos/${{ github.repository }}/traffic/popular/referrers > raw_referrers.json
|
|
35
41
|
|
|
36
|
-
# B. 格式化新数据
|
|
37
42
|
mkdir -p ghrs-data
|
|
38
43
|
echo "date,clones,uniques" > /tmp/new_data.csv
|
|
39
|
-
jq -r '.clones[] | "\(.timestamp[0:10]),\(.count),\(.uniques)"'
|
|
44
|
+
jq -r '.clones[] | "\(.timestamp[0:10]),\(.count),\(.uniques)"' raw_clones.json >> /tmp/new_data.csv
|
|
40
45
|
|
|
41
|
-
# C. 🧬 执行物理合并逻辑
|
|
42
46
|
LEDGER="ghrs-data/clones_ledger.csv"
|
|
43
47
|
if [ ! -f "$LEDGER" ]; then
|
|
44
48
|
cp /tmp/new_data.csv "$LEDGER"
|
|
45
49
|
else
|
|
46
|
-
echo "📡
|
|
50
|
+
echo "📡 执行物理序列重组..."
|
|
47
51
|
(head -n 1 "$LEDGER"; tail -n +2 "$LEDGER"; tail -n +2 /tmp/new_data.csv) | \
|
|
48
52
|
sed 's/\//-/g' | \
|
|
49
53
|
awk -F',' 'NR>1 {
|
|
@@ -52,30 +56,78 @@ jobs:
|
|
|
52
56
|
if(length(a[3])==1) a[3]="0"a[3];
|
|
53
57
|
printf "%s-%s-%s,%s,%s\n", a[1], a[2], a[3], $2, $3
|
|
54
58
|
}' | awk -F',' '!a[$1]++' | sort -r > /tmp/final_ledger.csv
|
|
55
|
-
|
|
56
59
|
echo "date,clones,uniques" > "$LEDGER"
|
|
57
60
|
cat /tmp/final_ledger.csv >> "$LEDGER"
|
|
58
61
|
fi
|
|
59
62
|
|
|
60
|
-
# D. 🔥 核心:产出 [bunker-stats.json]
|
|
61
|
-
# 确保 awk 逻辑百分之百执行
|
|
62
63
|
TOTAL_CLONES=$(awk -F',' 'NR>1 {sum+=$2} END {print sum}' "$LEDGER")
|
|
63
64
|
echo "{\"total_clones\": $TOTAL_CLONES}" > bunker-stats.json
|
|
64
|
-
echo "✅ 物理总数结算达成: $TOTAL_CLONES"
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
# 3. 🧬 【核心新增】:构筑战绩可视化图像
|
|
67
|
+
- name: Construct Visual Radar
|
|
68
|
+
run: |
|
|
69
|
+
echo "🤖 Pod 042: 执行‘图像构筑协议’..."
|
|
70
|
+
python3 <<EOF
|
|
71
|
+
import pandas as pd
|
|
72
|
+
import matplotlib.pyplot as plt
|
|
73
|
+
import os
|
|
74
|
+
|
|
75
|
+
# 读取账本并反转顺序(变为正序绘图)
|
|
76
|
+
df = pd.read_csv('ghrs-data/clones_ledger.csv')
|
|
77
|
+
df['date'] = pd.to_datetime(df['date'])
|
|
78
|
+
df = df.sort_values('date')
|
|
79
|
+
|
|
80
|
+
# 样式设置:地堡深邃黑
|
|
81
|
+
plt.style.use('dark_background')
|
|
82
|
+
fig, ax = plt.subplots(figsize=(10, 5))
|
|
83
|
+
|
|
84
|
+
# 绘制曲线(地堡绿)
|
|
85
|
+
ax.plot(df['date'], df['clones'], color='#33cc33', marker='o', markersize=4, linewidth=2, label='Daily Clones')
|
|
86
|
+
ax.fill_between(df['date'], df['clones'], color='#33cc33', alpha=0.2)
|
|
87
|
+
|
|
88
|
+
ax.set_title('YoRHa Bunker - Physical Construction Trend', fontsize=14, pad=20, color='#33cc33')
|
|
89
|
+
ax.set_ylabel('Clone Count', color='#888')
|
|
90
|
+
ax.grid(color='#444', linestyle='--', linewidth=0.5)
|
|
91
|
+
|
|
92
|
+
# 旋转日期防挤占
|
|
93
|
+
plt.xticks(rotation=45, color='#888')
|
|
94
|
+
plt.yticks(color='#888')
|
|
95
|
+
|
|
96
|
+
# 自动保存
|
|
97
|
+
os.makedirs('plots', exist_ok=True)
|
|
98
|
+
plt.tight_layout()
|
|
99
|
+
plt.savefig('plots/clones_trend.png', dpi=100)
|
|
100
|
+
EOF
|
|
101
|
+
|
|
102
|
+
# 4. [报表装修]
|
|
103
|
+
- name: Assemble Report
|
|
104
|
+
run: |
|
|
67
105
|
echo "# 🛰️ 地堡运行报告 (Sovereign Report)" > README.md
|
|
68
|
-
echo "### 📊 累计物理克隆总数: **$
|
|
69
|
-
echo "
|
|
106
|
+
echo "### 📊 累计物理克隆总数: **$(grep -oE "[0-9]+" bunker-stats.json | head -n 1)**" >> README.md
|
|
107
|
+
echo "" >> README.md
|
|
108
|
+
|
|
109
|
+
# 💡 物理植入图片
|
|
110
|
+
echo "#### 📈 战绩增长雷达 (Growth Radar)" >> README.md
|
|
111
|
+
echo "" >> README.md
|
|
112
|
+
echo "" >> README.md
|
|
113
|
+
|
|
114
|
+
echo "#### 📡 流量来源实时追踪 (Top Referrers)" >> README.md
|
|
115
|
+
echo "| 来源站点 | 总访问量 | 独立指挥官 |" >> README.md
|
|
116
|
+
echo "| :--- | :--- | :--- |" >> README.md
|
|
117
|
+
if [ -s raw_referrers.json ]; then
|
|
118
|
+
jq -r '.[] | "| \(.referrer) | \(.count) | \(.uniques) |"' raw_referrers.json >> README.md
|
|
119
|
+
else
|
|
120
|
+
echo "| 暂无外部来源信号 | - | - |" >> README.md
|
|
121
|
+
fi
|
|
70
122
|
echo "" >> README.md
|
|
71
|
-
echo "> 📡 信号状态:全频道在线 |
|
|
123
|
+
echo "> 📡 信号状态:全频道在线 | 数据物理封存点: $(date +'%Y-%m-%d %H:%M:%S') CST" >> README.md
|
|
72
124
|
|
|
73
|
-
#
|
|
74
|
-
- name:
|
|
125
|
+
# 5. 最终物理封存
|
|
126
|
+
- name: Sync Ledger to Cloud
|
|
75
127
|
run: |
|
|
76
128
|
git config user.name github-actions
|
|
77
129
|
git config user.email github-actions@github.com
|
|
78
130
|
git add -A
|
|
79
131
|
TOTAL=$(grep -oE "[0-9]+" bunker-stats.json | head -n 1)
|
|
80
|
-
git commit -m "chore:
|
|
132
|
+
git commit -m "chore: sovereign update v5.0 - total $TOTAL [$(date +'%Y-%m-%d')]" || echo "no changes"
|
|
81
133
|
git push origin github-repo-stats --force
|
package/package.json
CHANGED
|
@@ -55,7 +55,7 @@ export const exportDataToExcel = async (url, options, columns, fileName, formatt
|
|
|
55
55
|
if (col.exportRender) {
|
|
56
56
|
value = col.exportRender(value, item)
|
|
57
57
|
}
|
|
58
|
-
if (col.render) {
|
|
58
|
+
if (col.render&&!col.exportRender) {
|
|
59
59
|
const rendered = col.render(value, item)
|
|
60
60
|
value = typeof rendered === 'object' ? (rendered?.props?.children || value) : rendered
|
|
61
61
|
}
|
|
@@ -70,6 +70,59 @@ export const exportDataToExcel = async (url, options, columns, fileName, formatt
|
|
|
70
70
|
} */
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export const streamDownload = async ({ url, options, headers = {} }, fileName = '下载') => {
|
|
74
|
+
|
|
75
|
+
const response = await fetch(url, { ...options, headers })
|
|
76
|
+
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
console.error(response.statusText)
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const blob = await response.blob()
|
|
83
|
+
|
|
84
|
+
if (!blob.size) {
|
|
85
|
+
console.error('文件为空')
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const slice = blob.slice(0, 10)
|
|
90
|
+
const text = (await slice.text()).trim()
|
|
91
|
+
|
|
92
|
+
if (text.startsWith('{') || text.startsWith('[')) {
|
|
93
|
+
try {
|
|
94
|
+
const fullText = await blob.text()
|
|
95
|
+
const json = JSON.parse(fullText)
|
|
96
|
+
console.error(json?.msg ?? '系统异常')
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(error)
|
|
99
|
+
}
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const disposition = response.headers.get('content-disposition')
|
|
104
|
+
let name = fileName
|
|
105
|
+
|
|
106
|
+
if (disposition) {
|
|
107
|
+
const match = disposition.match(/filename\*=UTF-8''([^;]+)|filename="?([^";]+)"?/)
|
|
108
|
+
if (match) {
|
|
109
|
+
name = decodeURIComponent(match[1] || match[2])
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const link = document.createElement('a')
|
|
114
|
+
const urlObj = URL.createObjectURL(blob)
|
|
115
|
+
|
|
116
|
+
link.href = urlObj
|
|
117
|
+
link.download = name
|
|
118
|
+
|
|
119
|
+
document.body.appendChild(link)
|
|
120
|
+
link.click()
|
|
121
|
+
link.remove()
|
|
122
|
+
|
|
123
|
+
setTimeout(() => URL.revokeObjectURL(urlObj), 1000)
|
|
124
|
+
}
|
|
125
|
+
|
|
73
126
|
export const moneyRender = (value, { decimals = 2, currency = 'CNY', language = 'zh-CN', showSymbol = true } = {}) => {
|
|
74
127
|
if (value === null || value === undefined || value === '') return '-'
|
|
75
128
|
const number = Number(value)
|