52c6ef23621d760b8ba9661d657643ed81d00f93
Deploy to Server / deploy (push) Successful in 1m46s
- 统一口径定义:所有累计/总/人均/合计时长度量,一律只
计算 hours 行中 activity_id 存在于 activities 表中的记录
(排除 FK pragma 关闭时写入导致的幽灵 hours 行)
- server/users.ts:
· GET /(用户列表列的累计时长)
原 SELECT SUM(h.hours) FROM hours h → 增加 EXISTS 子查询
『AND EXISTS (SELECT 1 FROM activities a WHERE a.id=h.activity_id)』
· GET /:id 详情卡 total_hours_raw(给 enrichUser 用的原始数)
同样加 EXISTS 校验,然后仍用详情 activities 列表 reduce 覆
盖,保证卡片累计 = 列表合计 100% 一致,且与列表 SQL 同口径
- server/export.ts:
· export include_user_totals(导出报告的部员累计时长排行)
原 LEFT JOIN hours 直接 SUM → 改为 LEFT JOIN 预聚合子查
询 vh,vh 内先按 EXISTS(activities) 过滤,再 SUM/COUNT
· export include_activity_matrix(矩阵 _total、预览 KPI、
导出 Excel 的总时长列)
hoursRows 全表 SELECT → 增加 WHERE EXISTS(activities) 过
滤,matrix/remarkMatrix 只聚合有效活动行,再派生的用户级
_total 自然与 user_totals 同口径
- client/Export.tsx overviewCalc(导出报告顶部的累计总时长/
人均时长 KPI 卡片,之前只依赖 preview.hours.reduce):
· 优先级:
① 有 user_totals → 用后端同口径返回的 u.total_hours 聚合
(最靠谱)
② 否则有 activity_matrix → 用 matrix.user._total 聚合
(两者都基于同一 valid_hours 过滤)
③ Fallback:preview.hours.reduce(仅在只勾了明细单且没
勾排行/矩阵时才用)
· 计算完 totalHours 再 toFixed(1) 去浮点数误差,avgHours
同步 toFixed(1)
- client/user/Dashboard.tsx(部员侧义工号查询的个人累计页):
· 累计义工时长卡下半部新增『明细表合计』实时校验行:
data.activities.reduce 求和直接显示在卡片上,与大号累计
值左右对照,一眼确认有没有不一致
义工时长管理系统
功能概述
- 管理台:活动设置、义工时长登记、用户管理、数据导出(表格/图片)
- 用户端:输入姓名查询个人参与活动及累计义工时长
- 响应式:同时适配移动设备与 PC 端
技术栈
- 前端:React 18 + Vite 5 + TypeScript + TailwindCSS 3
- 后端:Node.js + Express + TypeScript
- 数据库:SQLite 3 (better-sqlite3)
- 部署:Docker + Gitea Actions 自动部署
- 域名:https://volunteer.bzw-hheb.top
初始管理员账号
- 用户名:
admin - 密码:
admin123
首次登录后请立即修改密码。
本地开发
# 安装后端依赖
cd server && npm install
# 安装前端依赖
cd ../client && npm install
# 启动后端(端口 3001)
cd server && npm run dev
# 启动前端(端口 5173,代理 /api 到 3001)
cd client && npm run dev
Docker 本地运行
docker compose up -d --build
部署配置(Gitea Secrets)
在仓库 Settings -> Secrets 中配置:
| 名称 | 说明 |
|---|---|
SSH_HOST |
服务器 IP/域名 |
SSH_PORT |
SSH 端口(默认 22) |
SSH_USER |
SSH 用户名 |
SSH_PRIVATE_KEY |
SSH 私钥(对应服务器上的公钥需加入 authorized_keys) |
数据持久化
SQLite 数据库文件位于容器 /app/server/data/volunteer.db,docker-compose.yml 中已配置为 bind mount 到 ./server/data。
Languages
TypeScript
98.8%
CSS
0.6%
Dockerfile
0.4%
HTML
0.2%