# child-point **Repository Path**: vi-me/child-point ## Basic Information - **Project Name**: child-point - **Description**: 儿童积分兑换系统 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-14 - **Last Updated**: 2026-01-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 儿童积分系统(Nuxt3 + Prisma + MySQL) > 面向手机端使用的多儿童积分系统:根据儿童日常表现累加/扣减积分,支持按周汇总与积分兑换(看电视、玩平板、买玩具等)。采用手机号+密码登录;单账号可管理多个儿童;一周从周一到周日;时区 Asia/Shanghai。 ## 技术栈与架构 - 前端:Nuxt 3(Vue 3)、Pinia、@vueuse/core - 后端:Nuxt Server Routes(Nitro) - ORM/DB:Prisma + MySQL - 认证:JWT(HttpOnly Cookie)+ 中间件解析到`event.context.auth` - 目录结构(同仓): - `pages/`、`layouts/`、`components/`、`plugins/` - `server/api/`(REST 路由) - `server/middleware/`(认证解析) - `server/utils/`(Prisma、Auth 工具) - `prisma/schema.prisma`(数据模型) - `.env`(`DATABASE_URL`, `JWT_SECRET`, `TZ=Asia/Shanghai`) ## 环境变量 ```bash DATABASE_URL="mysql://root:password@127.0.0.1:3306/child_points" JWT_SECRET="please-change-me" TZ="Asia/Shanghai" ``` ## 数据模型概览(Prisma) - `User`:phone(唯一)、passwordHash、createdAt - `Child`:隶属`User`,字段:name、birthday?、height?、avatarUrl?、createdAt - `ActivityTemplate`:可配置“事情”,`pointsPerUnit`可正可负,是否允许数量`allowQuantity`,`unitLabel?`,`description?` 备注,归档`isArchived` - `RewardTemplate`:可配置“兑换项”,`costPoints`为正;`limitPerWeek?`(可选限额),`isArchived` - `ActivityRecord`:日常记录(child+template+date+quantity),冗余快照`points`,可选`note` - `Redemption`:兑换记录(child+reward+date),冗余快照`pointsCost`,可选`note` - `PointsLedger`:积分流水(`earn|spend|adjust`),来源类型(`activity|redemption|adjustment`),用于余额与统计的真实来源 ## 核心业务流程图 ```mermaid flowchart TD A[注册/登录\n手机号+密码] -->|JWT Cookie| B{是否已有儿童} B -- 否 --> C[创建儿童资料] B -- 是 --> D[顶部切换当前儿童] C --> D D --> E[设置页面\n配置事情/兑换模板] D --> F[记录页面\n选择事情+数量] F --> G[写入 ActivityRecord] G --> H[写入 PointsLedger: earn/spend] D --> I[周汇总\n周一至周日] H --> I I --> J[发起积分兑换] J --> K{余额是否足够} K -- 否 --> I K -- 是 --> L[写入 Redemption] L --> M[写入 PointsLedger: spend] M --> I I --> N[统计页面\n记录/兑换列表与趋势] ``` ## API 概览(Server Routes) - 认证 - `POST /api/auth/register`:注册(phone, password) - `POST /api/auth/login`:登录,签发 JWT 到 HttpOnly `auth_token` - `POST /api/auth/logout`:登出,清 Cookie - `GET /api/auth/me`:获取当前用户信息 - 儿童(需登录,均校验归属) - `GET /api/children`、`POST /api/children`、`PATCH /api/children/:id`(支持 name、birthday|null、height、avatarUrl)、`DELETE /api/children/:id` - 事情模板(需登录) - `GET /api/activities/templates`、`POST`(支持 description)、`PATCH /:id`(支持 description、isArchived)、`POST /:id/archive`、`DELETE /:id` - 兑换模板(需登录) - `GET /api/rewards/templates`、`POST`、`PATCH /:id`(支持 limitPerWeek 置空)、`DELETE /:id` - 日常记录(需登录) - `GET /api/records?childId&date` - `POST /api/records`(写`ActivityRecord` + `PointsLedger`) - `DELETE /api/records/:id`(回滚对应流水) - 兑换(需登录) - `GET /api/redemptions?childId&range` - `POST /api/redemptions`(写`Redemption` + `PointsLedger`,校验余额) - `DELETE /api/redemptions/:id`(回滚对应流水) - 汇总统计(需登录) - `GET /api/summary/daily?childId&date` - `GET /api/summary/weekly?childId&weekStart`(周一至周日) - `GET /api/summary/balance?childId` - `GET /api/summary/timeline?childId&range` - 上传(需登录) - `POST /api/upload/avatar`:表单上传头像,校验类型/大小,落地`public/uploads`,返回访问 URL - `GET /api/uploads/:filename`:读取已上传文件,含基础防路径穿越校验 统一:所有查询/写入均以`userId`作用域过滤,`childId`必须属于当前登录用户。 ## 前端页面与交互 - `layouts/mobile.vue`:移动端布局;顶部儿童切换占位、底部 TabBar(设置/记录/周汇总/统计) - `pages/auth/login.vue`、`pages/auth/register.vue`:手机号+密码登录注册 - `pages/settings/index.vue`:儿童管理(新增/编辑姓名、生日、身高、头像上传),事情模板(增改查,支持备注 description、归档/删除),兑换模板(增改查) - `pages/record/index.vue`:按日记录事情、数量(若允许),即时汇总当日分 - `pages/weekly/index.vue`:周一至周日视图,展示每日与总分;支持发起兑换 - `pages/stats/index.vue`:记录与兑换列表、时间过滤与趋势图 - 状态管理(Pinia):`auth`、`children`、`templates`、`records` - 时区/起始周:dayjs + timezone + isoWeek(周一为起始) ## 计算规则与边界 - 余额 = `sum(earn) - sum(spend)`,来源`PointsLedger` - 当日/周得分:按日期范围 + `childId` 汇总`PointsLedger` - 负分事情:`ActivityTemplate.pointsPerUnit`可为负;`quantity`生效 - 回滚幂等:删除记录/兑换需同步删除对应流水 - 限额(可选):同一`RewardTemplate.limitPerWeek`周内总`spend`限制 ## 错误与校验 - 输入校验:zod(422 INVALID_INPUT) - 认证失败:401 UNAUTHORIZED - 业务不足:422 INSUFFICIENT_POINTS(兑换余额不足) - 冲突:409 PHONE_EXISTS - 未知错误:500,返回`requestId` ## 已实现的功能(当前进度) - 工程化:Nuxt3 初始化、依赖、移动端布局与路由 - 数据层:`prisma/schema.prisma` 完整定义(含 height/avatar/description/note),生成 Prisma Client - 认证中间件:`server/middleware/auth.ts`(解析 JWT 至`event.context.auth`) - 认证 API:注册/登录/登出/当前用户 - 业务 API:儿童 CRUD(含生日/身高/头像)、事情模板 CRUD+归档(含备注)、兑换模板 CRUD(周限额可空)、记录增删(含流水)、兑换增删(含流水、余额校验)、汇总(daily/weekly/balance/timeline)、头像上传与文件读取 - 前端页面:登录/注册、设置(儿童编辑、模板备注与归档)、记录、周视图、统计 ## 待实现 / 下一步 - 数据迁移与落库(根据 `.env` 的 `DATABASE_URL`) - 更全面的校验/测试与示例种子数据 - 生产部署与监控:持久化上传目录、备份策略、限流/告警 ## 本地开发与运行 1) 准备 MySQL,并创建数据库(如`child_points`) 2) 在项目根创建`.env`文件,填入`DATABASE_URL`、`JWT_SECRET`、`TZ` 3) 迁移数据库: ```bash npx prisma migrate dev --name init ``` 4) 启动开发: ```bash npm run dev ``` 5) 访问:`/auth/register` → `/auth/login` → `/record` ## 生产部署与环境 - 必填环境变量: - `DATABASE_URL`:MySQL 连接串 - `JWT_SECRET`:JWT 签名密钥 - `TZ=Asia/Shanghai` - 可选: - `NUXT_PUBLIC_BASE_URL`:前端公共基地址(如有反向代理) - 构建与启动: ```bash npm ci npx prisma migrate deploy npm run build npm run start ``` - 文件上传:头像存储于 `public/uploads`,需持久化该目录并保证写权限;静态读取经 `/api/uploads/:filename`。 - 反向代理:确保转发 Cookie(HttpOnly)及 API 前缀,开启 gzip/缓存(uploads 可长缓存)。 --- 如需调整数据模型或 API,请在提交前同步更新本文档对应章节与流程图。