local Lang  = require("common.Lang")
local Msg = require("core.Msg")
local ObjHuman = require("core.ObjHuman")
local InnerMsg = require("core.InnerMsg")
local Config = require("Config")
local MiddleManager = require("middle.MiddleManager")
local CommonDB = require("common.CommonDB")
local ZhuanpanExcel = require("excel.zhuanpan")
local ItemDefine = require("bag.ItemDefine")
local BagLogic = require("bag.BagLogic")
local Grid = require("bag.Grid")
local Broadcast = require("broadcast.Broadcast")
local RoleSystemLogic = require("roleSystem.RoleSystemLogic")
local RoleSystemDefine = require("roleSystem.RoleSystemDefine")
local ItemExcel = require("excel.item").item
local Util = require("common.Util")
local HeroExcel = require("excel.hero").hero
local ChatPaoMaLogic = require("chat.ChatPaoMaLogic")
local VipLogic = require("vip.VipLogic")
local EquipLogic = require("equip.EquipLogic")
local YunYingLogic = require("yunying.YunYingLogic")
local TriggerDefine = require("trigger.TriggerDefine")
local TriggerLogic = require("trigger.TriggerLogic")
local WeekTaskLogic = require("dailyTask.WeekTaskLogic")
local ClutterDataLogic = require("clutter.ClutterDataLogic")
local ClutterDataDefine = require("clutter.ClutterDataDefine")
local ZhuanpanGift = require("zhuanpan.ZhuanpanGift")
local Log = require("common.Log")
local DB = require("common.DB")
local LuaMongo = _G.lua_mongo
local MailManager = require("mail.MailManager")

-- 允许的渠道
local ALLOW_CHANNELS = {
	[11] = true,
	[17] = true, -- 美团2楼
}

DEFAULT_ZHUANPAN_TYPE_NORMAL 		= 1     -- 基础转盘
DEFAULT_ZHUANPAN_TYPE_GAOJI  		= 2     -- 高级转盘

DEFAULT_BUY_ZHUANPAN_CNT     		= 50    -- 购买许愿珠所需钻石
DEFAULT_FREE_REFRESH_TIME    		= 10800 -- 免费刷新的时间

ZHUAN_PAN_ITEM_KIND_CNT 			= 8 -- 转盘种类数量

DEFAULT_MAX_LUCK = 1000


REWARD_RECORD = { index={}, order={} }
REWARD_RECORD_CNT = 15

function getLuckConfig(mainType)
	local config = nil
	if mainType == DEFAULT_ZHUANPAN_TYPE_NORMAL then
	   config = ZhuanpanExcel.luck1
	elseif mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
	   config = ZhuanpanExcel.luck2
	end

	return config
end


function red(human, type)
    if not human.db.zhuanpan then return end
    local zhuanpan = human.db.zhuanpan[type]
    local config = getLuckConfig(type)
    if zhuanpan then
		local luck = zhuanpan.luck or 0
       local luckStatus = zhuanpan.luckStatus
       for k, v in pairs(config) do
           if luck >= k then
              if luckStatus[k] == 1 then
	       		    return true
	       		 end
           end
       end
    end
    if zhuanpan.free == 1 then
        return true
    end
end

-- 转盘查询
function query(human, mainType)
	local zhuanpanConfig = ZhuanpanExcel[mainType]
	if not zhuanpanConfig then return end

	-- 高级转盘判断等级
    local roleConfig = nil
    local flag, roleConfig = RoleSystemLogic.isOpen(human,RoleSystemDefine.ROLE_SYS_ID_501)
	if mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
		if flag ~= true then
			return Broadcast.sendErr(human, Lang.ZHUANPAN_GAIJI_NEED_LV)
		end
	end

	local oldCnt = getCntByType(human, mainType)

	local oldLuck
	local tOldStatus = nil
	if	human.db.zhuanpan and human.db.zhuanpan[mainType] then
		oldLuck = human.db.zhuanpan[mainType].luck
		if human.db.zhuanpan[mainType].luckStatus then
			tOldStatus = Util.copyTable(human.db.zhuanpan[mainType].luckStatus)
		end
	end

	-- 更新
	update(human, mainType)

	-- 初始化
	initDB(human, mainType, oldCnt,oldLuck, tOldStatus)

    local zuanpan = human.db.zhuanpan[mainType]

	local config = ZhuanpanExcel.define[mainType]
	local msgRet = Msg.gc.GC_ZHUANPAN_QUERY
	msgRet.type = mainType
    msgRet.isFirst = zuanpan.free == 1 and 1 or 0
	local now = os.time()
	local ts1 = now - zuanpan.ts1
	msgRet.refreshTime = 24 * 60 * 60 - ts1
	msgRet.freeRefreshTime = DEFAULT_FREE_REFRESH_TIME - (now - zuanpan.ts2)
	if msgRet.freeRefreshTime < 0 then
		msgRet.freeRefreshTime = 0
	end

	Grid.makeItem(msgRet.itemID, config.useItemID , 1)
	msgRet.itemCnt[0] = 2
	msgRet.itemCnt[1] = config.useItemCnt1
	msgRet.itemCnt[2] = config.useItemCnt2
	msgRet.drawCnt = config.drawCnt2
    Grid.makeItem(msgRet.zuanshiNeed, config.refreshCost[1][1], config.refreshCost[1][2])
    msgRet.needLv = config and config.lv or 0
    if mainType == DEFAULT_ZHUANPAN_TYPE_NORMAL then
       msgRet.needLv = ZhuanpanExcel.define[DEFAULT_ZHUANPAN_TYPE_GAOJI].lv
    elseif mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
       msgRet.needLv = ZhuanpanExcel.define[DEFAULT_ZHUANPAN_TYPE_NORMAL].lv
    end
	msgRet.needVipLv = VipLogic.getPowerNeedLv(VipLogic.VIP_POWER15)

	for i = 1, ZHUAN_PAN_ITEM_KIND_CNT do
		local data = zuanpan[i]

		local dataID = data.id
		local tempConfig = zhuanpanConfig[dataID]
		Grid.makeItem(msgRet.list[i].item, data.itemID, data.itemCnt)
		msgRet.list[i].id = dataID
		msgRet.list[i].chance = tempConfig.chance
		msgRet.list[i].maxCnt = tempConfig.getCnt
		msgRet.list[i].getCnt = 0
		if data.getCnt then
			msgRet.list[i].getCnt = data.getCnt
		end
	end

    local luckConfig = getLuckConfig(mainType)

	msgRet.luck = zuanpan.luck or 0
	local len = 0
	for k, v in pairs(luckConfig) do
	   len = len + 1
	   local net = msgRet.luckList[len]
	   net.id = k
	   net.status = zuanpan.luckStatus[k] or 0
	   Grid.makeItem(net.item, v.reward[1] , v.reward[2])
	end
	msgRet.luckList[0] = len

    len = 0
    if REWARD_RECORD[mainType] then
	   for k, v in ipairs(REWARD_RECORD[mainType]) do
	      len = len + 1
	      local net = msgRet.record[len]
	      net.id = k
          net.tips = v.tips
	      net.timer = v.timer
          net.order = v.order
	   end
    end
	msgRet.record[0] = len
    msgRet.list[0] = ZHUAN_PAN_ITEM_KIND_CNT

    len = 0
    local totalWeight = 0
    local chanceList = {}
	for k, v in pairs(zhuanpanConfig) do
	   totalWeight = totalWeight + v.chance
       chanceList[k] = chanceList[k] or {}
       local rewardWeight = 0
       for a, b in ipairs(v.rewardID) do
          rewardWeight = rewardWeight + b[3]
       end
       chanceList[k].totalWeight = rewardWeight
	end

    -- 大全重占比
    local temp = {}
    for k, v in pairs(zhuanpanConfig) do
        local chance = chanceList[k]
        local scale = v.chance / totalWeight
        for a, b in ipairs(v.rewardID) do
           local itemID = b[1]
           if not temp[itemID] then
               local weight = b[3] / chance.totalWeight
               len = len + 1
               local net = msgRet.chanceList[len]
               local itemConfig = ItemDefine.getConfig(itemID)
               if not itemConfig then
                  print(itemID)
               end
               net.tips = itemConfig and itemConfig.name or ""
               net.chance = weight * scale * 100
               temp[itemID] = 1
           end
        end
    end

	msgRet.chanceList[0] = len

    local red1 = red(human, DEFAULT_ZHUANPAN_TYPE_NORMAL)
    msgRet.red1 = red1 == true and 1 or 0

    local red2 = red(human, DEFAULT_ZHUANPAN_TYPE_GAOJI)
    msgRet.red2 = red2 == true and 1 or 0

	Msg.send(msgRet, human.fd)
end

function refresh(human, mainType, needTime, bRefreshLuck)
	if not ZhuanpanExcel[mainType] then
		return
	end

    local oldCnt = getCntByType(human, mainType)
	local oldLuck
	local tOldStatus = nil
	if	human.db.zhuanpan and human.db.zhuanpan[mainType] then
		oldLuck = human.db.zhuanpan[mainType].luck
		if human.db.zhuanpan[mainType].luckStatus and not bRefreshLuck then
			tOldStatus = Util.copyTable(human.db.zhuanpan[mainType].luckStatus)
		end
	end

    -- 更新
	update(human, mainType)

	-- 初始化
	initDB(human, mainType, oldCnt,oldLuck,tOldStatus)

	-- 判断是否免费
    if needTime then
	    local config = ZhuanpanExcel.define[mainType]
	    local needCost = 0
        local now = os.time()
	    local ts2 = now - human.db.zhuanpan[mainType].ts2
	    local leftTime = DEFAULT_FREE_REFRESH_TIME - ts2
	    if leftTime > 0 then
	    	needCost = config.refreshCost[1][2]
	    end

	    -- 判断消耗
	    local cnt = BagLogic.getItemCnt(human, config.refreshCost[1][1])
	    if cnt < needCost then
            return
        end
	    -- 扣消耗
	    if needCost > 0 then
            BagLogic.delItem(human, config.refreshCost[1][1], needCost, "zhuanpan_refresh")
	    end

	    -- 改db
        local dayStartTime = Util.getDayStartTime(now)
	    human.db.zhuanpan[mainType].ts2 = now
	    human.db.zhuanpan[mainType].ts1 = dayStartTime
    end

	addRandomReward(human, mainType)

	-- 通知客户端
    if needTime then
	    query(human, mainType)
    end
