diff --git a/.project-docs/30-worklog/tasks/20261009-makelore-product-implementation-eaa0ffa5.md b/.project-docs/30-worklog/tasks/20261009-makelore-product-implementation-eaa0ffa5.md new file mode 100644 index 00000000..b1136f3f --- /dev/null +++ b/.project-docs/30-worklog/tasks/20261009-makelore-product-implementation-eaa0ffa5.md @@ -0,0 +1,63 @@ +# Task: Implement approved product detailed design + +## Identity + +- Task ID: 20261009-makelore-product-implementation-eaa0ffa5 +- Mode: Feature +- Branch: codex/20261009-makelore-product-implementation-eaa0ffa5-product-implementation +- Worktree: D:\Datas\OthersProjects\.codex-worktrees\makelore\20261009-makelore-product-implementation-eaa0ffa5 +- Base commit: fe1a3775c5f785adaa6372bd7ea3102c79331656 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Implement the first complete D01 slice from approved detailed design 28ac43ba: current-account personal-wallet recharge, parent-operates-child-account guidance, visible recipient, and existing payment/order recovery. Own PointWallet/caller, Main billing messages, README and focused unit/Electron tests. + +## Intent And Constraints + +- Concurrent and Planning Gates Passed in this isolated feature worktree; current owner/reservation matches. Preserve all foreign work and canonical documents. User explicitly approved child-account recharge; old parent-only guidance is superseded for permanent point recharge only. Do not change legacy membership, identity, shared funding choice, provider callbacks or frozen order owner. No subagents, live payment, push, merge or deployment. +- Plan: implement backend qualification and both UI projections; test actual child/parent own-wallet orders, shared payer privacy, idempotency/recovery and account switching; run relevant backend/frontend and required client checks; commit each complete slice with evidence. D02–D08 remain subsequent implementation packages, not completion claims of this first slice. + +## Outcome + +- D01 implemented in this worktree. Sidebar passes the authenticated account nickname/username into PointWallet; the wallet explicitly displays the recipient and asks parents to log into the child's account before paying. +- Recharge uses the existing authenticated Main/Host API flow and still submits only product plus request ID. It does not submit a target child, change platform identity, transfer points or alter the selected funding source. +- Server-denied recharge copy no longer tells users to switch to a separate parent identity. Existing pending-order recovery, frozen pricing, payment status confirmation and unknown-outcome behavior remain in place. +- README records the approved current-account rule and WS rollout dependency. Existing captured server fixture remains a pre-D01 fixture; its false capability is still honored rather than fabricated. +- Companion WS implementation: task 20261009-community-product-implementation-b001b1db, branch codex/20261009-community-product-implementation-b001b1db-product-implementation; it updates account/billing capability and order eligibility, community UI and focused tests. LMS has no D01 code change. +- Work remains isolated; no primary checkout edits, subagents, merge, push, deployment or live payment. + +## Verification + +- PASS: `pnpm exec vitest run tests/unit/point-wallet.test.tsx tests/unit/sidebar-token-points.test.tsx tests/unit/works-billing-routes.test.ts tests/unit/works-billing-client.test.ts tests/unit/works-billing-server-contract.test.ts --maxWorkers=2`: five files, 41 tests passed. +- Focused assertions cover actual Sidebar recipient props; child account recharge submits no extra recipient; shared funding is not automatically changed; safe denial copy; old server capability remains authoritative. +- PASS: `pnpm run typecheck`. +- PASS: scoped `pnpm exec eslint` on all changed TS/TSX implementation and test files. +- PASS: `pnpm run build:vite` for Renderer, Main, preload and utility. +- PASS: `pnpm exec playwright test tests/e2e/permanent-point-wallet.spec.ts --workers=1`: one Electron E2E. Verified child recipient/guidance, pending-order reopen, frozen price, one order POST and credit only after server-confirmed success. +- Exact pinned pnpm 10.33.4 used; `pnpm install --frozen-lockfile --offline` used cached dependencies and respected approved scripts. No dependency or lockfile change. +- Companion WS checks passed: 68 backend tests, 138 web tests, web build and rendered-browser payment/account-switch scenarios with synthetic responses. +- Existing build warnings concern chunk size, mixed static/dynamic imports and Browserslist data. Tests use isolated fixtures/synthetic payments; real provider acceptance and real money settlement remain unverified. + +## Follow-ups + +- Integrate with WS D01 and deploy backend eligibility before expecting child-account recharge in this client. Existing 0094 permanent-points storage suffices; this slice adds no migration. +- Validate child-account token acceptance with the actual payment provider before production rollout. +- D02–D08 remain future implementation packages from design 28ac43ba. Subsequent client work covers the specified course/self-study/assignment/publication projections when the corresponding WS/LMS contracts are available. +- Retain the worktree/branch for review and integration. + +## Promotion Candidates + +- Target: .project-docs/40-domain/business-rules.md and .project-docs/20-architecture/system-overview.md. + Proposal: active current-account recharge uses the child's authenticated session when a parent pays for that child; recipient must be visible; account identity and selected shared funding remain unchanged. + Evidence: explicit user decision “家长登录孩子的号进行充值”, approved detailed design 28ac43ba, 41 focused unit tests, Electron E2E and companion WS tests. + Future impact: prevent reintroducing separate parent-wallet recharge guidance or client-side eligibility guesses. + Semantic conflict: supersedes old parent-only permanent-point recharge language; parent-only family administration and other legacy rules remain separate. + Human confirmation: already supplied by the user; canonical promotion still requires Integration Gate. +- Target: .project-docs/30-worklog/current-state.md. + Proposal: after integration, record D01's client implementation and WS rollout dependency, with D02–D08 still pending. + Evidence: this task and companion 20261009-community-product-implementation-b001b1db. + Future impact: keeps implementation and deployed/integrated state distinct. + Semantic conflict: no canonical changes permitted from this feature task. + Human confirmation: no additional product decision needed. diff --git a/README.md b/README.md index 3d9485fb..52bba669 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Makelore 是一个面向软件、视觉创作、智能机器人与个人云智 - AI 编程核心对话运行时:Electron Main 按需启动一个长驻 Pi Agent Server,同一进程内为每条 Conversation 建立独立的 Runtime、Session、凭证视图、扩展上下文和 JSONL 通道,同时复用 Pi 模块与进程启动成本;Server 异常退出时所有旧通道统一失效,下一次恢复只重启一个 Server。正式包中的 Agent Server 从明确传入的 `resources/pi-runtime` 根定位依赖清单与包入口,不从脚本所在目录或应用 `node_modules` 猜测解析。Renderer 不直接启动或调用 Pi,也不读取其 wire 类型。项目与 Conversation 元数据先从本地读取,选中对话后才按需准备对应逻辑线程;输入框不等待运行时准备完成。同一助手工具批次中,会取得项目写租约的 `bash`、`edit`、`write` 与动态产品工具按顺序执行,避免批次在命令启动前自锁;纯只读工具仍可并行。子 Agent 仍使用独立的短生命周期进程,以保留并发配额、故障和写入边界;macOS 上两类后台进程都使用 Electron 自带的 Helper,不在 Dock 中创建额外应用图标。 - AI 编程默认入口:进入 Code 后保持在 `/chat`。顶部返回箭头与侧栏收起图标同排,与右侧项目/会话名称对齐;折叠时保留返回和展开入口。macOS 为原生窗口按钮保留安全空间,窄侧栏仍可使用。下方麦洛品牌与“操作对话/作品”标签行对齐;更浅底色的“作品广场”与“新建项目”采用相同高度、圆角、边框与阴影,并位于品牌和新建项目之间,上下各留 10px;“我的项目”和项目列表紧接新建项目;项目配置及其资源子页不再重复显示此侧栏入口,保留配置页自身的返回操作。尚未选择本地项目时,页面以麦洛 M 标识和居中引导语建立焦点,下方提供独立的“新增项目”主操作以及可横向浏览、直接进入的已有项目卡片;项目创建和进入仍复用侧栏现有流程。已有项目的新 Conversation 在可编辑 Composer 上方沿用同一品牌引导,第一条消息出现后切换为正常时间线。 - AI 编程历史与配额反馈:Pi 上下文压缩不删除用户可见的 active-branch 历史,压缩摘要保持 Main-private;Renderer 首次渲染最近 120 个节点,向上滚动时按 100 个节点追加更早内容并保持阅读位置。Main 精确识别 `token_point_balance_exhausted` / `词元点数余额不足`,只向 Renderer 投影不可重试的稳定配额错误和安全中文提示,重开会话后仍可见且不暴露上游原始错误或 request id。 -- 账号词元点数:左下角账户菜单显示本人永久余额和预占,提供充值、充值订单与分页收支记录。真正新注册赠送 100 点,1 元充值 50 点,永久有效;旧账户不补送,旧会员/订阅/周额度/重置卡入口已移除。家长充值始终进入自己的钱包,青少年可查看自己的精确余额;使用他人共享钱包时仅显示可用状态,不显示付款人的余额或记录。Main 持有凭据并转发充值请求;付款结果未知时沿用原请求并核查原订单,只有服务端确认才显示到账。扫码信息缺失时可到网页账户页继续原订单,付款来源也由网页账户页管理。需配套部署 Works Square 永久点数 API 与 0094 迁移;本地模拟验收不代表真实支付通过。 +- 账号词元点数:左下角账户菜单显示本人永久余额和预占,提供充值、充值订单与分页收支记录。真正新注册赠送 100 点,1 元充值 50 点,永久有效;旧账户不补送,旧会员/订阅/周额度/重置卡入口已移除。家长为孩子充值时登录孩子的账号,页面显示当前昵称和账号,点数进入该账号自己的钱包;无需将孩子身份改为家长。当前有效账号可给本人充值,青少年可查看自己的精确余额;使用他人共享钱包时仅显示可用状态,不显示付款人的余额或记录。Main 持有凭据并转发充值请求;付款结果未知时沿用原请求并核查原订单,只有服务端确认才显示到账。扫码信息缺失时可到网页账户页继续原订单,付款来源也由网页账户页管理,充值不会自动切换共享/个人来源。需配套部署 Works Square 当前账号充值资格更新(本次无需新迁移,原永久点数基础为 0094);本地模拟验收不代表真实支付通过。 - 桌面性能策略:应用窗口使用不透明浅色表面并默认保留硬件合成;仅在显式安全模式或短时间内重复 GPU 进程崩溃时启用软件渲染,并把故障原因保存在用户数据目录。启动关键路径只创建本地应用壳,认证、代理、同步、更新和遥测在首帧后延迟初始化;开发版可通过 `app:performance` 快照观察 GPU、进程、WebContents、事件循环与 Renderer Long Task 聚合指标。 - 后台生命周期:Main 统一维护模块活动状态与任务租约。隐藏窗口、离开模块和关闭开发浏览器会释放非必要连接;生成、Code 执行、下载与发布构建持有租约并在完成后释放。已写入 Pi 的 prompt 或上下文整理即使确认超过 10 秒,也会继续持有运行所有权与后台租约,直到迟到响应、权威事件、明确失败或用户清理使其收敛;此时同一 Conversation 不接受重叠 mutation,其他 Conversation 不受影响。各模块的后台连接、轮询和子进程必须通过同一生命周期入口登记。 - 共享开发浏览器:AI 编程右侧提供项目级浏览器,用户与 Agent 查看并调试同一实时页面、Console 和 Network,支持本地与公网开发地址。 diff --git a/electron/api/routes/works-billing.ts b/electron/api/routes/works-billing.ts index b3cd7f2e..8ef67876 100644 --- a/electron/api/routes/works-billing.ts +++ b/electron/api/routes/works-billing.ts @@ -178,7 +178,7 @@ export async function handleWorksBillingRoutes(req: IncomingMessage, res: Server if (!response.ok) { const detail = record(payload).detail; const rejection = detail && typeof detail === 'object' && record(detail).outcome === 'definitive_rejection'; - const messages: Record = { 401: '登录已失效,请重新登录。', 403: '当前账户不能充值,请联系家长。', 404: '充值档位或订单已不可用,请刷新。', 409: '请先完成或核查已有充值订单;计费服务也可能正在切换。', 410: '账户服务版本不匹配,请更新客户端和服务端。' }; + const messages: Record = { 401: '登录已失效,请重新登录。', 403: '当前账号暂不能充值,请在社区账户页查看原因或联系支持。', 404: '充值档位或订单已不可用,请刷新。', 409: '请先完成或核查已有充值订单;计费服务也可能正在切换。', 410: '账户服务版本不匹配,请更新客户端和服务端。' }; sendJson(res, response.status >= 400 && response.status < 500 ? response.status : 502, { success: false, error: rejection ? '支付服务拒绝了本次请求,请稍后重试。' : messages[response.status] ?? '支付结果尚未确认,请刷新订单或联系运营核查,勿重复付款。', commandOutcome: rejection || [401, 403, 404, 422].includes(response.status) ? 'definitive_failure' : 'unknown', diff --git a/src/components/account/PointWallet.tsx b/src/components/account/PointWallet.tsx index 215e7a79..f4a31b27 100644 --- a/src/components/account/PointWallet.tsx +++ b/src/components/account/PointWallet.tsx @@ -21,7 +21,7 @@ const money = (cents: number) => `¥${(cents / 100).toFixed(2)}`; const time = (value: string) => new Date(value).toLocaleString('zh-CN'); const unresolved = (order: WorksRechargeOrder) => order.status === 'pending' || order.status === 'manual_review'; -export function PointWallet({ active = true }: { active?: boolean }) { +export function PointWallet({ active = true, accountName = '当前登录账号', accountUsername }: { active?: boolean; accountName?: string; accountUsername?: string }) { const [summary, setSummary] = useState(null); const [summaryError, setSummaryError] = useState(false); const [open, setOpen] = useState(false); @@ -166,7 +166,7 @@ export function PointWallet({ active = true }: { active?: boolean }) { {own &&

