Files
wangjia b5ab92a57e
ci / server (push) Failing after 13s
ci / design-tokens (push) Failing after 11s
fix: 应用 xhigh 代码评审的跨端修复
来自 xhigh code review 的正确性/健壮性修复,覆盖全部五端:
- server:鉴权 fail-closed、计量交叉校验与配额扣穿处理、WS 网关并发与关闭顺序、
  billing 行锁、redis Lua 过期与设备槽刷新、config 解析
- desktop:会话 epoch 防串话、WS 重连与 401 处理、api 客户端复用、统一 usePoll 轮询
- android:握手时序、请求头封装、账户状态派生、按需重组
- ios:finalize 宽限、串行采集、错误文案服务端优先、删除死代码 CommitController

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:50:08 +08:00

1915 lines
95 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>dudu — 项目 TODO</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0; padding: 0 0 80px;
font-family: "Noto Sans SC", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #1f2933; background: #f7f9fb; line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
header {
background: linear-gradient(135deg, #0A1F3B 0%, #15407D 100%);
color: #fff; padding: 36px 32px 28px;
}
header h1 { margin: 0 0 6px; font-size: 24px; font-weight: 700; }
header .header-meta { color: #ADC9EA; font-size: 13px; margin-top: 4px; }
.stats { display: flex; gap: 16px; margin-top: 14px; flex-wrap: wrap; }
.stat-pill {
background: rgba(255,255,255,0.12); border-radius: 20px;
padding: 4px 14px; font-size: 13px; color: #fff;
}
.stat-pill strong { font-size: 18px; font-weight: 700; margin-right: 2px; }
/* ── 筛选栏 ── */
.filter-bar {
position: sticky; top: 0; z-index: 10;
background: #fff; border-bottom: 1px solid #e4e9ef;
padding: 10px 28px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
.filter-group { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.filter-label { font-size: 12px; color: #7b8794; white-space: nowrap; }
.filter-chip, .filter-btn {
padding: 3px 12px; border-radius: 14px; border: 1.5px solid #d1d9e6;
background: #f7f9fb; color: #52606d; font-size: 12.5px; cursor: pointer;
transition: all .15s; white-space: nowrap;
}
.filter-chip:hover, .filter-btn:hover { border-color: #2563AC; color: #2563AC; }
.filter-chip.active, .filter-btn.active {
background: #2563AC; border-color: #2563AC; color: #fff;
}
.filter-sep { width: 1px; height: 20px; background: #e4e9ef; margin: 0 4px; }
/* ── 内容区 ── */
.wrap { max-width: 980px; margin: 0 auto; padding: 0 24px; }
.section-block { margin-bottom: 8px; }
.section-title {
font-size: 15px; font-weight: 700;
padding: 10px 14px; border-radius: 8px;
display: flex; align-items: center; gap: 8px; cursor: pointer;
margin: 20px 0 8px; user-select: none;
}
.section-title .s-count {
font-size: 12px; font-weight: 500; padding: 1px 8px; border-radius: 10px;
background: rgba(0,0,0,0.08);
}
.section-title .s-arrow { margin-left: auto; font-size: 12px; color: inherit; opacity: .6; }
/* 状态主题色 */
.st-open { background: #eff6ff; color: #1e40af; border-left: 4px solid #3b82f6; }
.st-doing { background: #fff7ed; color: #9a3412; border-left: 4px solid #f97316; }
.st-done { background: #fefce8; color: #854d0e; border-left: 4px solid #eab308; }
.st-accepted{ background: #f0fdf4; color: #166534; border-left: 4px solid #22c55e; }
ul.todo-list { list-style: none; margin: 0; padding: 0; }
.todo-card {
background: #fff; border: 1px solid #e4e9ef; border-radius: 10px;
padding: 14px 18px; margin-bottom: 10px;
border-left-width: 4px;
}
.todo-card.s-open { border-left-color: #3b82f6; }
.todo-card.s-doing { border-left-color: #f97316; }
.todo-card.s-done { border-left-color: #eab308; }
.todo-card.s-accepted { border-left-color: #22c55e; background: #f0fdf4; opacity: .8; }
.card-header { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; }
.item-title { font-weight: 600; font-size: 15px; flex: 1; }
.todo-card.s-accepted .item-title { text-decoration: line-through; color: #52606d; }
.card-badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.item-desc { font-size: 13.5px; color: #52606d; margin-top: 6px; }
.item-desc code {
background: #f0f2f5; padding: 1px 5px; border-radius: 3px;
font-family: "JetBrains Mono", monospace; font-size: 12.5px; color: #b91c1c;
}
.card-footer { display: flex; align-items: center; gap: 10px; margin-top: 10px; flex-wrap: wrap; }
.tag-row { display: flex; flex-wrap: wrap; gap: 6px; flex: 1; }
.item-meta { display: flex; flex-wrap: wrap; gap: 8px; font-size: 12px; color: #8aa3c4; }
.meta-date { white-space: nowrap; }
/* ── 标签样式 ── */
.tag { display: inline-block; padding: 2px 9px; border-radius: 10px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.t-block { background: #fde8e8; color: #b91c1c; }
.t-high { background: #fef0d8; color: #9a6700; }
.t-low { background: #e6f0fb; color: #1d4ed8; }
.t-tag { background: #efefef; color: #555; cursor: pointer; transition: .12s; }
.t-tag:hover { background: #d9e8fb; color: #1d4ed8; }
.ver-badge { font-weight: 700; color: #1f7a44; }
.sub-progress-badge { background: #f0f4ff; color: #3730a3; }
/* 状态徽章 */
.status-badge { font-size: 11.5px; }
.s-open { background: #dbeafe; color: #1e40af; }
.s-doing { background: #ffedd5; color: #9a3412; }
.s-done { background: #fef9c3; color: #854d0e; }
.s-accepted { background: #dcfce7; color: #166534; }
/* ── 折叠 ── */
.section-list-wrap.collapsed { display: none; }
.empty-tip { font-size: 14px; color: #8aa3c4; margin: 8px 0 24px; }
.hidden { display: none !important; }
/* ── 改动等级徽章 ── */
.tier-1 { background: #ede9fe; color: #6d28d9; }
.tier-2 { background: #ccfbf1; color: #0f766e; }
.tier-3 { background: #f1f5f9; color: #475569; }
/* ── 拒绝按钮 & 拒绝原因 ── */
.reject-btn {
padding: 3px 10px; border-radius: 6px; border: 1.5px solid #dc2626;
background: #fff; color: #dc2626; font-size: 12px; font-weight: 600;
cursor: pointer; transition: all .15s; white-space: nowrap;
}
.reject-btn:hover { background: #dc2626; color: #fff; }
.reject-note {
margin: 8px 0 4px; padding: 7px 12px; border-radius: 6px;
background: #fef2f2; border: 1px solid #fecaca;
font-size: 13px; color: #b91c1c; line-height: 1.5;
}
.reject-date { color: #ef9999; font-size: 12px; }
/* ── 确认闸(方案/改动说明)── */
.gate-block { margin: 10px 0 4px; padding: 10px 14px; border-radius: 8px; font-size: 13px; }
.gate-pending { background: #fff7ed; border: 1px solid #fdba74; }
.gate-granted { background: #f0fdf4; border: 1px solid #bbf7d0; }
.gate-info { background: #f1f5f9; border: 1px solid #e2e8f0; }
.gate-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.gate-badge { padding: 2px 9px; border-radius: 10px; font-size: 11.5px; font-weight: 700; white-space: nowrap; }
.gate-badge.pending { background: #f97316; color: #fff; }
.gate-badge.granted { background: #22c55e; color: #fff; }
.gate-badge.info { background: #94a3b8; color: #fff; }
.gate-kind { font-size: 12px; color: #52606d; }
.gate-date { font-size: 12px; color: #8aa3c4; margin-left: auto; }
.gate-note { color: #52606d; margin: 4px 0; white-space: pre-wrap; }
.gate-ref { font-size: 12.5px; color: #52606d; margin-top: 4px; }
.gate-note code, .gate-ref code {
background: #fff; padding: 1px 5px; border-radius: 3px;
font-family: "JetBrains Mono", monospace; font-size: 12px; color: #b91c1c;
}
.approve-btn {
margin-top: 8px; padding: 5px 14px; border-radius: 6px; border: none;
background: #16a34a; color: #fff; font-size: 12.5px; font-weight: 600; cursor: pointer;
transition: background .15s;
}
.approve-btn:hover { background: #15803d; }
.approve-cmd { margin-top: 10px; padding: 10px 12px; background: #1e293b; border-radius: 8px; }
.approve-cmd-label { font-size: 12px; color: #94a3b8; margin-bottom: 6px; }
.approve-cmd-code { font-family: "JetBrains Mono", monospace; font-size: 13px; color: #86efac; display: block; word-break: break-all; }
.approve-copy-btn { margin-top: 8px; padding: 4px 12px; border-radius: 6px; background: #334155; color: #e2e8f0; border: none; font-size: 12px; cursor: pointer; }
.approve-copy-btn:hover { background: #475569; }
.stat-pill.gate-stat { background: #f97316; }
/* ── 子任务区 ── */
.subtask-block {
margin-top: 12px; padding: 10px 14px;
background: #f8fafc; border: 1px solid #e4e9ef; border-radius: 8px;
}
.subtask-header {
display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap;
}
.subtask-label {
font-size: 11px; font-weight: 700; color: #7b8794;
text-transform: uppercase; letter-spacing: .6px;
}
.subtask-progress-text { font-size: 12px; color: #52606d; }
.subtask-progress-bar {
flex: 1; height: 5px; background: #e4e9ef; border-radius: 3px; min-width: 60px;
}
.subtask-progress-fill { height: 100%; background: #22c55e; border-radius: 3px; transition: width .4s; }
.subtask-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.subtask-item {
display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
padding: 6px 10px; border-radius: 6px; font-size: 13px;
background: #fff; border: 1px solid #e8edf2;
}
.subtask-item.s-done { background: #f0fdf4; border-color: #bbf7d0; }
.subtask-item.s-doing { background: #fff7ed; border-color: #fed7aa; }
.subtask-item.s-accepted { background: #f0fdf4; border-color: #bbf7d0; opacity: .75; }
.sub-icon {
width: 16px; text-align: center; font-size: 13px; font-weight: 700; flex-shrink: 0;
}
.sub-icon.s-done { color: #16a34a; }
.sub-icon.s-doing { color: #ea580c; }
.sub-icon.s-open { color: #93aec8; }
.sub-icon.s-accepted { color: #16a34a; }
.sub-sid {
font-family: "JetBrains Mono", monospace; font-size: 11.5px;
color: #52606d; font-weight: 700; min-width: 32px; flex-shrink: 0;
}
.sub-title { flex: 1; font-size: 13px; min-width: 120px; }
.subtask-item.s-done .sub-title,
.subtask-item.s-accepted .sub-title { text-decoration: line-through; color: #52606d; }
.sub-badges { display: flex; gap: 4px; flex-wrap: wrap; align-items: center; }
.dep-section { display: flex; align-items: center; gap: 3px; }
.dep-label { font-size: 11px; color: #8aa3c4; margin-right: 2px; }
.dep-badge {
padding: 1px 6px; border-radius: 8px; font-size: 11px;
font-weight: 700; font-family: monospace; cursor: default;
}
.dep-done { background: #dcfce7; color: #166534; }
.dep-pending { background: #fee2e2; color: #b91c1c; }
/* ── 拒绝 Modal ── */
.modal-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.45);
display: flex; align-items: center; justify-content: center; z-index: 999;
}
.modal-box {
background: #fff; border-radius: 12px; padding: 28px 32px;
width: min(480px, 94vw); box-shadow: 0 20px 60px rgba(0,0,0,.2);
}
.modal-box h3 { margin: 0 0 4px; font-size: 17px; color: #1f2933; }
.modal-subtitle { font-size: 13px; color: #52606d; margin: 0 0 18px; }
.modal-label { font-size: 13px; font-weight: 600; color: #374151; margin-bottom: 6px; display: block; }
.modal-label .req { color: #dc2626; margin-left: 2px; }
.modal-textarea {
width: 100%; border: 1.5px solid #d1d9e6; border-radius: 8px;
padding: 10px 12px; font-size: 14px; font-family: inherit; resize: vertical;
min-height: 88px; outline: none; transition: border-color .15s;
}
.modal-textarea:focus { border-color: #dc2626; }
.modal-textarea.error { border-color: #dc2626; background: #fff8f8; }
.modal-err { font-size: 12px; color: #dc2626; margin-top: 4px; display: none; }
.modal-err.show { display: block; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
.modal-btn { padding: 8px 20px; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; transition: all .15s; }
.modal-btn-cancel { background: #f3f4f6; color: #374151; }
.modal-btn-cancel:hover { background: #e5e7eb; }
.modal-btn-reject { background: #dc2626; color: #fff; }
.modal-btn-reject:hover { background: #b91c1c; }
.modal-cmd-wrap { margin-top: 16px; padding: 12px 14px; background: #1e293b; border-radius: 8px; }
.modal-cmd-label { font-size: 12px; color: #94a3b8; margin-bottom: 8px; }
.modal-cmd-code { font-family: "JetBrains Mono", "Fira Code", monospace; font-size: 13px; color: #86efac; word-break: break-all; display: block; }
.modal-copy-btn { margin-top: 10px; padding: 5px 14px; border-radius: 6px; background: #334155; color: #e2e8f0; border: none; font-size: 12px; cursor: pointer; }
.modal-copy-btn:hover { background: #475569; }
</style>
</head>
<body>
<header>
<div class="wrap">
<h1>dudu — 项目 TODO</h1>
<div class="header-meta">生成于 2026-06-12 · 真相源 todo/todo.json</div>
<div class="stats">
<div class="stat-pill"><strong>22</strong>全部</div>
<div class="stat-pill"><strong>4</strong>待开始</div>
<div class="stat-pill"><strong>0</strong>开发中</div>
<div class="stat-pill"><strong>18</strong>待验收</div>
<div class="stat-pill"><strong>0</strong>已验收</div>
</div>
</div>
</header>
<div class="filter-bar" id="filterBar">
<div class="filter-group">
<span class="filter-label">重要度</span>
<button class="filter-btn active" data-filter-level="all">全部</button>
<button class="filter-btn" data-filter-level="high">高优</button>
<button class="filter-btn" data-filter-level="mid">重要</button>
<button class="filter-btn" data-filter-level="low">一般</button>
</div>
<div class="filter-sep"></div>
<div class="filter-group">
<span class="filter-label">状态</span>
<button class="filter-btn active" data-filter-status="all">全部</button>
<button class="filter-btn" data-filter-status="open">待开始</button>
<button class="filter-btn" data-filter-status="doing">开发中</button>
<button class="filter-btn" data-filter-status="done">待验收</button>
<button class="filter-btn" data-filter-status="accepted">已验收</button>
</div>
<div class="filter-sep"></div>
<div class="filter-group">
<span class="filter-label">改动等级</span>
<button class="filter-btn active" data-filter-tier="all">全部</button>
<button class="filter-btn" data-filter-tier="1">一级</button>
<button class="filter-btn" data-filter-tier="2">二级</button>
<button class="filter-btn" data-filter-tier="3">三级</button>
</div>
<div class="filter-sep"></div>
<div class="filter-group">
<span class="filter-label">平台 / 标签</span>
<button class="filter-chip" data-filter-tag="Android">Android</button><button class="filter-chip" data-filter-tag="CI/CD">CI/CD</button><button class="filter-chip" data-filter-tag="Web">Web</button><button class="filter-chip" data-filter-tag="Windows">Windows</button><button class="filter-chip" data-filter-tag="iOS">iOS</button><button class="filter-chip" data-filter-tag="mac">mac</button><button class="filter-chip" data-filter-tag="前端">前端</button><button class="filter-chip" data-filter-tag="后端">后端</button><button class="filter-chip" data-filter-tag="外部">外部</button><button class="filter-chip" data-filter-tag="数据库">数据库</button><button class="filter-chip" data-filter-tag="文档">文档</button><button class="filter-chip" data-filter-tag="跨端">跨端</button>
</div>
</div>
<div class="wrap">
<div class="section-block" id="section-open">
<div class="section-title st-open" data-toggle="open">
📋 待开始 <span class="s-count">4</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-open">
<ul class="todo-list" id="list-open">
<li class="todo-card s-open"
data-id="1"
data-level="high"
data-status="open"
data-tier="2"
data-tags="跨端">
<div class="card-header">
<span class="item-title">M0 风险验证 spike</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">2/3 子任务</span>
</div>
</div>
<div class="item-desc">三项风险验证并行:iOS 键盘录音、Gummy 流式、macOS 注入。无前置依赖,第一波启动</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">2 / 3 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:67%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="1A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">1A</span>
<span class="sub-title">iOS 键盘扩展内录音可行性 spike(真机验证完全访问下 AVAudioSession;不可行则确认 deep link 降级)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="1B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">1B</span>
<span class="sub-title">Gummy 流式接入 demoGo 直连 DashScope WSwav 推流收 partial/final</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-open" data-sid="1C">
<span class="sub-icon s-open"></span>
<span class="sub-sid">1C</span>
<span class="sub-title">macOS 注入 + 不抢焦点浮层 demo(剪贴板+CGEvent、focusable:false 验证微信/浏览器/IDE</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-open">待开始</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="跨端">跨端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="2"
data-level="high"
data-status="open"
data-tier="3"
data-tags="外部">
<div class="card-header">
<span class="item-title">微信开放平台与商户号申请</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-3">三级</span>
<span class="tag sub-progress-badge">0/3 子任务</span>
</div>
</div>
<div class="item-desc">人工/外部流程,周期数周,是支付联调(#7)与上架的关键路径,第一天启动</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">0 / 3 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:0%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-open" data-sid="2A">
<span class="sub-icon s-open"></span>
<span class="sub-sid">2A</span>
<span class="sub-title">开放平台网站应用 + 移动应用注册提审</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-open">待开始</span></div>
</li>
<li class="subtask-item s-open" data-sid="2B">
<span class="sub-icon s-open"></span>
<span class="sub-sid">2B</span>
<span class="sub-title">微信支付商户号申请 + API 证书/密钥配置(入 Bitwarden</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-open">待开始</span></div>
</li>
<li class="subtask-item s-open" data-sid="2C">
<span class="sub-icon s-open"></span>
<span class="sub-sid">2C</span>
<span class="sub-title">软件著作权申请(输入法上架需要)</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-open">待开始</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="外部">外部</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="12"
data-level="high"
data-status="open"
data-tier="2"
data-tags="mac,Windows">
<div class="card-header">
<span class="item-title">桌面·联调与验收</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">0/3 子任务</span>
</div>
</div>
<div class="item-desc">汇聚点:依赖 #10 #11 全部与后端 #5 #6 #7</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">0 / 3 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:0%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-open" data-sid="12A">
<span class="sub-icon s-open"></span>
<span class="sub-sid">12A</span>
<span class="sub-title">全链路联调:快捷键→采集→流式→浮层→注入(mac+win 双平台)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-open">待开始</span></div>
</li>
<li class="subtask-item s-open" data-sid="12B">
<span class="sub-icon s-open"></span>
<span class="sub-sid">12B</span>
<span class="sub-title">延迟验收:first_partial P95&lt;500ms / release_to_commit&lt;300ms 实测调优</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-open">待开始</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-pending" title="12A 未完成">12A</span></span></div>
</li>
<li class="subtask-item s-open" data-sid="12C">
<span class="sub-icon s-open"></span>
<span class="sub-sid">12C</span>
<span class="sub-title">异常路径:断网重连/QUOTA/RATE_LIMITED/3 分钟截断逐项验证</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-open">待开始</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-pending" title="12A 未完成">12A</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="mac">mac</span> <span class="tag t-tag" data-tag="Windows">Windows</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-open"
data-id="22"
data-level="mid"
data-status="open"
data-tier="1"
data-tags="跨端,文档">
<div class="card-header">
<span class="item-title">跨端架构改进 backlog(评审后再做)</span>
<div class="card-badges">
<span class="tag status-badge s-open">待开始</span>
<span class="tag t-high">重要</span>
<span class="tag tier-1">一级</span>
</div>
</div>
<div class="item-desc">本轮不改代码,仅记录待评审:限制常量(180s/30次/1800s)服务端下发、gummy 断连重建分层、三端 WS 连接策略统一、错误码文案服务端下发、web 价格表第三份副本与 SeedPacks 单源、设备准入校验防随机 device_id 绕过限流</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="跨端">跨端</span> <span class="tag t-tag" data-tag="文档">文档</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="section-block" id="section-doing">
<div class="section-title st-doing" data-toggle="doing">
🔨 开发中 <span class="s-count">0</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-doing">
<ul class="todo-list" id="list-doing">
<p class="empty-tip">暂无条目</p>
</ul>
</div>
</div>
<div class="section-block" id="section-done">
<div class="section-title st-done" data-toggle="done">
🔍 待验收 <span class="s-count">18</span>
<span class="s-arrow">▴ 收起</span>
</div>
<div class="section-list-wrap " id="list-wrap-done">
<ul class="todo-list" id="list-done">
<li class="todo-card s-done"
data-id="3"
data-level="high"
data-status="done"
data-tier="1"
data-tags="后端,跨端">
<div class="card-header">
<span class="item-title">协议与共享基建</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-1">一级</span>
<span class="tag sub-progress-badge">4/4 子任务</span>
<button class="reject-btn" data-id="3" data-title="协议与共享基建">拒绝验收</button>
</div>
</div>
<div class="item-desc">pkg/protocol 是 5 端对齐的唯一真相源,多数任务前置。设计依据 doc/backend-architecture.html 第四章</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">4 / 4 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="3A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">3A</span>
<span class="sub-title">pkg/protocolWS 帧结构(start/stop/cancel/partial/final/usage/error+ 错误码常量</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="3B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">3B</span>
<span class="sub-title">pkg/protocolREST DTOme/packs/orders/feedback/metrics</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="3C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">3C</span>
<span class="sub-title">design-pipelinetokens.css 导出 DuduTheme.swift / DuduTheme.kt + CI 校验</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="3D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">3D</span>
<span class="sub-title">monorepo 骨架(server/desktop/android/ios 目录 + 根 README + CI 雏形)</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="跨端">跨端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="4"
data-level="high"
data-status="done"
data-tier="1"
data-tags="后端,数据库">
<div class="card-header">
<span class="item-title">后端·工程与数据层</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-1">一级</span>
<span class="tag sub-progress-badge">3/3 子任务</span>
<button class="reject-btn" data-id="4" data-title="后端·工程与数据层">拒绝验收</button>
</div>
</div>
<div class="item-desc">schema 见 doc/backend-architecture.html 第六章</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">3 / 3 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="4A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">4A</span>
<span class="sub-title">Go 工程骨架:gin + 配置加载 + 日志 + Docker composepg/redis</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="4B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">4B</span>
<span class="sub-title">store:全部表 migration + gorm models11 张表,见后端文档第六章)</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="4A 已完成">4A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="4C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">4C</span>
<span class="sub-title">Redis 接入 + Key 封装(quota/authqr/jwt:block/rate 滑动窗口)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="4A 已完成">4A</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="数据库">数据库</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="5"
data-level="high"
data-status="done"
data-tier="2"
data-tags="后端">
<div class="card-header">
<span class="item-title">后端·认证</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">3/3 子任务</span>
<button class="reject-btn" data-id="5" data-title="后端·认证">拒绝验收</button>
</div>
</div>
<div class="item-desc">跨 epic 依赖:#4 骨架;5C 需 #2A 微信凭证(开发期可用测试号 mock)</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">3 / 3 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="5A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">5A</span>
<span class="sub-title">JWT 签发/校验中间件 + jti 黑名单 + logout</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="5B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">5B</span>
<span class="sub-title">扫码登录状态机(POST/GET /v1/auth/qrRedis state TTL 120s</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="5A 已完成">5A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="5C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">5C</span>
<span class="sub-title">微信网页授权回调 + 移动端 code 换 token + unionid 关联建号</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="5A 已完成">5A</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="6"
data-level="high"
data-status="done"
data-tier="1"
data-tags="后端">
<div class="card-header">
<span class="item-title">后端·识别网关</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-1">一级</span>
<span class="tag sub-progress-badge">5/5 子任务</span>
<button class="reject-btn" data-id="6" data-title="后端·识别网关">拒绝验收</button>
</div>
</div>
<div class="item-desc">跨 epic 依赖:#3A 协议、#4 骨架。含 Provider 抽象/中继/计量/识别限制</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">5 / 5 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="6A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">6A</span>
<span class="sub-title">ASR Provider 接口 + mock provider(脚本化回放 partial/final 供测试)</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="6B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">6B</span>
<span class="sub-title">gummy providerDashScope WS 对接,含断连重建,基于 1B demo)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="6A 已完成">6A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="6C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">6C</span>
<span class="sub-title">WS 网关:连接鉴权 + 会话状态机 + 上下行双泵中继</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="6A 已完成">6A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="6D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">6D</span>
<span class="sub-title">计量与 usage 帧(实收帧时长累计、provider 时间戳交叉校验、2s 下发)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="6C 已完成">6C</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="6E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">6E</span>
<span class="sub-title">识别限制:3 分钟截断定稿 + 单设备单路 + 30 分钟滑动窗口(次数/时长)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="6C 已完成">6C</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="7"
data-level="high"
data-status="done"
data-tier="1"
data-tags="后端,数据库">
<div class="card-header">
<span class="item-title">后端·计费与配额</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-1">一级</span>
<span class="tag sub-progress-badge">4/4 子任务</span>
<button class="reject-btn" data-id="7" data-title="后端·计费与配额">拒绝验收</button>
</div>
</div>
<div class="item-desc">跨 epic 依赖:#47C/7D 真实支付需 #2B 商户号(可先沙箱)。时长账本设计见后端文档第五章</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">4 / 4 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="7A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">7A</span>
<span class="sub-title">quotaRedis Lua 原子扣减(先试用后余额)+ start 预检 + 扣穿当句不掐断</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="7B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">7B</span>
<span class="sub-title">quota:异步落库 ledger/asr_sessions + 余额冗余列 + 每日对账任务</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="7A 已完成">7A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="7C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">7C</span>
<span class="sub-title">billingGET /v1/packs + 下单(native code_url / app 支付参数)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="7D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">7D</span>
<span class="sub-title">billing:回调验签幂等入账(transaction_id 唯一)+ 订单轮询 + 5 分钟查单兜底</span>
<div class="sub-badges"><span class="tag tier-1">一级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="7C 已完成">7C</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="数据库">数据库</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="10"
data-level="high"
data-status="done"
data-tier="2"
data-tags="mac,Windows">
<div class="card-header">
<span class="item-title">桌面·Rust 系统能力</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">8/8 子任务</span>
<button class="reject-btn" data-id="10" data-title="桌面·Rust 系统能力">拒绝验收</button>
</div>
</div>
<div class="item-desc">六项系统能力九成可并行;10C 依赖 #3A 协议</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">8 / 8 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="10A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10A</span>
<span class="sub-title">Tauri 2 工程 + @dudu/design 接入 + data-theme 主题切换</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="10B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10B</span>
<span class="sub-title">全局快捷键 push-to-talkpress/release 事件 + 自定义改键)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="10C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10C</span>
<span class="sub-title">WS 客户端:预连接/心跳/指数退避重连 + 协议编解码</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="10D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10D</span>
<span class="sub-title">cpal 音频采集 + 16k 重采样 + 100ms 分帧 + 设备枚举/切换</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="10E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10E</span>
<span class="sub-title">macOS 文字注入(剪贴板+CGEvent+恢复)+ 辅助功能权限检测(基于 1C demo)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="10F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10F</span>
<span class="sub-title">Windows 文字注入(SendInput KEYEVENTF_UNICODE</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="10G">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10G</span>
<span class="sub-title">Rust↔Web 事件桥(hotkey/asr/account/audio 事件 + invoke 命令集)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="10B 已完成">10B</span><span class="dep-badge dep-done" title="10C 已完成">10C</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="10H">
<span class="sub-icon s-done"></span>
<span class="sub-sid">10H</span>
<span class="sub-title">metrics 模块:本地队列(20 条/60s)+ 批量上报 + 漏斗事件汇入</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="10C 已完成">10C</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="mac">mac</span> <span class="tag t-tag" data-tag="Windows">Windows</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="11"
data-level="high"
data-status="done"
data-tier="2"
data-tags="mac,Windows,前端">
<div class="card-header">
<span class="item-title">桌面·界面</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">6/6 子任务</span>
<button class="reject-btn" data-id="11" data-title="桌面·界面">拒绝验收</button>
</div>
</div>
<div class="item-desc">直接复用 design/ React 组件;可用 mock 事件与 #10 并行,最终联调依赖 10G 事件桥</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">6 / 6 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="11A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">11A</span>
<span class="sub-title">识别浮层窗口(透明/置顶/不抢焦点 + RecognitionOverlay 两态 + 错误态文案)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="11B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">11B</span>
<span class="sub-title">托盘 + 自绘托盘菜单(248px、accent 反白、检查更新入口)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="11C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">11C</span>
<span class="sub-title">设置窗口(四分组:输入/通用/账号与时长/帮助 + 持久化)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="11D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">11D</span>
<span class="sub-title">登录/购买窗口(扫码轮询 → 时长包 → 支付二维码轮询 → 完成)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="11E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">11E</span>
<span class="sub-title">反馈窗口(文字 + 图片粘贴/拖拽 + 诊断 Switch + 成功态)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="11F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">11F</span>
<span class="sub-title">首次启动引导(欢迎 → 权限 → 登录 → 试一试)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="11C 已完成">11C</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="mac">mac</span> <span class="tag t-tag" data-tag="Windows">Windows</span> <span class="tag t-tag" data-tag="前端">前端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="16"
data-level="high"
data-status="done"
data-tier="2"
data-tags="后端,数据库">
<div class="card-header">
<span class="item-title">后端·网关与计费正确性修复</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">7/7 子任务</span>
<button class="reject-btn" data-id="16" data-title="后端·网关与计费正确性修复">拒绝验收</button>
</div>
</div>
<div class="item-desc">code-review 发现的网关/计费严重 bug:截断或 provider 死亡后仍扣费、WriteJSON 无写超时致 goroutine 泄漏、WaitGroup Add 竞态、finish 漏在途扣费、扣穿当句未掐断致白嫖、GORM v2 锁失效、usage 帧 omitempty 吞合法 0 值</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">7 / 7 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="16A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16A</span>
<span class="sub-title">feed 先判截断再累计;provider 死亡/截断后终结会话停止扣费</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="16B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16B</span>
<span class="sub-title">wsConn 写超时(SetWriteDeadline)+finish 的 Wait 加超时+修 defer 顺序避免 goroutine 泄漏</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="16C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16C</span>
<span class="sub-title">resultsDone.Add(1) 移到 go 语句之前,消除 WaitGroup 竞态</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="16D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16D</span>
<span class="sub-title">finish 等待在途 consumeDelta 完成后再取 settle 快照,避免漏账</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="16E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16E</span>
<span class="sub-title">扣穿当句不掐断:consumeDelta 扣穿后置标志,本句 final 后下发 QUOTA_EXCEEDED 结束会话</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="16F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16F</span>
<span class="sub-title">billing.MarkPaid 改用 GORM v2 Clauses(锁)写法,恢复行锁</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="16G">
<span class="sub-icon s-done"></span>
<span class="sub-sid">16G</span>
<span class="sub-title">usage 帧 SessionSeconds/BalanceSeconds/TrialRemaining 去掉 omitempty,让 0 显式下发</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="数据库">数据库</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="17"
data-level="high"
data-status="done"
data-tier="2"
data-tags="后端">
<div class="card-header">
<span class="item-title">后端·认证限流与依赖装配修复</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">9/9 子任务</span>
<button class="reject-btn" data-id="17" data-title="后端·认证限流与依赖装配修复">拒绝验收</button>
</div>
</div>
<div class="item-desc">JWT 黑名单 fail-open、logout TTL 硬编码 8 天、滑动窗口 seq 键永不过期、设备槽 4min TTL 不续期、pickProvider 永远 mock(gummy 永不装配)、/v1/pay/notify 无鉴权+pickPay 永远 mock=免费充值、metrics/batch 无可选鉴权 user_id 恒空、AppLatest 裸读 env、死代码清理</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">9 / 9 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="17A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17A</span>
<span class="sub-title">JWT 黑名单检查 fail-closedRedis Exists 出错时拒绝并记录,不放行</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17B</span>
<span class="sub-title">logout Revoke TTL 用 token 实际过期时间(claims.ExpiresAt),不硬编码 8 天</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17C</span>
<span class="sub-title">滑动窗口 Lua 给 key:seq 计数器设 EXPIRE,消除永久泄漏键</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17D</span>
<span class="sub-title">设备槽会话期间续期 TTL(usageLoop 里 EXPIRE),避免长会话槽过期致多路</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17E</span>
<span class="sub-title">pickProvider 真正按 ASR_PROVIDER 装配 gummy(有 key 用 gummy),启动日志打印 real/mock</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17F</span>
<span class="sub-title">metrics/batch 挂可选鉴权中间件,注入 user_id</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17G">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17G</span>
<span class="sub-title">AppLatest 配置进 config.Load 启动校验,避免每请求裸读 env</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17H">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17H</span>
<span class="sub-title">清理死代码:AllowAudioSeconds、fmt 占位行、gummy lastText 字段</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="17I">
<span class="sub-icon s-done"></span>
<span class="sub-sid">17I</span>
<span class="sub-title">pay/notify 鉴权与验签:MockPay 加共享密钥校验或文档标注部署前必换真实验签</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="18"
data-level="high"
data-status="done"
data-tier="2"
data-tags="mac,Windows">
<div class="card-header">
<span class="item-title">桌面 Rust 客户端正确性修复</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-block">高优 · 紧急</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">8/8 子任务</span>
<button class="reject-btn" data-id="18" data-title="桌面 Rust 客户端正确性修复">拒绝验收</button>
</div>
</div>
<div class="item-desc">取消后晚到 final 残留 CommitBuffer 错上下文注入、stop spawn 无 abort/代际校验、350ms 固定延迟改事件驱动、token/device_id parse unwrap panic、重连不重建会话丢音频、UNAUTHORIZED 无处理+unbounded channel 堆积、emit 全局广播 6 窗口、reqwest Client 每次新建、platform=macos 不符约定、check_update 忽略 force</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">8 / 8 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="18A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18A</span>
<span class="sub-title">dictation 会话代际+取消守卫:取消后晚到 final 不残留、不注入错上下文</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18B</span>
<span class="sub-title">stop spawn 任务加代际校验,快速连说不串文本/不误隐藏新会话浮层</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18C</span>
<span class="sub-title">收尾注入改事件驱动(收到 stop 后 final/usage 即注入)350ms 仅作超时兜底</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18D</span>
<span class="sub-title">ws.rs token/device_id 用 HeaderValue::from_str 优雅处理非法值,不 unwrap panic</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18E</span>
<span class="sub-title">ws.rs 重连后若会话进行中重发 start 或通知客户端会话失败,避免静默丢音频</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18F</span>
<span class="sub-title">ws.rs 处理 UNAUTHORIZED(emit+清 token 引导登录)+音频 channel 改 bounded</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18G">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18G</span>
<span class="sub-title">asr/hotkey 事件改 emit_to(overlay) 定向,不广播 6 窗口</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="18H">
<span class="sub-icon s-done"></span>
<span class="sub-sid">18H</span>
<span class="sub-title">reqwest::Client 改 OnceLock 共享实例;metrics platform 改 mac/win;check_update 处理 force</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="mac">mac</span> <span class="tag t-tag" data-tag="Windows">Windows</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="8"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="后端">
<div class="card-header">
<span class="item-title">后端·用户与周边</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">4/4 子任务</span>
<button class="reject-btn" data-id="8" data-title="后端·用户与周边">拒绝验收</button>
</div>
</div>
<div class="item-desc">跨 epic 依赖:#4。含 v1.1 新增 feedback/telemetry</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">4 / 4 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="8A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">8A</span>
<span class="sub-title">GET /v1/me 聚合(balance/trial/account_state 判定)+ devices 登记</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="8B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">8B</span>
<span class="sub-title">GET /v1/app/latest 版本检查</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="8C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">8C</span>
<span class="sub-title">feedbackmultipart 接收 + magic bytes 校验 + OSS 转存 + 10 条/天限频</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="8D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">8D</span>
<span class="sub-title">telemetrymetrics/batch(白名单/截断/异步入库)+ 每日聚合 P50/P95 + 超标告警</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="9"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="后端,CI/CD">
<div class="card-header">
<span class="item-title">后端·集成测试</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">2/2 子任务</span>
<button class="reject-btn" data-id="9" data-title="后端·集成测试">拒绝验收</button>
</div>
</div>
<div class="item-desc">汇聚点:依赖 #5 #6 #7 #8 主体完成</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">2 / 2 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="9A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">9A</span>
<span class="sub-title">WS E2Ewav 推流断言 partial/final/usage 序列与扣费正确(mock+gummy 双跑)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="9B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">9B</span>
<span class="sub-title">支付 E2E:沙箱/构造回调测幂等、金额校验、入账 + 限频用例(31 次/超 1800s</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="后端">后端</span> <span class="tag t-tag" data-tag="CI/CD">CI/CD</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="13"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="Android">
<div class="card-header">
<span class="item-title">Android 输入法</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">6/6 子任务</span>
<button class="reject-btn" data-id="13" data-title="Android 输入法">拒绝验收</button>
</div>
</div>
<div class="item-desc">先于 iOS。UI 依赖 #3C 主题导出;联网依赖 #3A 与后端 #5 #6</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">6 / 6 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="13A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">13A</span>
<span class="sub-title">工程骨架 + DuduTheme.kt 接入(light/dark</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="13B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">13B</span>
<span class="sub-title">主 App:微信登录 + 启用引导三步 + 我的页(权限自检/试用进度/反馈入口)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="13A 已完成">13A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="13C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">13C</span>
<span class="sub-title">主 App:购买时长页 + 微信 APP 支付</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="13B 已完成">13B</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="13D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">13D</span>
<span class="sub-title">IME Service:键盘 UI 四态(工具条/功能键行/MicBar/partial 文本条)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="13A 已完成">13A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="13E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">13E</span>
<span class="sub-title">IME:录音 + WS 推流 + commitText 上屏 + 上滑取消</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="13D 已完成">13D</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="13F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">13F</span>
<span class="sub-title">反馈页 + metrics 队列(WorkManager flush</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="13B 已完成">13B</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="14"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="iOS">
<div class="card-header">
<span class="item-title">iOS 输入法</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">5/5 子任务</span>
<button class="reject-btn" data-id="14" data-title="iOS 输入法">拒绝验收</button>
</div>
</div>
<div class="item-desc">14D 依赖 #1A spike 结论(主路径或 deep link 降级)。UI 依赖 #3C</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">5 / 5 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="14A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">14A</span>
<span class="sub-title">工程骨架(App+Keyboard Extension+App Group+ DuduTheme.swift 接入</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="14B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">14B</span>
<span class="sub-title">主 App:微信登录 + 引导(含完全访问步)+ 我的 + 购买(APP 支付)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="14A 已完成">14A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="14C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">14C</span>
<span class="sub-title">键盘扩展:UI 四态 + textDocumentProxy 上屏框架</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="14A 已完成">14A</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="14D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">14D</span>
<span class="sub-title">键盘扩展:录音 + WS 推流(按 1A 结论实现主路径或 deep link 降级)</span>
<div class="sub-badges"><span class="tag tier-2">二级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="14C 已完成">14C</span></span></div>
</li>
<li class="subtask-item s-done" data-sid="14E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">14E</span>
<span class="sub-title">反馈页 + metricsApp Group 队列)+ iPad 布局适配</span>
<div class="sub-badges"><span class="tag tier-3">三级</span><span class="tag status-badge s-done">待验收</span><span class="dep-section"><span class="dep-label">依赖:</span><span class="dep-badge dep-done" title="14B 已完成">14B</span><span class="dep-badge dep-done" title="14C 已完成">14C</span></span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="iOS">iOS</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="15"
data-level="mid"
data-status="done"
data-tier="3"
data-tags="Web,前端">
<div class="card-header">
<span class="item-title">官网宣传页(web/index.html</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-3">三级</span>
<button class="reject-btn" data-id="15" data-title="官网宣传页(web/index.html">拒绝验收</button>
</div>
</div>
<div class="item-desc">单页静态宣传页,复用设计系统品牌语言,含按住说话交互演示;后续接真实下载链接与备案号</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Web">Web</span> <span class="tag t-tag" data-tag="前端">前端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-11</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="19"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="前端">
<div class="card-header">
<span class="item-title">桌面前端正确性修复</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">5/5 子任务</span>
<button class="reject-btn" data-id="19" data-title="桌面前端正确性修复">拒绝验收</button>
</div>
</div>
<div class="item-desc">登录 QR 不处理 expired+隐藏窗口永久轮询、设置整结构回写覆盖 token 致登出、overlay 错误帧 BAD_REQUEST/INTERNAL 无文案 setError 空、tray/login 窗口主题未初始化、轮询样板收敛 usePoll</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">5 / 5 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="19A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">19A</span>
<span class="sub-title">登录 QR 轮询处理 expired 自动换码;窗口隐藏(非卸载)时停止轮询</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="19B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">19B</span>
<span class="sub-title">设置窗口改增量写(set_settings 仅传改动项),不整结构覆盖 token</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="19C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">19C</span>
<span class="sub-title">overlay 错误帧兜底文案(BAD_REQUEST/INTERNAL/未知码),不 setError 空串</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="19D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">19D</span>
<span class="sub-title">tray/login 入口补主题初始化(get_settings→setThemePref)</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="19E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">19E</span>
<span class="sub-title">收敛轮询样板为 shared/usePoll 钩子(存活标志+清理+在途去重)</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="前端">前端</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="20"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="Android">
<div class="card-header">
<span class="item-title">Android 客户端正确性修复</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">5/5 子任务</span>
<button class="reject-btn" data-id="20" data-title="Android 客户端正确性修复">拒绝验收</button>
</div>
</div>
<div class="item-desc">onRelease 在 onOpen 前 sendStop 时序错乱致空文本、/v1/me 未发 X-Device-ID 致 touchDevice no-op、错误文案改 server-first、AccountRepository 冗余派生状态简化、KeyboardScreen partial 全屏重组收窄</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">5 / 5 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="20A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">20A</span>
<span class="sub-title">onRelease 等 onOpen 后再 sendStop(或缓冲 stop 待 start 后补发),修握手时序</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="20B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">20B</span>
<span class="sub-title">/v1/me 请求带 X-Device-ID 头,使 touchDevice 生效</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="20C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">20C</span>
<span class="sub-title">错误文案改 server message 优先、本地表仅兜底未知码</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="20D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">20D</span>
<span class="sub-title">AccountRepository 只存 token/balance/trialUsed/limitstate/trialRemaining 推导</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="20E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">20E</span>
<span class="sub-title">KeyboardScreen 拆分状态流,partial 只重组 PartialBar</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="Android">Android</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
<li class="todo-card s-done"
data-id="21"
data-level="mid"
data-status="done"
data-tier="2"
data-tags="iOS">
<div class="card-header">
<span class="item-title">iOS 客户端正确性修复</span>
<div class="card-badges">
<span class="tag status-badge s-done">待验收</span>
<span class="tag t-high">重要</span>
<span class="tag tier-2">二级</span>
<span class="tag sub-progress-badge">7/7 子任务</span>
<button class="reject-btn" data-id="21" data-title="iOS 客户端正确性修复">拒绝验收</button>
</div>
</div>
<div class="item-desc">finishing 阶段周期 usage 帧误判 stop 结算致丢字、AsrAudioCapture stop 与 tap 回调数据竞争崩溃、usage omitempty 0 值处理、CommitController 死代码删除、180s 本地计时器双轨移除、MetricsQueue 每事件全目录扫描、错误文案 server-first、deep link 剪贴板无条件覆盖</div>
<div class="subtask-block">
<div class="subtask-header">
<span class="subtask-label">子任务</span>
<span class="subtask-progress-text">7 / 7 完成</span>
<div class="subtask-progress-bar"><div class="subtask-progress-fill" style="width:100%"></div></div>
</div>
<ul class="subtask-list"><li class="subtask-item s-done" data-sid="21A">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21A</span>
<span class="sub-title">finishing 阶段只用收尾标志/final 结束会话,不把周期 usage 帧当 stop 结算致丢字</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="21B">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21B</span>
<span class="sub-title">AsrAudioCapture stop 与 tap 回调同步(串行队列/锁),消除数据竞争崩溃</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="21C">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21C</span>
<span class="sub-title">applyUsage 处理 usage 0 值(协议去 omitempty 后按 0 覆盖余额/试用)</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="21D">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21D</span>
<span class="sub-title">删除 CommitController 死代码(移出键盘 target)</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="21E">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21E</span>
<span class="sub-title">移除 iOS 180s 本地强制定稿计时器,统一以服务端 SESSION_LIMIT 为准</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="21F">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21F</span>
<span class="sub-title">MetricsQueue 改近似计数/flush 时 trim,不每事件全目录扫描</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li>
<li class="subtask-item s-done" data-sid="21G">
<span class="sub-icon s-done"></span>
<span class="sub-sid">21G</span>
<span class="sub-title">错误文案 server message 优先;deep link 剪贴板覆盖降级为兜底</span>
<div class="sub-badges"><span class="tag status-badge s-done">待验收</span></div>
</li></ul>
</div>
<div class="card-footer">
<div class="tag-row"><span class="tag t-tag" data-tag="iOS">iOS</span></div>
<div class="item-meta">
<span class="meta-date">🕐 2026-06-12</span>
</div>
</div>
</li>
</ul>
</div>
</div>
<div class="section-block" id="section-accepted">
<div class="section-title st-accepted" data-toggle="accepted">
✅ 已验收 <span class="s-count">0</span>
<span class="s-arrow">▾ 展开</span>
</div>
<div class="section-list-wrap collapsed" id="list-wrap-accepted">
<ul class="todo-list" id="list-accepted">
<p class="empty-tip">暂无条目</p>
</ul>
</div>
</div>
</div>
<!-- 拒绝验收 Modal -->
<div id="reject-modal" class="modal-overlay" style="display:none">
<div class="modal-box">
<h3>拒绝验收</h3>
<p class="modal-subtitle" id="reject-modal-subtitle"></p>
<label class="modal-label">拒绝原因<span class="req">*</span></label>
<textarea class="modal-textarea" id="reject-reason-input" rows="3" placeholder="请说明具体问题,Claude 将以此为依据修复…"></textarea>
<div class="modal-err" id="reject-reason-err">请填写拒绝原因</div>
<div id="reject-cmd-wrap" class="modal-cmd-wrap" style="display:none">
<div class="modal-cmd-label">在终端运行以下命令:</div>
<code class="modal-cmd-code" id="reject-cmd-text"></code>
<button class="modal-copy-btn" id="reject-copy-btn">复制命令</button>
</div>
<div class="modal-actions">
<button class="modal-btn modal-btn-cancel" id="reject-cancel-btn">取消</button>
<button class="modal-btn modal-btn-reject" id="reject-confirm-btn">确认拒绝</button>
</div>
</div>
</div>
<script>
(function() {
let curLevel = 'all';
let curStatus = 'all';
let curTier = 'all';
let curTags = new Set();
// 通过本地服务(http)打开时,按钮直接写库;用 file:// 直接打开则回退为「复制命令」
const SERVED = location.protocol === 'http:' || location.protocol === 'https:';
function postAction(url, payload) {
return fetch(url, {
method: 'POST',
headers: payload ? { 'Content-Type': 'application/json' } : undefined,
body: payload ? JSON.stringify(payload) : undefined,
}).then(r => r.json());
}
function applyFilter() {
document.querySelectorAll('.todo-card').forEach(card => {
const lvl = card.dataset.level;
const status = card.dataset.status;
const tier = card.dataset.tier;
const tags = card.dataset.tags ? card.dataset.tags.split(',') : [];
let show = true;
if (curLevel !== 'all' && lvl !== curLevel) show = false;
if (curStatus !== 'all' && status !== curStatus) show = false;
if (curTier !== 'all' && String(tier) !== curTier) show = false;
if (curTags.size > 0 && ![...curTags].some(t => tags.includes(t))) show = false;
card.classList.toggle('hidden', !show);
});
if (curStatus !== 'all') {
const wrap = document.getElementById('list-wrap-' + curStatus);
if (wrap) {
wrap.classList.remove('collapsed');
const title = document.querySelector('[data-toggle="' + curStatus + '"] .s-arrow');
if (title) title.textContent = '▴ 收起';
}
}
}
document.querySelectorAll('[data-filter-level]').forEach(btn => {
btn.addEventListener('click', () => {
curLevel = btn.dataset.filterLevel;
document.querySelectorAll('[data-filter-level]').forEach(b => b.classList.toggle('active', b === btn));
applyFilter();
});
});
document.querySelectorAll('[data-filter-tier]').forEach(btn => {
btn.addEventListener('click', () => {
curTier = btn.dataset.filterTier;
document.querySelectorAll('[data-filter-tier]').forEach(b => b.classList.toggle('active', b === btn));
applyFilter();
});
});
document.querySelectorAll('[data-filter-status]').forEach(btn => {
btn.addEventListener('click', () => {
curStatus = btn.dataset.filterStatus;
document.querySelectorAll('[data-filter-status]').forEach(b => b.classList.toggle('active', b === btn));
applyFilter();
});
});
document.querySelectorAll('[data-filter-tag]').forEach(btn => {
btn.addEventListener('click', () => {
const t = btn.dataset.filterTag;
if (curTags.has(t)) { curTags.delete(t); btn.classList.remove('active'); }
else { curTags.add(t); btn.classList.add('active'); }
applyFilter();
});
});
document.querySelectorAll('.t-tag').forEach(tag => {
tag.addEventListener('click', e => {
e.stopPropagation();
const t = tag.dataset.tag;
const chip = document.querySelector('[data-filter-tag="'+t+'"]');
if (chip) chip.click();
});
});
document.querySelectorAll('.section-title[data-toggle]').forEach(title => {
title.addEventListener('click', () => {
const st = title.dataset.toggle;
const wrap = document.getElementById('list-wrap-' + st);
const arrow = title.querySelector('.s-arrow');
if (!wrap) return;
const collapsed = wrap.classList.toggle('collapsed');
if (arrow) arrow.textContent = collapsed ? '▾ 展开' : '▴ 收起';
});
});
// ── 拒绝验收 Modal ──
const modal = document.getElementById('reject-modal');
const reasonInput = document.getElementById('reject-reason-input');
const reasonErr = document.getElementById('reject-reason-err');
const cmdWrap = document.getElementById('reject-cmd-wrap');
const cmdText = document.getElementById('reject-cmd-text');
const copyBtn = document.getElementById('reject-copy-btn');
let rejectId = null;
document.querySelectorAll('.reject-btn').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
rejectId = btn.dataset.id;
document.getElementById('reject-modal-subtitle').textContent = '#' + rejectId + ' ' + btn.dataset.title;
reasonInput.value = '';
reasonInput.classList.remove('error');
reasonErr.classList.remove('show');
cmdWrap.style.display = 'none';
document.getElementById('reject-confirm-btn').style.display = '';
copyBtn.textContent = '复制命令';
modal.style.display = 'flex';
setTimeout(() => reasonInput.focus(), 80);
});
});
document.getElementById('reject-cancel-btn').addEventListener('click', () => {
modal.style.display = 'none';
});
modal.addEventListener('click', e => {
if (e.target === modal) modal.style.display = 'none';
});
document.getElementById('reject-confirm-btn').addEventListener('click', () => {
const reason = reasonInput.value.trim();
if (!reason) {
reasonInput.classList.add('error');
reasonErr.classList.add('show');
reasonInput.focus();
return;
}
if (SERVED) {
postAction('/api/reject/' + rejectId, { reason })
.then(d => { if (d.ok) { location.reload(); } else { alert('拒绝失败:' + (d.error || '')); } })
.catch(() => alert('请求失败,确认本地服务是否在运行'));
return;
}
const cmd = '/todo reject ' + rejectId + ' ' + JSON.stringify(reason);
cmdText.textContent = cmd;
cmdWrap.style.display = 'block';
document.getElementById('reject-confirm-btn').style.display = 'none';
});
reasonInput.addEventListener('input', () => {
if (reasonInput.value.trim()) {
reasonInput.classList.remove('error');
reasonErr.classList.remove('show');
}
});
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(cmdText.textContent).then(() => {
copyBtn.textContent = '已复制 ✓';
});
});
// ── 确认闸:服务模式直接写库;file:// 模式回退为展开命令 ──
document.querySelectorAll('.approve-btn').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
const id = btn.dataset.id;
if (SERVED) {
btn.disabled = true; btn.textContent = '确认中…';
postAction('/api/approve/' + id)
.then(d => {
if (d.ok) { location.reload(); }
else { btn.disabled = false; btn.textContent = '确认执行 ✓'; alert('确认失败:' + (d.error || '')); }
})
.catch(() => { btn.disabled = false; btn.textContent = '确认执行 ✓'; alert('请求失败,确认本地服务是否在运行'); });
} else {
const box = document.getElementById('approve-cmd-' + id);
if (box) box.style.display = box.style.display === 'none' ? 'block' : 'none';
}
});
});
document.querySelectorAll('.approve-copy-btn').forEach(btn => {
btn.addEventListener('click', e => {
e.stopPropagation();
navigator.clipboard.writeText(btn.dataset.cmd).then(() => { btn.textContent = '已复制 ✓'; });
});
});
})();
</script>
</body>
</html>