我用 Cursor 三天從零到可上線:uni-app + Fastify 全端小程式復盤
我用 Cursor 在 三天左右的日曆窗口裡,把一個能往生產環境推的全端小程式「藥品過期提醒」從 0 推到可聯調的主幹:錄入效期、提醒、微信側能力等,技術棧是 uni-app (Vue 3) + Fastify + TypeScript + PostgreSQL,pnpm monorepo(apps/backend、apps/miniprogram、packages/shared-types)。同一套範圍,如果完全靠我自己啃文件、手寫樣板,主鏈路至少要 一週量級的人日;把重複勞動丟給模型之後,我把純寫碼 + Code Review 收在 大約兩個工作日,剩下時間主要花在微信控制台、真機聯調和安全收口上。下文按技術模組拆開寫,個人經驗,供參考。

我自己趟下來,覺得最值得帶走的是這三件事:
.cursor/rules):我把「只用 uni.*、遷移策略」寫進倉庫後,新開的對話明顯少跑偏。我做的是 藥品過期提醒:管藥品與效期、到期前提醒,並結合微信能力觸達(訂閱消息等);登入側我留了多種擴展(微信 / 抖音 / 信箱 OTP 等),資料要雲端同步,另外單獨跑 scheduler 掃庫發提醒。
技術上我沒有做「列表 CRUD 示範頁」:uni-app (Vue 3) + Fastify + PostgreSQL,pnpm 拆包;後端有 JWT + 登出後 token_version 作廢、前端有 樂觀更新與衝突回滾、另外還有 定時任務入口 / 守護行程思路 和 生產向 env 驗證,都是能對著 checklist 往真環境推的那種複雜度。
立項時我讓模型先幫我拆里程碑與風險,那段 Prompt 我放在 附錄 A,避免正文過長。
我畫的協作關係是:小程式端 ↔ 自建 HTTPS API ↔ PostgreSQL;packages/shared-types 只做編譯期共享 DTO(零執行期依賴)。請求進 Fastify 後:auth 外掛校驗 JWT,並將 tv(token)與 DB token_version 對齊;藥品寫入走集中 LWW + 邏輯刪除 的 service。到期提醒由 scheduler(獨立行程 / cron / PM2)讀庫後調微信訂閱消息;大檔案我走 upload 路由代理到物件儲存(按環境啟用)。
我自己單幹的時候,光是 workspace、tsconfig、ESLint、Fastify 外掛順序、pages.json 對齊,小半天到一天就沒了。這次我直接把約束寫滿丟給 Cursor:apps/backend(Fastify + TS + tsx watch)、apps/miniprogram(uni-app Vue 3)、packages/shared-types(純 DTO),要 pnpm-workspace.yaml、各包 package.json、基礎 tsconfig/ESLint,以及後端的 JWT 簽發/校驗和外掛式路由骨架。
Prompt(可直接複製)
text 體驗AI代碼助手 代碼解讀複製代碼請搭建 pnpm monorepo,包含三個包:
1)apps/backend:Fastify 4 + TypeScript。開發用 tsx watch;生產建構輸出 dist。配置 ESLint + 偏嚴格的 tsconfig。用 app.ts 集中註冊外掛與路由(按業務域拆成多個路由檔)。提供 JWT HS256 簽發/校驗工具;Fastify 鑑權外掛校驗 Bearer JWT,從 DB 掛載 request.user;預留 token_version(tv)用於登出作廢 JWT 的設計(若一次做不完,用註解說明後續接線)。
2)apps/miniprogram:uni-app Vue 3 + Pinia + TypeScript。最小 pages.json:首頁/我的兩個 tab,login、add 等頁面放在分包骨架中。業務程式碼只用 uni.*,不要直接寫 wx / tt。
3)packages/shared-types:純 TypeScript DTO/型別,給前後端共用,零執行期依賴。
另外生成:pnpm-workspace.yaml、根 package.json 腳本(如 dev:backend、dev:miniprogram、lint、typecheck:backend,名稱可微調但要在 README 片段裡寫清)。
硬性要求:後端啟動時必須先跑「環境變數驗證模組」,再載入任何會讀 JWT 密鑰、DATABASE_URL 等機密的模組;在 server 入口用簡短註解寫清啟動順序。
依賴盡量少;只用 pnpm。除腳手架外不要臆造產品功能。
產出價值:Fastify 路由/外掛習慣、JWT 骨架、能直接 dev 的 uni 最小工程。
人必須盯:對照官方文件核對、路徑別名、env 必須先於會讀密鑰的模組載入(避免 JWT 先於環境變數初始化)。
體感:純手寫腳手架約 半天~一天;我這邊 AI 出首版 + 自己對照文件過一遍,多在 40~90 分鐘(模板潔癖越重越久)。
我遇到的需求聽起來很土:增刪改、多端可看、弱網可用、聯網對帳。真正麻煩的是三件事:併發寫誰說了算、樂觀更新失敗怎麼回滾、刪除用物理刪還是邏輯刪。
Cursor 一上來就給我套了業界裡最常見的一套:LWW(updatedAt)+ is_deleted + 樂觀更新;服務端 batch 返回衝突、客戶端 rollback、登入後 batch/snapshot 對帳。我最後落成的形態大致是 services/medicine-write.ts 管寫入,Pinia 裡 pushUpdateToServer 一類方法管樂觀更新與回滾。
聯調時我遇到過最典型的坑:本地先改了 Pinia,列表 UI 立刻變新,請求失敗後資料彈回舊值,使用者會覺得「閃一下」。一開始我以為是介面偶發 500,後來才發現有時是 409/衝突分支沒統一進 rollback,有時是 batch 裡部分成功部分失敗,我只回滾了整條請求而沒有按 id 合併服務端返回。我讓 Cursor 對著 pushUpdateToServer 和 batch 回應結構把「失敗快照」收窄到請求前深拷貝,並在衝突時走服務端時間戳覆蓋——這類 bug 用 AI 改 diff 很快,但根因一定要自己用日誌和斷點釘死,否則模型會越補越亂。
Prompt(可直接複製)
text 體驗AI代碼助手 代碼解讀複製代碼在現有 Fastify + PostgreSQL 後端、以及 uni-app(Vue 3 + Pinia)客戶端上,實現「藥品列表」域的完整寫入與同步。
後端要求:
- CRUD 使用邏輯刪除:is_deleted 布林欄位;參與同步的資料不要物理硬刪。
- LWW:更新時比較 updatedAt(或客戶端上報的 updatedAt);若服務端更新,則在統一 JSON envelope 裡返回該 id 的衝突資訊。
- 提供 POST batch:接收一批建立/更新/刪除操作,按 id 返回結果與衝突列表。
- 提供適合登入後對帳的 snapshot 或 list 介面(在註解裡寫清推薦客戶端呼叫順序)。
寫入路徑集中到單一 service 模組(例如 medicine-write.ts),路由層只編排參數與 HTTP。
前端要求:
- Pinia 樂觀更新:先改本地狀態 + 持久化,再調 API;失敗則回滾到請求前快照。
- 登入成功後:先把本地待同步變更用 batch 推上去,再拉 snapshot/list 合併。
型別與 packages/shared-types 對齊。僅在併發邊界不直觀處加簡短英文註解。
人必須盯:欄位與產品是否一致、列表查詢與索引、空態/歸檔/重複 ID 等用例。AI 鋪主幹,人收口與測試矩陣。
體感:純手寫 2~3 天;我這邊 大半天~1 天 能出可測主幹(需求不亂改的前提下)。
這條鏈路我自己走過一遍:code → session → 手機號或信箱 OTP → 落庫 → JWT。微信側還有 AES、各種錯誤碼,最耗時的永遠是對齊文件和真機,不是寫那幾行 TypeScript。
Prompt(可直接複製)
text 體驗AI代碼助手 代碼解讀複製代碼實現微信小程式「手機號登入」:uni-app 前端 + Fastify 後端。
前端(uni-app):
- 用 uni.login 取 code;按目前基礎庫版本接入官方「手機號」能力(若模板/元件 ID 因專案而異,用 TODO 標出)。
- POST 到後端,grantType 為 mini_phone,欄位與後端約定一致。
後端(Fastify):
- 調微信 code2Session 換 session;所需 env(appId/appSecret 等)寫在說明裡,程式碼裡禁止寫死密鑰。
- 按微信文件解密手機號載荷;upsert 使用者;簽發 JWT,claims 含 sub(使用者 id)、tv(token_version 整數)。
- POST /v1/auth/login 的請求體按 grantType 做可擴展聯合型別;為後續 email_otp 預留分支或桩實作。
- 錯誤用統一 JSON envelope + 穩定業務錯誤碼;不要把密鑰或微信原始錯誤體原樣返回給客戶端。
檔案拆分參考:wechat-mini-login.ts(服務)、auth-routes.ts(HTTP)、pages/pkg/login/login.vue(頁面)。未寫自動化測試時,附手動聯調檢查清單。
分層示例(與倉庫對齊時可自查路徑):services/wechat-mini-login.ts、routes/auth-routes.ts、pages/pkg/login/login.vue。
體感:業內首次接某家小程式登入,1~2 天聯調很常見;我這次有 AI 鋪骨架,主要卡在 控制台、真機、env,首登跑通多在 小半天量級。
定時掃庫 → 模板消息 → 平台 API → 可觀測;多實例還要想 advisory lock / 幂等 / 單 worker。我讓 Cursor 先出 daemon + dry-run + 結構化日誌 的骨架,跑幾個實例、重試策略、模板 ID 合規仍是我拍板。
調度相關的完整 Prompt 我放在 附錄 B,正文不再重複貼大段。
我後期的節奏基本是:把 tsc / ESLint 輸出整段貼進對話 → 讓模型按檔案小步改 → 我自己做 diff review。401 統一進 uni.request 封裝、堆疊深度決定 navigateBack 還是 switchTab;SMTP 把連線 / 問候 / 讀寫超時拆開,日誌裡能分清是 CONN 掛了還是 MAIL 階段掛了——這些「修修補補」用的 Prompt 我收在 附錄 C,正文只保留思路。
下表我用 「人日」量級 粗估(1 人日 ≈ 8 小時有效產出),方便和上文「約一週 vs 約兩天寫碼」對齊;不是精確會計。
| 模組 | 純手寫粗估 | AI 輔助 + 我 | 相對節省(粗) |
|---|---|---|---|
| 腳手架與工程化 | ~0.5 人日 | ~0.15 人日 | 約 七成 |
| CRUD + 同步與衝突 | ~3 人日 | ~0.5 人日 | 約 八成 |
| 登入與聯調 | ~1.5 人日 | ~0.5 人日 | 約 七成 |
| 定時任務與部署腳本 | ~1 人日 | ~0.25 人日 | 約 七成五 |
| 打磨與修問題 | ~1 人日 | ~0.25 人日 | 約 七成五 |
| 合計(量級) | ~7 人日 | ~1.6 人日 | 約省四分之三工時 |
我吃過虧:只在對話裡口頭說「別用 wx」,過兩週新頁面照樣直連平台 API,import 風格也全亂。*`.cursor/rules/.mdc`** 寫進倉庫之後,新開 Agent 明顯少跑偏——官方文件見 Rules for AI。
起草 Rules 的 Prompt 我放在 附錄 D。我自己習慣先花 二三十分鐘 寫一版能跑的規則,後面每踩一個坑就 追加一條,比從頭寫長篇 README 划算。
我自己復盤下來,省下來的主要是這三塊:
但我仍然要自己扛:架構取捨、安全(鑑權/密鑰/日誌)、關鍵測試、對最終 diff 簽字。工具再強,方向盤還是在我手裡。
#ifdef 與跨端坑怎麼寫進規則附錄 A:立項與邊界(點擊展開)```
text 體驗AI代碼助手 代碼解讀複製代碼我要做一款微信小程式「藥品過期提醒」,並自建後端。
MVP 範圍:維護藥品與效期;到期前提醒使用者(訂閱消息或等價能力);登入後多端資料同步;至少支援一種登入方式(微信手機號),後續可擴展信箱 OTP。
明確不做(MVP):不對接藥房系統、不依賴 OCR、不做複雜營運後台(除非極簡單占位頁)。
技術約束:客戶端 uni-app Vue 3;服務端 Fastify + PostgreSQL;pnpm monorepo,獨立 shared-types/DTO 包;JWT 鑑權;邏輯刪除 + LWW 同步;生產環境必須做 env 驗證、缺關鍵變數時 fail-fast。
請輸出:可落在「約三個日曆日」內的主線里程碑(腳手架 → CRUD/同步 → 登入 → 提醒/任務 → 打磨上線),並列出需要提前注意的風險(審核、模板 ID、scheduler 幂等與多實例等)。
**附錄 B:提醒調度與部署**(點擊展開)```
text 體驗AI代碼助手 代碼解讀複製代碼在現有 Node + PostgreSQL 棧上,增加「藥品到期提醒」調度能力。
交付物:
- 獨立入口腳本(或小型 daemon),可被 cron 或 PM2 拉起:查詢 DB 中到期待提醒記錄,組裝微信訂閱消息載荷,呼叫發送 HTTP(若模板 ID 未配置,用清晰 TODO 桩掉真實請求)。
- 每次執行打結構化日誌:開始/結束、處理條數、失敗原因碼。
- 多實例幂等:在註解裡對比 Postgres advisory lock 與「單 runner 標記」等方案;任選一種簡單防護,避免多行程下重複觸達,並寫清取捨。
- 極簡 PM2 ecosystem.config.cjs 或 Dockerfile 註解二選一(示例即可)。
環境變數:DATABASE_URL、微信相關變數、LOG_LEVEL;生產/類生產模式下缺關鍵變數則 fail-fast。
提供 dry-run 模式:只打日誌描述將發送什麼,不真實呼叫發送介面。
附錄 C:Lint / 401 / SMTP 超時(點擊展開)Prompt C1 — 修型別 / Lint
text 體驗AI代碼助手 代碼解讀複製代碼只修復下面貼出的 TypeScript / ESLint 報錯對應問題。最小 diff,不要順手重構無關程式碼。優先遵守倉庫 README 與 Cursor Rules 裡已有約定。
--- 從下一行起貼上 tsc 或 eslint 完整輸出 ---
Prompt C2 — 401 統一處理
text 體驗AI代碼助手 代碼解讀複製代碼在 uni-app 工程裡,把 HTTP 封裝集中到現有 API 助手(路徑以倉庫為準,常見為 utils/api.ts)。
要求:
- 收到 HTTP 401:清除本地 token;只彈一次使用者可見的 modal;若多個並發請求同時 401,要做去重,避免連彈多次。
- 使用者點掉 modal 後:若頁面棧深度 > 1 則 navigateBack(),否則 switchTab 回首頁或 reLaunch 到登入頁——選一種行為寫死即可,並用簡短英文註解說明原因(便於程式碼審閱)。
- 不要新增 npm 依賴。
Prompt C3 — SMTP / 發信超時拆分(後端)
text 體驗AI代碼助手 代碼解讀複製代碼在郵件發送客戶端(SMTP 或廠商 SDK)裡,把超時拆細:連線超時、問候階段超時、socket 讀寫超時;分別暴露為環境變數並給合理預設值。日誌裡要能區分失敗發生在 CONN 還是 MAIL 階段;禁止列印憑據或郵件正文。
附錄 D:起草 Cursor Rules(點擊展開)```
text 體驗AI代碼助手 代碼解讀複製代碼為本倉庫起草一份 .cursor/rules/*.mdc(Markdown),給 Cursor Agent 用:
輸出:單個 .mdc 檔案可直接保存的正文。
掘金若不支援 `<details>` 摺疊,可將本節整段複製到編輯器「程式碼塊」或移入語雀/飛書後再鏈回。
---
### 結尾
寫完這篇我最大的感受是:**AI 沒有替我負責**,它只是把「搜文件、起樣板、改低級錯」從時間軸上挪走了;真正費神的還是**微信那條長鏈路、同步語義、以及上線前我敢不敢簽字**。如果你也在做小程式 + 自建後端,歡迎 **留言區** 丟你的卡點,我能幫上忙會回。覺得有用可以 **點個讚**,也方便後面還想寫續篇時有個動力。
---
---
原文出處:https://juejin.cn/post/7640008164921114634