核心能力 · Five Capabilities
每个能力先讲"用户/业务上拿到什么",再补一行真实路径与 commit。所有能力共一条边界:登录与支付一律不自建,走 wx-gateway 多租户;模型路由一律走 src/lib/azure.ts,业务代码不直连 Azure。
架构 · User → image-studio → 3 deps
应用本体是一份 Next.js + Prisma + Postgres,内部三 vertical 共库共 worker 池;对外依赖压到 3 个:Azure OpenAI(出图)、wx-gateway(登录)、wxpay(付款,也走 wx-gateway)。
关键里程碑 · Timeline
从首页改造与登录沉浸式开场,中段做完两步支付集成切到网关统一收银,再把素材体系拆 vertical,末段补 gallery 一键编辑 + middleware 双因子放行。⭐ 标记的是结构性里程碑。
名场面 · 1 个最值钱的坑(深度)
这条不是一次性踩坑,是相隔三天的两个 commit。先用客户端 JSAPI 接进来,iOS 微信内置浏览器不稳;改走网关托管的统一收银台页。背后真正想解决的不是 SDK bug,是商户号资质要不要每个 MVP 项目各自申请一份这个根本问题。
充值链路从 wx.chooseWXPay 切到 /pay/checkout/[id] 网关统一收银
- 症状
- 62a67ea 上线后,微信内浏览器可拉起付款,但 iOS 微信 H5 / 外部浏览器有概率拉不起 — 用户截图找客服,日志里 WeixinJSBridge is not defined 间歇出现。同时商户号 v3 平台证书 + secret + AppID 散落在每个 MVP 项目各自的 .env,谁泄漏谁背锅。
- 根因
- 微信支付有两条调起路径:客户端 JSAPI(wx.chooseWXPay)只在微信内置浏览器 + 公众号 OAuth 拿到 openid 后稳定,在 H5 外部浏览器和部分 webview 必断;JSAPI 走网关统一收银台则由网关侧 redirect 到带签名的付款页、由网关侧调起再 redirect 回业务方 — 这条路把 SDK 调起的环境检查、商户号配置、回调验签全收口到一处。
- 修法
- (1) 62a67ea 集成 wxpay_jsapi via wx-gateway:POST /api/checkout/start → 网关 /pay/create,落 Payment(status=pending) 拿 { payOrderId, checkoutUrl }。(2) c5ef489 改用统一收银台页:前端直接 window.location.href = checkoutUrl 跳 https://wx.mvp.restry.cn/pay/checkout/[id],弃 wx.chooseWXPay。(3) Webhook → /api/wx/payment-webhook 事件 payment.paid 加积分事务幂等(39c9948)。
- commit
- 62a67ea 接入 · d02f73b /topup 加 wxpay 按钮 · c5ef489 切统一收银 · 79df947 middleware 双因子放行后台可查 Payment。
// app/api/checkout/start/route.ts const r = await fetch(`${WX_GATEWAY_BASE}/pay/create`, { method: 'POST', headers: hmacHeaders(WX_GATEWAY_APP_NAME, WX_GATEWAY_SECRET, body), body: JSON.stringify({ openid, amountFen, returnUrl: '/topup?paid=1' }) }) const { payOrderId, checkoutUrl } = await r.json() await prisma.payment.create({ data: { payOrderId, userId, status: 'pending', ... } }) // 前端拿到 checkoutUrl 后:window.location.href = checkoutUrl ← c5ef489 切点
📊 当前现状 · Status
不写"功能正常 / 反馈良好"这种话。下面五个分组每条都对应可 grep 的事实:生产部署 / 数据规模 / 接入依赖 / 最近 7 天主要变动 / 运维状态。
生产部署
- 主域:design.mvp.restry.cn · pm2 端口 3789 · nginx 反代
- PM2 进程:image-studio(prod 跑 pack 分支,不是 main)
- 部署链路:mvp-deployer(deploy.mvp.restry.cn)zip → manifest → 异步 build → Caddy 自动 HTTPS
- 同主机邻居:packsmith(3792)· image-studio-test(3790)
- 隐藏后台:/mgmt-7bx3k · middleware 双因子放行,不在任何导航
数据规模
- 27 张表(schema.prisma):User · Session · WechatAccount · AccessKey · AdminSession
- 出图域:AIModel · AIEndpoint · Generation · GeneratedImage · Prompt · PromptImage · UserUpload
- 积分/支付:CreditLedger · TopupCode · TopupHistory · TopupRequest · Payment
- 增长域:Invite · CheckIn · WelcomeBonusLog · Announcement · Notification · Feedback
- 独立 Postgres 库:共享 host docker 127.0.0.1:5432 · db / user image_studio
接入依赖(3 个外部)
- wx-gateway:app 名 design-studio-prod(不是 image-studio!)· 网关 https://wx.mvp.restry.cn · 同时承担扫码登录 + wxpay JSAPI
- Azure OpenAI:gpt-image-2 / gpt-image-15 部署 · 国内可用 · 凭据在 vault image-studio/AZURE_OPENAI_* · 重试只重 5xx,429 透传(见债务 P0)
- mvp-deployer:https://deploy.mvp.restry.cn · token 在 vault mvp-deployer/TOKEN · 禁 SSH 走人工,全 HTTP API
最近 7 天主要变动(git log)
- 13be9c7 docs(hermes): add project memory + fix SKILL stack(PG 不是 SQLite)
- 79df947 fix(middleware): allow admin token for dual-auth /api routes
- 2539695 fix(gallery+generate): edit affordance preserves view-large click
- 84d0551 feat(gallery): 一键编辑 button → /generate 预填 sourceImage
- 124e779 feat(brand): neutral root brand + /general 写真卡片 modal 交互
- c5ef489 feat(payment): 切 wx-gateway unified checkout page
- d02f73b feat(payment): /topup 加 wxpay 按钮
- 62a67ea feat(payment): 集成 wxpay_jsapi via wx-gateway
运维状态
- 微信扫码 + wxpay 通:统一收银台后,iOS / 外部浏览器拉起问题不复发,客服无 SDK 相关工单
- 新人风控三维上线(32c55e0):IP / 设备指纹 / openid 三维去重 + WelcomeBonusLog 全留痕,异常注册可回溯
- 邀请返佣 + 首充结算(8f3d0fc):邀请人 15% / 被邀人 10% / 单人封顶 1000,事务幂等
- worker 限流:max_global=8 · max_per_user=3,高峰排队但不雪崩;Azure 429 透传到用户为已知坑
- 分支策略:prod 跑 pack,main 停在 2539695 · 不直合 main
- gitnexus 索引:2470 symbols / 3976 relationships / 106 flows 已建
🚧 未结清债务 · Open Debt
不写"P1 待办 N 条"这种模糊罗列。每条 4 列:优先级(P0 立刻 / P1 本周 / P2 排期) · 工作量(S < 0.5d · M 1-2d · L 3d+) · 待办 · 现状是什么(看了就知道离修好还差什么)。来源:.hermes/memory.md §6 历史踩坑 + README 已知问题 + 近 30 commits 中 fix 标注。
| 优先级 | 工作量 | 待办 | 现状(还差什么) |
|---|---|---|---|
| P0 | M | Azure 429 配额耗尽透传到用户 | 现状:src/lib/azure.ts:78-83 只重试 5xx,429 直接透出 "We are currently servicing too many requests"。修法 A:加 Retry-After 退避 + 队列降级;B:portal 申请扩配额。memory §6.5 标 "等拍板未实施"。 |
| P0 | S | pack 分支与 main 分叉 | prod 实跑 pack,main 停在 2539695 不能直合。当前靠人脑记"别合 main",一旦误操作 prod 回退。需流程化:CI 阻止 PR 到 main / pack 单向 merge / 或把 pack 设默认分支。 |
| P1 | M | 部署 zip 漏带未提交修改 | memory §6.2 铁律:zip 前必 git stash -u 或先 commit/push,否则"功能消失"。当前靠人记,缺 pre-deploy 钩子检查 working tree dirty 即阻止。 |
| P1 | M | prisma migrate deploy 未进 postDeploy | memory §6.3:v2 deployer 不自动跑 migrate,必须放 manifest.postDeploy。漏放就出现 WxFinalizeLog 这类新表 P2021 报错。需把 migrate 步骤固化进项目模板 manifest。 |
| P1 | S | DATABASE_URL 密码被 mask 成 *** | memory §6.4:/api/inventory 拉到的 DATABASE_URL 密码是 ***,重灌就 P1000 鉴权失败。修法:inventory 跳过敏感字段 + 文档要求从 vault 取真值 vault get image-studio/DATABASE_URL。 |
| P1 | L | 部署素材包瘦身常态化 | memory §6.1:legacy public/prompts/<legacy>/* 排除后从 125MB → 29MB,只留 xiaofang 120 目录。后续素材增长需常态策略:CDN 托管 / 按 vertical 拆 zip / 增量部署。 |
| P2 | M | 三 vertical 共用 worker 池 | 当前 max_global=8 是全 vertical 共池,高峰 /pack 把 /general 挤队。需按 vertical 分配配额或按订阅等级优先级队列。 |
| P2 | S | inbound webhook 失败无重投兜底 | wx-gateway 推 payment.paid 失败,本地无重投。当前靠网关侧重试 + 5 分钟 query cron 兜底,但本项目缺 inbound webhook 失败队列与告警。 |