# 小菜记账 (Xiaocai Accounting) 可爱 × 高级的个人记账小程序。让记账从负担变成享受。 ## Tech Stack ### 前端 (client/) - **框架**: Uni-app (Vue 3 + Composition API) - **状态管理**: Pinia - **样式**: SCSS (Claymorphism 风格) - **图标**: Lucide Icons (SVG 组件,禁止 emoji) - **图表**: uCharts (小程序端) - **目标平台**: 微信小程序 / H5 ### 后端 (server/) - **运行时**: Node.js + Express - **数据库**: MySQL 8.0 (mysql2 连接池) - **认证**: HMAC-SHA256 签名 token,30 天过期 - **文件上传**: multer (头像,最大 2MB) - **备份**: Node.js mysql2 导出 (不依赖 mysqldump) - **CORS**: 已配置,支持 credentials ## Design System 完整设计规格: `docs/superpowers/specs/2026-05-27-xiaocai-design.md` ### 核心设计语言: Claymorphism (软3D粘土风格) - **主色调**: 珊瑚橙 `#FF8C69` — 温暖、活泼、有亲和力 - **背景色**: 暖奶油 `#FFF8F0` — 比纯白更柔和 - **文字色**: 暖深棕 `#2D1B1B` — 不用纯黑 - **圆角**: 卡片 20px, 按钮 14-16px - **阴影**: 双层 (外阴影 + 内阴影,模拟粘土质感) ### 字体 - 英文: Fredoka (标题) + Nunito (正文) - 中文: PingFang SC / 系统默认 - 数字和金额使用 Fredoka,等宽数字 ### 关键原则 - 最小触摸目标 44×44px - 禁止使用 emoji 作为图标,统一使用 Icon 组件 - 尊重 prefers-reduced-motion - 文字对比度 ≥ 4.5:1 (WCAG AA) ### 小程序胶囊适配 - 微信小程序右上角有胶囊按钮,页面顶部右侧元素(图标、按钮)需避开 - 使用 `system.ts` 导出的 `capsuleRight` 计算安全距离 - 导航栏添加动态 padding: `:style="{ paddingRight: capsuleRight + 'px' }"` - 居中标题页面(统计、账单、我的)不需要处理 - 有右侧元素的页面(首页 bell、编辑页保存按钮)必须处理 ## Project Conventions ### 命名 - 目录: kebab-case (`category-manage/`, `transaction-item/`) - 组件: PascalCase (`AmountCard.vue`, `TransactionItem.vue`) - 变量/函数: camelCase (`totalExpense`, `formatAmount()`) - 常量: UPPER_SNAKE_CASE (`MAX_BUDGET`, `STORAGE_KEY`) - CSS 类: BEM 或 Tailwind 风格 ### 文件组织 ``` client/src/ ├── pages/ # 页面 (uni-app 规范) │ ├── index/ # 首页仪表盘 │ ├── add/ # 添加/编辑记录 │ ├── stats/ # 统计分析 │ ├── bills/ # 全部账单 (下拉刷新/上拉加载) │ ├── profile/ # 我的(身份切换入口) │ ├── profile-edit/ # 编辑资料(昵称+头像+签名) │ ├── group-manage/ # 群组管理(创建/加入/退出/解散) │ ├── category-manage/ # 分类管理 │ ├── budget/ # 预算设置 │ ├── notifications/ # 通知中心 │ ├── feedback/ # 意见反馈 │ ├── privacy/ # 隐私政策 │ └── admin/ # 管理后台 (index/users/notifications/feedback/config) ├── components/ # 公共组件 │ ├── BudgetBar/ # 预算进度条 │ ├── CategoryIcon/ # 分类图标 (首字+颜色) │ ├── ChartWrapper/ # uCharts 图表封装 │ ├── Icon/ # 图标组件 (PNG 映射) │ ├── Numpad/ # 数字键盘 │ ├── SaveSuccess/ # 保存成功动画 │ ├── Skeleton/ # 骨架屏 │ └── TransactionItem/ # 交易列表项 ├── stores/ # Pinia stores │ ├── transaction.ts # 交易 CRUD (自动传递 group_id) │ ├── category.ts # 分类 CRUD │ ├── budget.ts # 预算管理 │ ├── stats.ts # 统计概览 (自动传递 group_id) │ ├── user.ts # 用户信息 (昵称/头像) │ └── group.ts # 群组管理 (身份切换) ├── api/ # API 封装 │ ├── auth.ts # 登录接口 │ ├── transaction.ts # 交易接口 │ ├── category.ts # 分类接口 │ ├── budget.ts # 预算接口 │ ├── stats.ts # 统计接口 │ ├── user.ts # 用户信息接口 (含头像上传) │ ├── group.ts # 群组接口 │ ├── notification.ts # 通知接口 │ ├── filter.ts # 筛选方案接口 │ ├── feedback.ts # 反馈接口 │ ├── config.ts # 系统配置接口 │ ├── admin.ts # 管理后台接口 │ └── index.ts # barrel 导出 ├── utils/ │ ├── format.ts # 金额/日期格式化 │ ├── request.ts # HTTP 请求封装 (401 自动重登录+队列重试) │ ├── system.ts # 系统信息 (statusBarHeight, capsuleRight) │ └── app-ready.ts # App 启动就绪机制 (waitForReady) ├── config.ts # 全局配置 (API_BASE) └── static/icons/ # tabBar 图标 (PNG) server/src/ ├── index.ts # Express 入口 (CORS, JSON, 静态文件, 路由注册) ├── middleware/auth.ts # HMAC token 验证 (timingSafeEqual) ├── routes/ │ ├── auth.ts # 登录 / token 签发 (返回用户信息) │ ├── transaction.ts # 交易 CRUD (支持 group_id 视图切换) │ ├── category.ts # 分类 CRUD (默认+自定义) │ ├── budget.ts # 预算 CRUD (按月) │ ├── stats.ts # 统计查询 (支持 group_id) │ ├── user.ts # 用户信息 + 头像上传 │ ├── group.ts # 群组 CRUD (创建/加入/退出/解散) │ ├── notification.ts # 通知/公告 CRUD │ ├── filter.ts # 筛选方案 CRUD │ ├── feedback.ts # 用户反馈 (提交/查询/处理) │ ├── config.ts # 系统配置 (公开读/管理员写) │ ├── admin.ts # 管理后台 (仪表盘/用户管理) │ └── backup.ts # 备份 API ├── utils/ │ ├── backup.ts # Node.js 数据库备份 (gzip) │ └── date.ts # 日期工具 └── db/ ├── connection.ts # MySQL 连接池 ├── init.ts # 建表 + seed + 迁移 (幂等) ├── schema.sql # 表结构 └── seed.sql # 默认分类数据 + 系统配置 ``` ### 数据库表 - `users` — 用户 (openid, nickname, avatar_url, slogan, role) - `categories` — 分类 (user_id=0 默认, 其他=自定义) - `transactions` — 交易记录 (user_id, group_id, amount 分) - `budgets` — 预算 (user_id, month, amount) - `groups` — 群组 (name, invite_code, created_by) - `group_members` — 群组成员 (group_id, user_id, role) - `notifications` — 通知/公告 (type, title, content, is_pinned, is_urgent) - `notification_reads` — 公告已读记录 (notification_id, user_id) - `saved_filters` — 保存的筛选方案 (user_id, name, filters) - `feedbacks` — 用户反馈 (user_id, type, content, status) - `sys_config` — 系统配置 (config_key, config_value) ### 金额处理 - 存储单位: 分 (整数,避免浮点精度问题) - 显示单位: 元 (保留两位小数) - 格式化: `¥ 1,234.56` (¥ 后空格,千分位分隔) - 收入前缀 `+`,支出前缀 `-` - 使用 `formatAmount()` 带 ¥ 前缀,`formatAmountRaw()` 纯数字 ### 数据持久化 - 前端本地存储 Key: - `xc:token` — 认证 token - `xc:userId` — 用户 ID - `xc:nickname` — 用户昵称 - `xc:avatar_url` — 头像文件名 - `xc:currentGroupId` — 当前选中群组 ID (null=个人模式) - 后端 MySQL 数据库 - API 基础地址: `config.ts` 中的 `API_BASE` ### 认证流程 - H5: 页面加载时自动 demo-login 获取 token - 微信小程序: `App.vue` onLaunch 调用 `wx.login` 获取 code,换 token - Token: HMAC-SHA256 签名,30 天过期 - 401 处理: 请求入队 → 自动重登录 → 队列中所有请求重试 - 启动就绪: `app-ready.ts` 的 `waitForReady()` 确保页面在登录完成后才加载数据 ### 一起记 (群组) - 群组仅做数据聚合,记录始终属于记录者 (user_id) - 个人视图: `WHERE user_id = 我` (所有记录) - 群组视图: `WHERE user_id IN (群组成员)` — 统计所有群组成员的个人账单 - 群组账单 = 所有群组成员的个人账单之和 - 退出群组: 该用户的 group_id 设为 NULL,记录回到个人视图 - 身份切换: 「我的」页底部弹窗选择,全局生效 ### Git - 分支: `master` → `main` (注意远程) - Commit 风格: 中文描述,简洁明了 - `feat: 添加记账页面` - `fix: 修复金额计算精度问题` ## Commands ```bash # 前端开发 (H5) cd client && npm run dev:h5 # 前端开发 (微信小程序) cd client && npm run dev:mp-weixin # 前端构建 cd client && npm run build:mp-weixin # 后端开发 cd server && npm run dev # 后端构建 cd server && npm run build # 数据库初始化 cd server && npm run db:init ``` ## 部署 - 开发服务: `dev.xiaocai.j35.site:3000` - 正式服务: `xiaocai.j35.site` - MySQL: 本地 3306 端口 - 小程序开发版自动连接 dev 服务,正式版/H5 连接线上 (config.ts) ### 手动部署步骤 ```bash # 1. 本地构建后端 cd server && npm run build # 2. 复制 dist 目录到服务器 scp -r server/dist/* root@106.14.208.43:/var/www/xiaocai/ # 3. SSH 到服务器 ssh root@106.14.208.43 # 4. 安装依赖(如 node_modules 已存在可跳过) cd /var/www/xiaocai && npm install --production # 5. 重启服务 pm2 restart xiaocai-server # 或直接运行 NODE_ENV=production node dist/index.js ``` ## 开发 @DEV.md