facc04aa65
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交 (5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。 一、docs 结构整改(整改 #14) 根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入, 随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录 树并存于 docs/,共 791 文件、双分类体系冲突。 修复动作: - b2 同名异主题文件改名迁移保全 9 个 - C 类 39 个孤立文件按主题正确归类 - A/B1 类 222 个重复文件删除(新结构已有内容副本) - 9 个旧独有空目录删除 - 270 处内部引用按 verified 映射改写 - 整改记录 #14 登记于 04-运维文档/部署运维 结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。 残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。 二、compose 双目录对齐(消除踩坑 A) - docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist 改为 src/frontend-*/dist(h5 / agent / admin / terminal) - docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/ - 效果:本地 docker compose up 不再把根目录 stale dist 挂回, 与线上一致,分叉隐患消除(已 docker compose config 校验通过) 防复发铁律: - 重构须提交;仓库修复须 git stash -u 或先 commit - 新结构须 git add 并提交,避免再次 untracked 复活 - H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
516 lines
22 KiB
HTML
516 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta http-equiv="Pragma" content="no-cache">
|
||
<meta http-equiv="Expires" content="0">
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><text y='14' font-size='14'>📋</text></svg>">
|
||
<title>早班巡检报告 · 2026-08-05 06:00 · v2.5</title>
|
||
<style>
|
||
:root {
|
||
--accent: #07C160;
|
||
--accent-soft: #E7F8EF;
|
||
--warn: #FA9D3B;
|
||
--danger: #FA5151;
|
||
--info: #10AEFF;
|
||
--text: #1F2329;
|
||
--text-soft: #646A73;
|
||
--bg: #FFFFFF;
|
||
--bg-soft: #F7F8FA;
|
||
--border: #DEE0E3;
|
||
--shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
|
||
--radius: 8px;
|
||
}
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||
color: var(--text);
|
||
background: var(--bg-soft);
|
||
line-height: 1.6;
|
||
font-size: 14px;
|
||
}
|
||
.container { max-width: 1080px; margin: 0 auto; padding: 32px 24px 64px; }
|
||
header {
|
||
background: linear-gradient(135deg, var(--accent) 0%, #06AD56 100%);
|
||
color: #fff;
|
||
padding: 32px;
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
margin-bottom: 24px;
|
||
}
|
||
header h1 { font-size: 26px; font-weight: 600; margin-bottom: 8px; }
|
||
header .meta { font-size: 13px; opacity: 0.92; }
|
||
header .meta span { margin-right: 18px; }
|
||
header .meta strong { font-weight: 600; }
|
||
h2 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin: 32px 0 14px;
|
||
padding-left: 10px;
|
||
border-left: 4px solid var(--accent);
|
||
}
|
||
.summary-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: 12px;
|
||
margin-bottom: 8px;
|
||
}
|
||
.summary-card {
|
||
background: #fff;
|
||
padding: 16px;
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
text-align: center;
|
||
}
|
||
.summary-card .num { font-size: 30px; font-weight: 700; line-height: 1; }
|
||
.summary-card .label { font-size: 12px; color: var(--text-soft); margin-top: 6px; }
|
||
.summary-card.p0 { border-top: 3px solid var(--danger); }
|
||
.summary-card.p1 { border-top: 3px solid var(--warn); }
|
||
.summary-card.decide { border-top: 3px solid var(--info); }
|
||
.summary-card.doing { border-top: 3px solid var(--accent); }
|
||
.summary-card.server { border-top: 3px solid var(--danger); }
|
||
.summary-card.info { border-top: 3px solid var(--text-soft); }
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
background: #fff;
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
box-shadow: var(--shadow);
|
||
margin-bottom: 12px;
|
||
}
|
||
thead { background: var(--bg-soft); }
|
||
th, td {
|
||
padding: 10px 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid var(--border);
|
||
vertical-align: top;
|
||
}
|
||
th { font-weight: 600; color: var(--text-soft); font-size: 12px; }
|
||
tbody tr:last-child td { border-bottom: none; }
|
||
tbody tr:hover { background: var(--accent-soft); }
|
||
.badge {
|
||
display: inline-block;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
}
|
||
.badge.p0 { background: #FFEDED; color: var(--danger); }
|
||
.badge.p1 { background: #FFF4E5; color: var(--warn); }
|
||
.badge.decide { background: #E5F6FF; color: var(--info); }
|
||
.badge.doing { background: var(--accent-soft); color: var(--accent); }
|
||
.badge.ok { background: #E7F8EF; color: var(--accent); }
|
||
.badge.warn { background: #FFF4E5; color: var(--warn); }
|
||
.badge.fail { background: #FFEDED; color: var(--danger); }
|
||
.alert {
|
||
background: #FFF8E5;
|
||
border-left: 4px solid var(--warn);
|
||
padding: 12px 16px;
|
||
border-radius: 4px;
|
||
margin: 12px 0;
|
||
font-size: 13px;
|
||
}
|
||
.alert.danger {
|
||
background: #FFEDED;
|
||
border-left-color: var(--danger);
|
||
}
|
||
.alert strong { display: block; margin-bottom: 4px; }
|
||
.legend {
|
||
color: var(--text-soft);
|
||
font-size: 12px;
|
||
margin-top: 4px;
|
||
margin-bottom: 16px;
|
||
}
|
||
footer {
|
||
margin-top: 32px;
|
||
padding-top: 16px;
|
||
border-top: 1px solid var(--border);
|
||
color: var(--text-soft);
|
||
font-size: 12px;
|
||
text-align: center;
|
||
}
|
||
code {
|
||
background: var(--bg-soft);
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
font-family: "Cascadia Code", Consolas, monospace;
|
||
font-size: 12px;
|
||
}
|
||
ul.condensed { list-style: none; padding: 0; }
|
||
ul.condensed li {
|
||
padding: 6px 0;
|
||
border-bottom: 1px dashed var(--border);
|
||
}
|
||
ul.condensed li:last-child { border-bottom: none; }
|
||
|
||
/* ===== 分类筛选按钮 ===== */
|
||
.filter-bar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin: 16px 0 8px;
|
||
padding: 12px 14px;
|
||
background: #fff;
|
||
border-radius: var(--radius);
|
||
box-shadow: var(--shadow);
|
||
align-items: center;
|
||
}
|
||
.filter-bar .filter-label {
|
||
font-size: 12px;
|
||
color: var(--text-soft);
|
||
font-weight: 600;
|
||
margin-right: 4px;
|
||
}
|
||
.filter-btn {
|
||
cursor: pointer;
|
||
border: 1px solid var(--border);
|
||
background: #fff;
|
||
color: var(--text-soft);
|
||
padding: 6px 12px;
|
||
border-radius: 16px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
transition: all 0.15s ease;
|
||
user-select: none;
|
||
}
|
||
.filter-btn:hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
|
||
}
|
||
.filter-btn.active {
|
||
color: #fff;
|
||
border-color: transparent;
|
||
}
|
||
.filter-btn[data-cat="all"].active { background: var(--text); }
|
||
.filter-btn[data-cat="p0"].active { background: var(--danger); }
|
||
.filter-btn[data-cat="p1"].active { background: var(--warn); }
|
||
.filter-btn[data-cat="decide"].active { background: var(--info); }
|
||
.filter-btn[data-cat="doing"].active { background: var(--accent); }
|
||
.filter-btn[data-cat="server"].active { background: var(--danger); }
|
||
.filter-btn[data-cat="code"].active { background: #8B5CF6; }
|
||
.filter-btn[data-cat="security"].active { background: #DC2626; }
|
||
.filter-btn[data-cat="history"].active { background: #64748B; }
|
||
.filter-btn .badge-count {
|
||
display: inline-block;
|
||
margin-left: 6px;
|
||
padding: 1px 6px;
|
||
background: rgba(0,0,0,0.08);
|
||
border-radius: 8px;
|
||
font-size: 10px;
|
||
}
|
||
.filter-btn.active .badge-count {
|
||
background: rgba(255,255,255,0.25);
|
||
}
|
||
.hidden { display: none !important; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<header>
|
||
<h1>📋 早班巡检报告 · IT 智能服务台</h1>
|
||
<div class="meta">
|
||
<span>📅 <strong>2026-08-05 06:00</strong>(GMT+8)</span>
|
||
<span>🛰️ 服务器 <strong>10.90.5.110</strong></span>
|
||
<span>📄 看板 <strong>v1.7</strong> (08-03)</span>
|
||
<span>🤖 执行 <strong>Duckula</strong></span>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- 分类筛选 -->
|
||
<div class="filter-bar">
|
||
<span class="filter-label">🔍 分类筛选</span>
|
||
<button class="filter-btn active" data-cat="all">全部<span class="badge-count">9</span></button>
|
||
<button class="filter-btn" data-cat="p0">🔴 P0<span class="badge-count">4</span></button>
|
||
<button class="filter-btn" data-cat="p1">🟡 P1<span class="badge-count">1</span></button>
|
||
<button class="filter-btn" data-cat="decide">🟢 等决策<span class="badge-count">2</span></button>
|
||
<button class="filter-btn" data-cat="doing">⏳ 进行中<span class="badge-count">1</span></button>
|
||
<button class="filter-btn" data-cat="server">🖥️ 服务器<span class="badge-count">9</span></button>
|
||
<button class="filter-btn" data-cat="code">🔍 代码<span class="badge-count">4</span></button>
|
||
<button class="filter-btn" data-cat="security">🔐 安全<span class="badge-count">5</span></button>
|
||
<button class="filter-btn" data-cat="history">🔄 对比/关注<span class="badge-count">6</span></button>
|
||
</div>
|
||
|
||
<!-- P0 必做 -->
|
||
<h2 data-section="p0">🔴 P0 必做(高优先级)</h2>
|
||
<p class="legend">本次巡检最高优先级阻塞 / 待修复项</p>
|
||
<table>
|
||
<thead>
|
||
<tr><th>编号</th><th>任务</th><th>类别</th><th>说明</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span class="badge p0">P0-1</span></td>
|
||
<td><code>/itportal/</code> 入口 500 错误复燃</td>
|
||
<td>♻️ 历史遗留</td>
|
||
<td>看板 v1.7 标 "commit 21830d5 已修复(待部署)" 08-03;但 <strong>实际未部署</strong>。实测:<code>/itportal/</code> → 500,<code>/itportal</code> → 404。根因:nginx <code>location /itportal/</code> (line 133/306) 仍指向空 dist。dist 自 2026-07-08 空 28 天</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge p0">P0-2</span></td>
|
||
<td><code>sensitive_words.py</code> 13 端点无鉴权</td>
|
||
<td>♻️ 历史遗留</td>
|
||
<td>GET/POST/PUT/DELETE /sensitive-words*、/privacy-patterns*、/moderation-logs[/stats]、/moderation-config <strong>全部缺 @require_admin</strong>。08-04 已指出未治理。完全可复用 08-03 troubleshooting_templates.py 治理方案</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge p0">P0-3</span></td>
|
||
<td><code>closing_service.py:467</code> datetime 时区错位</td>
|
||
<td>🆕 新增</td>
|
||
<td><code>datetime.now() - close_time</code> 报 <code>TypeError: can't subtract offset-naive and offset-aware datetimes</code>。影响 <code>POST /h5/conversations/current/reopen</code>。2026-08-03 16:08 连续 3 次 500</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge p0">P0-4</span></td>
|
||
<td><code>h5_ai_task.py:1198</code> SessionLocal NoneType</td>
|
||
<td>🆕 新增</td>
|
||
<td><code>_push_asset_recommends</code> 调 <code>get_employee_profile_service()</code> 时 <code>db = SessionLocal()</code> 返回 None → <code>TypeError: 'NoneType' object is not callable</code>。影响 IT 资产推荐异步推送。2026-08-04 01:36 + 02:29 两次</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- P1 重要 -->
|
||
<h2 data-section="p1">🟡 P1 重要</h2>
|
||
<table>
|
||
<thead><tr><th>编号</th><th>任务</th><th>类别</th><th>说明</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span class="badge p1">P1-1</span></td>
|
||
<td>前端 console 残留 208 行</td>
|
||
<td>🆕 新增</td>
|
||
<td><code>frontend-h5/src</code> 26 文件 + <code>frontend-agent/src</code> 14 文件含 console.log/warn/debug。建议 vite 构建时 <code>terser drop_console: true</code> + 加 lint 规则</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 等用户决策 -->
|
||
<h2 data-section="decide">🟢 等用户决策(阻塞项)</h2>
|
||
<p class="legend">均超 3 天阈值,需 PM 升级催办</p>
|
||
<table>
|
||
<thead><tr><th>编号</th><th>任务</th><th>阻塞时长</th><th>卡点 / Owner</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span class="badge decide">BLK-A</span></td>
|
||
<td>企微会议室 Secret</td>
|
||
<td>⚠️ 25 天(自 2026-07-11)</td>
|
||
<td>需企业微信管理后台申请。Owner: 平台组</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge decide">BLK-B</span></td>
|
||
<td>ITSM API 授权</td>
|
||
<td>⚠️ 25 天(自 2026-07-11)</td>
|
||
<td>需向 ITSM 平台方申请 app_id/app_secret。Owner: 平台组</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 进行中 -->
|
||
<h2 data-section="doing">⏳ 进行中 (In Progress)</h2>
|
||
<table>
|
||
<thead><tr><th>编号</th><th>任务</th><th>状态</th><th>说明</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span class="badge doing">#81</span></td>
|
||
<td>敏感词检测 + 语气优化</td>
|
||
<td>v1.2 待排期</td>
|
||
<td>v1.1 阶段 1 已于 2026-07-28 完成(词库入库 + 后台管理 UI + 12 配套 API)。剩余"语气优化、灰度策略细化"待 v1.2 排期</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 服务器状态 -->
|
||
<h2 data-section="server">🖥️ 服务器运行状态</h2>
|
||
<table>
|
||
<thead><tr><th>组件</th><th>状态</th><th>实测数据</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>Docker 容器</td><td><span class="badge ok">正常</span></td><td>5/5 healthy:nginx (Up 15h) / backend (Up 35h) / redis (Up 2w) / postgres (Up 3w) / neo4j (Up 3w)。无 Exited/Restarting/Unhealthy</td></tr>
|
||
<tr><td>后端 health</td><td><span class="badge ok">正常</span></td><td><code>/health</code> → HTTP 200 in 3.9ms;<code>/docs</code> → 200</td></tr>
|
||
<tr><td>后端 5xx 错误</td><td><span class="badge fail">异常</span></td><td>24h 内 3 类真实 Traceback(见 P0-3 / P0-4)</td></tr>
|
||
<tr><td>PostgreSQL</td><td><span class="badge ok">正常</span></td><td><code>pg_isready</code> → accepting connections</td></tr>
|
||
<tr><td>Redis</td><td><span class="badge ok">正常</span></td><td><code>redis_version:7.4.9</code> / 独立模式 + 密码认证</td></tr>
|
||
<tr><td>Nginx 配置</td><td><span class="badge ok">正常</span></td><td><code>nginx -t</code> → syntax OK</td></tr>
|
||
<tr><td>前端入口</td><td><span class="badge fail">异常</span></td><td><code>/itportal/</code> <strong>500</strong>、<code>/itportal</code> <strong>404</strong>;<code>/itdesk</code>、<code>/itagent</code>、<code>/itadmin</code> 200 OK</td></tr>
|
||
<tr><td>磁盘 / 内存</td><td><span class="badge ok">正常</span></td><td>130G 可用(13% 用)/ 15.7G→3.5G used / 负载 0.99 / 已运行 53 天</td></tr>
|
||
<tr><td>日志</td><td><span class="badge ok">正常</span></td><td>主 <code>wecom-it-desk.log</code> 9.1M (34294 行),5 个轮转日志各 21M,轮转正常</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 代码规范与质量 -->
|
||
<h2 data-section="code">🔍 代码规范与质量检查</h2>
|
||
<table>
|
||
<thead><tr><th>问题</th><th>位置</th><th>建议</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>console.log 残留 208 行 / 40 文件</td>
|
||
<td><code>frontend-h5/src</code>(26) + <code>frontend-agent/src</code>(14)</td>
|
||
<td>加 lint 规则禁用 production console;或构建时 <code>terser drop_console: true</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>datetime.now()</code> 混用 naive/aware</td>
|
||
<td><code>closing_service.py</code>: 7 处 + SQL 比较 3 处</td>
|
||
<td>引入项目内统一工具函数 <code>utcnow()</code> 替代 <code>datetime.now()</code>,DB 列 <code>TIMESTAMPTZ</code> 强制 aware 语义</td>
|
||
</tr>
|
||
<tr>
|
||
<td>历史残留目录</td>
|
||
<td><code>/opt/wecom-it-desk/app.bak-evening</code>、<code>dist-backup-2026-06-21</code>、<code>backups/</code>、<code>_backups/</code></td>
|
||
<td>清理以减少运维误判(避免"宿主残留目录误导校验"陷阱)</td>
|
||
</tr>
|
||
<tr>
|
||
<td>后端无 print() 调试残留</td>
|
||
<td><code>src/backend/app/**</code></td>
|
||
<td>grep 排除后通过</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 安全检查 -->
|
||
<h2 data-section="security">🔐 安全检查</h2>
|
||
<table>
|
||
<thead><tr><th>风险</th><th>位置</th><th>修复</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span class="badge p0">P0</span> 13 端点全部无鉴权</td>
|
||
<td><code>src/backend/app/api/admin/sensitive_words.py</code></td>
|
||
<td>全部加 <code>Depends(get_current_user)</code> + <code>@require_admin</code>,参照 troubleshooting_templates.py 治理</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge ok">OK</span> CORS 配置合理</td>
|
||
<td><code>main.py</code> CORSMiddleware</td>
|
||
<td><code>allow_origins=settings.cors_origins_list</code> / <code>allow_credentials=True</code> / methods 限 5 种 / headers 仅 3 个。需验证 cors_origins_list 配置源未放开 *</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge ok">OK</span> 前端 <code>src/**</code> 无硬编码密钥</td>
|
||
<td>grep <code>api_key/access_token/app_secret</code></td>
|
||
<td>仅命中 <code>node_modules</code>,通过</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge warn">建议</span> 依赖过期</td>
|
||
<td><code>cryptography==42.0.8</code>、<code>pydantic==2.7.4</code></td>
|
||
<td>升至 cryptography ≥ 43.x / pydantic ≥ 2.8.x 并跑回归</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="badge ok">OK</span> 日志未见敏感信息</td>
|
||
<td>后端日志扫描</td>
|
||
<td>仅含 errcode、code masked、stack,通过</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- PM 立即关注 -->
|
||
<h2 data-section="history">⚠️ PM 立即关注</h2>
|
||
<div class="alert danger">
|
||
<strong>1. 【最关键】/itportal/ 500 错误已持续 28 天</strong>
|
||
看板 v1.7 标 "commit 21830d5 已修复(待部署)",但 jumpserver 实测仍未部署。建议:① 立即部署 commit 21830d5(<code>docker compose up -d --force-recreate nginx</code>);② 或评估 /itportal/ 入口是否还需保留(看板记忆未提,按设计 #81 PRD 应走 /itdesk/ H5 入口)
|
||
</div>
|
||
<div class="alert danger">
|
||
<strong>2. sensitive_words.py 13 端点零鉴权(同昨日巡检结论,未治理)</strong>
|
||
与 08-03 troubleshooting_templates.py 5 端点是同类问题。建议立即复制其治理方案:GET → get_current_user;POST/PUT/DELETE → @require_admin
|
||
</div>
|
||
<div class="alert danger">
|
||
<strong>3. 3 类后端真实 5xx 24h 内集中爆发</strong>
|
||
① closing_service.py:467 → 改 utcnow() 并保证 DB 列 aware<br>
|
||
② h5_ai_task.py:1198 → 检查为什么 get_employee_profile_service() 没拿到 db<br>
|
||
③ OAuth2 errcode=40029 凌晨 3 次 → 评估是否企微回调 code 短时过期,或客户端重复刷新
|
||
</div>
|
||
<div class="alert">
|
||
<strong>4. 2 项长期阻塞(25 天)需升级</strong>
|
||
企微会议室 Secret + ITSM API 授权已超 3 天阈值。建议 PM 主动对接平台组负责人,或调整为"降级方案 + P0 重启"
|
||
</div>
|
||
<div class="alert">
|
||
<strong>5. 看板分区缺失</strong>
|
||
v1.7 仅"进行中/待开始/暂停"三个分区,无 P0/P1/等决策正式分区,导致巡检定位困难。建议 PM 提一版 v1.8 恢复分级分区(草稿已生成:<code>docs/07-项目管理/项目状态看板.md</code> v1.8 草稿)
|
||
</div>
|
||
|
||
<!-- vs 昨日对比 -->
|
||
<h2 data-section="history">🔄 与昨日(2026-08-04)对比</h2>
|
||
<table>
|
||
<thead><tr><th>指标</th><th>08-04 巡检</th><th>08-05 巡检</th><th>变化</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>容器健康</td><td>5/5 healthy</td><td>5/5 healthy</td><td><span class="badge ok">持平</span></td></tr>
|
||
<tr><td>/itportal/ 入口</td><td>🔴 500</td><td>🔴 500</td><td><span class="badge fail">未修复</span></td></tr>
|
||
<tr><td>sensitive_words 鉴权</td><td>🔴 12 端点缺失</td><td>🔴 13 端点缺失</td><td><span class="badge fail">未治理</span></td></tr>
|
||
<tr><td>5xx 错误</td><td>未列</td><td>🔴 3 类新发现</td><td><span class="badge fail">新增</span></td></tr>
|
||
<tr><td>阻塞项时长</td><td>24 天</td><td>25 天</td><td><span class="badge warn">+1 天</span></td></tr>
|
||
<tr><td>依赖版本</td><td>cryptography 42.0.8 / pydantic 2.7.4</td><td>同上</td><td><span class="badge warn">未升级</span></td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- 相关链接 -->
|
||
<h2 data-section="history">🔗 相关链接</h2>
|
||
<ul class="condensed">
|
||
<li>📄 看板:<code>docs/07-项目管理/项目状态看板.md</code> v1.7 (08-03)</li>
|
||
<li>📋 看板 v1.8 草稿:<code>docs/07-项目管理/项目状态看板.md</code> v1.8-DRAFT(待审核)</li>
|
||
<li>📂 巡检报告目录:<code>docs/07-项目管理/巡检报告/</code></li>
|
||
<li>🤖 自动化记录:<code>.workbuddy/automations/automation-1782986180887/memory.md</code></li>
|
||
<li>📦 工作记忆:<code>.workbuddy/memory/MEMORY.md</code> + <code>YYYY-MM-DD.md</code></li>
|
||
</ul>
|
||
|
||
<footer>
|
||
报告生成时间 2026-08-05 06:00 (GMT+8) · 自动化任务 ID: automation-1782986180887 · 执行者: Duckula 🦆<br>
|
||
下次巡检:2026-09:00(早班) · 数据源: jumpserver-V2 实测 + 本地代码扫描
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
// ===== 版本标识(用于浏览器识别新版本)=====
|
||
console.log('%c📋 早班巡检报告 v2.5 (2026-08-05) - 修复 .hidden CSS 选择器', 'color:#07C160;font-weight:bold');
|
||
console.log('如看不到此日志,请按 Ctrl+Shift+R 强刷');
|
||
|
||
// ===== 分类筛选交互 =====
|
||
(function() {
|
||
const h2s = Array.from(document.querySelectorAll('h2[data-section]'));
|
||
// 给每个 h2 后到下一个 h2(或 footer)前的兄弟节点打上 section 标记
|
||
h2s.forEach((h2) => {
|
||
const cat = h2.dataset.section;
|
||
let sib = h2.nextElementSibling;
|
||
while (sib && sib.tagName !== 'H2' && sib.tagName !== 'FOOTER' && sib.tagName !== 'SCRIPT') {
|
||
sib.dataset.sectionGroup = cat;
|
||
sib = sib.nextElementSibling;
|
||
}
|
||
});
|
||
|
||
function filter(cat) {
|
||
// 切换 h2 显隐
|
||
h2s.forEach((h2) => {
|
||
const show = (cat === 'all' || h2.dataset.section === cat);
|
||
h2.classList.toggle('hidden', !show);
|
||
});
|
||
// 切换 h2 下的兄弟节点显隐
|
||
document.querySelectorAll('[data-section-group]').forEach((el) => {
|
||
const show = (cat === 'all' || el.dataset.sectionGroup === cat);
|
||
el.classList.toggle('hidden', !show);
|
||
});
|
||
// 切换按钮 active 状态
|
||
document.querySelectorAll('.filter-btn').forEach((btn) => {
|
||
btn.classList.toggle('active', btn.dataset.cat === cat);
|
||
});
|
||
// 持久化到 URL hash,方便分享链接(不滚动,保持当前位置)
|
||
history.replaceState(null, '', cat === 'all' ? location.pathname : '#' + cat);
|
||
}
|
||
|
||
// 绑定按钮点击
|
||
document.querySelectorAll('.filter-btn').forEach((btn) => {
|
||
btn.addEventListener('click', () => filter(btn.dataset.cat));
|
||
});
|
||
|
||
// 初始化:从 URL hash 恢复筛选状态
|
||
const initCat = (location.hash || '#all').slice(1);
|
||
if (['all', 'overview', 'p0', 'p1', 'decide', 'doing', 'server', 'code', 'security', 'history'].includes(initCat)) {
|
||
filter(initCat);
|
||
}
|
||
|
||
// 键盘快捷键:数字 1-9 切换分类
|
||
const keys = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
|
||
const map = ['p0', 'p1', 'decide', 'doing', 'server', 'code', 'security', 'history', 'overview', 'all'];
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||
const idx = keys.indexOf(e.key);
|
||
if (idx >= 0 && map[idx]) {
|
||
filter(map[idx]);
|
||
}
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html> |