40760aa884
- server:Go 网关(WS 流式识别中继/计费配额/微信登录支付 mock/反馈/埋点),gummy provider 已真实联调 - desktop:Tauri 2(全局快捷键 push-to-talk/浮层/托盘/设置/登录购买/反馈/首启引导) - android:Compose 主 App + IME(键盘内录音直传) - ios:App + 键盘扩展(1A spike 实证键盘内不可录音,走 deep link 听写) - design/design-pipeline:设计系统 + token 导出 iOS/Android 主题 - doc:前后端设计文档(HTML);web:官网宣传页;todo:任务看板 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
433 lines
24 KiB
HTML
433 lines
24 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>dudu · 需要你做的事</title>
|
||
<style>
|
||
:root {
|
||
--accent: #4F6EF7; --accent-text: #3D58DB; --accent-soft: #EEF1FE;
|
||
--bg: #F6F7FA; --card: #FFFFFF;
|
||
--text-1: #1B1E26; --text-2: #5A6072; --text-3: #A6ABB8;
|
||
--border-1: #E4E6EB; --border-2: #D2D5DD;
|
||
--positive: #16A34A; --positive-soft: #DCFCE7;
|
||
--warning: #E8890C; --warning-soft: #FCEFD7;
|
||
--danger: #DC2626; --danger-soft: #FEE2E2;
|
||
--mono: ui-monospace, "SF Mono", Menlo, monospace;
|
||
}
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0; padding: 0 0 80px;
|
||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
|
||
background: var(--bg); color: var(--text-1); line-height: 1.7;
|
||
}
|
||
header {
|
||
background: var(--accent); color: #fff; padding: 36px 32px 30px;
|
||
}
|
||
header h1 { margin: 0 0 4px; font-size: 26px; }
|
||
header p { margin: 0; opacity: .85; font-size: 14px; }
|
||
.progress-wrap { margin-top: 18px; max-width: 520px; }
|
||
.progress-text { font-size: 13px; margin-bottom: 6px; font-family: var(--mono); }
|
||
.progress-bar { height: 8px; background: rgba(255,255,255,.25); border-radius: 4px; overflow: hidden; }
|
||
.progress-fill { height: 100%; background: #fff; border-radius: 4px; width: 0; transition: width .3s cubic-bezier(.2,.8,.2,1); }
|
||
.wrap { max-width: 880px; margin: 0 auto; padding: 0 24px; }
|
||
|
||
.group-title {
|
||
display: flex; align-items: center; gap: 10px;
|
||
font-size: 16px; font-weight: 700; margin: 36px 0 14px; padding: 10px 16px;
|
||
border-radius: 8px; border-left: 4px solid;
|
||
}
|
||
.g-now { background: var(--danger-soft); color: #991B1B; border-color: var(--danger); }
|
||
.g-week { background: var(--warning-soft); color: #92400E; border-color: var(--warning); }
|
||
.g-later{ background: var(--accent-soft); color: var(--accent-text); border-color: var(--accent); }
|
||
.group-title .count { margin-left: auto; font-size: 12px; font-weight: 500; opacity: .75; }
|
||
|
||
.task {
|
||
background: var(--card); border: 1px solid var(--border-1); border-radius: 14px;
|
||
margin-bottom: 14px; overflow: hidden;
|
||
}
|
||
.task.done { opacity: .55; }
|
||
.task-head {
|
||
display: flex; align-items: flex-start; gap: 14px;
|
||
padding: 16px 20px; cursor: pointer; user-select: none;
|
||
}
|
||
.task-head:hover { background: #FCFCFD; }
|
||
.check {
|
||
flex: none; width: 24px; height: 24px; margin-top: 2px;
|
||
border: 2px solid var(--border-2); border-radius: 7px; background: var(--card);
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
color: transparent; cursor: pointer; transition: all .12s;
|
||
}
|
||
.task.done .check { background: var(--positive); border-color: var(--positive); color: #fff; }
|
||
.task-title { font-size: 16px; font-weight: 600; }
|
||
.task.done .task-title { text-decoration: line-through; color: var(--text-2); }
|
||
.task-meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 5px; font-size: 12px; }
|
||
.pill { padding: 1px 10px; border-radius: 999px; font-weight: 600; }
|
||
.pill-time { background: var(--accent-soft); color: var(--accent-text); }
|
||
.pill-block { background: var(--danger-soft); color: var(--danger); }
|
||
.pill-ref { background: #F0F1F4; color: var(--text-2); font-family: var(--mono); font-weight: 500; }
|
||
.chevron { margin-left: auto; color: var(--text-3); transition: transform .15s; flex: none; margin-top: 6px; }
|
||
.task.open .chevron { transform: rotate(90deg); }
|
||
|
||
.task-body { display: none; border-top: 1px solid var(--border-1); padding: 18px 22px 20px 58px; }
|
||
.task.open .task-body { display: block; }
|
||
.why {
|
||
font-size: 13.5px; color: var(--text-2); background: var(--bg);
|
||
border-left: 3px solid var(--border-2); padding: 8px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;
|
||
}
|
||
.flow-label { font-size: 12px; font-weight: 700; color: var(--text-3); letter-spacing: .08em; margin: 14px 0 8px; }
|
||
ol.flow { margin: 0; padding-left: 4px; counter-reset: step; list-style: none; }
|
||
ol.flow > li {
|
||
counter-increment: step; position: relative; padding: 0 0 14px 38px; font-size: 14px;
|
||
}
|
||
ol.flow > li::before {
|
||
content: counter(step); position: absolute; left: 0; top: 1px;
|
||
width: 24px; height: 24px; border-radius: 50%;
|
||
background: var(--accent-soft); color: var(--accent-text);
|
||
font-size: 12.5px; font-weight: 700; display: flex; align-items: center; justify-content: center;
|
||
font-family: var(--mono);
|
||
}
|
||
ol.flow > li:not(:last-child)::after {
|
||
content: ""; position: absolute; left: 11.5px; top: 27px; bottom: 1px; width: 1px; background: var(--border-1);
|
||
}
|
||
ol.flow .sub { display: block; font-size: 13px; color: var(--text-2); margin-top: 2px; }
|
||
code {
|
||
background: #F0F1F4; color: var(--accent-text); padding: 1px 7px; border-radius: 4px;
|
||
font-family: var(--mono); font-size: 12.5px;
|
||
}
|
||
.deliver {
|
||
margin-top: 14px; padding: 10px 14px; border-radius: 8px;
|
||
background: var(--positive-soft); font-size: 13.5px;
|
||
}
|
||
.deliver b { color: var(--positive); }
|
||
.warn-inline { color: var(--danger); font-weight: 600; }
|
||
a { color: var(--accent-text); }
|
||
footer { text-align: center; color: var(--text-3); font-size: 13px; margin-top: 44px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<h1>需要你做的事</h1>
|
||
<p>dudu 项目 · 只有你能办的事项清单 · 勾选状态保存在本地浏览器 · 更新于 2026-06-11</p>
|
||
<div class="progress-wrap">
|
||
<div class="progress-text" id="ptext">0 / 0 已完成</div>
|
||
<div class="progress-bar"><div class="progress-fill" id="pfill"></div></div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="wrap">
|
||
|
||
<!-- ════════ 现在就做 ════════ -->
|
||
<div class="group-title g-now">🔴 现在就做 — 直接阻塞开发,每项 ≤ 15 分钟<span class="count">3 项</span></div>
|
||
|
||
<div class="task" data-id="spike-ios">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">iOS spike 真机验证:键盘扩展能否录音</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">约 5 分钟</span>
|
||
<span class="pill pill-block">阻塞 iOS 全线(14D 技术路线)</span>
|
||
<span class="pill pill-ref">看板 1A</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:iOS 键盘扩展内能否直接录音决定 iPhone/iPad 的产品体验走"键盘内主路径"还是"deep link 降级方案"。工程我已写好并通过模拟器编译,只缺真机结论。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>终端执行 <code>open ios/spike-kb-mic/DuduKbMicSpike.xcodeproj</code><span class="sub">没有付费开发者账号也行:用免费 Apple ID 个人签名(7 天有效,spike 够用)</span></li>
|
||
<li>Xcode 中给 <b>App 和 KeyboardExt 两个 target</b> 分别设置签名<span class="sub">Signing & Capabilities → Team 选你的 Apple ID;Bundle ID 冲突就在前面加自己的前缀</span></li>
|
||
<li>选 App scheme + 你的 iPhone(数据线连接 + 信任电脑),⌘R 安装运行</li>
|
||
<li>手机上打开 App → 点「测试 App 内录音(对照组)」→ <b>允许麦克风权限</b><span class="sub">关键一步:键盘扩展自己弹不出权限框,必须靠容器 App 先拿到授权</span></li>
|
||
<li>设置 → 通用 → 键盘 → 添加新键盘 → DuduKbMicSpike → 点进去打开<b>「允许完全访问」</b></li>
|
||
<li>打开备忘录,长按地球键切到 DuduKbMicSpike 键盘 → 点「测试键盘内录音(1 秒)」</li>
|
||
<li>把键盘上显示的结果原文发给 Claude<span class="sub">「✅ 收到 N 帧」= 主路径可行 |「❌ 0 帧 / 报错」= 走降级方案</span></li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>键盘上显示的结果文字(截图也行)</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="dashscope-key">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">开通阿里云百炼,拿 DashScope API Key</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">约 15 分钟</span>
|
||
<span class="pill pill-block">阻塞真实语音识别(6B/9A/12B 延迟验收)</span>
|
||
<span class="pill pill-ref">看板 6B</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:后端 gummy provider 代码已写好,全链路目前跑的是 mock 假识别。拿到 Key 后改一个环境变量即切真实大模型,桌面端就能说真话出真字。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>登录阿里云 → 搜「百炼」或直达 <code>bailian.console.aliyun.com</code> → 同意开通服务<span class="sub">新用户一般送免费额度;gummy-realtime 按音频时长计费,先确认下价格页</span></li>
|
||
<li>控制台右上角「API-KEY 管理」→ 创建 API-KEY(<code>sk-</code> 开头)</li>
|
||
<li>存入 Bitwarden:<code>rbw add dashscope-api-key</code>(密码字段填 Key)<span class="sub">按全局规则不放明文文件/环境变量</span></li>
|
||
<li>告诉 Claude「key 已就绪,条目名 dashscope-api-key」</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>Bitwarden 条目名(不要直接发 Key 内容)</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="desktop-dev">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">桌面端首次联调:在你的 Mac 上跑起来</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">约 10 分钟</span>
|
||
<span class="pill pill-block">阻塞 12A 全链路联调(无头环境验证不了快捷键/注入)</span>
|
||
<span class="pill pill-ref">看板 12A</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:快捷键监听、麦克风采集、文字注入、浮层不抢焦点这四件事只有真实桌面环境能验证。我能改代码、看日志,但按键和说话得你来。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>起后端(终端 1):<code>cd server && docker compose up -d && go run ./cmd/server</code><span class="sub">确认输出 dudu server listening;当前 ASR_PROVIDER=mock,说什么都会出预置句子,属正常</span></li>
|
||
<li>起桌面端(终端 2):<code>cd desktop && npm run tauri dev</code><span class="sub">首次会编译 Rust,几分钟;之后秒起</span></li>
|
||
<li>系统弹窗依次授予:<b>麦克风权限</b>、<b>辅助功能权限</b>(系统设置 → 隐私与安全性 → 辅助功能,勾选 dudu/终端)</li>
|
||
<li>设置窗口里点「微信登录」走 mock 扫码流程(开发期任意 code 都能登录)</li>
|
||
<li>打开备忘录,光标点进正文,<b>按住 ⌘⇧Space 说一句话再松开</b></li>
|
||
<li>预期:浮层弹出「聆听中」→ 流式出字 → 松开后文字注入备忘录<span class="sub">任何一步不符合预期,把两个终端的报错输出发给 Claude</span></li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>「跑通了」或具体卡在哪一步 + 终端报错</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════ 本周启动 ════════ -->
|
||
<div class="group-title g-week">🟠 本周启动 — 外部审核周期长,越早提交越好<span class="count">4 项</span></div>
|
||
|
||
<div class="task" data-id="domain-icp">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">购买域名 + ICP 备案</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">操作 1 小时 · 审核 1-3 周</span>
|
||
<span class="pill pill-block">微信开放平台回调域名、支付回调、宣传页上线的共同前置</span>
|
||
<span class="pill pill-ref">前置于 2A/2B</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:微信「网站应用」的授权回调域名<span class="warn-inline">必须是已备案域名</span>,支付回调和官网同理。备案周期 1-3 周,是整条微信链路的最长前置,必须最先动。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>在阿里云万网查询并购买域名(如 <code>duduime.com</code> / <code>dudu.app</code> 类,注意 .app 等部分后缀不支持备案,<b>选 .com/.cn 稳妥</b>)</li>
|
||
<li>域名实名认证(企业主体,需营业执照)</li>
|
||
<li>阿里云 ICP 代备案系统提交:营业执照、法人身份证、域名证书、网站信息(名称写「dudu 语音输入法官网」类)<span class="sub">需要一台阿里云服务器/轻量应用服务器作为备案载体——顺手买一台最低配 ECS,之后部署后端也用它</span></li>
|
||
<li>等管局审核(1-3 周),期间会有核验电话/短信</li>
|
||
<li>备案号下来后告诉 Claude → 我更新宣传页页脚 + 配置部署</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>域名 + 备案号 + ECS 实例信息</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="wechat-open">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">微信开放平台:账号认证 + 创建两个应用</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">材料半天 · 审核约 7 个工作日</span>
|
||
<span class="pill pill-block">阻塞真实微信登录(桌面扫码 + 移动端一键登录)</span>
|
||
<span class="pill pill-ref">看板 2A</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:当前登录走 mock。桌面扫码用「网站应用」,手机 App 用「移动应用」,两个都要建,靠 unionid 把同一用户归一(后端已按此实现)。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>注册 <code>open.weixin.qq.com</code> 账号(企业邮箱)→ 完成<b>开发者资质认证</b>(¥300/年,需营业执照、对公打款验证)</li>
|
||
<li>创建<b>网站应用</b>:名称「dudu 语音输入」、官网地址、授权回调域名填备案好的域名<span class="sub">依赖上一项的备案域名;审核约 7 个工作日</span></li>
|
||
<li>创建<b>移动应用</b>:iOS 填 Bundle ID(建议 <code>app.dudu.ime</code>)、Android 填包名 + 签名 MD5<span class="sub">Android 签名:先让 Claude 生成正式签名 keystore,再取 MD5 填入</span></li>
|
||
<li>两个应用审核通过后,把 4 个凭证存入 Bitwarden:<code>rbw add wechat-web-appid</code>、<code>wechat-web-secret</code>、<code>wechat-mobile-appid</code>、<code>wechat-mobile-secret</code></li>
|
||
<li>告诉 Claude「微信凭证就绪」→ 我替换 mock 实现并联调真实扫码</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>Bitwarden 4 个条目名 + unionid 是否已开通的确认</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="wechat-pay">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">微信支付商户号 + API 证书配置</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">材料半天 · 审核 1-5 个工作日</span>
|
||
<span class="pill pill-block">阻塞真实收款(7D 真实支付联调)</span>
|
||
<span class="pill pill-ref">看板 2B</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:订单/入账/对账逻辑已完成并通过测试,差真实商户号验签收款。Native(桌面扫码)和 APP(移动收银台)两个支付产品都要开。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>在 <code>pay.weixin.qq.com</code> 申请商户号:营业执照、法人身份证、对公账户、经营信息(类目选软件/SaaS 类)</li>
|
||
<li>审核通过后做对公账户打款验证、签署协议</li>
|
||
<li>产品中心开通 <b>Native 支付</b> 和 <b>APP 支付</b> 两个产品</li>
|
||
<li>账户中心 → API 安全:设置 <b>APIv3 密钥</b>(32 位)、申请并下载<b>商户 API 证书</b>(apiclient_key.pem)</li>
|
||
<li>产品中心 → AppID 账号管理:<b>绑定开放平台的两个 AppID</b>(关键,不绑无法下单)</li>
|
||
<li>入 Bitwarden:<code>rbw add wxpay-mchid</code>、<code>wxpay-apiv3-key</code>、<code>wxpay-cert-serial</code>、私钥 PEM 存 <code>wxpay-private-key</code><span class="sub">回调地址我来配(需要备案域名 + HTTPS)</span></li>
|
||
<li>告诉 Claude「商户号就绪」→ 我接 wechatpay-go 真实实现,先 1 分钱真实订单联调</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>Bitwarden 4 个条目名 + 已绑定 AppID 的确认</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="copyright">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">软件著作权申请</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">材料 1 小时 · 登记 30-60 个工作日</span>
|
||
<span class="pill pill-block">阻塞应用商店上架(华为/小米等安卓市场硬性要求)</span>
|
||
<span class="pill pill-ref">看板 2C</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:国内安卓应用市场上架要求软著证书,周期最长(30-60 个工作日),不卡开发但卡发布,宜早提交。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>让 Claude 准备材料:源代码文档(前后各 30 页,每页 50 行)+ 软件说明书<span class="sub">代码已有,说一声我就导出整理好</span></li>
|
||
<li>中国版权保护中心 <code>ccopyright.com.cn</code> 注册账号(企业主体)</li>
|
||
<li>在线填报:软件全称(如「嘟嘟语音输入法软件 V1.0」)、开发完成日期、开发方式(独立开发)等</li>
|
||
<li>上传材料 + 邮寄纸质件(按系统提示);官方登记免费,着急可走代理加急(数百到千元)</li>
|
||
<li>收到受理通知 → 等证书(电子版即可用于上架)</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>受理号;证书下来后存档</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ════════ 稍后 ════════ -->
|
||
<div class="group-title g-later">⚪ 稍后 — 不阻塞当前开发,到节点再做<span class="count">3 项</span></div>
|
||
|
||
<div class="task" data-id="apple-dev">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">Apple Developer Program(¥688/年)</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">申请 1-2 天</span>
|
||
<span class="pill pill-ref">iOS 正式开发与上架前</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:spike 用免费签名即可,但正式开发需要它——TestFlight 内测、微信 SDK 的 Universal Link、App Group 正式配置、App Store 上架都依赖。iOS 主线(#14)开工前办好即可。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>决定主体:个人账号(快)或公司账号(需邓白氏编码 D-U-N-S,多 1-2 周)<span class="sub">付费软件 + 微信支付建议公司主体,与微信/商户号主体一致</span></li>
|
||
<li><code>developer.apple.com</code> 注册并缴费</li>
|
||
<li>开通后告诉 Claude:我来配 App ID(<code>app.dudu.ime</code>)、App Group、键盘扩展 entitlements</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>账号就绪通知 + Team ID</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="prod-infra">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">生产环境资源开通(阿里云)</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">约 1 小时</span>
|
||
<span class="pill pill-ref">内测部署前</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:内测前需要一套生产环境。备案时买的 ECS 可先兼任,正式内测建议按下面清单补齐(都选与百炼同地域,降低识别链路延迟)。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>ECS(备案那台可复用,2C4G 起步)+ 绑定域名 + HTTPS 证书(阿里云免费 DV 证书)</li>
|
||
<li>RDS PostgreSQL 最低配 + Redis 社区版最低配(或先单机 Docker 跑,量起来再上托管)</li>
|
||
<li>OSS Bucket(反馈图片,私有读写)+ 创建子账号 AK 存入 Bitwarden:<code>rbw add aliyun-oss-ak</code></li>
|
||
<li>告诉 Claude → 我写部署脚本(Docker Compose / systemd)+ 上线宣传页和后端</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>各资源连接信息(密钥走 Bitwarden)</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="task" data-id="acceptance">
|
||
<div class="task-head">
|
||
<span class="check">✓</span>
|
||
<div>
|
||
<div class="task-title">验收看板上的「待验收」条目(持续)</div>
|
||
<div class="task-meta">
|
||
<span class="pill pill-time">随时 · 每条 1 分钟</span>
|
||
<span class="pill pill-ref">当前 6 条待验收</span>
|
||
</div>
|
||
</div>
|
||
<span class="chevron">›</span>
|
||
</div>
|
||
<div class="task-body">
|
||
<div class="why">为什么:按工作流约定,accepted 状态只有你能标。当前待验收:#3 协议与基建、#4 数据层、#5 认证、#7 计费配额、#8 用户与周边、#15 宣传页。</div>
|
||
<div class="flow-label">流程</div>
|
||
<ol class="flow">
|
||
<li>打开看板 <code>todo/todo.html</code> 浏览「待验收」分组,或直接看交付物(文档/宣传页/测试输出)</li>
|
||
<li>认可 → 对 Claude 说 <code>/todo done 3</code>(逐条);或一句话「3 4 5 7 8 15 都验收」</li>
|
||
<li>不认可 → <code>/todo reject <id> <原因></code>,条目自动退回并升为最高优先级</li>
|
||
</ol>
|
||
<div class="deliver"><b>交付:</b>验收或拒绝的指令</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<footer>dudu · 需要你做的事 · 配套看板 <a href="todo.html">todo.html</a></footer>
|
||
|
||
<script>
|
||
// 折叠展开 + 勾选持久化(localStorage)
|
||
const KEY = 'dudu-forme-v1';
|
||
const saved = JSON.parse(localStorage.getItem(KEY) || '{}');
|
||
const tasks = [...document.querySelectorAll('.task')];
|
||
|
||
function refresh() {
|
||
const done = tasks.filter((t) => t.classList.contains('done')).length;
|
||
document.getElementById('ptext').textContent = `${done} / ${tasks.length} 已完成`;
|
||
document.getElementById('pfill').style.width = (done / tasks.length * 100) + '%';
|
||
}
|
||
|
||
tasks.forEach((t) => {
|
||
const id = t.dataset.id;
|
||
if (saved[id]) t.classList.add('done');
|
||
t.querySelector('.task-head').addEventListener('click', (e) => {
|
||
if (e.target.closest('.check')) return;
|
||
t.classList.toggle('open');
|
||
});
|
||
t.querySelector('.check').addEventListener('click', () => {
|
||
t.classList.toggle('done');
|
||
saved[id] = t.classList.contains('done');
|
||
localStorage.setItem(KEY, JSON.stringify(saved));
|
||
refresh();
|
||
});
|
||
});
|
||
refresh();
|
||
// 默认展开第一组未完成项
|
||
const firstOpen = tasks.find((t) => !t.classList.contains('done'));
|
||
if (firstOpen) firstOpen.classList.add('open');
|
||
</script>
|
||
</body>
|
||
</html>
|