Files
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

3.9 KiB
Raw Permalink Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

项目简介

微信小程序「生日提醒」+ 自建 Node.js 后端。小程序管理人员和纪念日,后端负责 openid 换取、数据云端备份、定时推送订阅消息。

常用命令

# 后端本地开发(进 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 标志决定展示和计算逻辑用哪套。

纪念日数据结构关键字段

{
  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