end

-- 刷新数据
function updateDaily(human)
	refresh(human, DEFAULT_ZHUANPAN_TYPE_NORMAL)
    refresh(human, DEFAULT_ZHUANPAN_TYPE_GAOJI)

    human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].free = 1
    human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].freeCnt = 0

	ZhuanpanGift.updateDaily(human)
end


-- 抽奖
function getReward(human, mainType, cnt)
	local config = ZhuanpanExcel.define[mainType]
	if not ZhuanpanExcel[mainType] then
		return
	end
	local needItemID = config.useItemID
	local needItemCnt = nil

	if cnt == 1 then
		needItemCnt = config.useItemCnt1
	elseif cnt == config.drawCnt2 then
		needItemCnt = config.useItemCnt2
		-- 多次抽奖需要达到指定vip等级
		--[[local needVipLv = VipLogic.getPowerNeedLv(VipLogic.VIP_POWER15)
		if VipLogic.getVipLv(human) < needVipLv then
			return Broadcast.sendErr(human, Util.format(Lang.ROLE_VIP_ERROR2, needVipLv))
		end]]
	end
	if needItemCnt == nil then
		return
	end

	local oldCnt = getCntByType(human, mainType)
	local oldLuck
	local tOldStatus = nil
	if human.db.zhuanpan and human.db.zhuanpan[mainType] then
		oldLuck = human.db.zhuanpan[mainType].luck
		if human.db.zhuanpan[mainType].luckStatus then
			tOldStatus = Util.copyTable(human.db.zhuanpan[mainType].luckStatus)
		end
	end

	-- 更新
	update(human, mainType)

	-- 初始化
	initDB(human, mainType, oldCnt,oldLuck, tOldStatus)

	local zhuanpan = human.db.zhuanpan[mainType]
	if not BagLogic.checkItemCnt(human, needItemID, needItemCnt) then
		return
	end

	-- 每日可抽取次数检测
	-- local dayTimes = ClutterDataLogic.GetZhuanPanTimes(human)
	-- if dayTimes < cnt then
	-- 	return Broadcast.sendErr(human, Lang.JINBI_EXCHANGE_ERR_CNT)
	-- end

	-- --更新每次抽取次数
	-- dayTimes = dayTimes - cnt

	-- 改为只更新次数
	ClutterDataLogic.UpdateZhuanPanTimes(human, cnt)


	-- 扣消耗
	BagLogic.delItem(human, needItemID, needItemCnt, "zhuanpan_get")


	local msgRet = Msg.gc.GC_ZHUANPAN_GET_REWARD
	msgRet.type = mainType
	msgRet.list[0] = cnt
    local len = 0
	for i = 1, cnt do
		local result = getLuckDrawSingle(human, mainType)
		if result == nil then
			assert(nil)
		end

		-- 改db
		local data = zhuanpan[result]
		local zhuanpanConfig = ZhuanpanExcel[mainType]
		local tempConfig = zhuanpanConfig[data.id]
		local nowGetCnt = data.getCnt or 0
		local maxGetCnt = tempConfig.getCnt
		if maxGetCnt ~= 0 then
			data.getCnt = nowGetCnt + 1
		end

		-- 加道具
		local itemID = data.itemID
		local itemCnt = data.itemCnt
		BagLogic.addItem(human, itemID, itemCnt, "zhuanpan_get")

		local itemConfig = ItemExcel[itemID]
		local heroConfig = HeroExcel[itemID]
		-- 存在部分 英雄配表里面的id 和装备配表重合了
		if itemConfig and itemConfig.subType == ItemDefine.ITEM_SUBTYPE_SUIPIAN and heroConfig and heroConfig.star == 5 then
			if mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
				ChatPaoMaLogic.broadcast(human, ChatPaoMaLogic.PAOMA_TYPE_BROAD_TYPE11, tempConfig.grade, itemID)
			else
				ChatPaoMaLogic.broadcast(human, ChatPaoMaLogic.PAOMA_TYPE_BROAD_TYPE10, tempConfig.grade, itemID)
			end
		end


		if tempConfig.getCnt > 0 then
            if not REWARD_RECORD[mainType] then
               REWARD_RECORD[mainType] = {}
            end

            local record = REWARD_RECORD[mainType]
            local index = REWARD_RECORD.index[mainType] or 1
            local order = REWARD_RECORD.order[mainType] or 1
		    record[index] = record[index] or {}
            record[index].tips = Util.format(Lang.ZHUANPAN_RECORD_TWO,  human.db.name, ItemDefine.getValue(itemID,"name"), itemCnt)
	        record[index].timer = os.time()
            record[index].order = order

			index = index + 1
	        if index > REWARD_RECORD_CNT then
	           index = 1
	        end
            REWARD_RECORD.order[mainType] = order + 1
            REWARD_RECORD.index[mainType] = index
		end

		msgRet.list[i] = data.id

        len = len + 1
        if not ItemDefine.isEquip(itemID) then
           Grid.makeItem(msgRet.item[len], itemID, itemCnt)
        else
           EquipLogic.makeEquipItemOne(human, msgRet.item[len])
        end
	end
	msgRet.item[0] = len


	-- 增加积分
	local itemID  = ItemDefine.ITEM_LUCK_ID
	local itemCnt = cnt * 10
	BagLogic.addItem(human, itemID, itemCnt, "zhuanpan_back")
    msgRet.luckItem[0] = 1
    Grid.makeItem(msgRet.luckItem[1], itemID, itemCnt)

	zhuanpan.cnt = zhuanpan.cnt or 0
	zhuanpan.cnt = zhuanpan.cnt + cnt

	zhuanpan.luck = zhuanpan.luck or 0
	zhuanpan.luck = zhuanpan.luck + cnt * 10
	zhuanpan.luckStatus = zhuanpan.luckStatus or {}

	local luckConfig = getLuckConfig(mainType)
	-- 集火幸运值 奖励
    local luckStatus = zhuanpan.luckStatus
    for k, v in pairs(luckConfig) do
        if zhuanpan.luck >= k then
            if luckStatus[k] == nil then
			   luckStatus[k] = 1
			end
        end
	end

    Msg.send(msgRet, human.fd)


    query(human, mainType)

    YunYingLogic.onCallBack(human, "onFindStar", cnt)

	TriggerLogic.PublishEvent(TriggerDefine.EVENT_TYPE_FINDSTAR, human.db._id, cnt)
    --周任务，寻星
	WeekTaskLogic.recordWeekTaskFinishCnt(human, WeekTaskLogic.WEEK_TASK_ID_1, cnt)
end

-- 购买许愿珠
function buyCnt(human, mainType, cnt)
	if cnt < 1 then return end

	if mainType ~= DEFAULT_ZHUANPAN_TYPE_NORMAL then
		return
	end

	local needZuanshi = 50 * cnt

	-- 判断消耗
	if not ObjHuman.checkRMB(human, needZuanshi) then
		return
	end

	-- 扣消耗
	ObjHuman.decZuanshi(human, -needZuanshi, "zhuanpan_buy_cnt", ItemDefine.ITEM_BASE_QIYUANZHU_ID, cnt)

	-- 增加物品
	BagLogic.addItem(human, ItemDefine.ITEM_BASE_QIYUANZHU_ID, cnt, "zhuanpan_buy_cnt")

	-- 通知客户端
	local msgRet = Msg.gc.GC_BUY_ZHUANPAN_CNT
	Msg.send(msgRet, human.fd)
end

------------------------------功能函数--------------------------------------
function addRandomReward(human, mainType)
	local zhuanpanConfig = ZhuanpanExcel[mainType]
	if not zhuanpanConfig then return end
	local nowLv = human.db.lv
	local itemID = nil
	local itemCnt = nil
	local cntIndex = 0
	for i = 1, #zhuanpanConfig do
		local tempConfig = zhuanpanConfig[i]
		if not tempConfig then return end
		if mainType == DEFAULT_ZHUANPAN_TYPE_NORMAL then
			if nowLv >= tempConfig.minLv and nowLv <= tempConfig.maxLv then
				itemID, itemCnt = getRandomItem(human, tempConfig)
                if itemID and itemCnt then
				   cntIndex = cntIndex + 1
				   human.db.zhuanpan[mainType][cntIndex] = {}
				   human.db.zhuanpan[mainType][cntIndex].id = i
				   human.db.zhuanpan[mainType][cntIndex].itemID = itemID
				   human.db.zhuanpan[mainType][cntIndex].itemCnt = itemCnt
                end
			end
		elseif mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
			itemID, itemCnt = getRandomItem(human, tempConfig)
            if itemID and itemCnt then
			   cntIndex = cntIndex + 1
			   human.db.zhuanpan[mainType][cntIndex] = {}
			   human.db.zhuanpan[mainType][cntIndex].id = i
			   human.db.zhuanpan[mainType][cntIndex].itemID = itemID
			   human.db.zhuanpan[mainType][cntIndex].itemCnt = itemCnt
            end
		end
	end
end

function getLuckDrawSingle(human, mainType)
	local zhuanpanConfig = ZhuanpanExcel[mainType]
	if not zhuanpanConfig then return end
	local totalWeight = 0
	local recordData = nil

	for i = 1, ZHUAN_PAN_ITEM_KIND_CNT do
		local data = human.db.zhuanpan[mainType][i]
		local drawID = data.id
		local tempConfig = zhuanpanConfig[drawID]
		local nowGetCnt = data.getCnt or 0
		local maxGetCnt = tempConfig.getCnt
		if maxGetCnt == 0 or maxGetCnt > nowGetCnt then
			totalWeight = totalWeight + tempConfig.chance
		end
	end

	local randNum = math.random(1,totalWeight)
	for i = 1, ZHUAN_PAN_ITEM_KIND_CNT do
		local data = human.db.zhuanpan[mainType][i]
		local drawID = data.id
		local tempConfig = zhuanpanConfig[drawID]
		local nowGetCnt = data.getCnt or 0
		local maxGetCnt = tempConfig.getCnt
		if maxGetCnt == 0 or maxGetCnt > nowGetCnt then
			local tempWeight = tempConfig.chance
			if randNum <= tempWeight then
				return i
			end
			randNum = randNum - tempWeight
		end
	end
