从“步数排行榜”到“AI 健身管家”——Workout Challenge 如何用 Django + React 把同事圈卷成伦敦程序员的团建神器
关键词:Docker 化部署、Strava 自动同步、Celery 定时任务、隐私优先、AI 营养贴士、暗黑模式、响应式邮件
开场白:当 Slack 通知响起,“@all,九月步数挑战开始!”
如果你也在伦敦科技公司待过,一定熟悉这种场景——
-
iPhone 用户把 Apple Watch 环合上截图甩群; -
Android 同事反手甩一张 Google Fit 柱状图; -
骑折叠车通勤的设计师把 Garmin 码表数据导成 CSV,还要手动换算公里数; -
HR 小姐姐把 Excel 贴在 Notion,结果有人把步数填到“卡路里”列……
痛点总结:设备割裂、数据格式乱、隐私心里打鼓、统计表格劝退。
Workout Challenge 的出现,就是要把这场“跨物种”健身社交,变成“一次 Docker compose up”就能 hosting 的私域竞赛。下面带你拆它的技术骨骼。
一、整体架构:把“前端仪表盘-后端 API-定时任务”装进一只容器
┌---------------------------┐
│ Nginx (80) │
│ ├─ / → React 静态资源 │
│ └─ /api → Gunicorn(Django)│
└------------┬--------------┘
│Celery Worker
│Celery Beat
│Redis
▼
┌---------------------------┐
│ Postgres / SQLite │
└---------------------------┘
-
前端:React + MUI,暗黑模式用 prefers-color-scheme
自动切换,图表用 Chart.js,把“环闭合”做成圆环进度条,UI 一秒“果味”。 -
后端:Django + Django-REST-framework,JWT 登录,权限到“竞赛”级,保证 A 公司的排行榜不会把 B 公司数据泄露。 -
任务队列:Celery + Redis,每天 04:00 向 Strava 拉数据,自动生成周一排行榜、周四个人报告。 -
邮件模板:MJML 写的响应式 HTML,在 Outlook/Gmail/Apple Mail 都能“颜值在线”。
二、30 秒本地体验(真·可跑通)
# 1) 有 Docker 就行
docker run -p 80:80 vanalmsick/workout_challenge
# 浏览器打开 localhost,注册→建赛→手动加数据,即刻出榜。
# 2) 想源码级 Hack
git clone <repo>
cd workout_challenge
# 前端
cd src-frontend && npm i && npm start
# 后端
cd src-backend && python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
# 环境变量示例
export DEBUG=true STRAVA_CLIENT_ID=xxxxx STRAVA_CLIENT_SECRET=xxxxx
python manage.py migrate
python manage.py runserver
# 定时任务
redis-server & # 另窗
celery -A workout_challenge worker -l INFO
celery -A workout_challenge beat -l INFO
三、生产部署:docker-compose.yml 的“每一行”都藏着血泪教训
下面这份是作者在伦敦某金融公司内网落地的“最终版”,把调试端口也留好,方便你排雷:
version: '3.9'
services:
workoutchallenge:
image: vanalmsick/workout_challenge
ports:
- "80:80"
- "5555:5555" # Celery Flower,仅限内网
- "9001:9001" # Supervisord
- "8000:8000" # Django Admin
volumes:
- /usr/pi/workout_challenge/django:/data # 持久化上传头像、SQLite(可选)
environment:
- POSTGRES_HOST=workoutchallenge-database
- POSTGRES_DB=workoutchallenge
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=use_a_vault_pls
- MAIN_HOST=https://fit.yourcompany.com
- SECRET_KEY=<32-random-chars>
- STRAVA_CLIENT_ID=12345
- STRAVA_CLIENT_SECRET=<from-strava>
- EMAIL_HOST=smtp.gmail.com
- EMAIL_PORT=465
- EMAIL_HOST_USER=fit@yourcompany.com
- EMAIL_HOST_PASSWORD=<app-password>
- OPENAI_API_KEY=<optional-for-ai-tips>
restart: unless-stopped
depends_on:
database:
condition: service_healthy
database:
image: postgres:15
environment:
- POSTGRES_PASSWORD=use_a_vault_pls
volumes:
- /usr/pi/workout_challenge/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
踩坑提示:
-
MAIN_HOST
一定带协议https://
,否则 Strava OAuth 回调被挡。 -
用 Gmail SMTP 记得开“两步验证→应用专用密码”,公司 Exchange 同理。 -
如果跑在树莓派, arm64
镜像已就绪,镜像体积 ≈ 300 MB,已压到极限。
四、Strava 同步的“魔法 15 分钟”
-
登录 Strava → Settings → My API Application → 创建得到 CLIENT_ID
&CLIENT_SECRET
。 -
用户首次点击“Link Strava”→ OAuth 回调 → 后台拿 refresh_token
→ 存库。 -
Celery Beat 每天 04:00 触发 sync_strava
任务:-
批量调用 /athlete/activities
,分页拉到昨日数据; -
按“竞赛规则”过滤(例如仅 Ride/Run,最短 10 min); -
写库同时触发“积分重算”→ 排行榜实时更新。
-
速率限制:普通应用 100 req/15 min、1000/天;申请 Strava Developer Program 可升到 300/3000,记得在 README 里附申请链接,帮读者省邮件往返。
五、AI 营养小贴士:把 OpenAI 当成“健身邮件彩蛋”
在周一排行榜邮件末尾,加一段由 gpt-3.5-turbo
写的“本周饮食/恢复建议”:
prompt = f"""
You are a witty UK nutritionist. Based on the stats below, give 2 fun tips
(50 words max each, emoji allowed, no medical claims).
Total workouts: {count}, avg kcal: {avg_kcal}, longest distance: {max_km} km.
"""
用户反馈:“为了看 AI 段子,居然主动把 workout 记满!”——技术价值 = 提升数据完整性。
六、暗黑模式与响应式邮件:把“像素眼”程序员也留住
-
前端用 useMediaQuery('(prefers-color-scheme: dark)')
实时切换主题。 -
邮件用 MJML 写一次,自动输出 Outlook + Gmail + 手机端兼容 HTML;暗黑模式通过 @media (prefers-color-scheme: dark)
内嵌 CSS 完成。
实测:iOS 邮件夜间模式打开,背景自动 #1c1c1e,文字 #f2f2f7,排行榜彩色条不变形,程序员群直呼“舒适”。
七、FAQ:提前回答同事/老板的 6 个灵魂拷问
Q1:步数会不会被公司后台偷窥?
A: 数据存在你们自己的 Postgres,源码可审,Django 权限到“竞赛”级,DBA 只能看到哈希 ID。
Q2:没 Strava 的同事怎么办?
A: 支持手动填“时长/距离/卡路里/次数”,还能上传 GPX,后台用 gpxpy
解析,一样算分。
Q3:如果某人谎报?
A: 规则引擎支持“每日/单次上限”,比如跑步最多 30 km 计分;异常数据自动标红,管理员一键“质询”邮件。
Q4:我们想加“平板支撑分钟”新指标?
A: 在 CompetitionGoal 模型里加一行 metric='plank_min'
,前端自动识别,不用改表结构。
Q5:会不会给 Strava 刷爆限额?
A: 同步任务用 Celery 限流 + 令牌桶,开发者计划 3000/天 足够 500 人使用。
Q6:能集成企业微信/Slack 推送吗?
A: 只要新增 slack_webhook
字段,Celery 任务里 POST 一份 JSON,十分钟搞定。
收束:当技术把“健身”做成数据产品
Workout Challenge 的精巧在于:没有炫技的“AI 大模型”,却把 Docker、Celery、OAuth、MJML 这些老朋友拧成一股绳,解决了一个真实的小痛点——“跨设备步数排行榜”。
如果你正愁“团建怎么玩”“内网能不能跑”“数据会不会泄露”,docker run
一行命令,就能让全公司秒变“环闭合”战场。剩下的,只是你和同事谁先把积分刷到 100% 的故事。
下一步行动
-
Star 项目,把镜像拉到本地跑一次 → 评估 UI 是否合老板口味; -
用 docker-compose.yml
把调试端口留好,内部 PoC 一天搞定; -
申请 Strava Developer Program,把 1000 次/天升到 3000,给全公司发邀请链接; -
打开 src-backend/ai_tips.py
,把 prompt 改成你们行业梗,让 AI 邮件彩蛋成为周一最期待的段子。
愿你在下一次“步数挑战”里,既保住隐私,也保住第一名。
“Closing the rings, without closing your data.”