- Go 45.2%
- JavaScript 40.4%
- CSS 13.5%
- Dockerfile 0.5%
- HTML 0.3%
- Other 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| cmd/server | ||
| docker | ||
| internal | ||
| web | ||
| .dockerignore | ||
| .gitattributes | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
PokerGames - 斗地主
一个基于 Go 与 React 的三人斗地主 MVP。玩法按常见竞技斗地主规则初始化:
- 54 张牌,三人各发 17 张,3 张底牌;
- 按座位顺序叫 0/1/2/3 分:叫到 3 分立即当地主;无人叫分自动重新发牌;叫分最高者成为地主;
- 地主拿到底牌,地主与两名农民对抗,先出完手牌的一方获胜;
- 服务端完整校验牌型(单张、对子、三带、顺子、连对、飞机、四带二、炸弹、王炸),接牌必须严格更大;
- 接不上可选择“不出”,两人连续不出后由上一轮出牌者重新出牌。
规则参考:
百度百科:斗地主
Pagat: Dou Dizhu
技术栈
后端
- Go 1.27 + Gin
- GORM + SQLite
- JWT 认证
- WebSocket 房间状态广播
前端
- React 18 + Vite
- Ant Design 5
- React Router
- axios
- WebSocket
Pokers牌面、牌桌背景与斗地主音效资源- 支持标准版与 Emoji 版两套牌面,由后台统一切换
启动
首次运行安装前端依赖并生成编译产物:
cd web
npm install
npm run build
cd ..
之后只需启动后端,前后端共用 8080 端口:
go run ./cmd/server
Docker 运行
项目附带 Dockerfile(前端 Node 构建 → 后端 Go 编译 → Alpine 运行时,纯 Go SQLite 无需 CGO)与 docker-compose.yml:
docker compose up -d --build
- 打开
http://localhost:8080; - SQLite 数据保存在 Docker 命名卷
poker-data(挂在容器/app/data),重启/升级容器不丢数据;若改用宿主机目录绑定(如./data:/app/data)也无需手工授权——容器入口脚本会先修复数据目录归属,再以非 root 用户启动服务; - 前端会注册 Service Worker(
/sw.js):首次访问后把音效、牌面、牌背与桌布等pokers资源缓存进浏览器,重复出牌/听音不再反复请求网络;改动音效或牌面后请更新web/public/sw.js顶部的VERSION并重新构建,旧缓存会自动清除; - 生产环境务必通过
.env或环境变量覆盖JWT_SECRET;OauthGo 第三方登录可在启动前用OAUTHGO_*注入,也可启动后在管理后台/admin在线配置; - 常用命令:
docker compose logs -f poker查看日志、docker compose down停止(加-v才会删除数据卷)。
后端默认监听 http://localhost:8080,同时托管 web/dist 中的前端编译产物。React 前端路由会自动回退到 index.html,因此刷新任意前端路由不会返回 404。可通过 PORT、DATABASE_URL、JWT_SECRET 环境变量覆盖配置。
机器人支持通过管理后台“机器人 AI”设置或 BotAI 接口接入自定义策略。默认使用本地内置启发式策略;后台配置优先于 POKER_AI_ENDPOINT 环境变量。选择 HTTP AI 后,服务端会以 POST JSON 调用外部 AI:
- 请求:
{ "player": {...}, "hand": [...], "state": {...} } - 响应:叫分返回
{ "type": "bid", "bid": 0-3 };出牌返回{ "type": "play", "cards": [...] };不出返回{ "type": "pass" } - 所有外部 AI 动作仍由服务端校验,调用失败或响应非法时自动使用内置策略。
后端目录按职责拆分:
cmd/server:服务启动入口;internal/config:环境配置;internal/model:GORM 数据模型;internal/auth:JWT、角色和访问控制;internal/game:牌组、牌局状态和内存房间;internal/server:Gin 路由、HTTP/WebSocket 处理器。
打开 http://localhost:8080,注册三个账号后创建房间并加入即可开始。登录后可在“个人中心”设置头像与昵称(牌桌上展示昵称),头像支持本地上传(前端压缩后保存)或粘贴图片地址。
第三方登录(OauthGo,o.1v.fit)
接入 OauthGo 第三方登录聚合服务。配置可在管理后台(/admin)→ 第三方登录 · OauthGo 在线编辑并保存到数据库,立即生效;也可通过环境变量作为首次兜底:
| 环境变量 | 说明 |
|---|---|
OAUTHGO_BASE |
服务地址,默认 https://o.1v.fit |
OAUTHGO_APPID |
应用 ID |
OAUTHGO_APPKEY |
应用密钥 |
OAUTHGO_TYPES |
启用的渠道,逗号分隔,如 qq,gitee |
管理后台保存的配置优先于环境变量;将表单留空可回退到环境变量/默认值。相关管理接口:
GET/PUT /api/admin/oauth。
配置后:
- 登录页出现“第三方登录”按钮;首次登录会自动创建本地账号(登录名为
渠道_随机串),并写入昵称/头像; - 已在“个人中心 → 第三方登录绑定”绑定过的账号,会直接登录对应本地账号;
- 登录态下可随时绑定/解绑:不同第三方平台可同时绑定,但同一平台仅保留一个账号(绑定新账号会替换该平台的旧账号;同一
渠道+uid不能被两个本地账号占用);任一已绑定平台均可一键登录;绑定页在 OauthGo 完成授权后自动回跳。
登录页支持“记住我”:勾选后登录凭据保存在浏览器长期存储中,未勾选则仅保存在当前会话;退出登录会同时清理两种存储。牌面、绿色牌桌背景和出牌/叫牌音效会从 web/public/pokers 加载。
访问 http://localhost:8080/admin 可打开管理员技术设置页面,查看服务状态并调整牌桌主题、音效和动画。创建或进入无效房间时,页面会显示可返回大厅的错误提示,不再停留在空白页面。
后台管理中的牌面设置会持久化到 SQLite;管理员可预览两套完整牌面并切换当前版本,游戏桌面会按照服务端当前配置加载对应牌面。
用户角色:
player:普通玩家,可创建、加入和进行游戏;admin:管理员,可访问后台管理;super_admin:超级管理员,第一个注册用户(UID = 1)自动获得;guest:游客,可浏览大厅但不能创建、加入或操作游戏。
前端页面路径为 /login 登录页、/ 大厅、/profile 用户个人信息、/room/:id 牌桌、/admin 后台管理。
API
| 方法 | 路径 | 作用 |
|---|---|---|
| POST | /api/auth/register |
注册 |
| POST | /api/auth/login |
登录并获取 JWT |
| POST | /api/auth/guest |
游客进入 |
| POST | /api/auth/forgot-password |
申请找回密码(向已验证邮箱发送验证码) |
| POST | /api/auth/reset-password |
用邮箱验证码重置密码 |
| POST | /api/auth/password |
已登录用户修改密码 |
| POST | /api/auth/email-code |
为待绑定邮箱发送验证码 |
| POST | /api/auth/email-verify |
用验证码完成邮箱绑定/验证 |
| GET | /api/rooms |
查看等待中的房间 |
| POST | /api/rooms |
创建房间 |
| POST | /api/rooms/:id/join |
加入房间 |
| POST | /api/rooms/:id/ready |
切换准备状态 |
| POST | /api/rooms/:id/start |
三人后发牌并开始叫牌 |
| POST | /api/rooms/:id/bid |
叫牌 |
| POST | /api/rooms/:id/play |
出牌(服务端校验牌型与大小) |
| POST | /api/rooms/:id/pass |
不出(两人连续不出后重新出牌) |
| POST | /api/rooms/:id/restart |
对局结束后重新开局 |
| POST | /api/rooms/:id/leave |
离开等待中的房间 |
| GET | /api/me |
当前用户资料与对局统计 |
| PUT | /api/me/profile |
更新昵称 / 头像 |
| GET | /api/auth/oauth/providers |
已启用的第三方登录渠道 |
| GET | /api/auth/oauth/start |
发起第三方登录 / 绑定(mode=login|bind) |
| GET | /api/auth/oauth/callback |
OauthGo 授权回跳,完成登录或绑定 |
| GET | /api/me/oauth |
查看已绑定的第三方渠道 |
| DELETE | /api/me/oauth/:provider |
解绑第三方渠道 |
| GET | /ws/rooms/:id |
房间状态 WebSocket |
| GET | /api/admin/stats |
管理员查看基础统计 |
| GET/PUT | /api/admin/mail |
查看 / 保存 SMTP 发信配置 |
| POST | /api/admin/mail/test |
发送一封测试邮件 |
找回密码与邮箱验证
玩家注册时可填写邮箱(选填)。邮箱在“个人中心 → 邮箱绑定”通过 6 位验证码验证后,才可用于找回密码:
- 验证码 10 分钟内有效、只能使用一次、连续输错 5 次作废,同一邮箱 60 秒内不可重复发送;
- 未登录时在登录页点击“忘记密码?→ 用邮箱重置”,输入邮箱、收到的验证码和新密码即可重置;
POST /api/auth/forgot-password对未注册的邮箱同样返回成功提示,避免通过接口探测账号是否存在;- 密码重置或修改后,此前签发的 JWT 全部失效(用户会看到“登录状态已失效,请重新登录”),需要重新登录;
- 游客账号没有密码与邮箱,相关接口返回 403。
发信走 SMTP,可在管理后台(/admin)→ 邮件服务 · SMTP 在线配置并保存到数据库(发件人、服务器、端口、账号密码、隐式 TLS),也可用环境变量作为首次兜底:
| 环境变量 | 说明 |
|---|---|
SMTP_HOST |
SMTP 服务器,如 smtp.example.com |
SMTP_PORT |
端口,465 常配隐式 TLS,587 使用 STARTTLS |
SMTP_USERNAME |
登录账号(可留空表示匿名中继) |
SMTP_PASSWORD |
登录密码 / 授权码 |
SMTP_FROM |
发件人邮箱 |
SMTP_TLS |
true 表示从连接建立即启用 TLS |
未配置 SMTP 时验证码只会写入服务端日志(
[mail:not-configured]),玩家收不到邮件;配置完成后可在后台“发送测试邮件”验证。为安全起见,服务器未提供 TLS 时服务端会拒绝发送账号密码。
当前版本完成了认证、房间、发牌、完整叫分(含无人叫分重新发牌)、完整牌型识别与严格压牌比较、不出与轮次控制、胜负判定,以及等待房间的离开与对局结束后的重新开局。计分与牌局回放可在此状态机上继续扩展。
服务端每 5 秒执行一次生命周期检查:等待中的房间超过 60 秒没有玩家进入会自动删除;进行中的对局如果检测到玩家掉线超过 60 秒,会将对局强制结束并通知仍在线玩家。
大厅会显示等待房间的剩余销毁时间,已过期房间不会返回到大厅列表。对局中的玩家掉线后,其他玩家会看到重连倒计时,游戏操作会暂停;玩家在 60 秒内重连后自动恢复对局。
结构化的特点:每个阶段有任务