end

function initDB(human, mainType, cnt, oldluck, tOldStatus)
	if human.db.zhuanpan ~= nil and
       human.db.zhuanpan[mainType] ~= nil then
		return
	end
    local now = os.time()

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan[mainType] = {}

    local dayStartTime = Util.getDayStartTime(now)
	human.db.zhuanpan[mainType].ts1 = dayStartTime
	human.db.zhuanpan[mainType].ts2 = now - DEFAULT_FREE_REFRESH_TIME
	human.db.zhuanpan[mainType].cnt = cnt
	human.db.zhuanpan[mainType].luck = oldluck
	human.db.zhuanpan[mainType].free = 1
	if tOldStatus then
		human.db.zhuanpan[mainType].luckStatus = tOldStatus
	else
		human.db.zhuanpan[mainType].luckStatus = {}
	end

	addRandomReward(human, mainType)
end

function update(human, mainType)
	if human.db.zhuanpan == nil or human.db.zhuanpan[mainType] == nil then
		return
	end
	if human.db.zhuanpan[mainType].ts1 then
		-- 防止前端 在结束倒计时结束 发送查询 , 这边还没有结束 返回一样的数据
		local now = os.time() + 1
		local ts1 = now - human.db.zhuanpan[mainType].ts1
		local leftTime = 24 * 60 * 60 - ts1
		if leftTime <= 0 then
			human.db.zhuanpan[mainType] = nil
		end
	end
end

function getRandomItem(human, tempConfig)
    local totalWeight = 0
    for k, v in ipairs(tempConfig.rewardID) do
       totalWeight = totalWeight + v[3]
    end

    local random = math.random(1, totalWeight)
    for k, v in ipairs(tempConfig.rewardID) do
       local weight = v[3]
       local itemID = v[1]
	   local itemCnt = v[2]
       if random <= weight then
          return itemID, itemCnt
       end
       random = random - weight
    end
end

function zhuanpanByGm(human,mainType,val)
	if not mainType and not val then
		return
	end
	if mainType == 0 or mainType > 2 or val < 5 then
		return
	end
	if human.db.zhuanpan[mainType] then
		local now = os.time()
		human.db.zhuanpan[mainType].ts1 = now - 24 * 60 * 60  + val
	end
end

function getCntByType(human, mainType)
	local num = 0
	if	human.db.zhuanpan and human.db.zhuanpan[mainType] then
		human.db.zhuanpan[mainType].cnt = human.db.zhuanpan[mainType].cnt or 0
		num = human.db.zhuanpan[mainType].cnt
	end
	return num
end

function getLuck(human, msg)
	local mainType = msg.type
    local config = getLuckConfig(mainType)
	if not config then
		return
	end

	local zhuanpan = human.db.zhuanpan[mainType]
	if zhuanpan == nil then
	   return
	end

	if zhuanpan.luck <= 0 then
        return
    end

	local luckStatus = zhuanpan.luckStatus
	if luckStatus == nil then
		luckStatus = {}
		zhuanpan.luckStatus = luckStatus
	end

	-- 收集所有可领取的奖励（status == 1）
	local itemList = {}
	local itemListLen = 0

	-- 遍历所有配置的奖励，找到可领取的（status == 1）
	for k, v in pairs(config) do
		if luckStatus[k] == 1 then
			-- 标记为已领取
			luckStatus[k] = 2

			-- 添加奖励到背包
			BagLogic.addItem(human, v.reward[1], v.reward[2], "zhuanpan_back")

			-- 收集到奖励列表
			itemListLen = itemListLen + 1
			itemList[itemListLen] = {v.reward[1], v.reward[2]}
		end
	end

	-- 如果没有可领取的奖励，直接返回
	if itemListLen == 0 then
		return
	end

	-- 循环处理幸运值扣除和重新激活奖励（可能领取多轮）
	while true do
		-- 计算是否需要重新扣除幸运值 重新激活奖励
		local luck = zhuanpan.luck
		local calc = true
		for k, v in pairs(config) do
			if luckStatus[k] == nil or luckStatus[k] ~= 2 then
				calc = false
			end
		end

		-- 如果所有奖励都已领取，扣除幸运值并重新激活
		if calc == true then
			zhuanpan.luckStatus = {}
			luck = luck - DEFAULT_MAX_LUCK
			luck = luck < 0 and 0 or luck
			zhuanpan.luck = luck

			-- 重新激活幸运值奖励
			luckStatus = zhuanpan.luckStatus
			for k, v in pairs(config) do
				if zhuanpan.luck >= k then
					if luckStatus[k] == nil then
						luckStatus[k] = 1
					end
				end
			end

			-- 如果重新激活后还有可领取的奖励，继续循环领取
			local hasMore = false
			for k, v in pairs(config) do
				if luckStatus[k] == 1 then
					hasMore = true
					-- 领取这个奖励
					luckStatus[k] = 2
					BagLogic.addItem(human, v.reward[1], v.reward[2], "zhuanpan_back")
					itemListLen = itemListLen + 1
					itemList[itemListLen] = {v.reward[1], v.reward[2]}
				end
			end

			-- 如果没有更多可领取的奖励，退出循环
			if not hasMore then
				break
			end
		else
			-- 不是所有奖励都已领取，退出循环
			break
		end
	end

	-- 发送所有奖励列表
	if itemListLen > 0 then
		BagLogic.sendItemGetList(human, itemList, "zhuanpan_back")
	end

	query(human, msg.type)
end

function isDot(human)

   local red1 = red(human, DEFAULT_ZHUANPAN_TYPE_NORMAL)
   if red1 then return true end

   --local red2 = red(human, DEFAULT_ZHUANPAN_TYPE_GAOJI)
   --if red2 then return true end

   return false
end

function getFree(human)
	local oldLuck
	local tOldStatus = nil
	if human.db.zhuanpan and human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL] then
		oldLuck = human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].luck
		if human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].luckStatus then
			tOldStatus = Util.copyTable(human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].luckStatus)
		end
	end
    initDB(human,DEFAULT_ZHUANPAN_TYPE_NORMAL,nil, oldLuck,tOldStatus)
    if human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].free ~= 1 then
        return
    end

    human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].free = 0
    local zhuanpanConfig = ZhuanpanExcel.define[DEFAULT_ZHUANPAN_TYPE_NORMAL]

    local len = #zhuanpanConfig.freeCnt
    local totalWeight = 0
    for i = 1,len do
        totalWeight = totalWeight + zhuanpanConfig.freeCnt[i][2]
    end

    local itemCnt = nil
    local randomWeight = math.random(1,totalWeight)
    for i = 1,len do
        if randomWeight < zhuanpanConfig.freeCnt[i][2] then
            itemCnt = zhuanpanConfig.freeCnt[i][1]
            break
        else
            randomWeight = randomWeight - zhuanpanConfig.freeCnt[i][2]
        end
    end
    if itemCnt then
        local item = {}
        item[1] = {zhuanpanConfig.useItemID,itemCnt}
        item[2] = zhuanpanConfig.freeitem[1]
        BagLogic.addItemList(human, item, "zhuanpan_get")
    else
        assert()
    end
end

-- 检查账号下是否有任何角色已领取每日固定奖励（账号级别判断）
local function checkAccountDailyFixedReward(account)
	if not account then
		return false
	end
	
	local QueryByAccount = {account = account}
	local fields = {zhuanpan = 1}
	LuaMongo.find(DB.db_char, QueryByAccount, fields)
	
	while true do
		local data = {}
		if not LuaMongo.next(data) then
			break
		end
		
		if data.zhuanpan and data.zhuanpan.dailyFixedReward then
			local getTime = data.zhuanpan.dailyFixedReward.getTime
			if getTime and Util.isSameDay(getTime) then
				return true  -- 账号下已有角色今日领取过
			end
		end
	end
	
	return false  -- 账号下没有角色今日领取过
end

-- 获取账号下最早创建的角色注册日期（账号级别判断）
local function getAccountEarliestCreateTime(account)
	if not account then
		return nil
	end
	
	local QueryByAccount = {account = account}
	local fields = {createTime = 1}
	LuaMongo.find(DB.db_char, QueryByAccount, fields)
	
	local earliestTime = nil
	while true do
		local data = {}
		if not LuaMongo.next(data) then
			break
		end
		
		if data.createTime then
			if not earliestTime or data.createTime < earliestTime then
				earliestTime = data.createTime
			end
		end
	end
	
	return earliestTime
end

-- 检查渠道17是否已超过注册后15天（基于当前角色注册时间）
local function isChannel18Over15Days(human)
	if not human or not human.db or not human.db.createTime then
		return false
	end
	
	local createTime = human.db.createTime
	local registerDayStart = Util.getDayStartTime(createTime)
	local now = os.time()
	local todayDayStart = Util.getDayStartTime(now)
	
	-- 计算从注册日期到今天已经过了多少天（注册日期是第1天）
	local daysSinceRegister = math.floor((todayDayStart - registerDayStart) / 86400) + 1
	
	-- 如果超过15天，则不能领取
	return daysSinceRegister > 7
end

-- 检查账号下是否有任何角色已领取一次性奖励（账号级别判断）
local function checkAccountOnceReward(account)
	if not account then
		return false
	end

	local QueryByAccount = {account = account}
	local fields = {zhuanpan = 1}
	LuaMongo.find(DB.db_char, QueryByAccount, fields)

	while true do
		local data = {}
		if not LuaMongo.next(data) then
			break
		end

		if data.zhuanpan and data.zhuanpan.onceReward then
			local getTime = data.zhuanpan.onceReward.getTime
			if getTime then
				return true  -- 账号下已有角色领取过
			end
		end
	end

	return false  -- 账号下没有角色领取过
