Files
wxserver/CLAUDE.md
T
yuming e9c7330f31
部署到群晖 / deploy (push) Successful in 57s
数据自愈迁移 + 后端同步幂等,版本号 v2.1.7 → v2.1.8
后端(server/src/index.js)——两处同步幂等:

1. deleteAnniversary 删除不存在的记录时返回 success:true。
   原先返回 false,被前端 sync.js:34 当作失败重新入队,那条删除会永远留在
   pending_sync_queue 里每次启动重试且永远清不掉。

2. updateAnniversary / updatePerson 在记录不存在时退化为插入(upsert)。
   同一类问题:本地才是主真相源,若某条记录当初的 add 没同步成功,
   之后所有 update 都会失败并无限重试。

前端 —— 新增 utils/migrate.js,在 app.js onLaunch 执行:

3. 补齐老数据缺失的农历字段。早期 lunar_birthday 类型只记公历日期,
   没有 lunarMonth/lunarDay,这类记录会被安全降级成按公历算——不崩,但每年日期是错的。
   用它存的公历日期反算回农历补齐。

4. 为孤儿纪念日重建人员。部分纪念日的 personId 指向已不存在的人(历史上删人没级联干净),
   首页按 persons 遍历所以隐形,日历页按 anniversaries 遍历会显示成「未知」。
   用记录自带的 personName 重建,并沿用原 personId,纪念日无需改动。

迁移放在客户端而非后端跑 SQL:本地 wx.Storage 是主真相源,改服务端会被客户端同步覆盖。
每次启动都跑而非记版本号:函数是纯检测式的,无坏数据时零写入零请求,
还能顺带覆盖「从云端拉回坏数据」的情况。顺便把原本是死代码的 initData() 替换掉。

⚠️ 本次后端有改动,需要重新部署;部署顺序应为先后端、后小程序。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 15:55:44 +08:00

107 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-sqlite3Node ≥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
```