- 精华
- 阅读权限
- 90
- 贡献
- 人
- 好友
- 相册
- 分享
- 听众
- 收听
- 注册时间
- 2010-1-8
- 在线时间
- 小时
- 最后登录
- 1970-1-1
|
- // 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[64]; // 奖品名称
- };
- // 执行抽奖返回
- struct stDrawDoRet {
- WORD cmd;
- WORD para; // 4
- DWORD activityId;
- BYTE ret; // 0成功,1失败,2包裹满
- DWORD drawTimes; // 实际抽了几次
- DWORD prizeCount; // 奖品数量(用于变长数组)
- // PrizeInfo prizes[prizeCount]; // 奖品列表
- };
- // 活动信息请求
- struct stDrawInfoReq {
- WORD cmd;
- WORD para; // 5
- DWORD activityId; // 0表示请求所有活动
- };
- // 活动基本信息
- struct ActivityInfo {
- DWORD id;
- char name[64];
- char desc[256];
- 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[count];
- };
- // 抽奖记录
- struct DrawLog {
- DWORD logId;
- DWORD activityId;
- DWORD prizeType;
- DWORD itemId;
- DWORD count;
- DWORD time;
- char prizeName[64];
- };
- // 记录请求
- 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[count];
- };
- // 全服广播(服务器推送给所有客户端)
- struct stDrawGlobalNotify {
- WORD cmd;
- WORD para; // 9
- DWORD activityId;
- char playerName[64];
- char prizeName[64];
- DWORD prizeLevel; // 奖品等级(用于显示不同颜色)
- char msg[256]; // 完整消息
- };
- // 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[256];
- };
- #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)[1]); // 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[i]
- 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;
复制代码
|
|