end

-- 检查账号下是否有任何角色已领取订阅奖励（账号级别判断）
local function checkAccountSubscribeReward(account)
	if not account then
		return false
	end

	local QueryByAccount = {account = account}
	local fields = {zhuanpan = 1}
	LuaMongo.find(DB.db_char, QueryByAccount, fields)

	while true do
		local data = {}
		if not LuaMongo.next(data) then
			break
		end

		if data.zhuanpan and data.zhuanpan.subscribeReward then
			local getTime = data.zhuanpan.subscribeReward.getTime
			if getTime then
				return true  -- 账号下已有角色领取过
			end
		end
	end

	return false  -- 账号下没有角色领取过
end

-- 从newUniqueTag解析渠道ID (格式: "channelID|serverTag|account")
local function getChannelIdFromNewUniqueTag(newUniqueTag)
	if not newUniqueTag then
		return nil
	end
	local parts = {}
	for part in string.gmatch(newUniqueTag, "([^|]+)") do
		table.insert(parts, part)
	end
	if #parts >= 1 then
		return tonumber(parts[1])
	end
	return nil
end

-- 获取每日固定奖励配置（根据渠道ID）
local function getDailyFixedRewardConfig(channelId)
	if channelId == 17 then
		-- 渠道17（美图2楼）：[[101,20000],[111,5000]]
		return {
			{101, 20000},
			{111, 5000}
		}
	else
		-- 渠道11（默认）：[[102, 50]]
		return {
			{102, 50}
		}
	end
end

-- 每日固定奖励查询
function dailyFixedRewardQuery(human)
	local msgRet = Msg.gc.GC_ZHUANPAN_DAILY_FIXED_QUERY
	if not msgRet then
		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] 错误: msgRet为nil")
		return
	end

	-- 每日更新检查
	ObjHuman.updateDaily(human)

	local account = human.db.account
	local isReceived = false
	-- 先读取当日是否已领取（必须在 or {} 初始化之前读，否则可能被空表覆盖导致刚写入的 getTime 丢失）
	local getTime = nil
	if human.db.zhuanpan and human.db.zhuanpan.dailyFixedReward then
		getTime = human.db.zhuanpan.dailyFixedReward.getTime
	end
	if getTime and Util.isSameDay(getTime) then
		isReceived = true
	else
		isReceived = checkAccountDailyFixedReward(account)
	end

	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] getTime=" .. tostring(getTime) .. ", isReceived=" .. tostring(isReceived) .. ", account=" .. tostring(account))

	-- 初始化数据库（放在读取 getTime 之后，避免覆盖已有 dailyFixedReward 导致 isReceived 错误）
	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.dailyFixedReward = human.db.zhuanpan.dailyFixedReward or {}

	-- 渠道17（美团2楼）需要满足：
	-- 1) 当日从2楼登录（mtFrom2floor == 1，且 mtFrom2floorDay 是今天）
	-- 2) 订阅奖励已领取且在7日有效期内
	local newUniqueTag = human.db.newUniqueTag or human.newUniqueTag
	local channelId = getChannelIdFromNewUniqueTag(newUniqueTag)
	if channelId == 17 then
		-- 2楼登录状态（mtFrom2floor 可能为字符串；Util.isSameDay 同天返回 now 时间戳，否则 nil，转为布尔）
		local from2floorOk = false
		if tonumber(human.db.mtFrom2floor) == 1 and human.db.mtFrom2floorDay then
			from2floorOk = (Util.isSameDay(human.db.mtFrom2floorDay) and true or false)
		end

		-- 订阅相关状态（mtSubscribe 可能为字符串 "1"，用 tonumber 统一判断）
		local isSubscribed = (tonumber(human.db.mtSubscribe) == 1)
		local zhuanpan = human.db.zhuanpan
		local hasSubscribeReward = zhuanpan and zhuanpan.subscribeReward and zhuanpan.subscribeReward.getTime
		local inValidPeriod = hasSubscribeReward and isSubscribeRewardInValidPeriod(human)

		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] channelId=17 from2floorOk=" .. tostring(from2floorOk) .. ", mtFrom2floor=" .. tostring(human.db.mtFrom2floor) .. ", mtFrom2floorDay=" .. tostring(human.db.mtFrom2floorDay) .. ", isSubscribed=" .. tostring(isSubscribed) .. ", hasSubscribeReward=" .. tostring(hasSubscribeReward) .. ", inValidPeriod=" .. tostring(inValidPeriod))

		if not isSubscribed then
			-- 未订阅：不可领取
			msgRet.status = 3 -- 未激活（未订阅）
		elseif isReceived then
			-- 当日已领取：固定为 2，不因 mtFrom2floor 被置 0 而变成 0
			msgRet.status = 2  -- 已领取（今日已领取）
		elseif not from2floorOk then
			-- 当天未从2楼进入：不可领取
			msgRet.status = 0
		else
			if not hasSubscribeReward or not inValidPeriod then
				-- 已订阅，但订阅奖励尚未领取，或已超过7日有效期
				msgRet.status = 3  -- 未激活（订阅奖励未领或15天已满）
			else
				msgRet.status = 1  -- 可领取（已激活且在7日内，且今日未领）
			end
		end
	else
		-- 其他渠道保持原有逻辑
		if isReceived then
			msgRet.status = 2  -- 已领取（账号级别）
		else
			msgRet.status = 1  -- 可领取
		end
	end

	-- 根据渠道ID设置奖励物品
	local newUniqueTag = human.db.newUniqueTag or human.newUniqueTag
	local channelId = getChannelIdFromNewUniqueTag(newUniqueTag)
	local rewardConfig = getDailyFixedRewardConfig(channelId or 11)  -- 默认渠道11
	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] 查询奖励信息: rewardConfig",rewardConfig)
	-- 循环处理所有奖励物品
	for i = 1, #rewardConfig do
		Grid.makeItem(msgRet.reward[i], rewardConfig[i][1], rewardConfig[i][2])
	end
	msgRet.reward[0] = #rewardConfig  -- 设置数组长度

	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] channelId=" .. tostring(channelId) .. ", status=" .. tostring(msgRet.status))

	if human.fd then
		Msg.send(msgRet, human.fd)
	else
		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] 错误: human.fd为nil，无法发送协议")
	end
end

-- 每日固定奖励领取
function dailyFixedRewardGet(human, channelId)
	if not ALLOW_CHANNELS[channelId] then
		Log.write(
				Log.LOGID_DEBUG,
				"[dailyFixedRewardGet] 渠道不匹配: channelId=" .. (channelId or "nil") .. ", 允许=11,16,18"
		)
		return Broadcast.sendErr(human, "渠道不匹配")
	end

	-- 每日更新检查
	ObjHuman.updateDaily(human)

	-- 初始化数据库
	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.dailyFixedReward = human.db.zhuanpan.dailyFixedReward or {}

	-- 账号级别判断：先检查当前角色的内存数据，再检查数据库
	local account = human.db.account
	local isReceived = false
	
	-- 先检查当前角色的内存数据
	local getTime = human.db.zhuanpan.dailyFixedReward.getTime
	if getTime and Util.isSameDay(getTime) then
		isReceived = true
	else
		-- 再检查账号下其他角色（数据库）
		isReceived = checkAccountDailyFixedReward(account)
	end
	
	if isReceived then
		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 账号今日已领取: account="..(account or "nil"))
		return Broadcast.sendErr(human, "今日已领取")
	end
	
	-- 渠道17（美团2楼）额外条件：
	-- 1) 当日从2楼登录（mtFrom2floor == 1 且 mtFrom2floorDay 为今天）
	-- 2) 订阅奖励已领取且在7日有效期内
	if channelId == 17 then
		-- 2楼登录状态（mtFrom2floor 可能为字符串，Util.isSameDay 同天返回时间戳，转为布尔）
		local from2floorOk = false
		if tonumber(human.db.mtFrom2floor) == 1 and human.db.mtFrom2floorDay then
			from2floorOk = (Util.isSameDay(human.db.mtFrom2floorDay) and true or false)
		end

		if not from2floorOk then
			Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 渠道17当日未从2楼进入，无法领取: account="..(account or "nil"))
			return Broadcast.sendErr(human, "条件不足，无法领取")
		end

		-- 订阅有效期检查（mtSubscribe 可能为字符串 "1"）
		if tonumber(human.db.mtSubscribe) ~= 1 or not isSubscribeRewardInValidPeriod(human) then
			Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 渠道17订阅未激活或已过期，无法领取: account="..(account or "nil"))
			return Broadcast.sendErr(human, "订阅未激活或已过期")
		end
	end

	BagLogic.cleanMomentItemList()

	-- 根据渠道ID发放不同的奖励（循环处理所有奖励）
	local rewardConfig = getDailyFixedRewardConfig(channelId)
	for i = 1, #rewardConfig do
		BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, rewardConfig[i][1], rewardConfig[i][2])
	end
	
	-- 复用已有的日志类型，避免未定义的logType导致断言
	local success, err = pcall(function()
		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
	end)
	if not success then
		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 错误: 添加到背包失败: "..(err or "unknown"))
		return Broadcast.sendErr(human, "发放奖励失败")
	end

	-- 记录领取时间
	local nowTime = os.time()
	human.db.zhuanpan.dailyFixedReward.getTime = nowTime

	-- 渠道17：领取成功后，消耗当天的2楼登录资格，防止同日重复触发
	if channelId == 17 then
		human.db.mtFrom2floor = 0
	end

	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 领取成功 account=" .. tostring(human.db.account) .. " channelId=" .. tostring(channelId) .. " getTime=" .. tostring(nowTime) .. " mtFrom2floor=" .. tostring(human.db.mtFrom2floor))

	-- 领取成功后下发查询协议
	local querySuccess, queryErr = pcall(function()
		dailyFixedRewardQuery(human)
	end)
	if not querySuccess then
		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 错误: 发送查询协议失败: "..(queryErr or "unknown"))
		-- 即使查询协议发送失败，奖励已经发放，所以不返回错误
	end
	-- Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 领取完成")
