征途抽奖脚本
// CmdDrawActivity.h#ifndef CMD_DRAW_ACTIVITY_H
#define CMD_DRAW_ACTIVITY_H
#include "zType.h"
namespace Cmd {
namespace Draw {
// 协议主ID: 0x3000-0x30FF
const WORD CMD_DRAW_ACTIVITY = 0x3000;
// 子协议
enum {
PARA_DRAW_CHECK_REQ = 1, // 检查抽奖资格
PARA_DRAW_CHECK_RET = 2, // 检查结果
PARA_DRAW_DO_REQ = 3, // 执行抽奖
PARA_DRAW_DO_RET = 4, // 抽奖结果
PARA_DRAW_INFO_REQ = 5, // 请求活动信息
PARA_DRAW_INFO_RET = 6, // 活动信息
PARA_DRAW_LOG_REQ = 7, // 请求抽奖记录
PARA_DRAW_LOG_RET = 8, // 记录返回
PARA_DRAW_GLOBAL_NOTIFY = 9, // 全服广播
PARA_DRAW_GM_CMD = 10, // GM命令
};
// 奖品类型
enum PrizeType {
PRIZE_TYPE_ITEM = 1, // 道具
PRIZE_TYPE_MONEY = 2, // 金币
PRIZE_TYPE_EXP = 3, // 经验
PRIZE_TYPE_WING = 4, // 翅膀
PRIZE_TYPE_PET = 5, // 宠物
PRIZE_TYPE_TITLE = 6, // 称号
PRIZE_TYPE_NONE = 99, // 未中奖
};
// 检查请求
struct stDrawCheckReq {
WORD cmd; // 0x3000
WORD para; // 1
DWORD activityId; // 活动ID
DWORD drawTimes; // 抽几次(1或10)
};
// 检查返回
struct stDrawCheckRet {
WORD cmd;
WORD para; // 2
DWORD activityId;
BYTE ret; // 0成功,1活动未开启,2次数不足,3道具不足,4包裹满
DWORD remainTimes;// 剩余次数
DWORD needItemId; // 需要道具ID
DWORD needItemNum;// 需要数量
};
// 执行抽奖请求
struct stDrawDoReq {
WORD cmd;
WORD para; // 3
DWORD activityId;
DWORD drawTimes; // 1或10
BYTE useMoney; // 是否使用元宝代替道具(0否1是)
};
// 单次奖品信息
struct PrizeInfo {
DWORD prizeId; // 奖品配置ID
DWORD type; // PrizeType
DWORD itemId; // 物品ID(如果是物品)
DWORD count; // 数量
DWORD level; // 等级/品质
DWORD broadcast; // 是否广播(0/1)
char name; // 奖品名称
};
// 执行抽奖返回
struct stDrawDoRet {
WORD cmd;
WORD para; // 4
DWORD activityId;
BYTE ret; // 0成功,1失败,2包裹满
DWORD drawTimes; // 实际抽了几次
DWORD prizeCount; // 奖品数量(用于变长数组)
// PrizeInfo prizes;// 奖品列表
};
// 活动信息请求
struct stDrawInfoReq {
WORD cmd;
WORD para; // 5
DWORD activityId; // 0表示请求所有活动
};
// 活动基本信息
struct ActivityInfo {
DWORD id;
char name;
char desc;
BYTE enabled; // 是否开启
DWORD startTime; // 开始时间
DWORD endTime; // 结束时间
DWORD dailyLimit; // 每日限制
DWORD costItemId; // 消耗道具
DWORD costItemNum;// 消耗数量
DWORD costMoney; // 替代元宝
BYTE hasGuarantee;// 是否有保底
DWORD guaranteeTimes; // 保底次数
};
// 活动信息返回
struct stDrawInfoRet {
WORD cmd;
WORD para; // 6
DWORD count;
// ActivityInfo activities;
};
// 抽奖记录
struct DrawLog {
DWORD logId;
DWORD activityId;
DWORD prizeType;
DWORD itemId;
DWORD count;
DWORD time;
char prizeName;
};
// 记录请求
struct stDrawLogReq {
WORD cmd;
WORD para; // 7
DWORD activityId; // 0表示所有
DWORD startTime;
DWORD endTime;
};
// 记录返回
struct stDrawLogRet {
WORD cmd;
WORD para; // 8
DWORD count;
// DrawLog logs;
};
// 全服广播(服务器推送给所有客户端)
struct stDrawGlobalNotify {
WORD cmd;
WORD para; // 9
DWORD activityId;
char playerName;
char prizeName;
DWORD prizeLevel; // 奖品等级(用于显示不同颜色)
char msg; // 完整消息
};
// GM命令
enum GMCmd {
GM_CMD_RELOAD_CONFIG = 1, // 重载配置
GM_CMD_RESET_PLAYER = 2, // 重置玩家数据
GM_CMD_SET_STOCK = 3, // 设置库存
GM_CMD_SET_STATUS = 4, // 开启/关闭活动
GM_CMD_ADD_TIMES = 5, // 增加玩家次数
GM_CMD_QUERY_INFO = 6, // 查询信息
};
struct stDrawGMCmd {
WORD cmd;
WORD para; // 10
DWORD gmCmd;
DWORD targetId; // 玩家ID或活动ID
DWORD param1;
DWORD param2;
char paramStr;
};
#pragma pack()
}
}
#endif
// DrawActivityClient.h
#ifndef DRAW_ACTIVITY_CLIENT_H
#define DRAW_ACTIVITY_CLIENT_H
#include "zTCPClient.h"
#include "zSingleton.h"
#include "CmdDrawActivity.h"
using namespace Cmd::Draw;
class DrawActivityClient : public zTCPBufferClient, public zSingleton<DrawActivityClient>
{
public:
DrawActivityClient(const std::string& ip, WORD port);
virtual ~DrawActivityClient();
bool connectToServer();
virtual void run();
virtual bool msgParse(const BYTE* data, const DWORD len);
// 发送给抽奖服务器
bool sendToDrawServer(const void* data, DWORD len);
// 转发给客户端
void sendToClient(DWORD userId, const void* data, DWORD len);
private:
std::string serverIp;
WORD serverPort;
bool reconnect();
};
#define DRAW_CLIENT DrawActivityClient::getInstance()
#endif
// DrawActivityClient.cpp
#include "DrawActivityClient.h"
#include "SceneUserManager.h"
#include "SceneUser.h"
#include "ScenesServer.h"
#include "LuaEngine.h"
DrawActivityClient::DrawActivityClient(const std::string& ip, WORD port)
: serverIp(ip), serverPort(port)
{
}
DrawActivityClient::~DrawActivityClient()
{
}
bool DrawActivityClient::connectToServer()
{
if(!connect(serverIp.c_str(), serverPort))
{
Zebra::logger->error("连接抽奖服务器失败 %s:%d", serverIp.c_str(), serverPort);
return false;
}
// 发送注册包
struct {
WORD cmd;
WORD para;
DWORD serverId;
DWORD serverType;
} regCmd = {0x3000, 0, ScenesService::getInstance().getServerID(), 1};
return sendCmd(®Cmd, sizeof(regCmd));
}
void DrawActivityClient::run()
{
while(!ScenesService::getInstance().isTerminate())
{
if(!isConnected())
{
if(!reconnect())
{
zThread::msleep(5000);
continue;
}
}
zTCPBufferClient::run();
}
}
bool DrawActivityClient::reconnect()
{
close();
return connectToServer();
}
bool DrawActivityClient::msgParse(const BYTE* data, const DWORD len)
{
if(len < sizeof(NullCmd))
return false;
NullCmd* cmd = (NullCmd*)data;
if(cmd->cmd != CMD_DRAW_ACTIVITY)
return false;
// 转发给Lua处理
lua_State* L = LuaEngine::getInstance()->getLuaState();
if(!L) return false;
lua_getglobal(L, "DrawActivityManager");
if(!lua_istable(L, -1))
{
lua_pop(L, 1);
return false;
}
lua_getfield(L, -1, "onServerMessage");
if(!lua_isfunction(L, -1))
{
lua_pop(L, 2);
return false;
}
// 压入参数:协议号,数据指针,长度
lua_pushinteger(L, ((WORD*)data)); // para
lua_pushlightuserdata(L, (void*)data);
lua_pushinteger(L, len);
if(lua_pcall(L, 3, 1, 0) != 0)
{
Zebra::logger->error("DrawActivity Lua error: %s", lua_tostring(L, -1));
lua_pop(L, 1);
return false;
}
bool ret = lua_toboolean(L, -1);
lua_pop(L, 1);
return ret;
}
bool DrawActivityClient::sendToDrawServer(const void* data, DWORD len)
{
return sendCmd(data, len);
}
void DrawActivityClient::sendToClient(DWORD userId, const void* data, DWORD len)
{
SceneUser* user = SceneUserManager::getMe().getUserByID(userId);
if(user)
{
user->sendCmdToMe(data, len);
}
}
<!-- config/draw_activities.xml -->
<activities>
<!-- 幸运大转盘 -->
<activity id="1001" name="幸运大转盘" enabled="true"
start_time="2024-01-01 00:00:00" end_time="2024-12-31 23:59:59"
daily_limit="50" cost_type="item" cost_item="584" cost_num="1"
cost_money="100" draw_mode="single">
<guarantee enabled="true" times="50" pool="rare" reset="true"/>
<broadcast min_level="4">恭喜【{player}】鸿运当头,获得【{prize}】!</broadcast>
<prizes>
<!-- 传说级(5星) -->
<prize id="10001" type="wing" item_id="1001" name="炽天使之翼"
level="5" weight="10" broadcast="true" server_limit="3"/>
<!-- 史诗级(4星) -->
<prize id="10002" type="item" item_id="585" count="10" name="史诗强化石"
level="4" weight="100" broadcast="true"/>
<!-- 稀有级(3星) -->
<prize id="10003" type="item" item_id="584" count="5" name="进阶石礼包"
level="3" weight="500"/>
<prize id="10004" type="money" count="50000" name="金币"
level="3" weight="1000"/>
<!-- 普通级(1-2星) -->
<prize id="10005" type="item" item_id="583" count="3" name="强化石"
level="2" weight="3000"/>
<prize id="10006" type="exp" count="10000" name="经验"
level="1" weight="5390"/>
</prizes>
</activity>
<!-- 十连抽宝箱 -->
<activity id="1002" name="神秘宝箱" enabled="true"
daily_limit="999" cost_type="item" cost_item="587" cost_num="10"
draw_mode="multi">
<guarantee enabled="true" times="10" pool="purple" reset="false"/>
<prizes>
<prize id="20001" type="pet" item_id="2001" name="神兽·青龙"
level="5" weight="5" broadcast="true" server_limit="1" daily_limit="1"/>
<prize id="20002" type="item" item_id="588" count="1" name="紫色装备箱"
level="4" weight="50" broadcast="true"/>
<!-- 保底奖品 -->
<prize id="20003" type="item" item_id="589" count="5" name="紫色碎片"
level="4" weight="100"/>
</prizes>
</activity>
</activities>
这些是我做个翅膀随便写下 自己去修改
-- scripts/jackpot/jackpot_manager.lua
-- 全服奖池分红系统
local JackpotManager = {}
local FLDB = require("database.flserver_db")-- 账号数据库连接
local GameDB = require("database.game_db") -- 游戏数据库连接
local Timer = require("Timer")
-- 配置
JackpotManager.CONFIG = {
JACKPOT_LIMIT = 1000000000,-- 10亿触发
CHECK_INTERVAL = 60000, -- 每分钟检查一次
DISTRIBUTE_BATCH = 1000, -- 每批处理1000人
MIN_ONLINE_TIME = 3600, -- 最小在线时长(秒)才有资格
}
-- 当前状态
JackpotManager.state = {
currentAmount = 0,
isDistributing = false,
lastCheckTime = 0,
currentBatch = nil
}
-- 初始化
function JackpotManager.init()
-- 加载当前奖池金额
local row = GameDB.queryOne("SELECT current_amount, status FROM global_jackpot WHERE id=1")
if row then
JackpotManager.state.currentAmount = tonumber(row.current_amount) or 0
if tonumber(row.status) == 1 then-- 待发放状态,继续发放
JackpotManager.resumeDistribute()
end
else
-- 初始化奖池
GameDB.execute("INSERT INTO global_jackpot (id, current_amount) VALUES (1, 0)")
end
-- 启动定时检查
Timer.scheduleRepeat(JackpotManager.CONFIG.CHECK_INTERVAL, JackpotManager.checkAndDistribute)
Log.info("JackpotManager initialized, current jackpot: " .. JackpotManager.state.currentAmount)
end
-- 奖池流入(每次抽奖抽税时调用)
function JackpotManager.addToJackpot(activityId, userId, amount)
if amount <= 0 then return end
-- 更新数据库(原子操作)
local sql = string.format(
"UPDATE global_jackpot SET current_amount = current_amount + %d, total_in = total_in + %d WHERE id=1",
amount, amount
)
local ok = GameDB.execute(sql)
if ok then
-- 记录流入
GameDB.execute(string.format(
"INSERT INTO jackpot_inflow (activity_id, user_id, amount, inflow_time) VALUES (%d, %d, %d, %d)",
activityId, userId, amount, os.time()
))
-- 更新内存
JackpotManager.state.currentAmount = JackpotManager.state.currentAmount + amount
-- 检查是否达到阈值
if JackpotManager.state.currentAmount >= JackpotManager.CONFIG.JACKPOT_LIMIT
and not JackpotManager.state.isDistributing then
Log.info("Jackpot reached limit: " .. JackpotManager.state.currentAmount)
-- 异步触发发放(避免阻塞)
Timer.once(1000, JackpotManager.startDistribute)
end
end
end
-- 检查并发放(定时器调用)
function JackpotManager.checkAndDistribute()
if JackpotManager.state.isDistributing then return end
-- 从数据库获取最新金额(防止多服竞争)
local row = GameDB.queryOne("SELECT current_amount, status FROM global_jackpot WHERE id=1 FOR UPDATE")
if not row then return end
local amount = tonumber(row.current_amount) or 0
local status = tonumber(row.status) or 0
JackpotManager.state.currentAmount = amount
if amount >= JackpotManager.CONFIG.JACKPOT_LIMIT and status == 0 then
JackpotManager.startDistribute()
end
end
-- 开始发放流程
function JackpotManager.startDistribute()
if JackpotManager.state.isDistributing then return false end
JackpotManager.state.isDistributing = true
Log.info("Starting jackpot distribution, amount: " .. JackpotManager.state.currentAmount)
-- 1. 锁定奖池状态
local ok = GameDB.execute("UPDATE global_jackpot SET status=2 WHERE id=1 AND status=0")
if not ok then
Log.error("Failed to lock jackpot, maybe another server is processing")
JackpotManager.state.isDistributing = false
return false
end
-- 2. 查询FLServerDB获取所有有效账号
local accounts = FLDB.query([[
SELECT DISTINCT a.id as account_id,
(SELECT MAX(id) FROM game_user.user_active
WHERE account_id=a.id AND last_login_time > UNIX_TIMESTAMP() - 2592000) as user_id
FROM account a
WHERE a.status = 1
AND a.create_time < UNIX_TIMESTAMP() - 86400-- 注册超过1天
AND (SELECT COUNT(*) FROM account_ban ab WHERE ab.account_id=a.id AND ab.end_time > UNIX_TIMESTAMP()) = 0
]])
if not accounts or #accounts == 0 then
Log.error("No valid accounts found for jackpot distribution")
JackpotManager.resetJackpot()
return false
end
-- 3. 计算每人分得
local totalAmount = JackpotManager.state.currentAmount
local playerCount = #accounts
local perAmount = math.floor(totalAmount / playerCount)
local remainder = totalAmount - (perAmount * playerCount)-- 余数给前N个玩家
if perAmount < 1 then
Log.warn("Jackpot amount too small for distribution")
JackpotManager.resetJackpot()
return false
end
-- 4. 生成分红批次
local batchNo = "JP" .. os.time() .. "_" .. math.random(1000,9999)
GameDB.execute(string.format(
"INSERT INTO jackpot_dividend (jackpot_id, batch_no, total_amount, player_count, per_amount, distribute_time, status) "..
"VALUES (1, '%s', %d, %d, %d, %d, 1)",
batchNo, totalAmount, playerCount, perAmount, os.time()
))
JackpotManager.state.currentBatch = {
batchNo = batchNo,
accounts = accounts,
perAmount = perAmount,
remainder = remainder,
total = playerCount,
processed = 0,
success = 0,
failed = 0
}
-- 5. 写入明细表(分批插入)
local batchInsert = {}
for i, acc in ipairs(accounts) do
local amount = perAmount
if i <= remainder then amount = amount + 1 end-- 余数分配
table.insert(batchInsert, string.format("('%s', %d, %d, %d, 0)",
batchNo, acc.account_id, acc.user_id or 0, amount))
if #batchInsert >= 500 then
local sql = "INSERT INTO jackpot_player_dividend (batch_no, account_id, user_id, amount, status) VALUES " ..
table.concat(batchInsert, ",")
GameDB.execute(sql)
batchInsert = {}
end
end
if #batchInsert > 0 then
local sql = "INSERT INTO jackpot_player_dividend (batch_no, account_id, user_id, amount, status) VALUES " ..
table.concat(batchInsert, ",")
GameDB.execute(sql)
end
-- 6. 开始分批发放
Timer.once(100, function() JackpotManager.processBatch(1) end)
-- 全服广播预告
WorldBroadcast(string.format("【全服福利】奖池累计已达10亿金币,正在为%d位玩家发放分红,每人约%d金币!",
playerCount, perAmount))
return true
end
-- 分批处理发放
function JackpotManager.processBatch(startIdx)
local batch = JackpotManager.state.currentBatch
if not batch then return end
local endIdx = math.min(startIdx + JackpotManager.CONFIG.DISTRIBUTE_BATCH - 1, batch.total)
for i = startIdx, endIdx do
local acc = batch.accounts
if acc then
local amount = batch.perAmount
if i <= batch.remainder then amount = amount + 1 end
local ok, err = JackpotManager.sendToPlayer(acc, amount, batch.batchNo)
if ok then
batch.success = batch.success + 1
GameDB.execute(string.format(
"UPDATE jackpot_player_dividend SET status=1, send_time=%d WHERE batch_no='%s' AND account_id=%d",
os.time(), batch.batchNo, acc.account_id
))
else
batch.failed = batch.failed + 1
GameDB.execute(string.format(
"UPDATE jackpot_player_dividend SET status=2, error_msg='%s' WHERE batch_no='%s' AND account_id=%d",
tostring(err):sub(1, 250), batch.batchNo, acc.account_id
))
-- 失败时放入邮件队列稍后重发
JackpotManager.queueMailReward(acc.account_id, acc.user_id, amount, batch.batchNo)
end
end
batch.processed = batch.processed + 1
end
-- 更新进度
GameDB.execute(string.format(
"UPDATE jackpot_dividend SET status=1 WHERE batch_no='%s'",
batch.batchNo
))
Log.info(string.format("Jackpot distribution progress: %d/%d (success:%d, failed:%d)",
batch.processed, batch.total, batch.success, batch.failed))
-- 继续下一批或完成
if endIdx < batch.total then
Timer.once(100, function() JackpotManager.processBatch(endIdx + 1) end)
else
JackpotManager.finishDistribute()
end
end
-- 发放给单个玩家
function JackpotManager.sendToPlayer(accountInfo, amount, batchNo)
local accountId = accountInfo.account_id
local userId = accountInfo.user_id
-- 方式1:玩家在线,直接加金币
if userId and userId > 0 then
local user = SceneUserManager.getUserByID(userId)
if user and user:isOnline() then
-- 直接加金币
local ok = user:addMoney(amount, "jackpot_dividend_" .. batchNo)
if ok then
-- 发送通知
user:sendSysMsg(string.format("恭喜您获得全服奖池分红 %d 金币!", amount))
-- 播放特效
user:playEffect("jackpot_win")
-- 记录日志
Log.info(string.format("Jackpot sent to online user %d (account %d), amount: %d",
userId, accountId, amount))
return true
end
end
end
-- 方式2:玩家离线,写入待领取表(登录时发放)
-- 或者发送邮件
return JackpotManager.sendMailReward(accountId, userId, amount, batchNo)
end
-- 发送邮件奖励(离线玩家)
function JackpotManager.sendMailReward(accountId, userId, amount, batchNo)
local mailSql = string.format([[
INSERT INTO mail_system (receiver_id, title, content, attachment, send_time, expire_time, status)
VALUES (%d, '全服奖池分红', '恭喜!全服奖池累计10亿金币,您分得%d金币(批次:%s)',
'{"money":%d}', %d, %d, 0)
]], userId or accountId, amount, batchNo, amount, os.time(), os.time() + 2592000)-- 30天过期
local ok = GameDB.execute(mailSql)
if ok then
Log.info(string.format("Jackpot mail sent to account %d, amount: %d", accountId, amount))
return true, "mail_sent"
else
return false, "mail_failed"
end
end
-- 队列化邮件奖励(用于失败重试)
function JackpotManager.queueMailReward(accountId, userId, amount, batchNo)
-- 写入重试队列
GameDB.execute(string.format(
"INSERT INTO jackpot_retry_queue (batch_no, account_id, user_id, amount, create_time) VALUES ('%s', %d, %d, %d, %d)",
batchNo, accountId, userId or 0, amount, os.time()
))
end
-- 完成发放
function JackpotManager.finishDistribute()
local batch = JackpotManager.state.currentBatch
if not batch then return end
-- 更新总表状态
GameDB.execute(string.format(
"UPDATE jackpot_dividend SET status=2 WHERE batch_no='%s'",
batch.batchNo
))
-- 清空奖池
GameDB.execute("UPDATE global_jackpot SET current_amount=0, status=0, last_winner_time=" .. os.time() .. " WHERE id=1")
JackpotManager.state.currentAmount = 0
JackpotManager.state.isDistributing = false
JackpotManager.state.currentBatch = nil
-- 全服广播完成
WorldBroadcast(string.format("【全服福利】奖池分红发放完成!共%d位玩家分享10亿金币!", batch.total))
Log.info(string.format("Jackpot distribution completed. Total: %d, Success: %d, Failed: %d",
batch.total, batch.success, batch.failed))
end
-- 重置奖池(出错时)
function JackpotManager.resetJackpot()
GameDB.execute("UPDATE global_jackpot SET status=0 WHERE id=1")
JackpotManager.state.isDistributing = false
JackpotManager.state.currentBatch = nil
end
-- 恢复发放(服务器重启后继续)
function JackpotManager.resumeDistribute()
Log.info("Resuming jackpot distribution...")
local row = GameDB.queryOne("SELECT batch_no FROM jackpot_dividend WHERE status=1 ORDER BY id DESC LIMIT 1")
if row then
local batchNo = row.batch_no
-- 加载未完成的明细
local list = GameDB.query(string.format(
"SELECT account_id, user_id, amount FROM jackpot_player_dividend WHERE batch_no='%s' AND status=0",
batchNo
))
if list and #list > 0 then
JackpotManager.state.currentBatch = {
batchNo = batchNo,
accounts = list,
perAmount = 0,-- 不需要重新计算
total = #list,
processed = 0,
success = 0,
failed = 0
}
-- 继续发放
Timer.once(1000, function() JackpotManager.processBatch(1) end)
Log.info("Resumed batch " .. batchNo .. " with " .. #list .. " remaining players")
else
-- 没有待发放的,标记完成
GameDB.execute(string.format("UPDATE jackpot_dividend SET status=2 WHERE batch_no='%s'", batchNo))
GameDB.execute("UPDATE global_jackpot SET current_amount=0, status=0 WHERE id=1")
JackpotManager.state.isDistributing = false
end
else
JackpotManager.resetJackpot()
end
end
-- GM命令:手动触发
function JackpotManager.gmForceDistribute()
if JackpotManager.state.isDistributing then
return false, "正在发放中"
end
-- 检查当前金额
local row = GameDB.queryOne("SELECT current_amount FROM global_jackpot WHERE id=1")
if not row or tonumber(row.current_amount) < 1000000 then-- 至少100万才允许手动触发
return false, "奖池金额不足(至少100万)"
end
JackpotManager.startDistribute()
return true, "已开始发放"
end
-- GM命令:查看奖池状态
function JackpotManager.gmStatus()
local row = GameDB.queryOne("SELECT * FROM global_jackpot WHERE id=1")
if row then
return {
current = tonumber(row.current_amount),
total_in = tonumber(row.total_in),
total_out = tonumber(row.total_out),
status = tonumber(row.status),
is_distributing = JackpotManager.state.isDistributing
}
end
return nil
end
-- 注册到全局
_G.JackpotManager = JackpotManager
return JackpotManager
-- 玩家抽奖数据
CREATE TABLE draw_player_data (
user_id BIGINT NOT NULL,
activity_id INT NOT NULL,
total_count INT DEFAULT 0 COMMENT '总抽奖次数',
today_count INT DEFAULT 0 COMMENT '今日次数',
last_draw_time INT DEFAULT 0 COMMENT '最后抽奖时间',
guarantee_count INT DEFAULT 0 COMMENT '保底计数',
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, activity_id),
INDEX idx_activity (activity_id)
) ENGINE=InnoDB;
-- 抽奖日志(分表或按月分区)
CREATE TABLE draw_logs (
id BIGINT AUTO_INCREMENT,
user_id BIGINT NOT NULL,
activity_id INT NOT NULL,
prize_type VARCHAR(16),
item_id INT,
count INT,
prize_level INT COMMENT '奖品等级',
draw_time INT,
PRIMARY KEY (id),
INDEX idx_user_time (user_id, draw_time),
INDEX idx_activity_time (activity_id, draw_time)
) ENGINE=InnoDB PARTITION BY RANGE (draw_time) (
PARTITION p202401 VALUES LESS THAN (1706745600),
PARTITION p202402 VALUES LESS THAN (1709251200),
PARTITION p202403 VALUES LESS THAN (1711929600)
);
-- 全服库存
CREATE TABLE draw_stock (
activity_id INT NOT NULL,
prize_id INT NOT NULL,
remain_count INT DEFAULT 0,
total_count INT DEFAULT 0,
update_time TIMESTAMP,
PRIMARY KEY (activity_id, prize_id)
) ENGINE=InnoDB;
-- 活动配置(用于热重载备份)
CREATE TABLE draw_config (
activity_id INT PRIMARY KEY,
config_json TEXT,
update_time TIMESTAMP,
operator VARCHAR(64)
) ENGINE=InnoDB;
虽不懂但谢谢 感觉好高级·!!!: 感觉好高级 谢谢大佬的分享 虽然不会,感谢分享: 谢谢分享
页:
[1]