Over the 5-day May Day holiday, I built a WeChat mini program — called "Kuaiji Group Chat".
The requirement in one sentence: a tourist walks to a Shaoxing scenic spot, scans a QR code, and watches the local historical figures "argue" in a WeChat group. Lu Xun's Former Residence unlocks the Lu Xun family group, Shen Garden unlocks the Lu You & Tang Wan group, the Orchid Pavilion unlocks the Wang Xizhi group — 6 groups in total.
This post only covers how it was built — the script data structure, the spectator-mode state machine, the LBS radius check, AI calls, the day-by-day rhythm, and the pitfalls. Where the idea came from and why Shaoxing, I'll skip.
The three-piece tech stack
| Layer | Choice | Why |
|---|---|---|
| Front end | WeChat mini program | LBS permissions are smoothest natively, no download, faster review than an app |
| Backend | Tencent CloudBase (cloud functions + NoSQL + cloud storage) | Integrated, no self-ops |
| AI | CloudBase built-in Hunyuan / DeepSeek | cloud.extend.AI called directly in cloud functions, zero integration |
| Dev tools | WeChat DevTools (built-in AI coding, "IDE" below) + Claude Code | Mini program body in the IDE; scripts and cloud functions in Claude Code |
Why not Next.js + Supabase + Vercel: the overseas stack hits three walls in China — latency, filing, WeChat ecosystem integration — impossible to run through in 5 days. The detailed comparison was in the earlier post "2026 one-person-company tech stack"; won't repeat here.
Data structure: turning "group chat" into database documents
The whole product's data model is just 4 collections:
// 1. groups: metadata for the 6 groups
{
_id: "lu-xun-family",
name: "鲁迅家族のChat",
spotId: "luxun-guli", // linked check-in spot
avatarUrl: "...",
memberIds: ["lu-xun", "zhou-zuoren", "zhu-an", "xu-guangping", "run-tu"],
unlockOrder: 1 // open to all users by default
}
// 2. members: all character cards
{
_id: "lu-xun",
displayName: "鲁迅",
age: 48,
personality: "刻薄敏感爱反讽,写作时抽烟",
avatarUrl: "...",
artifactNotes: ["《呐喊》", "线装日记本"] // linked clickable artifacts
}
// 3. scripts: group chat scripts (8-15 messages per group, unlocked in segments)
{
_id: "lu-xun-family-act1",
groupId: "lu-xun-family",
unlockSpotId: "luxun-guli-entrance", // unlocked at which LBS point
messages: [
{ from: "zhou-zuoren", text: "大哥,请你以后不要再到后边院子里来。", type: "text" },
{ from: "zhou-zuoren", text: "[图片]", type: "image", imageUrl: "..." },
{ from: "lu-xun", text: "[已撤回一条消息]", type: "system" },
{ from: "zhu-an", text: "大先生,今天的茴香豆刚买好。", type: "text" },
// ...
]
}
// 4. user_progress: user unlock progress
{
_id: "openid-xxx",
unlockedScripts: ["lu-xun-family-act1", "shen-yuan-act1"],
stamps: ["luxun-guli", "shen-yuan"], // stamp collection
reactions: { "lu-xun-family-act1": ["🍶", "