end

-- 抖音渠道一次性奖励查询
function onceRewardQuery(human)
	local msgRet = Msg.gc.GC_ZHUANPAN_ONCE_REWARD_QUERY
	if not msgRet then
		Log.write(Log.LOGID_DEBUG, "[onceRewardQuery] 错误: msgRet为nil")
		return
	end

	-- 初始化数据库
	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.onceReward = human.db.zhuanpan.onceReward or {}

	-- 账号级别判断：先检查当前角色的内存数据，再检查数据库
	local account = human.db.account
	local isReceived = false
	
	-- 先检查当前角色的内存数据
	local getTime = human.db.zhuanpan.onceReward.getTime
	if getTime then
		isReceived = true
	else
		-- 再检查账号下其他角色（数据库）
		isReceived = checkAccountOnceReward(account)
	end
	
	if isReceived then
		msgRet.status = 2  -- 已领取（账号级别）
	else
		msgRet.status = 1  -- 可领取
	end

	-- 设置奖励物品列表：[[102,500],[118,10],[111,100000]]
	local success, err = pcall(function()
		Grid.makeItem(msgRet.reward[1], 102, 500)
		Grid.makeItem(msgRet.reward[2], 118, 10)
		Grid.makeItem(msgRet.reward[3], 111, 100000)
		msgRet.reward[0] = 3  -- 设置数组长度
	end)

	if not success then
		Log.write(Log.LOGID_DEBUG, "[onceRewardQuery] 错误: 设置奖励物品失败: "..tostring(err))
		return
	end

	if human.fd then
		Msg.send(msgRet, human.fd)
	end
end

-- 抖音渠道一次性奖励领取
function onceRewardGet(human, channelId)

	if not ALLOW_CHANNELS[channelId] then
		Log.write(
				Log.LOGID_DEBUG,
				"[onceRewardGet] 渠道不匹配: channelId=" .. (channelId or "nil") .. ", 允许=11,12"
		)
		return Broadcast.sendErr(human, "渠道不匹配")
	end

	-- 初始化数据库
	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.onceReward = human.db.zhuanpan.onceReward or {}

	-- 账号级别判断：先检查当前角色的内存数据，再检查数据库
	local account = human.db.account
	local isReceived = false
	
	-- 先检查当前角色的内存数据
	local getTime = human.db.zhuanpan.onceReward.getTime
	if getTime then
		isReceived = true
	else
		-- 再检查账号下其他角色（数据库）
		isReceived = checkAccountOnceReward(account)
	end
	
	if isReceived then
		Log.write(Log.LOGID_DEBUG, "[onceRewardGet] 账号已领取过，无法重复领取: account="..(account or "nil"))
		return Broadcast.sendErr(human, "已领取过，无法重复领取")
	end

	BagLogic.cleanMomentItemList()

	-- 发放奖励：[[102,500],[118,10],[111,100000]]
	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 102, 500)
	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 118, 10)
	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 111, 100000)

	local success, err = pcall(function()
		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
	end)
	if not success then
		Log.write(Log.LOGID_DEBUG, "[onceRewardGet] 错误: 添加到背包失败: "..(err or "unknown"))
		return Broadcast.sendErr(human, "发放奖励失败")
	end

	-- 记录领取时间（永久记录）
	human.db.zhuanpan.onceReward.getTime = os.time()

	local querySuccess, queryErr = pcall(function()
		onceRewardQuery(human)
	end)
	if not querySuccess then
		Log.write(Log.LOGID_DEBUG, "[onceRewardGet] 错误: 发送查询协议失败: "..(queryErr or "unknown"))
		-- 即使查询协议发送失败，奖励已经发放，所以不返回错误
	end
end

-- 检查当前角色订阅奖励是否在7日有效期内（从订阅奖励领取时间起算）
function isSubscribeRewardInValidPeriod(human)
	if not human or not human.db then
		return false
	end
	local zhuanpan = human.db.zhuanpan
	if not zhuanpan or not zhuanpan.subscribeReward or not zhuanpan.subscribeReward.getTime then
		return false
	end
	local activateTime = zhuanpan.subscribeReward.getTime
	local activateDayStart = Util.getDayStartTime(activateTime)
	local nowTs = os.time()
	local todayDayStart = Util.getDayStartTime(nowTs)
	-- 激活日为第1天，7日内有效
	local daysSinceActivate = math.floor((todayDayStart - activateDayStart) / 86400) + 1
	return daysSinceActivate <= 7
end

-- 检查美团2楼玩家每日固定奖励领取情况（晚上11点调用）
function checkMtFrom2floorDailyReward()
	if _G.is_middle == true then
		return
	end
	
	local QueryMtFrom2floor = {mtFrom2floor = 1}
	local fields = {zhuanpan = 1, createTime = 1, mtSubscribe = 1, account = 1}
	LuaMongo.find(DB.db_char, QueryMtFrom2floor, fields)
	
	local now = os.time()
	local todayDayStart = Util.getDayStartTime(now)
	local dailyMailSentCount = 0
	local subscribeMailSentCount = 0
	
	while true do
		local data = {}
		if not LuaMongo.next(data) then
			break
		end
		
		-- 检查是否在订阅有效期内（如果从未激活订阅奖励，视为未激活，不发每日固定奖励）
		if data.mtSubscribe ~= 1 then
			goto continue
		end

		local inValidPeriod = false
		if data.zhuanpan and data.zhuanpan.subscribeReward and data.zhuanpan.subscribeReward.getTime then
			local activateTime = data.zhuanpan.subscribeReward.getTime
			local activateDayStart = Util.getDayStartTime(activateTime)
			local daysSinceActivate = math.floor((todayDayStart - activateDayStart) / 86400) + 1
			inValidPeriod = (daysSinceActivate <= 7)
		end

		-- 订阅奖励未激活或已过期，则不发每日固定奖励
		if not inValidPeriod then
			goto continue
		end
		
		-- 检查今天是否已领取每日固定奖励
		local hasReceivedToday = false
		if data.zhuanpan and data.zhuanpan.dailyFixedReward then
			local getTime = data.zhuanpan.dailyFixedReward.getTime
			if getTime and Util.isSameDay(getTime) then
				hasReceivedToday = true
			end
		end
		
		-- 如果今天没领取，发送邮件，并将 mtFrom2floor 置为0，防止当天再次通过登录触发
		if not hasReceivedToday then
			local title = "每日固定奖励提醒"
			local content = "您今日尚未领取每日固定奖励，请及时领取！"
			local rewardItems = {{101, 20000}, {111, 5000}}  -- 渠道17的奖励
			
			local success, err = pcall(function()
				MailManager.add(MailManager.SYSTEM, data._id, title, content, rewardItems)
				-- 更新数据库：标记今日已通过邮件发放，并清除当天的2楼标记
				local updateFields = {
					["zhuanpan.dailyFixedReward.getTime"] = now,
					["mtFrom2floor"] = 0,
				}
				LuaMongo.update(DB.db_char, {_id = data._id}, updateFields, false, false)
			end)
			
			if success then
				dailyMailSentCount = dailyMailSentCount + 1
				Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送每日固定奖励邮件并更新状态成功: uuid="..data._id)
			else
				Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送每日固定奖励邮件或更新状态失败: uuid="..data._id..", err="..tostring(err))
			end
		end
		
		-- 检查订阅奖励：如果已订阅（mtSubscribe == 1）且未领取，发送邮件
		if data.mtSubscribe == 1 then
			-- 检查账号下是否已领取订阅奖励
			local hasReceivedSubscribe = checkAccountSubscribeReward(data.account)
			
			if not hasReceivedSubscribe then
				local title = "订阅奖励提醒"
				local content = "您已订阅但尚未领取订阅奖励，请及时领取！"
				local rewardItems = {{101, 50000}, {112, 100}}  -- 订阅奖励
				
				local success, err = pcall(function()
					MailManager.add(MailManager.SYSTEM, data._id, title, content, rewardItems)
				end)
				
				if success then
					subscribeMailSentCount = subscribeMailSentCount + 1
					Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送订阅奖励邮件成功: uuid="..data._id)
				else
					Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送订阅奖励邮件失败: uuid="..data._id..", err="..tostring(err))
				end
			end
		end
		
		::continue::
	end
	
	Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 检查完成，每日固定奖励邮件数量: "..dailyMailSentCount..", 订阅奖励邮件数量: "..subscribeMailSentCount)
end

-- 美团订阅奖励查询（同时接收并存储订阅状态）
function subscribeRewardQuery(human, msg)
	local msgRet = Msg.gc.GC_ZHUANPAN_SUBSCRIBE_REWARD_QUERY
	if not msgRet then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardQuery] 错误: msgRet为nil")
		return
	end

	-- 如果前端上传了订阅状态，则进行存储
	if msg and msg.mtSubscribe ~= nil then
		local val = tonumber(msg.mtSubscribe) or 0
		-- 只从 0 -> 1，避免重复覆盖或回退
		if val == 1 and human.db then
			human.db.mtSubscribe = human.db.mtSubscribe or 0
			if human.db.mtSubscribe ~= 1 then
				human.db.mtSubscribe = 1
				Log.write(Log.LOGID_DEBUG, "[subscribeRewardQuery] 更新订阅状态为1, account="..tostring(human.db.account))
			end
		end
	end

	-- 初始化数据库
	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.subscribeReward = human.db.zhuanpan.subscribeReward or {}

	-- 检查是否订阅（mtSubscribe == 1）
	local isSubscribed = (human.db.mtSubscribe == 1)
	
	-- 账号级别判断：先检查当前角色的内存数据，再检查数据库
	local account = human.db.account
	local isReceived = false
	
	if isSubscribed then
		-- 先检查当前角色的内存数据
		local getTime = human.db.zhuanpan.subscribeReward.getTime
		if getTime then
			isReceived = true
		else
			-- 再检查账号下其他角色（数据库）
			isReceived = checkAccountSubscribeReward(account)
		end
	end
	
	if not isSubscribed then
		msgRet.status = 0  -- 不可领取（未订阅）
	elseif isReceived then
		msgRet.status = 2  -- 已领取
	else
		msgRet.status = 1  -- 可领取
	end

	-- 设置奖励物品列表：[[101,50000],[112,100]]
	local success, err = pcall(function()
		Grid.makeItem(msgRet.reward[1], 101, 50000)
		Grid.makeItem(msgRet.reward[2], 112, 100)
		msgRet.reward[0] = 2  -- 设置数组长度
	end)
	
	if not success then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardQuery] 错误: 设置奖励物品失败: "..tostring(err))
		return
	end

	if human.fd then
		Msg.send(msgRet, human.fd)
	end
