# Lunaly **Repository Path**: zhudev2026/lunaly ## Basic Information - **Project Name**: Lunaly - **Description**: 微信小程序 用于记录月经例假并结合AI给出分析建议的个人健康管理工具。 对女性用户,可通过此工具管理个人例假期间健康。 对于男性用户,可通过此工具提前预防爱人例假期间可能出现的异常。有助于已婚男士避免在老婆例假期做错事。 - **Primary Language**: Unknown - **License**: Unlicense - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-28 - **Last Updated**: 2026-09-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Lunaly · 微信小程序部署与配置说明 > 女性经期 AI 辅助记录与分析工具。微信原生小程序 + 微信云开发(CloudBase)。 > 本文档对应需求规格 `Lunaly-小程序开发提示词.md` v1.0(决策已锁定版)。 > > **发布形态:自用 / 体验版,个人主体,不公开发布。** --- ## 0. 目录结构 ``` . ├── miniprogram/ 小程序端 │ ├── app.js / app.json / app.wxss │ ├── pages/index/ 首页(会话) │ ├── pages/history/ 历史数据(含任意历史日期补录) │ ├── pages/chart/ 分析图(六态日历 + 三个辅助图表) │ ├── pages/settings/ 设置(存储 / 模型 / 口令 / 通知 / 关于) │ ├── components/chip-group/ 快捷追问卡片 │ ├── components/calendar-heatmap/ 日历图 │ ├── components/sync-banner/ 同步状态条 │ ├── core/crypto.js PBKDF2-SHA256 + AES-256-GCM(纯 JS,零依赖) │ ├── core/mnemonic.js 24 词助记词(内置 1024 词表) │ ├── core/cycle.js 周期推算与预测(确定性) │ ├── core/calendar-state.js 日历状态机(六态 + 三级置信度) │ ├── core/risk.js 风险识别与就医提示 │ ├── core/record.js 记录模型与**字段级合并** │ ├── core/date-util.js 日期工具 │ ├── storage/adapter.js 存储适配器接口 │ ├── storage/cloudbase.js 方式一:微信云开发数据库(默认) │ ├── storage/tencent-docs.js 方式二:腾讯文档智能表 │ ├── storage/cos.js 方式三:对象存储 COS(ETag 乐观锁) │ ├── storage/queue.js 端上加密写重试队列 │ ├── storage/cache.js 端上加密缓存(仅本地模式依赖) │ ├── storage/index.js 存储门面(页面只用它) │ ├── utils/session.js 隐私口令 + 云开发控制面读写 │ ├── utils/llm.js 大模型客户端(算术绝不让模型做) │ └── config.js **非敏感**配置(不含任何密钥) ├── cloudfunctions/ │ ├── getOpenId/ 返回 openid │ ├── docsOAuthCallback/ 腾讯文档 OAuth 回调 + 智能表 API 代理 │ ├── cosSts/ COS 临时密钥签发(按 openid 收窄权限) │ ├── llmProxy/ 大模型中转(解析 / 反馈 / 器械单据图片) │ └── subscribe/ 一次性订阅消息攒票与下发 ├── tests/ 单元测试(Node 直接运行,无需依赖) ├── tools/gen_icons.py 生成 tabBar 图标(仅用标准库) ├── .env.example 云函数环境变量清单 └── project.config.json ``` > 相对交付清单的**增补**(不影响任何【已锁定】决策): > `cloudfunctions/getOpenId`、`storage/cache.js`、以及 `docsOAuthCallback` 额外承担的 > API 代理职责。原因见各自文件头注释。 --- ## 1. 准备环境 | 项 | 要求 | |---|---| | 微信开发者工具 | 稳定版最新 | | 小程序 AppID | 个人主体(自用/体验版) | | 微信云开发 | 开通环境,记下**环境 ID** | | Node.js | 仅用于跑单元测试(≥16) | | Python | 仅用于重新生成图标(可选,≥3.8) | ### 1.1 数据库集合与权限 在云开发控制台 → 数据库中新建两个集合: | 集合 | 用途 | 建议安全规则 | |---|---|---| | `lunaly_users` | **控制面**:openid、加密凭证包、启用标志、票数 | 仅创建者可读写 | | `lunaly_records` | 业务记录(仅当存储方式 = 云开发数据库时使用) | 仅创建者可读写 | 两个集合的安全规则都选「**仅创建者可读写**」:`doc._openid == auth.openid`。 > ⚠️ 合规红线:`lunaly_users` **绝不能**出现任何业务字段(经期记录、症状、预测结果)。 > `lunaly_records` 只在用户选择"云开发数据库"作为存储时才写入。 ### 1.2 云函数部署 对每个云函数目录:右键 →「上传并部署:云端安装依赖」。 其中 `cosSts` 依赖 `qcloud-cos-sts`(已写在 package.json 中,云端会自动安装)。 --- ## 2. 配置 ### 2.1 小程序端(非敏感) 编辑 `miniprogram/config.js`: ```js cloudEnv: '你的云开发环境ID', subscribeTemplateIds: ['模板ID1', '模板ID2', '模板ID3'], ``` ### 2.2 云函数环境变量(敏感,绝不进仓库) 在「云开发控制台 → 云函数 → 选中函数 → 配置 → 环境变量」中填写, 完整清单见 `.env.example`: | 函数 | 变量 | |---|---| | llmProxy | `AI_API_ENDPOINT`、`AI_API_KEY`、`AI_MODEL`(默认 `hy3`)、`VISION_API_ENDPOINT`、`VISION_API_KEY`、`VISION_MODEL` | | cosSts | `COS_SECRET_ID`、`COS_SECRET_KEY`、`COS_BUCKET`、`COS_REGION` | | docsOAuthCallback | `DOCS_CLIENT_ID`、`DOCS_CLIENT_SECRET`、`DOCS_REDIRECT_URI` | | subscribe | `SUBSCRIBE_TEMPLATE_ID` | > 【禁止】把 Client Secret、主账号密钥、API Key 写进任何前端文件或提交进 Git。 --- ## 3. 使用 ### 3.1 首次启动 1. 打开小程序 → 首页会展示隐私说明(§12-5 强制项)。 2. 进入「设置」→「隐私口令」→「一键生成高强度密钥」。 3. **抄写 24 个助记词**(只展示这一次),填写第 N 个单词完成校验后启用。 4. 口令丢失**无法找回**,唯一兜底路径是「重新配置存储」。UI 已明确告知。 > 派生使用 PBKDF2(100000 次迭代)以保证抗暴力破解,首次解锁在手机上约需 1–3 秒, > 界面会显示进度条。这是 §6.1【已锁定】参数,请勿下调。 ### 3.2 记录 - **文字**:直接说「今天来例假了,量中等,肚子有点疼」。 - **语音**:长按话筒(微信同声传译插件)。语音只转文字,原始音频不保存。 - **图片**:仅限器械与单据类(排卵试纸、体温计、药盒、检验单)。 强制先过内容安全检测,解析后立即删除原图,**只留文字总结**。 - **追问**:缺经量或痛经时弹出 chip 卡片,点选即写入;**绝不阻塞保存**, 跳过则存档并在历史页标「待补充」。 ### 3.3 补录与迁移 - 历史页可用日期选择器跳到任意过去日期补录(无时间限制)。 - 补录任意历史日期后,日历图与预测结果**立即**同步修正(无需重算迁移代码)。 --- ## 4. 三种存储方式的配置 ### 4.1 微信云开发数据库(默认) 零配置。设置页点「配置」即启用。 **此模式数据存放于本小程序的云开发环境**(UI 已标注)。 ### 4.2 腾讯文档智能表 1. 打开 https://docs.qq.com/open/ → 注册**个人开发者**(身份认证,审核 1–3 个工作日)。 2. 创建第三方应用,取得 `Client ID` / `Client Secret`。 3. 在云函数 `docsOAuthCallback` 的环境变量中填入: `DOCS_CLIENT_ID`、`DOCS_CLIENT_SECRET`、`DOCS_REDIRECT_URI`。 4. `DOCS_REDIRECT_URI` 必须是**云开发 HTTP 访问服务的默认域名**(HTTPS): 云开发控制台 → HTTP 访问服务 → 开启后复制默认域名,形如 `https://.service.tcloudbase.com/`。 5. 小程序内点「配置」→ 扫码授权 → 自动创建「Lunaly」目录与智能表并写入表头。 **已知限制**:Access Token 30 天、Refresh Token 1 年;免费额度为每个应用 20000 次 API 调用(所有用户共享)。代码已实现自动刷新,刷新失败会引导重新授权 并进入仅本地模式。 > 若腾讯文档官方接口字段名与本仓库 `docsOAuthCallback/index.js` 不一致, > 请以官方文档为准修改该文件(文件头已标注文档地址)。 ### 4.3 对象存储 COS 1. 在 CAM 控制台创建**子账号**,生成 SecretId / SecretKey。 【强制】权限策略限定为:指定 bucket、资源前缀 `lunaly/{openid}/*`、 动作仅 `GetObject / PutObject / ListBucket`。 【禁止】使用主账号密钥。 2. 在云函数 `cosSts` 的环境变量中填入 `COS_SECRET_ID` / `COS_SECRET_KEY` / `COS_BUCKET` / `COS_REGION`。 3. 小程序内点「配置」→ 填写 bucket / region / 子账号密钥 → 自动连通性测试。 **数据组织**:按月分片 `lunaly/{openid}/2026-09.json`;写入带 `If-Match` ETag 乐观锁, 冲突自动重拉 → 字段级合并 → 重试(最多 3 次)。 **连通性测试包含越权校验**:故意访问策略外路径,若未被拒绝则判定配置不合格。 > 若 `wx.request` 报域名不合法,请在开发者工具勾选「不校验合法域名」调试, > 正式使用时把 `.cos..myqcloud.com` 加入小程序后台的 > request 合法域名。 --- ## 5. 大模型接入(§7) | 阶段 | 默认模型 | 额度来源 | |---|---|---| | 测试期 | 微信云开发内置 AI(混元 **Hy3**) | 「AI 小程序成长计划」资源包 | | 正式期 | 用户自己的 TokenHub Hy3 | 用户自己领取的免费额度 | - 报名「AI 小程序成长计划」取得接入地址与密钥后,填入 `llmProxy` 的 `AI_API_ENDPOINT` / `AI_API_KEY`。 - **Hy4 为付费模型,不作为默认模型。** - 未配置或调用失败时,端上会用**确定性规则**兜底生成反馈,绝不静默失败 (§7.3 开发者额度永久兜底)。 - **BYOK 事实披露**:配置自定义模型时,请求与 API Key 必须经云函数中转。 Key 端上加密保存,云函数仅内存解密使用、不写日志、不落库, 但"内存中可见"无法消除 —— 「设置 → 关于」中已如实告知用户。 > **算术绝不让大模型做**(§7.2 铁律):周期长度、预测区间、周期第几天、 > 日历状态全部由 `core/` 下的确定性纯函数计算,模型只做「解析 JSON」和 > 「拿已算好的结果组织语言」两件事。 --- ## 6. 订阅消息(§10) 1. 小程序后台 → 功能 → 订阅消息 → 选用模板,复制**模板 ID**。 2. 填入 `miniprogram/config.js` 的 `subscribeTemplateIds`(一次最多 3 个)。 3. 把要用于推送的模板 ID 填进云函数 `subscribe` 的 `SUBSCRIBE_TEMPLATE_ID`。 4. 给 `subscribe` 函数配置**定时触发器**(如每天 10:00),调用 `action: 'consumeAndSend'`、`kind: 'period_upcoming'`。 **攒票机制**:每次成功授权累积票数(存于控制面),预测区间前 3 天消耗一张票推送; 票数不足自动降级为首页提醒卡片,不静默失败。 **约束**:无诱导式弹窗、首次打开首页不硬弹授权;用户拒绝后不再请求。 --- ## 7. 单元测试 无需任何第三方依赖,直接用 Node 运行: ```bash node tests/core.test.js # 81 项:日期/记录合并/预测/六态/风险/助记词 node tests/crypto.test.js # 15 项:FIPS-197 / RFC 6070 向量 + 与 Node crypto 交叉验证 node tests/storage.test.js # 35 项:缓存/队列/字段级合并/仅本地模式/迁移 ``` 覆盖的验收标准: | # | 标准 | 验证位置 | |---|---|---| | 2 | 断网仍可记录,恢复后重放无丢失无重复 | `tests/storage.test.js` | | 3 | 补录历史后日历与预测立即修正 | `tests/core.test.js` | | 5 | 两台设备改同一天不同字段,合并后都在 | `tests/core.test.js` + `storage.test.js` | | 6 | 预测输出区间 + 置信度,样本增多区间收窄 | `tests/core.test.js` | | 7 | 相同输入结果完全一致(可复现) | `tests/core.test.js` | | 9 | 解绑后远端数据仍在,需二次确认才删除 | `tests/storage.test.js` | --- ## 8. 已锁定的硬约束速查(代码级自查清单) ``` 【禁止】云主机模式(已删除) 【禁止】云开发持有业务数据 → lunaly_users 只含控制面字段 【禁止】把"无记录"当作"非经期" → cycle.extractStarts 只用 period.isStart 【禁止】把推算值写进数据库当作事实 → 所有推算读取时实时计算,标 derived 【禁止】预测只输出单点日期 → predict() 恒返回 [startLow, startHigh] 【禁止】让大模型做算术 → llm.js 的 buildFacts 只传已算好的值 【禁止】出现"安全期"标签 → STATE_TEXT 只含易孕期/不易孕期 【禁止】存储原始语音流与图片文件 → 语音只存文字;图片解析后 deleteTempFile 【禁止】支持敏感影像(经血/卫生巾)上传 → vision 的 prompt 强制拒绝并提示 【禁止】整条覆盖式写入 → record.mergeRecord 逐字段比较 _fts 【禁止】解绑时删除远端数据(除非二次确认)→ settings.onUnbind / onPurgeRemote 【禁止】默认模型使用付费的 Hy4 → config.defaultModel = 'hy3' 【禁止】接入手机系统日历做通知 → 仅 subscribeMessage 【禁止】诱导式订阅消息授权弹窗 → 仅在设置页/关键节点由用户主动触发 【禁止】前端代码或仓库中出现 Client Secret / 主账号密钥 → 仅云函数环境变量 ``` --- ## 9. 已知限制与后续事项 1. **腾讯文档接口字段**以官方开放平台文档为准,若与实现不一致需按官方文档调整 `cloudfunctions/docsOAuthCallback/index.js`(文件头已标注文档地址)。 2. **云开发环境 6 个月到期**:设置页应在到期前 30 天提示续费并建议迁移到自有存储 (`config.envExpireWarnDays` 已预留,提示逻辑可接在此处)。 3. 若未来要公开发布,需额外处理:健康医疗类目资质、《个人信息保护法》敏感个人信息 的单独告知同意与影响评估、生成式 AI 服务的算法备案与安全评估。 当前架构未引入会阻碍这些实现的设计债。 4. tabBar 图标由 `tools/gen_icons.py` 生成(纯标准库)。如需替换: ```bash python tools/gen_icons.py ```