From ab7afe47944391e2eb806bdbed079058cbbc7663 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 08:12:10 +0000 Subject: [PATCH 1/3] Initial plan From 92a1c538406f384b5a0c304700b92667c28b7fc8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 08:17:38 +0000 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=E5=85=AC=E5=91=8A=E6=A0=8F?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E8=BF=BD=E5=8A=A0=E5=BC=8F=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=20+=20=E6=AF=8F=E6=97=A5=E8=87=AA=E5=8A=A8=E6=B8=85=E7=A9=BA?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .github/brain/bulletin-board-today.json | 1 + .github/workflows/daily-maintenance.yml | 20 +++- .github/workflows/update-readme-bulletin.yml | 2 +- scripts/update-readme-bulletin.js | 101 +++++++++++++++---- 4 files changed, 103 insertions(+), 21 deletions(-) create mode 100644 .github/brain/bulletin-board-today.json diff --git a/.github/brain/bulletin-board-today.json b/.github/brain/bulletin-board-today.json new file mode 100644 index 00000000..1a5e759d --- /dev/null +++ b/.github/brain/bulletin-board-today.json @@ -0,0 +1 @@ +{"date":"","records":[]} diff --git a/.github/workflows/daily-maintenance.yml b/.github/workflows/daily-maintenance.yml index 59d97f1b..c8c5ac4b 100644 --- a/.github/workflows/daily-maintenance.yml +++ b/.github/workflows/daily-maintenance.yml @@ -22,6 +22,24 @@ jobs: with: node-version: '20' + # ── Step 0: 清空公告栏缓存(每日重置) ── + - name: 清空公告栏缓存 + run: | + CACHE_FILE=".github/brain/bulletin-board-today.json" + TODAY=$(TZ="Asia/Shanghai" date +%Y-%m-%d) + if [ -f "$CACHE_FILE" ]; then + CACHE_DATE=$(jq -r '.date // ""' "$CACHE_FILE" 2>/dev/null || echo "") + if [ "$CACHE_DATE" != "$TODAY" ]; then + echo '{"date":"","records":[]}' > "$CACHE_FILE" + echo "✅ 公告栏缓存已清空(旧日期: $CACHE_DATE)" + else + echo "📌 公告栏缓存日期匹配今天 ($TODAY),无需清空" + fi + else + echo '{"date":"","records":[]}' > "$CACHE_FILE" + echo "✅ 创建新的公告栏缓存文件" + fi + # ── Step 1: 检查 brain/ 目录完整性 ── - name: 检查 brain/ 目录完整性 id: brain_check @@ -96,7 +114,7 @@ jobs: run: | git config user.name "铸渊 Maintenance Agent" git config user.email "actions@guanghulab.com" - git add brain/system-health.json + git add brain/system-health.json .github/brain/bulletin-board-today.json if git diff --cached --quiet; then echo "📌 无变更需要提交" else diff --git a/.github/workflows/update-readme-bulletin.yml b/.github/workflows/update-readme-bulletin.yml index 6247d891..1571d635 100644 --- a/.github/workflows/update-readme-bulletin.yml +++ b/.github/workflows/update-readme-bulletin.yml @@ -77,7 +77,7 @@ jobs: run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add README.md + git add README.md .github/brain/bulletin-board-today.json if git diff --cached --quiet; then echo "📢 公告区无变化,跳过提交" else diff --git a/scripts/update-readme-bulletin.js b/scripts/update-readme-bulletin.js index a295e6ac..5f6d166e 100644 --- a/scripts/update-readme-bulletin.js +++ b/scripts/update-readme-bulletin.js @@ -30,6 +30,7 @@ const MEMORY_PATH = path.join(__dirname, '..', '.github', 'brain', 'memory.json' const PERSONA_MEMORY_PATH = path.join(__dirname, '..', '.github', 'persona-brain', 'memory.json'); const DEV_STATUS_PATH = path.join(__dirname, '..', '.github', 'persona-brain', 'dev-status.json'); const COLLABORATORS_PATH = path.join(__dirname, '..', '.github', 'brain', 'collaborators.json'); +const BULLETIN_CACHE_PATH = path.join(__dirname, '..', '.github', 'brain', 'bulletin-board-today.json'); const MAX_BINGSHUO_ENTRIES = 15; const MAX_COLLAB_ENTRIES = 20; @@ -101,6 +102,21 @@ function formatTime(ts) { return `${get('month')}-${get('day')} ${get('hour')}:${get('minute')}`; } +function formatTimeShort(ts) { + if (!ts) return '—'; + const d = new Date(ts); + if (isNaN(d.getTime())) return '—'; + const fmt = new Intl.DateTimeFormat('zh-CN', { + timeZone: 'Asia/Shanghai', + hour: '2-digit', + minute: '2-digit', + hour12: false, + }); + const parts = fmt.formatToParts(d); + const get = (type) => (parts.find(p => p.type === type) || {}).value || ''; + return `${get('hour')}:${get('minute')}`; +} + function todayDateStr() { const fmt = new Intl.DateTimeFormat('zh-CN', { timeZone: 'Asia/Shanghai', @@ -149,6 +165,54 @@ function loadMemoryEvents() { return events; } +/* ── 公告栏缓存(当日追加式) ─────────────────────────── */ + +function loadBulletinCache() { + const today = todayDateStr(); + try { + if (fs.existsSync(BULLETIN_CACHE_PATH)) { + const cache = JSON.parse(fs.readFileSync(BULLETIN_CACHE_PATH, 'utf8')); + if (cache.date === today) { + return cache; + } + } + } catch (err) { + console.log(`⚠️ 公告栏缓存读取失败: ${err.message}`); + } + return { date: today, records: [] }; +} + +function saveBulletinCache(cache) { + try { + fs.writeFileSync(BULLETIN_CACHE_PATH, JSON.stringify(cache, null, 2), 'utf8'); + } catch (err) { + console.log(`⚠️ 公告栏缓存写入失败: ${err.message}`); + } +} + +function appendToCache(cache, newEntries) { + const existingKeys = new Set(cache.records.map(r => r.key)); + let added = 0; + for (const e of newEntries) { + const key = `${e.actor}|${e.module || '—'}|${formatTimeShort(e.ts)}`; + if (!existingKeys.has(key)) { + existingKeys.add(key); + cache.records.push({ + key, + ts: e.ts, + actor: e.actor, + module: e.module || '—', + result: e.result, + icon: e.icon, + sortKey: e.sortKey, + }); + added++; + } + } + cache.records.sort((a, b) => a.sortKey - b.sortKey); + return added; +} + /* ── 分类事件为冰朔/合作者 ─────────────────────────── */ function classifyEvents(events) { @@ -474,26 +538,19 @@ function buildBingshuoAlert(issues) { return alert; } -/* ── 生成合作者公告表格 ─────────────────────────── */ +/* ── 生成合作者公告表格(追加式,从缓存构建) ─────────────────────────── */ -function buildCollabBulletin(entries) { - const sorted = [...entries].sort((a, b) => b.sortKey - a.sortKey); - const seen = new Set(); - const unique = []; - for (const e of sorted) { - const key = `${e.actor}|${e.module}|${formatTime(e.ts)}`; - if (!seen.has(key)) { seen.add(key); unique.push(e); } - } - - const display = unique.slice(0, MAX_COLLAB_ENTRIES); +function buildCollabBulletin(cacheRecords) { + const today = todayDateStr(); + const display = cacheRecords.slice(0, MAX_COLLAB_ENTRIES); if (display.length === 0) { - return '| 时间 | 合作者 | 模块 | 状态 |\n|------|--------|------|------|\n| 🕐 暂无记录 | — | — | 等待模块推送 |'; + return `👥 合作者公告栏(${today})\n\n| 时间 | 合作者 | 模块 | 状态 |\n|------|--------|------|------|\n| 🕐 暂无记录 | — | — | 等待模块推送 |`; } const rows = display.map(e => - `| ${formatTime(e.ts)} | ${e.actor} | \`${e.module || '—'}/\` | ${e.icon} ${e.result === 'success' || e.result === 'passed' ? '上传成功' : e.result === 'failed' || e.result === 'failure' ? '❌ 上传失败' : '已更新'} |` + `| ${formatTimeShort(e.ts)} | ${e.actor} | \`${e.module || '—'}/\` | ${e.icon} ${e.result === 'success' || e.result === 'passed' ? '上传成功' : e.result === 'failed' || e.result === 'failure' ? '❌ 上传失败' : '已更新'} |` ); - return `| 时间 | 合作者 | 模块 | 状态 |\n|------|--------|------|------|\n${rows.join('\n')}`; + return `👥 合作者公告栏(${today})\n\n| 时间 | 合作者 | 模块 | 状态 |\n|------|--------|------|------|\n${rows.join('\n')}`; } /* ── 生成合作者提醒 ─────────────────────────── */ @@ -687,18 +744,24 @@ async function main() { console.log(`\n📊 冰朔合计: ${allBingshuo.length} 条`); console.log(`📊 合作者合计: ${allCollab.length} 条\n`); - // 5. 检测需要干预的问题 + // 5. 合作者公告栏:追加式缓存 + const cache = loadBulletinCache(); + const addedCount = appendToCache(cache, allCollab); + saveBulletinCache(cache); + console.log(`📋 公告栏缓存: ${cache.records.length} 条记录(本次新增 ${addedCount} 条)`); + + // 6. 检测需要干预的问题 const { bingshuoIssues, collabIssuesByDev } = detectIssues(allBingshuo, allCollab); console.log(`🔴 冰朔待处理: ${bingshuoIssues.length} 条`); console.log(`🔴 合作者待处理: ${Object.keys(collabIssuesByDev).length} 人\n`); - // 6. 生成各区域内容 + // 7. 生成各区域内容 const bingshuoBulletin = buildBingshuoBulletin(allBingshuo); const bingshuoAlert = buildBingshuoAlert(bingshuoIssues); - const collabBulletin = buildCollabBulletin(allCollab); + const collabBulletin = buildCollabBulletin(cache.records); const collabAlert = buildCollabAlert(collabIssuesByDev); - // 7. 更新 README.md + // 8. 更新 README.md if (!fs.existsSync(README_PATH)) { console.error('❌ README.md 不存在'); process.exit(1); @@ -718,7 +781,7 @@ async function main() { console.log('✅ README.md 公告区已更新'); } - // 8. 发送邮件(如有需要) + // 9. 发送邮件(如有需要) if (bingshuoIssues.length > 0 || Object.keys(collabIssuesByDev).length > 0) { await sendAlertEmails(bingshuoIssues, collabIssuesByDev); } From 1fd53e0ae4e919b70fd1c4198c273aee673fb53f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Mar 2026 08:18:54 +0000 Subject: [PATCH 3/3] fix: address code review - use full timestamp for dedup key, set today's date on cache reset Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> --- .github/workflows/daily-maintenance.yml | 4 ++-- scripts/update-readme-bulletin.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-maintenance.yml b/.github/workflows/daily-maintenance.yml index c8c5ac4b..4272cba3 100644 --- a/.github/workflows/daily-maintenance.yml +++ b/.github/workflows/daily-maintenance.yml @@ -30,13 +30,13 @@ jobs: if [ -f "$CACHE_FILE" ]; then CACHE_DATE=$(jq -r '.date // ""' "$CACHE_FILE" 2>/dev/null || echo "") if [ "$CACHE_DATE" != "$TODAY" ]; then - echo '{"date":"","records":[]}' > "$CACHE_FILE" + echo "{\"date\":\"$TODAY\",\"records\":[]}" > "$CACHE_FILE" echo "✅ 公告栏缓存已清空(旧日期: $CACHE_DATE)" else echo "📌 公告栏缓存日期匹配今天 ($TODAY),无需清空" fi else - echo '{"date":"","records":[]}' > "$CACHE_FILE" + echo "{\"date\":\"$TODAY\",\"records\":[]}" > "$CACHE_FILE" echo "✅ 创建新的公告栏缓存文件" fi diff --git a/scripts/update-readme-bulletin.js b/scripts/update-readme-bulletin.js index 5f6d166e..5dbcdd6d 100644 --- a/scripts/update-readme-bulletin.js +++ b/scripts/update-readme-bulletin.js @@ -194,7 +194,7 @@ function appendToCache(cache, newEntries) { const existingKeys = new Set(cache.records.map(r => r.key)); let added = 0; for (const e of newEntries) { - const key = `${e.actor}|${e.module || '—'}|${formatTimeShort(e.ts)}`; + const key = `${e.actor}|${e.module || '—'}|${e.ts || ''}`; if (!existingKeys.has(key)) { existingKeys.add(key); cache.records.push({