end

-- 美团订阅奖励领取
function subscribeRewardGet(human, channelId)
	-- 检查渠道，只有渠道ID=17（美团2楼）才能领取
	if not channelId or channelId ~= 17 then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 渠道不匹配: channelId="..(channelId or "nil")..", 需要=18")
		return Broadcast.sendErr(human, "渠道不匹配")
	end

	-- 检查是否订阅（mtSubscribe == 1）
	if human.db.mtSubscribe ~= 1 then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 未订阅: mtSubscribe="..(human.db.mtSubscribe or "nil"))
		return Broadcast.sendErr(human, "未订阅，无法领取")
	end

	-- 初始化数据库
	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.subscribeReward = human.db.zhuanpan.subscribeReward or {}

	-- 账号级别判断：先检查当前角色的内存数据，再检查数据库
	local account = human.db.account
	local isReceived = false
	
	-- 先检查当前角色的内存数据
	local getTime = human.db.zhuanpan.subscribeReward.getTime
	if getTime then
		isReceived = true
	else
		-- 再检查账号下其他角色（数据库）
		isReceived = checkAccountSubscribeReward(account)
	end
	
	if isReceived then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 账号已领取过，无法重复领取: account="..(account or "nil"))
		return Broadcast.sendErr(human, "已领取过，无法重复领取")
	end

	BagLogic.cleanMomentItemList()

	-- 发放奖励：[[101,50000],[112,100]]
	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 101, 50000)
	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 112, 100)
	
	-- 使用已存在的日志类型，避免日志定义缺失导致的断言
	local success, err = pcall(function()
		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
	end)
	if not success then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 错误: 添加到背包失败: "..(err or "unknown"))
		return Broadcast.sendErr(human, "发放奖励失败")
	end

	-- 记录领取时间（永久记录）
	human.db.zhuanpan.subscribeReward.getTime = os.time()

	local querySuccess, queryErr = pcall(function()
		subscribeRewardQuery(human)
	end)
	if not querySuccess then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 错误: 发送订阅奖励查询协议失败: "..(queryErr or "unknown"))
		-- 即使查询协议发送失败，奖励已经发放，所以不返回错误
	end

	-- 领取订阅奖励后，同时下发每日固定奖励查询协议
	local dailyQuerySuccess, dailyQueryErr = pcall(function()
		dailyFixedRewardQuery(human)
	end)
	if not dailyQuerySuccess then
		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 错误: 发送每日固定奖励查询协议失败: "..(dailyQueryErr or "unknown"))
		-- 即使查询协议发送失败，奖励已经发放，所以不返回错误
	end
end


-----------------------淘宝小程序桌面活动-----------------------

-- 奖励配置（占位，按需修改）
local TB_ENTER_REWARD        = {{112, 100}}   -- 桌面进入奖励（每日1次）
local TB_SECOND_FLOOR_REWARD = {{127, 10}}    -- 二楼进入奖励（每日1次）
local TB_DESKTOP_REWARD      = {{118, 5}}     -- 添加桌面奖励（仅1次）

-- 渠道校验：仅 pf == "tbMiniGame" 的玩家可参与
local function isTbMiniApp(human)
	return human.pf_info and human.pf_info.pf == "tbMiniGame"
end

-- 淘宝小程序桌面活动查询
-- status: 1=桌面进入 2=二楼进入
function tbminiappQuery(human, status)
	Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 收到请求 account=" .. tostring(human and human.db and human.db.account)
		.. " pf=" .. tostring(human and human.pf)
		.. " status=" .. tostring(status))

	local msgRet = Msg.gc.GC_ZHUANPAN_TB_QUERY
	if not msgRet then
		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: msgRet为nil")
		return
	end

	if not isTbMiniApp(human) then
		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 渠道不匹配 pf=" .. tostring(human and human.pf))
		return Broadcast.sendErr(human, "渠道不匹配")
	end

	ObjHuman.updateDaily(human)

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.tbminiapp = human.db.zhuanpan.tbminiapp or {}
	local tb = human.db.zhuanpan.tbminiapp

	-- 是否已添加桌面
	msgRet.isDesktopAdded = tb.desktopAdded and 1 or 0

	-- 是否已领取添加桌面奖励（一次性）
	msgRet.isDesktopRewardReceived = (tb.desktopReward and tb.desktopReward.getTime) and 1 or 0

	-- 是否已领取桌面进入奖励（每日1次）
	local enterTime = tb.enterReward and tb.enterReward.getTime
	msgRet.isEnterRewardReceived = (enterTime and Util.isSameDay(enterTime)) and 1 or 0

	-- 是否已领取二楼进入奖励（每日1次）
	local floorTime = tb.floorReward and tb.floorReward.getTime
	msgRet.isFloorRewardReceived = (floorTime and Util.isSameDay(floorTime)) and 1 or 0

	-- 填充奖励道具信息
	local ok, err = pcall(function()
		Grid.makeItem(msgRet.enterReward,   TB_ENTER_REWARD[1][1],        TB_ENTER_REWARD[1][2])
		Grid.makeItem(msgRet.floorReward,   TB_SECOND_FLOOR_REWARD[1][1], TB_SECOND_FLOOR_REWARD[1][2])
		Grid.makeItem(msgRet.desktopReward, TB_DESKTOP_REWARD[1][1],      TB_DESKTOP_REWARD[1][2])
	end)
	if not ok then
		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: Grid.makeItem失败: " .. tostring(err))
		return
	end

	Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 准备发送"
		.. " isDesktopAdded=" .. tostring(msgRet.isDesktopAdded)
		.. " isDesktopRewardReceived=" .. tostring(msgRet.isDesktopRewardReceived)
		.. " isEnterRewardReceived=" .. tostring(msgRet.isEnterRewardReceived)
		.. " isFloorRewardReceived=" .. tostring(msgRet.isFloorRewardReceived)
		.. " fd=" .. tostring(human.fd))

	if human.fd then
		Msg.send(msgRet, human.fd)
		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 发送成功 account=" .. tostring(human.db.account))
	else
		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: human.fd为nil，无法发送协议 account=" .. tostring(human.db.account))
	end
end

-- 淘宝小程序添加桌面上报：记录添加状态
function tbminiappAddDesktop(human)
	if not isTbMiniApp(human) then
		return Broadcast.sendErr(human, "渠道不匹配")
	end

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.tbminiapp = human.db.zhuanpan.tbminiapp or {}
	human.db.zhuanpan.tbminiapp.desktopAdded = true

	Log.write(Log.LOGID_DEBUG, "[tbminiappAddDesktop] 添加桌面成功 account=" .. tostring(human.db.account))

	local querySuccess, queryErr = pcall(function()
		tbminiappQuery(human, 0)
	end)
	if not querySuccess then
		Log.write(Log.LOGID_DEBUG, "[tbminiappAddDesktop] 错误: 发送查询协议失败: "..(queryErr or "unknown"))
	end
end

-- 淘宝小程序活动奖励领取
-- status: 1=桌面进入奖励（每日1次）2=二楼进入奖励（每日1次）3=添加桌面奖励（仅1次）
function tbminiappRewardGet(human, status)
	if not isTbMiniApp(human) then
		return Broadcast.sendErr(human, "渠道不匹配")
	end

	ObjHuman.updateDaily(human)

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.tbminiapp = human.db.zhuanpan.tbminiapp or {}
	local tb = human.db.zhuanpan.tbminiapp

	local rewardConfig = nil

	if status == 1 then
		-- 桌面进入奖励（每日1次）
		local enterTime = tb.enterReward and tb.enterReward.getTime
		if enterTime and Util.isSameDay(enterTime) then
			Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 今日已领取桌面进入奖励 account=" .. tostring(human.db.account))
			return Broadcast.sendErr(human, "今日已领取桌面进入奖励")
		end
		rewardConfig = TB_ENTER_REWARD

	elseif status == 2 then
		-- 二楼进入奖励（每日1次）
		local floorTime = tb.floorReward and tb.floorReward.getTime
		if floorTime and Util.isSameDay(floorTime) then
			Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 今日已领取二楼进入奖励 account=" .. tostring(human.db.account))
			return Broadcast.sendErr(human, "今日已领取二楼进入奖励")
		end
		rewardConfig = TB_SECOND_FLOOR_REWARD

	elseif status == 3 then
		-- 添加桌面奖励（仅1次）
		if not tb.desktopAdded then
			Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 尚未添加桌面 account=" .. tostring(human.db.account))
			return Broadcast.sendErr(human, "尚未添加桌面")
		end
		if tb.desktopReward and tb.desktopReward.getTime then
			Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 添加桌面奖励已领取 account=" .. tostring(human.db.account))
			return Broadcast.sendErr(human, "添加桌面奖励已领取")
		end
		rewardConfig = TB_DESKTOP_REWARD

	else
		return Broadcast.sendErr(human, "无效的status参数")
	end

	-- 发放奖励
	BagLogic.cleanMomentItemList()
	for _, v in ipairs(rewardConfig) do
		BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, v[1], v[2])
	end
	local success, err = pcall(function()
		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
	end)
	if not success then
		Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 错误: 添加到背包失败: "..(err or "unknown"))
		return Broadcast.sendErr(human, "发放奖励失败")
	end

	-- 记录领取时间
	local nowTime = os.time()
	if status == 1 then
		tb.enterReward = tb.enterReward or {}
		tb.enterReward.getTime = nowTime
	elseif status == 2 then
		tb.floorReward = tb.floorReward or {}
		tb.floorReward.getTime = nowTime
	elseif status == 3 then
		tb.desktopReward = tb.desktopReward or {}
		tb.desktopReward.getTime = nowTime
	end

	Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 领取成功 status=" .. tostring(status) .. " account=" .. tostring(human.db.account))

	-- 下发查询协议
	local querySuccess, queryErr = pcall(function()
		tbminiappQuery(human, 0)
	end)
	if not querySuccess then
		Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 错误: 发送查询协议失败: "..(queryErr or "unknown"))
	end