永久有效 · 预占 {points(own.reserved)} 点

} {usingShared &&

AI 编程使用共享点数:{selected?.shared_available ? '可用' : '已用尽,请联系付款人'}

} {summary?.funding_error &&

{summary.funding_error}

} - {isWorksTokenPointBalanceExhausted(own) && !usingShared &&

{summary?.can_recharge ? '点数已用尽,充值后可继续使用。' : '点数已用尽,请联系家长。'}

} + {isWorksTokenPointBalanceExhausted(own) && !usingShared &&

{summary?.can_recharge ? '点数已用尽,请由家长完成当前账号充值。' : '点数已用尽,当前账号暂不能充值,请联系支持。'}

} {summaryError && } @@ -192,6 +192,8 @@ export function PointWallet({ active = true }: { active?: boolean }) { +

充值到:{accountName}{accountUsername && accountUsername !== accountName ? `(${accountUsername})` : ''}

+

为孩子充值时,请由家长登录孩子的账号完成付款。点数仅进入当前账号的钱包。

{summary?.funding_error &&

{summary.funding_error}

} {usingShared &&

AI 编程使用共享点数({selected?.shared_available ? '可用' : '已用尽'})。下方余额、充值与记录均属于你自己的钱包;充值不会自动切换付款来源。

}
{(['recharge', 'orders', 'history'] as const).map((value) => )}
@@ -199,7 +201,7 @@ export function PointWallet({ active = true }: { active?: boolean }) { {error &&

{error}

} {loading &&

正在读取记录…

} {tab === 'recharge' &&
- {summary && !summary.can_recharge &&

仅家长身份可以充值,请联系家长。你仍可查看自己的余额和收支。

} + {summary && !summary.can_recharge &&

当前账号暂不能充值,请在社区账户页查看原因或联系支持。你仍可查看自己的余额和收支。

} {summary?.can_recharge && !summary.payment_configured &&

充值暂未开放,请稍后再试。

} {pending && checkout?.id !== pending.id &&

已有{labels[pending.status]}订单:{money(pending.amount_cents)} / {points(pending.point_amount)} 点。请先完成或联系运营核查。

} {(!checkout || !unresolved(checkout)) &&
{summary?.recharge_products.map((product) => )}
} diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 874529f0..53ba75c9 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -872,7 +872,7 @@ export function Sidebar({
- {authUser && } + {authUser && }