# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 项目简介 微信小程序「生日提醒」+ 自建 Node.js 后端。小程序管理人员和纪念日,后端负责 openid 换取、数据云端备份、定时推送订阅消息。 ## 常用命令 ```bash # 后端本地开发(进 server 目录) cd server npm install npm run dev # node --watch 热重载,监听 3000 # 后端生产启动 npm start # Docker 部署 docker compose up -d --build # 手动触发一次提醒任务(调试用) curl -X POST http://localhost:3000/api/reminder/run ``` 小程序侧无构建命令,用**微信开发者工具**直接打开项目根目录。 ## 架构总览 ### 两个独立部分 | 部分 | 路径 | 说明 | |------|------|------| | 微信小程序 | `pages/` `utils/` `app.js` | 原生微信小程序,无框架 | | 自建后端 | `server/` | Express + better-sqlite3,Node ≥18 | > `cloudfunctions/` 目录是早期云开发遗留,已被 `server/` 取代,**不再使用**。 ### 小程序数据流 ``` 本地 wx.Storage(主存储) ↕ 写时 fire-and-forget 自建后端 SQLite(云端备份) ``` - **本地是主真相源**:所有读写先操作 wx.Storage 内存缓存(`utils/storage.js`),再异步同步后端。 - **失败入队**:`utils/sync.js` 维护 `pending_sync_queue`,同步失败的操作下次启动时 `flush()`。 - **新设备恢复**:`app.js` 启动时,若本地为空则调用 `storage.pullFromCloudIfEmpty()` 从后端拉取全量数据。 ### 后端 API 协议 所有接口用统一的 `action` 字段区分操作,openid 通过 `x-openid` 请求头传递: ``` POST /api/login # wx.login code → openid POST /api/person # action: add / update / delete / sync / get POST /api/anniversary # action: add / update / delete / sync / get POST /api/reminder/run # 手动触发定时任务 ``` 前端 URL 自动选择:开发者工具 → `http://localhost:3000`;体验版/正式版 → `https://wxserver.ymxixi.space`(见 `utils/api.js:resolveBaseUrl`)。 ### 后端主要模块 | 文件 | 职责 | |------|------| | `server/src/index.js` | Express 路由 + 所有 CRUD 函数 | | `server/src/db.js` | better-sqlite3 初始化、建表、旧库 ALTER 迁移 | | `server/src/reminder.js` | cron 定时任务,扫描 `remindEnabled=1` 的纪念日并发微信订阅消息 | | `server/src/wx.js` | 微信接口封装(code2session、sendSubscribeMessage) | | `server/src/lunar.js` | 农历转公历,与前端 `utils/lunar.js` 算法一致 | ### 农历处理 `utils/lunar.js`(小程序)和 `server/src/lunar.js`(后端)是同一套寿星万年历算法,覆盖 1900–2100 年。纪念日记录同时存储公历字段(`solarMonth/solarDay`)和农历字段(`lunarMonth/lunarDay/isLeapMonth`),`isLunar` 标志决定展示和计算逻辑用哪套。 ### 纪念日数据结构关键字段 ```js { id, personId, personName, type, // 'birthday' | 'wedding' | 'engagement' | 'other' customTypeName, // type='other' 时的自定义名称 isLunar, // true = 农历生日 solarMonth, solarDay, // 公历月日(必填,农历纪念日也存转换后的公历供排序) lunarMonth, lunarDay, isLeapMonth, // 农历字段(isLunar=true 时有效) importance, // 'high' | 'medium' | 'low' remindEnabled, remindDays } ``` `lunar_birthday` 是老数据兼容类型,与 `birthday` 等效,由 `isLunar` 决定是否农历,常量和后端均有兼容映射。 ## 后端环境变量 参考 `server/.env.example`,关键变量: ``` WX_APPID # 微信小程序 AppID WX_APPSECRET # 微信小程序 AppSecret WX_TEMPLATE_ID # 订阅消息模板 ID REMINDER_CRON # 默认 "0 9 * * *"(每天9点,Asia/Shanghai) DB_PATH # SQLite 路径,Docker 中挂载到 ./data/birthday.db ```