end


-----------------------游戏圈任务-----------------------

-- 游戏圈任务配置
-- reward: {itemId, count}  target: 完成目标数量  type: "once"永久/"daily"每日/"weekly"每周
local YOUXI_TASK_CONFIG = {
	[1] = { reward = {118, 10},  target = 1,  taskType = "once"   },  -- 首次加入游戏圈: 10连抽
	[2] = { reward = {117, 1}, target = 1,  taskType = "daily"  },  -- 今日点赞1次: 金币100
	[3] = { reward = {117, 1}, target = 1,  taskType = "daily"  },  -- 今日发表评论1次: 金币100
	[4] = { reward = {117, 5}, target = 10, taskType = "weekly" },  -- 每周点赞10次: 金币100
	[5] = { reward = {117, 5}, target = 20, taskType = "weekly" },  -- 每周发表评论20次: 金币100
}

-- 获取本周一0点时间戳
local function getWeekStartTime()
	local now = os.time()
	local t = os.date("*t", now)
	local daysFromMonday = (t.wday - 2) % 7  -- wday: 1=Sun, 2=Mon
	local todayStart = Util.getDayStartTime(now)
	return todayStart - daysFromMonday * 86400
end

-- 判断时间戳是否在本周（本周一0点）之后
local function isCurrentWeek(timestamp)
	if not timestamp then return false end
	return timestamp >= getWeekStartTime()
end

-- 更新每日 SDK 进度（当天自然日，同天取最大值；跨天把昨天 count 累加入本周合计）
-- weekTotal 始终不含当天，getWeekProgress 统一加上今日 count
local function updateDailyCount(field, newCount)
	field = field or {}
	local now = os.time()
	if field.dayTime and Util.isSameDay(field.dayTime) then
		-- 同一天：仅在 newCount > 0 时更新；weekTotal 不动（不含今天）
		if newCount > 0 then
			field.count = newCount
		end
	else
		-- 新的一天：把昨天的 count 累加到本周合计，重置今天的记录
		local weekTotal = field.weekTotal or 0
		if field.weekTime and isCurrentWeek(field.weekTime) then
			-- 同一周：把昨天（或更早今天之前）的 count 并入 weekTotal
			field.weekTotal = weekTotal + (field.count or 0)
		else
			-- 新的一周：重置，weekTotal 不含今天
			field.weekTotal = 0
		end
		field.count    = newCount
		field.dayTime  = now
		field.weekTime = now
	end
	return field
end

-- 获取本周累计进度：weekTotal（不含今天）+ 今日 count
local function getWeekProgress(field)
	if not field then return 0 end
	local weekTotal = 0
	if field.weekTime and isCurrentWeek(field.weekTime) then
		weekTotal = field.weekTotal or 0
	end
	-- 今天的 count 始终不在 weekTotal 里，直接加上
	if field.dayTime and Util.isSameDay(field.dayTime) then
		return weekTotal + (field.count or 0)
	end
	return weekTotal
end

-- 检查账号下是否有任何角色已领取指定游戏圈任务（账号级别判断）
local function checkAccountYouxiTaskClaimed(account, taskId)
	if not account then return false end
	local config = YOUXI_TASK_CONFIG[taskId]
	if not config then return false end
	LuaMongo.find(DB.db_char, {account = account}, {zhuanpan = 1})
	while true do
		local data = {}
		if not LuaMongo.next(data) then break end
		local yt = data.zhuanpan and data.zhuanpan.youxiTask
		if yt then
			if config.taskType == "once" then
				if yt.joinGetTime then return true end
			elseif config.taskType == "daily" then
				local field = (taskId == 2) and yt.dailyLike or yt.dailyComment
				if field and field.getTime and Util.isSameDay(field.getTime) then return true end
			elseif config.taskType == "weekly" then
				local field = (taskId == 4) and yt.dailyLike or yt.dailyComment
				if field and field.weekGetTime and isCurrentWeek(field.weekGetTime) then return true end
			end
		end
	end
	return false
end

-- 游戏圈任务查询（前端上传 SDK 数据，后端存储并计算进度）
function youxiTaskQuery(human, msg)
	local msgRet = Msg.gc.GC_ZHUANPAN_YOUXI_TASK_QUERY
	if not msgRet then
		Log.write(Log.LOGID_DEBUG, "[youxiTaskQuery] 错误: msgRet为nil")
		return
	end

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.youxiTask = human.db.zhuanpan.youxiTask or {}
	local yt = human.db.zhuanpan.youxiTask

	-- 存储 SDK 上报数据并更新进度
	if msg then
		-- 任务1：加入游戏圈（joinTime > 0 表示已加入）
		if msg.joinTime and msg.joinTime > 0 and not yt.joined then
			yt.joined = true
		end
		-- 任务2/4：点赞（count=0 时也需调用，以便跨天时将昨日 count 累入 weekTotal）
		if msg.likeCount ~= nil then
			yt.dailyLike = updateDailyCount(yt.dailyLike, msg.likeCount)
		end
		-- 任务3/5：评论（同上）
		if msg.commentCount ~= nil then
			yt.dailyComment = updateDailyCount(yt.dailyComment, msg.commentCount)
		end
	end

	local ok, err = pcall(function()
		for taskId = 1, 5 do
			local config = YOUXI_TASK_CONFIG[taskId]
			local taskData = msgRet.tasks[taskId]
			taskData.taskId = taskId
			taskData.target = config.target
			Grid.makeItem(taskData.reward, config.reward[1], config.reward[2])

			if config.taskType == "once" then
				taskData.progress = yt.joined and 1 or 0
				local claimed = yt.joinGetTime or checkAccountYouxiTaskClaimed(human.db.account, taskId)
				if claimed then
					taskData.status = 2
				elseif yt.joined then
					taskData.status = 1
				else
					taskData.status = 0
				end

			elseif config.taskType == "daily" then
				local field    = (taskId == 2) and yt.dailyLike or yt.dailyComment
				local dayCount = (field and field.dayTime and Util.isSameDay(field.dayTime)) and (field.count or 0) or 0
				taskData.progress = math.min(dayCount, config.target)
				local getTime = field and field.getTime
				local claimed = (getTime and Util.isSameDay(getTime)) or checkAccountYouxiTaskClaimed(human.db.account, taskId)
				if claimed then
					taskData.status = 2
				elseif dayCount >= config.target then
					taskData.status = 1
				else
					taskData.status = 0
				end

			elseif config.taskType == "weekly" then
				local field     = (taskId == 4) and yt.dailyLike or yt.dailyComment
				local weekCount = getWeekProgress(field)
				taskData.progress = math.min(weekCount, config.target)
				local weekGetTime = field and field.weekGetTime
				local claimed = (weekGetTime and isCurrentWeek(weekGetTime)) or checkAccountYouxiTaskClaimed(human.db.account, taskId)
				if claimed then
					taskData.status = 2
				elseif weekCount >= config.target then
					taskData.status = 1
				else
					taskData.status = 0
				end
			end
		end
		msgRet.tasks[0] = 5
	end)

	if not ok then
		Log.write(Log.LOGID_DEBUG, "[youxiTaskQuery] 错误: " .. tostring(err))
		return
	end

	if human.fd then
		Msg.send(msgRet, human.fd)
	end
end

-- 游戏圈任务领取（前端协议调用）
-- taskId: 1-5
function youxiTaskGetReward(human, taskId)
	local config = YOUXI_TASK_CONFIG[taskId]
	if not config then
		Log.write(Log.LOGID_DEBUG, "[youxiTaskGetReward] 无效taskId=" .. tostring(taskId))
		return Broadcast.sendErr(human, "无效的任务ID")
	end

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.youxiTask = human.db.zhuanpan.youxiTask or {}
	local yt = human.db.zhuanpan.youxiTask
	local now = os.time()

	-- 校验条件 & 重复领取（账号级别判断，同账号多角色共享）
	-- 先查当前角色内存，没有再查 DB 其他角色（与美团订阅奖励模式一致）
	local account = human.db.account
	if config.taskType == "once" then
		local claimed = yt.joinGetTime or checkAccountYouxiTaskClaimed(account, taskId)
		if claimed then
			return Broadcast.sendErr(human, "已领取过")
		end
		if not yt.joined then
			return Broadcast.sendErr(human, "尚未加入游戏圈")
		end

	elseif config.taskType == "daily" then
		local field = (taskId == 2) and yt.dailyLike or yt.dailyComment
		local getTime = field and field.getTime
		local claimed = (getTime and Util.isSameDay(getTime)) or checkAccountYouxiTaskClaimed(account, taskId)
		if claimed then
			return Broadcast.sendErr(human, "今日已领取")
		end
		local dayCount = (field and field.dayTime and Util.isSameDay(field.dayTime)) and (field.count or 0) or 0
		if dayCount < config.target then
			return Broadcast.sendErr(human, "任务未完成")
		end

	elseif config.taskType == "weekly" then
		local field = (taskId == 4) and yt.dailyLike or yt.dailyComment
		local weekGetTime = field and field.weekGetTime
		local claimed = (weekGetTime and isCurrentWeek(weekGetTime)) or checkAccountYouxiTaskClaimed(account, taskId)
		if claimed then
			return Broadcast.sendErr(human, "本周已领取")
		end
		if getWeekProgress(field) < config.target then
			return Broadcast.sendErr(human, "任务未完成")
		end
	end

	-- 发放奖励
	BagLogic.cleanMomentItemList()
	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, config.reward[1], config.reward[2])
	local success, err = pcall(function()
		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
	end)
	if not success then
		Log.write(Log.LOGID_DEBUG, "[youxiTaskGetReward] 发放奖励失败 task=" .. taskId .. " err=" .. tostring(err))
		return Broadcast.sendErr(human, "发放奖励失败")
	end

	-- 记录领取时间
	if config.taskType == "once" then
		yt.joinGetTime = now
	elseif config.taskType == "daily" then
		local key = (taskId == 2) and "dailyLike" or "dailyComment"
		yt[key] = yt[key] or {}
		yt[key].getTime = now
	elseif config.taskType == "weekly" then
		local key = (taskId == 4) and "dailyLike" or "dailyComment"
		yt[key] = yt[key] or {}
		yt[key].weekGetTime = now
	end

	Log.write(Log.LOGID_DEBUG, "[youxiTaskGetReward] 领取成功 task=" .. taskId .. " account=" .. tostring(human.db.account))

	-- 领取后下发最新查询（不传 msg，不更新 SDK 数据）
	youxiTaskQuery(human, nil)
