酒吧德州扑克娱乐小程序开发Java技术搭建源码案例
·
🍺 酒吧德州扑克娱乐小程序 — Java全栈开发源码案例
2026年5月最新实战方案 · SpringBoot + UniApp + WebSocket + 微信支付 完整源码级解析
📋 一、项目全景总览
┌─────────────────────────────────────────────────────────┐
│ 酒吧德州扑克 + 小酒馆点餐娱乐小程序 │
├──────────────┬──────────────┬───────────────────────────┤
│ 🃏 扑克模块 │ 🍺 酒馆模块 │ 👤 用户模块 │
├──────────────┼──────────────┼───────────────────────────┤
│ • 快速匹配 │ • 扫码点餐 │ • 微信一键登录 │
│ • 私密房间 │ • 桌台绑定 │ • 积分/存酒管理 │
│ • 牌局实时同步 │ • 存酒/取酒 │ • 战绩/盈亏统计 │
│ • 牌型自动判定 │ • 优惠券 │ • 充值/提现 │
│ • 记分牌工具 │ • 订单管理 │ • 会员等级 │
│ • 胜率计算器 │ • 微信支付 │ • 抽位置功能 │
└──────────────┴──────────────┴───────────────────────────┘
🏗️ 二、技术架构选型(2026年主流方案)
| 层级 | 技术选型 | 说明 |
|---|---|---|
| 后端框架 | SpringBoot 3.2 + JDK 21 | 当前企业级主流,性能最优 |
| 数据库 | MySQL 8.0 | 核心业务数据 |
| 缓存/实时 | Redis 7 | 房间状态 + 在线用户 + WebSocket |
| 实时通信 | Spring WebSocket | 牌局状态毫秒级推送 |
| 前端小程序 | UniApp (Vue3) → 微信小程序 | 一套代码多端运行 |
| 管理后台 | Vue3 + Element Plus | 运营管理系统 |
| 微信支付 | wechatpay-java 0.2.12 | 充值 + 点餐支付 |
| 部署 | Docker + Nginx | 一键部署,生产级 |
💡 JDK版本选择:根据2026年企业使用占比,JDK 17(48%) 和 JDK 21(32%) 为主流。推荐使用 JDK 21,商业支持到2031年。
💾 三、数据库设计(12张核心表)
sql
-- ============================================
-- 酒吧德州扑克小酒馆 · 数据库初始化脚本
-- ============================================
CREATE DATABASE poker_tavern DEFAULT CHARSET utf8mb4;
USE poker_tavern;
-- 1. 用户表
CREATE TABLE `user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`openid` VARCHAR(64) UNIQUE COMMENT '微信openid',
`nickname` VARCHAR(50) DEFAULT '',
`avatar` VARCHAR(255) DEFAULT '',
`balance` DECIMAL(10,2) DEFAULT 0.00,
`chips` INT DEFAULT 10000 COMMENT '游戏筹码',
`level` INT DEFAULT 1,
`total_profit` DECIMAL(10,2) DEFAULT 0.00,
`status` TINYINT DEFAULT 1,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 2. 游戏房间表
CREATE TABLE `poker_room` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`room_no` VARCHAR(20) UNIQUE COMMENT '房间号',
`blind_level` INT DEFAULT 1 COMMENT '盲注 1/2 2/4 5/10',
`max_players` INT DEFAULT 9,
`status` TINYINT DEFAULT 0 COMMENT '0等待 1游戏中 2已结束',
`creator_id` BIGINT NOT NULL,
`pot` INT DEFAULT 0 COMMENT '底池',
`phase` TINYINT DEFAULT 0 COMMENT '0翻前 1翻牌 2转牌 3河牌',
`community_cards` JSON,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 3. 牌局记录表
CREATE TABLE `poker_hand` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`room_id` BIGINT NOT NULL,
`player_id` BIGINT NOT NULL,
`hole_cards` VARCHAR(20) COMMENT '底牌 AH-KH',
`final_rank` INT COMMENT '0高牌~9皇家同花顺',
`win_chips` INT DEFAULT 0,
`is_winner` TINYINT DEFAULT 0,
`played_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 4. 菜单/商品表
CREATE TABLE `menu_item` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`category` VARCHAR(20) DEFAULT '酒水',
`price` DECIMAL(10,2) NOT NULL,
`image` VARCHAR(255) DEFAULT '',
`stock` INT DEFAULT 999,
`status` TINYINT DEFAULT 1,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 5. 点餐订单表
CREATE TABLE `dinner_order` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`order_no` VARCHAR(32) UNIQUE,
`user_id` BIGINT NOT NULL,
`table_no` VARCHAR(10) COMMENT '桌台号',
`items` JSON NOT NULL,
`total_amount` DECIMAL(10,2) NOT NULL,
`status` TINYINT DEFAULT 0,
`pay_time` DATETIME DEFAULT NULL,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 6. 存酒记录表
CREATE TABLE `wine_storage` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`wine_name` VARCHAR(100) NOT NULL,
`quantity` INT DEFAULT 1,
`store_date` DATE NOT NULL,
`status` TINYINT DEFAULT 0,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 7. 充值记录表
CREATE TABLE `recharge_log` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`user_id` BIGINT NOT NULL,
`amount` DECIMAL(10,2) NOT NULL,
`chips_add` INT NOT NULL,
`pay_type` VARCHAR(20) DEFAULT 'wechat',
`trans_id` VARCHAR(64),
`status` TINYINT DEFAULT 0,
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;
-- 初始化测试数据
INSERT INTO `user` (`openid`, `nickname`, `chips`) VALUES
('test_001', '扑克王', 50000),
('test_002', '斗地主', 30000);
🎮 四、核心后端源码(Java)
4.1 扑克牌核心类
java
// ========== Card.java ==========
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Card {
public enum Suit { HEARTS, DIAMONDS, CLUBS, SPADES }
public enum Rank {
TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8),
NINE(9), TEN(10), JACK(11), QUEEN(12), KING(13), ACE(14);
private final int value;
Rank(int value) { this.value = value; }
public int getValue() { return value; }
}
private Suit suit;
private Rank rank;
@Override
public String toString() {
return suit.name().charAt(0) + rank.name().charAt(0);
}
}
// ========== Deck.java ==========
@Component
public class Deck {
public List<Card> createDeck() {
List<Card> deck = new ArrayList<>(52);
for (Card.Suit suit : Card.Suit.values()) {
for (Card.Rank rank : Card.Rank.values()) {
deck.add(new Card(suit, rank));
}
}
return deck;
}
public void shuffle(List<Card> deck) {
Collections.shuffle(deck);
}
}
4.2 ⭐⭐ 牌型判定引擎(核心算法)
java
@Component
public class PokerHandEvaluator {
/**
* 从7张牌中选出最好的5张,返回牌型等级
* 0=高牌 1=一对 2=两对 3=三条 4=顺子 5=同花
* 6=葫芦 7=四条 8=同花顺 9=皇家同花顺
*/
public int evaluate(List<Card> sevenCards) {
List<List<Card>> combos = combinations(sevenCards, 5);
int bestRank = -1;
for (List<Card> combo : combos) {
bestRank = Math.max(bestRank, rankHand(combo));
}
return bestRank;
}
private int rankHand(List<Card> cards) {
Collections.sort(cards, Comparator.comparingInt(c -> c.getRank().getValue()).reversed());
boolean flush = cards.stream().map(Card::getSuit).distinct().count() == 1;
boolean straight = checkStraight(cards);
Map<Integer, Long> freq = cards.stream()
.collect(Collectors.groupingBy(c -> c.getRank().getValue(), Collectors.counting()));
List<Long> counts = new ArrayList<>(freq.values());
Collections.sort(counts, Collections.reverseOrder());
if (flush && straight && cards.get(0).getRank().getValue() == 14) return 9;
if (flush && straight) return 8;
if (counts.get(0) == 4) return 7;
if (counts.get(0) == 3 && counts.get(1) == 2) return 6;
if (flush) return 5;
if (straight) return 4;
if (counts.get(0) == 3) return 3;
if (counts.get(0) == 2 && counts.get(1) == 2) return 2;
if (counts.get(0) == 2) return 1;
return 0;
}
private boolean checkStraight(List<Card> cards) {
List<Integer> vals = cards.stream()
.map(c -> c.getRank().getValue()).distinct().sorted().toList();
if (vals.size() < 5) return false;
// A-2-3-4-5 特殊顺子
if (vals.contains(14) && vals.contains(2) && vals.contains(3)
&& vals.contains(4) && vals.contains(5)) return true;
for (int i = 0; i <= vals.size() - 5; i++) {
if (vals.get(i + 4) - vals.get(i) == 4) return true;
}
return false;
}
private List<List<Card>> combinations(List<Card> cards, int k) {
List<List<Card>> result = new ArrayList<>();
combine(cards, k, 0, new ArrayList<>(), result);
return result;
}
private void combine(List<Card> cards, int k, int start,
List<Card> current, List<List<Card>> result) {
if (current.size() == k) {
result.add(new ArrayList<>(current));
return;
}
for (int i = start; i < cards.size(); i++) {
current.add(cards.get(i));
combine(cards, k, i + 1, current, result);
current.remove(current.size() - 1);
}
}
}
4.3 ⭐⭐⭐ WebSocket 牌局实时同步
java
@ServerEndpoint("/ws/room/{roomId}")
@Component
@Slf4j
public class PokerWebSocket {
private static final Map<String, Set<Session>> ROOM_SESSIONS = new ConcurrentHashMap<>();
private static final Map<String, PokerRoomState> ROOM_STATES = new ConcurrentHashMap<>();
@OnOpen
public void onOpen(Session session, @PathParam String roomId) {
ROOM_SESSIONS.computeIfAbsent(roomId, k -> ConcurrentHashMap.newKeySet()).add(session);
log.info("玩家加入房间: {}, 在线: {}", roomId, ROOM_SESSIONS.get(roomId).size());
}
@OnMessage
public void onMessage(String message, @PathParam String roomId) {
try {
GameMessage msg = JSON.parseObject(message, GameMessage.class);
PokerRoomState state = ROOM_STATES.computeIfAbsent(roomId, k -> new PokerRoomState());
switch (msg.getAction()) {
case "bet" -> processBet(roomId, state, msg);
case "call" -> processCall(roomId, state, msg);
case "raise" -> processRaise(roomId, state, msg);
case "fold" -> processFold(roomId, state, msg);
case "all_in" -> processAllIn(roomId, state, msg);
case "deal" -> processDeal(roomId, state, msg);
}
broadcast(roomId, state.toMessage());
} catch (Exception e) {
log.error("处理消息失败: {}", e.getMessage());
}
}
@OnClose
public void onClose(Session session, @PathParam String roomId) {
Set<Session> sessions = ROOM_SESSIONS.get(roomId);
if (sessions != null) sessions.remove(session);
if (sessions != null && sessions.isEmpty()) {
ROOM_SESSIONS.remove(roomId);
ROOM_STATES.remove(roomId);
log.info("房间解散: {}", roomId);
}
}
private void processBet(String roomId, PokerRoomState state, GameMessage msg) {
PokerPlayer player = state.getPlayer(msg.getPlayerId());
player.setChips(player.getChips() - msg.getAmount());
state.setCurrentBet(Math.max(state.getCurrentBet(), msg.getAmount()));
state.setPot(state.getPot() + msg.getAmount());
}
private void processFold(String roomId, PokerRoomState state, GameMessage msg) {
state.getPlayer(msg.getPlayerId()).setFolded(true);
checkWinner(roomId, state);
}
private void checkWinner(String roomId, PokerRoomState state) {
List<PokerPlayer> activePlayers = state.getPlayers().stream()
.filter(p -> !p.isFolded()).toList();
if (activePlayers.size() == 1) {
PokerPlayer winner = activePlayers.get(0);
winner.setChips(winner.getChips() + state.getPot());
state.setStatus(RoomStatus.ENDED);
log.info("[{}] 玩家{} 独赢底池 {}", roomId, winner.getId(), state.getPot());
} else if (activePlayers.size() > 1 && state.getPhase() >= 4) {
compareHands(roomId, state, activePlayers);
}
}
/** 比牌判定赢家 */
private void compareHands(String roomId, PokerRoomState state, List<PokerPlayer> players) {
PokerHandEvaluator evaluator = new PokerHandEvaluator();
PokerPlayer bestPlayer = null;
int bestRank = -1;
for (PokerPlayer p : players) {
List<Card> allCards = new ArrayList<>(p.getHoleCards());
allCards.addAll(state.getCommunityCards());
int rank = evaluator.evaluate(allCards);
if (rank > bestRank) {
bestRank = rank;
bestPlayer = p;
}
}
bestPlayer.setChips(bestPlayer.getChips() + state.getPot());
state.setStatus(RoomStatus.ENDED);
state.setWinner(bestPlayer.getId());
log.info("[{}] 玩家{} 以牌型{}赢得 {}", roomId, bestPlayer.getId(), bestRank, state.getPot());
}
private void broadcast(String roomId, GameMessage msg) {
ROOM_SESSIONS.get(roomId).forEach(s -> {
try {
s.getAsyncRemote().sendText(JSON.toJSONString(msg));
} catch (Exception e) { /* ignore */ }
});
}
// ===== 房间状态内部类 =====
@Data
static class PokerRoomState {
private String roomId;
private List<PokerPlayer> players = new ArrayList<>();
private List<Card> communityCards = new ArrayList<>();
private int pot = 0;
private int currentBet = 0;
private int phase = 0;
private RoomStatus status = RoomStatus.WAITING;
private Long winner = null;
public PokerPlayer getPlayer(Long playerId) {
return players.stream().filter(p -> p.getId().equals(playerId)).findFirst().orElse(null);
}
public GameMessage toMessage() {
GameMessage msg = new GameMessage();
msg.setAction("state_update");
msg.setPlayers(players);
msg.setCommunityCards(communityCards.stream().map(Card::display).toList());
msg.setPot(pot);
msg.setCurrentBet(currentBet);
msg.setPhase(phase);
msg.setStatus(status.ordinal());
return msg;
}
}
@Data
static class PokerPlayer {
private Long id;
private String nickname;
private int chips;
private boolean isDealer;
private boolean isFolded;
private boolean isAllIn;
private List<Card> holeCards;
}
enum RoomStatus { WAITING, PLAYING, ENDED }
}
// ========== GameMessage.java ==========
@Data
public class GameMessage {
private String action;
private Long playerId;
private Integer amount;
private List<String> cards;
private Integer pot;
private Integer phase;
private Integer status;
private List<PokerWebSocket.PokerPlayer> players;
private Long winner;
}
4.4 REST API 控制器
java
@RestController
@RequestMapping("/api")
@CrossOrigin
@Slf4j
public class GameController {
@Autowired private PokerRoomService roomService;
@Autowired private UserService userService;
@Autowired private OrderService orderService;
// ====== 房间管理 ======
@PostMapping("/room/create")
public R<String> createRoom(@RequestBody CreateRoomReq req) {
String roomNo = "R" + System.currentTimeMillis() % 100000;
roomService.createRoom(roomNo, req.getBlindLevel(), req.getCreatorId());
return R.ok(roomNo);
}
@GetMapping("/room/list")
public R<List<RoomVO>> roomList() {
return R.ok(roomService.getWaitingRooms());
}
@PostMapping("/room/{roomNo}/join")
public R<String> joinRoom(@PathVariable String roomNo, @RequestBody JoinRoomReq req) {
roomService.joinRoom(roomNo, req.getUserId(), req.getChips());
return R.ok("加入成功");
}
// ====== 用户 ======
@PostMapping("/user/login")
public R<UserVO> login(@RequestBody WxLoginReq req) {
return R.ok(userService.wxLogin(req.getCode()));
}
@PostMapping("/user/recharge")
public R<String> recharge(@RequestBody RechargeReq req) {
userService.recharge(req.getUserId(), req.getAmount());
return R.ok("充值成功");
}
// ====== 点餐 ======
@PostMapping("/order/create")
public R<String> createOrder(@RequestBody OrderReq req) {
orderService.createOrder(req);
return R.ok("下单成功");
}
@PostMapping("/order/pay")
public R<String> payOrder(@RequestBody PayReq req) {
orderService.payOrder(req.getOrderId(), req.getUserId());
return R.ok("支付成功");
}
// ====== 存酒 ======
@PostMapping("/wine/store")
public R<String> storeWine(@RequestBody WineReq req) {
orderService.storeWine(req);
return R.ok("存酒成功");
}
// ====== 战绩 ======
@GetMapping("/user/stats")
public R<UserStatsVO> getStats(@RequestHeader("Authorization") String token) {
return R.ok(userService.getStats(JwtUtil.getUserId(token)));
}
}
4.5 微信支付对接
java
@Service
@Slf4j
public class PayService {
@Autowired private WeChatPayClient weChatPayClient;
public String createRechargeOrder(Long userId, BigDecimal amount) {
JSONObject params = new JSONObject();
params.put("appid", "wx1234567890abcdef");
params.put("mchid", "1234567890");
params.put("description", "德州扑克充值-" + userId);
params.put("out_trade_no", "RC" + System.currentTimeMillis());
params.put("notify_url", "https://your-domain.com/api/pay/notify");
params.put("amount", amount.multiply(new BigDecimal(100)).intValue());
JSONObject result = weChatPayClient.post("v3/pay/transactions/jsapi", params,
JSONObject.class, AutoCertificateExtension.class, new HostNameCertificateVerifier());
return result.getString("prepay_id");
}
@PostMapping("/api/pay/notify")
public String payNotify(@RequestBody String body) {
try {
JSONObject data = JSON.parseObject(body);
String outTradeNo = data.getString("out_trade_no");
String transId = data.getString("transaction_id");
log.info("支付回调: {} {}", outTradeNo, transId);
// 更新订单状态 + 增加筹码
orderService.updateOrderStatus(outTradeNo, 1);
userService.addChips(data.getString("openid"),
data.getInteger("total_fee") / 100);
return "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
} catch (Exception e) {
log.error("支付回调失败: {}", e.getMessage());
return "{\"code\":\"FAIL\",\"message\":\"\"}";
}
}
}
📱 五、UniApp 小程序前端源码
5.1 ⭐ 牌桌页面(核心)
vue
<!-- pages/table/table.vue -->
<template>
<view class="poker-table">
<!-- 公共牌区域 -->
<view class="community-area">
<view class="pot">💰 底池: {{ potChips }}</view>
<view class="cards-row">
<view v-for="(card, i) in communityCards" :key="i" class="card">{{ card }}</view>
<view v-if="communityCards.length < 5" class="card empty">?</view>
</view>
<view class="phase">{{ phaseText }}</view>
</view>
<!-- 玩家座位 -->
<view v-for="player in players" :key="player.id"
class="seat" :class="{ folded: player.folded }"
:style="{ left: player.x + 'rpx', top: player.y + 'rpx' }">
<image class="avatar" :src="player.avatar" />
<text class="name">{{ player.nickname }}</text>
<text class="chips">💰{{ player.chips }}</text>
<view v-if="player.isMe" class="me-badge">我</view>
</view>
<!-- 我的手牌 -->
<view class="my-cards" v-if="isMyTurn">
<view v-for="card in myCards" :key="card" class="card my-card">{{ card }}</view>
</view>
<!-- 操作按钮 -->
<view class="actions">
<button class="btn-fold" @click="doAction('fold')">❌ 弃牌</button>
<button class="btn-call" @click="doAction('call')">跟注 {{ currentBet }}</button>
<button class="btn-raise" @click="doAction('raise')">⬆️ 加注</button>
<button class="btn-allin" @click="doAction('all_in')">🔥 ALL IN</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue'
import { connectWS, sendWS } from '@/utils/ws'
const roomId = ref('')
const ws = ref<WebSocket | null>(null)
const potChips = ref(0)
const communityCards = ref<string[]>([])
const players = ref<any[]>([])
const myCards = ref<string[]>([])
const currentBet = ref(0)
const phase = ref(0)
const isMyTurn = ref(false)
const phaseText = computed(() => {
const map = ['翻牌前', '翻牌', '转牌', '河牌']
return map[phase.value] || ''
})
onMounted(() => {
const pages = getCurrentPages()
const cur = pages[pages.length - 1]
roomId.value = cur.options?.roomId || ''
ws.value = connectWS(`wss://your-domain.com/ws/room/${roomId.value}`)
ws.value.onmessage = (res) => {
const msg = JSON.parse(res.data)
if (msg.action === 'deal') { myCards.value = msg.myCards || []; isMyTurn.value = true }
if (msg.action === 'community') { communityCards.value = msg.cards || [] }
if (msg.action === 'pot_update') { potChips.value = msg.pot || 0 }
if (msg.action === 'player_update') { players.value = msg.players || [] }
if (msg.action === 'phase') { phase.value = msg.phase || 0; isMyTurn.value = msg.isMyTurn || false }
if (msg.action === 'bet_update') { currentBet.value = msg.currentBet || 0 }
if (msg.action === 'winner') { uni.showToast({ title: `玩家${msg.winnerName}获胜!`, icon: 'none' }) }
}
})
const doAction = (action: string) => {
sendWS(ws.value, {
action,
playerId: getApp().globalData.userId,
amount: action === 'all_in' ? 999999 : currentBet.value
})
}
onUnmounted(() => { ws.value?.close() })
</script>
<style scoped>
.poker-table {
width: 100vw; height: 100vh;
background: radial-gradient(ellipse, #1a5c2a, #0d3318);
position: relative; overflow: hidden;
}
.seat { position: absolute; width: 160rpx; text-align: center; color: #fff; }
.card {
width: 80rpx; height: 112rpx;
background: #fff; border-radius: 12rpx;
display: inline-flex; align-items: center; justify-content: center;
font-weight: bold; margin: 4rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.4);
}
.btn-allin {
background: linear-gradient(135deg, #ff4444, #cc0000) !important;
color: #fff !important; font-size: 32rpx; font-weight: bold;
}
</style>
5.2 WebSocket 封装
javascript
// utils/ws.js
let wsInstance = null
export function connectWS(url) {
wsInstance = uni.connectSocket({ url })
wsInstance.onOpen(() => console.log('WebSocket 已连接'))
wsInstance.onMessage((res) => {
if (wsInstance._onMessage) wsInstance._onMessage(res)
})
wsInstance.onClose(() => {
console.log('WebSocket 已断开')
setTimeout(() => connectWS(url), 5000) // 5秒重连
})
return wsInstance
}
export function sendWS(ws, data) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send({ data: JSON.stringify(data) })
}
}
🐳 六、Docker 一键部署
docker-compose.yml
yaml
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: poker_tavern
ports: ["3306:3306"]
volumes: [mysql_data:/var/lib/mysql]
redis:
image: redis:7-alpine
ports: ["6379:6379"]
volumes: [redis_data:/data]
poker-server:
build: ./poker-server
ports: ["8080:8080"]
depends_on: [mysql, redis]
nginx:
image: nginx:alpine
ports: ["80:80", "443:443"]
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./dist:/usr/share/nginx/html
volumes:
mysql_data:
redis_data:
nginx.conf
nginx
server {
listen 80;
server_name your-domain.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://poker-server:8080/api/;
proxy_set_header Host $host;
}
location /ws/ {
proxy_pass http://poker-server:8080/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
部署命令
bash
# 1. 打包后端
cd poker-server && mvn clean package -DskipTests
# 2. 打包小程序
cd my-poker-tavern && npm run build:mp-weixin
# 3. 一键启动
docker-compose up -d --build
# 4. 查看日志
docker-compose logs -f poker-server
更多推荐


所有评论(0)