end

-----------------------添加桌面（本地存储，账号级，同游戏圈写法）-----------------------

-- 检查账号下所有角色是否已添加过桌面（查本服MongoDB）
local function checkAccountDesktopAdded(account, channelId, platform)
	if not account then return false end
	local key = tostring(channelId) .. "_" .. tostring(platform)
	LuaMongo.find(DB.db_char, {account = account}, {zhuanpan = 1})
	while true do
		local data = {}
		if not LuaMongo.next(data) then break end
		local da = data.zhuanpan and data.zhuanpan.desktopAdded
		if da and da[key] then return true end
	end
	return false
end

-- 上报添加桌面
function dyDesktopSetLocal(human, channelId, platform)
	if not channelId or not platform or platform == "" then
		return Broadcast.sendErr(human, "参数缺失")
	end

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.desktopAdded = human.db.zhuanpan.desktopAdded or {}
	local key = tostring(channelId) .. "_" .. tostring(platform)
	human.db.zhuanpan.desktopAdded[key] = true

	Log.write(Log.LOGID_DEBUG, "[dyDesktopSetLocal] 添加桌面 account=" .. tostring(human.db.account)
		.. " channelId=" .. tostring(channelId) .. " platform=" .. tostring(platform))

	local ok, err = pcall(function()
		dyDesktopQueryLocal(human, channelId, platform)
	end)
	if not ok then
		Log.write(Log.LOGID_DEBUG, "[dyDesktopSetLocal] 错误: 下发查询失败: " .. tostring(err))
	end
end

-- 查询桌面添加状态
function dyDesktopQueryLocal(human, channelId, platform)
	if not channelId or not platform or platform == "" then
		return Broadcast.sendErr(human, "参数缺失")
	end

	local msgRet = Msg.gc.GC_ZHUANPAN_DY_DESKTOP_QUERY
	if not msgRet then return end

	human.db.zhuanpan = human.db.zhuanpan or {}
	human.db.zhuanpan.desktopAdded = human.db.zhuanpan.desktopAdded or {}
	local key = tostring(channelId) .. "_" .. tostring(platform)

	local added = human.db.zhuanpan.desktopAdded[key]
	if not added then
		added = checkAccountDesktopAdded(human.db.account, channelId, platform)
	end

	msgRet.channelId      = channelId
	msgRet.platform       = platform
	msgRet.isDesktopAdded = added and 1 or 0

	Log.write(Log.LOGID_DEBUG, "[dyDesktopQueryLocal] account=" .. tostring(human.db.account)
		.. " channelId=" .. tostring(channelId) .. " platform=" .. tostring(platform)
		.. " isDesktopAdded=" .. tostring(msgRet.isDesktopAdded))

	if human.fd then
		Msg.send(msgRet, human.fd)
	end
end


-----------------------添加桌面（跨服中心服存储，账号级多区服共享）-----------------------
--
---- 从 newUniqueTag 解析出 serverId 和 account（格式: "channelID|serverTag|account"）
--local function parseUniqueTag(newUniqueTag)
--	local parts = {}
--	for part in string.gmatch(newUniqueTag, "([^|]+)") do
--		table.insert(parts, part)
--	end
--	return tonumber(parts[2]), parts[3]  -- serverId, account
--end
--
---- 普通服：上报添加桌面 → 发消息给中心服写入
--function dyDesktopSet(human, channelId, platform)
--	if not channelId or not platform or platform == "" then
--		return Broadcast.sendErr(human, "参数缺失")
--	end
--
--	local newUniqueTag = human.db.newUniqueTag or human.newUniqueTag
--	if not newUniqueTag then
--		return Broadcast.sendErr(human, "账号标识缺失")
--	end
--	local serverId, account = parseUniqueTag(newUniqueTag)
--
--	local tMsg = InnerMsg.lw.LW_DESKTOP_SET
--	tMsg.nSrcServerID = Config.SVR_INDEX
--	tMsg.channelId    = channelId
--	tMsg.serverId     = serverId or 0
--	tMsg.account      = account or ""
--	tMsg.platform     = platform
--	InnerMsg.sendMsg(0, tMsg)
--
--	Log.write(Log.LOGID_DEBUG, "[dyDesktopSet] 发送中心服 channelId=" .. tostring(channelId)
--		.. " serverId=" .. tostring(serverId) .. " account=" .. tostring(account) .. " platform=" .. tostring(platform))
--end
--
---- 普通服：查询桌面添加状态 → 发消息给中心服查询
--function dyDesktopQuery(human, channelId, platform)
--	if not channelId or not platform or platform == "" then
--		return Broadcast.sendErr(human, "参数缺失")
--	end
--
--	local newUniqueTag = human.db.newUniqueTag or human.newUniqueTag
--	if not newUniqueTag then
--		return Broadcast.sendErr(human, "账号标识缺失")
--	end
--	local serverId, account = parseUniqueTag(newUniqueTag)
--
--	local tMsg = InnerMsg.lw.LW_DESKTOP_QUERY
--	tMsg.nSrcServerID = Config.SVR_INDEX
--	tMsg.channelId    = channelId
--	tMsg.serverId     = serverId or 0
--	tMsg.account      = account or ""
--	tMsg.platform     = platform
--	InnerMsg.sendMsg(0, tMsg)
--
--	Log.write(Log.LOGID_DEBUG, "[dyDesktopQuery] 发送中心服 channelId=" .. tostring(channelId)
--		.. " serverId=" .. tostring(serverId) .. " account=" .. tostring(account) .. " platform=" .. tostring(platform))
--end
--
---- 普通服：收到中心服查询回包，找到玩家下发 GC 协议
--function NS_DesktopQueryResult(msg)
--	-- 用三个字段重新拼出 newUniqueTag 来查找在线玩家
--	local newUniqueTag = tostring(msg.channelId) .. "|" .. tostring(msg.serverId) .. "|" .. tostring(msg.account)
--	local human = ObjHuman.onlineNewUniqueTag[newUniqueTag]
--	if not human or not human.fd then
--		Log.write(Log.LOGID_DEBUG, "[NS_DesktopQueryResult] 玩家不在线 newUniqueTag=" .. newUniqueTag)
--		return
--	end
--
--	local msgRet = Msg.gc.GC_ZHUANPAN_DY_DESKTOP_QUERY
--	if not msgRet then return end
--
--	msgRet.channelId      = msg.channelId
--	msgRet.platform       = msg.platform
--	msgRet.isDesktopAdded = msg.isDesktopAdded
--
--	Msg.send(msgRet, human.fd)
--end
--
---- 中心服：收到普通服上报，写入 MongoDB 后回包最新状态
--function CS_DesktopSet(msg)
--	CommonDB.SetDesktopData(msg.channelId, msg.serverId, msg.account, msg.platform)
--
--	Log.write(Log.LOGID_DEBUG, "[CS_DesktopSet] 写入完成 channelId=" .. tostring(msg.channelId)
--		.. " serverId=" .. tostring(msg.serverId) .. " account=" .. tostring(msg.account) .. " platform=" .. tostring(msg.platform))
--
--	local tMsg = InnerMsg.wl.WL_DESKTOP_QUERY
--	tMsg.channelId      = msg.channelId
--	tMsg.serverId       = msg.serverId
--	tMsg.account        = msg.account
--	tMsg.platform       = msg.platform
--	tMsg.isDesktopAdded = 1
--
--	local fd = MiddleManager.getFDBySvrIndex(msg.nSrcServerID)
--	InnerMsg.sendMsg(fd, tMsg)
--end
--
---- 中心服：收到普通服查询请求，读 MongoDB 后回包
--function CS_DesktopQuery(msg)
--	local added = CommonDB.IsDesktopAdded(msg.channelId, msg.serverId, msg.account, msg.platform)
--
--	local tMsg = InnerMsg.wl.WL_DESKTOP_QUERY
--	tMsg.channelId      = msg.channelId
--	tMsg.serverId       = msg.serverId
--	tMsg.account        = msg.account
--	tMsg.platform       = msg.platform
--	tMsg.isDesktopAdded = added and 1 or 0
--
--	local fd = MiddleManager.getFDBySvrIndex(msg.nSrcServerID)
--	InnerMsg.sendMsg(fd, tMsg)
--end


-- SDK 回调奖励发放（内部调用，与任务奖励独立）
-- items: {{itemId, count}, ...}
function youxiSdkReward(human, items)
	if not items or #items == 0 then
		Log.write(Log.LOGID_DEBUG, "[youxiSdkReward] items为空 account=" .. tostring(human.db.account))
		return
	end
	BagLogic.cleanMomentItemList()
	for _, v in ipairs(items) do
		BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, v[1], v[2])
	end
	local success, err = pcall(function()
		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
	end)
	if not success then
		Log.write(Log.LOGID_DEBUG, "[youxiSdkReward] 发放失败 err=" .. tostring(err) .. " account=" .. tostring(human.db.account))
	else
		Log.write(Log.LOGID_DEBUG, "[youxiSdkReward] 发放成功 cnt=" .. #items .. " account=" .. tostring(human.db.account))
	end
end