gitxsm 4 месяцев назад
Родитель
Сommit
b3f6cbf41d
51 измененных файлов с 2535 добавлено и 469 удалено
  1. 3 1
      script/common/Dict.lua
  2. 3 1
      script/common/Dict2.lua
  3. 144 59
      script/common/InnerProto.lua
  4. 0 8
      script/common/ProtoID.lua
  5. 4 0
      script/core/ObjHuman.lua
  6. 1 0
      script/module/anotherWorldBattle/AnotherWorldBattleCS.lua
  7. 103 23
      script/module/anotherWorldBattle/AnotherWorldBattleNS.lua
  8. 7 2
      script/module/anotherWorldBattle/Proto.lua
  9. 35 14
      script/module/areaBattle/AreaBattleCS.lua
  10. 0 1
      script/module/areaBattle/AreaBattleDB.lua
  11. 3 0
      script/module/areaBattle/AreaBattleDefine.lua
  12. 10 4
      script/module/areaBattle/AreaBattleNS.lua
  13. 6 1
      script/module/bag/Grid.lua
  14. 21 1
      script/module/chengjiu/ChengjiuLogic.lua
  15. 1 0
      script/module/combat/BeSkill.lua
  16. 106 7
      script/module/combat/CombatBuff.lua
  17. 40 8
      script/module/combat/CombatObj.lua
  18. 1 1
      script/module/combat/JibanLogic.lua
  19. 172 43
      script/module/combat/Skill.lua
  20. 14 1
      script/module/combat/TargetMode.lua
  21. 20 0
      script/module/dailyTask/HonorJourney.lua
  22. 1 1
      script/module/drawCard/UnlimitDrawLogic.lua
  23. 5 0
      script/module/elf/ElfLogic.lua
  24. 6 0
      script/module/equip/EquipLogic.lua
  25. 15 0
      script/module/equip/EquipLogicGrid.lua
  26. 4 2
      script/module/hero/HeroEquip.lua
  27. 41 28
      script/module/hero/HeroLogic.lua
  28. 35 4
      script/module/roleSystem/RoleStorageBox.lua
  29. 2 2
      script/module/scene/Handler.lua
  30. 6 0
      script/module/talisman/TalismanLogic.lua
  31. 1 1
      script/module/topup/BuyLogic.lua
  32. 149 6
      script/module/topup/GiftLogic.lua
  33. 76 25
      script/module/treasurechest/TreasureChestLogic.lua
  34. 1 1
      script/module/voucher/VoucherShopDefine.lua
  35. 6 0
      script/module/winnerRelic/WinnerRelicLogic.lua
  36. 2 1
      script/module/xianzhi/Proto.lua
  37. 74 32
      script/module/xianzhi/XianzhiLogic.lua
  38. 216 116
      script/module/zhuanpan/ZhuanpanLogic.lua
  39. 2 0
      webServer/src/channels/factory/ChannelFactory.ts
  40. 439 0
      webServer/src/channels/handlers/HuaweiChannelHandler.ts
  41. 386 53
      webServer/src/channels/handlers/MiniappChannelHandler.ts
  42. 18 0
      webServer/src/config/account_status_table.sql
  43. 21 1
      webServer/src/config/channelConfig.ts
  44. 1 1
      webServer/src/config/dbConfig.ts
  45. 7 3
      webServer/src/config/thirdParams.ts
  46. 12 1
      webServer/src/controller/ApiController.ts
  47. 140 10
      webServer/src/controller/MiniAppController.ts
  48. 91 0
      webServer/src/model/AccountStatusModel.ts
  49. 6 2
      webServer/src/router/miniapp.ts
  50. 27 2
      webServer/src/sql/query.ts
  51. 51 2
      webServer/src/utils/serverManager.ts

+ 3 - 1
script/common/Dict.lua

@@ -55755,4 +55755,6 @@ _ENV[ [[xysame五六六us]] ]=true
 _ENV[ [[数卷残编言论集]] ]=true
 _ENV[ [[httpwwwbbbsndfcn]] ]=true
 _ENV[ [[异氟烷]] ]=true
-_ENV[ [[祈翠]] ]=true
+_ENV[ [[祈翠]] ]=true
+_ENV[ [[出版自由]] ]=true
+_ENV[ [[我方代表出习]] ]=true

+ 3 - 1
script/common/Dict2.lua

@@ -55755,4 +55755,6 @@ _ENV[ [[xysame五六六us]] ]=true
 _ENV[ [[数卷残编言论集]] ]=true
 _ENV[ [[httpwwwbbbsndfcn]] ]=true
 _ENV[ [[异氟烷]] ]=true
-_ENV[ [[祈翠]] ]=true
+_ENV[ [[祈翠]] ]=true
+_ENV[ [[出版自由]] ]=true
+_ENV[ [[我方代表出习]] ]=true

+ 144 - 59
script/common/InnerProto.lua

@@ -965,71 +965,154 @@ WL_AREABATTLE_VIDEOSHOW_QUERY = {
 
 --------------------- 巅峰战场跨服协议开始 -----------------------------------
 -- 普通服请求跨服排名数据(普通->中心)
-LW_PEAK_BATTLEFIELD_QUERY_RANK_O2C = {
-  {"nSrcServerID",          "int"},       -- 服务器ID
-  {"nRankType",             "int"},       -- 排名类型(1=个人排名,2=服务器排名)
-}
+-- LW_PEAK_BATTLEFIELD_QUERY_RANK_O2C = {
+--   {"nSrcServerID",          "int"},       -- 服务器ID
+--   {"nRankType",             "int"},       -- 排名类型(1=个人排名,2=服务器排名)
+-- }
 
--- 请求跨服排名数据(中心->数据服)
-WL_PEAK_BATTLEFIELD_QUERY_RANK_C2D = {
-  {"nSrcServerID",          "int"},       -- 服务器ID
-  {"nRankType",             "int"},       -- 排名类型
-}
+-- -- 请求跨服排名数据(中心->数据服)
+-- WL_PEAK_BATTLEFIELD_QUERY_RANK_C2D = {
+--   {"nSrcServerID",          "int"},       -- 服务器ID
+--   {"nRankType",             "int"},       -- 排名类型
+-- }
 
--- 请求跨服排名数据(数据服->中心)
-LW_PEAK_BATTLEFIELD_QUERY_RANK_D2C = {
-  {"nSrcServerID",          "int"},       -- 服务器ID
-  {"nRankType",             "int"},       -- 排名类型
-  {"nIsEnd",                "int"},       -- 是否发送完成 0 未 1 发送完
-  {"tRankInfo",             "table"},     -- 排行榜数据
-  {"nFirst",                "int"},       -- 首次发送 1 是 0 否
-}
+-- -- 请求跨服排名数据(数据服->中心)
+-- LW_PEAK_BATTLEFIELD_QUERY_RANK_D2C = {
+--   {"nSrcServerID",          "int"},       -- 服务器ID
+--   {"nRankType",             "int"},       -- 排名类型
+--   {"nIsEnd",                "int"},       -- 是否发送完成 0 未 1 发送完
+--   {"tRankInfo",             "table"},     -- 排行榜数据
+--   {"nFirst",                "int"},       -- 首次发送 1 是 0 否
+-- }
 
--- 获取到跨服排名数据(中心->普通)
-WL_PEAK_BATTLEFIELD_QUERY_RANK_C2O = {
-  {"nFirst",                "int"},       -- 首次发送 1 是 0 否
-  {"nIsEnd",                "int"},       -- 是否发送完成 0 未 1 发送完
-  {"tRankInfo",             "table"},     -- 排行榜数据
-}
+-- -- 获取到跨服排名数据(中心->普通)
+-- WL_PEAK_BATTLEFIELD_QUERY_RANK_C2O = {
+--   {"nFirst",                "int"},       -- 首次发送 1 是 0 否
+--   {"nIsEnd",                "int"},       -- 是否发送完成 0 未 1 发送完
+--   {"tRankInfo",             "table"},     -- 排行榜数据
+-- }
 
--- 普通服通知排名变化(普通->中心)
-LW_PEAK_BATTLEFIELD_RANK_CHANGE_O2C = {
-  {"uuid",                  "string"},    -- 玩家uuid
-  {"name",                  "string"},    -- 玩家名字
-  {"head",                  "int"},       -- 玩家头像ID
-  {"headFrame",             "int"},       -- 玩家头像框
-  {"nSrcServerID",          "int"},       -- 服务器ID
-  {"nRank",                 "int"},       -- 当前排名
-  {"nValue",                "int"},       -- 排名值(如积分等)
-  {"servername",            "string"},    -- 服务器名称
-}
+-- -- 普通服通知排名变化(普通->中心)
+-- LW_PEAK_BATTLEFIELD_RANK_CHANGE_O2C = {
+--   {"uuid",                  "string"},    -- 玩家uuid
+--   {"name",                  "string"},    -- 玩家名字
+--   {"head",                  "int"},       -- 玩家头像ID
+--   {"headFrame",             "int"},       -- 玩家头像框
+--   {"nSrcServerID",          "int"},       -- 服务器ID
+--   {"nRank",                 "int"},       -- 当前排名
+--   {"nValue",                "int"},       -- 排名值(如积分等)
+--   {"servername",            "string"},    -- 服务器名称
+-- }
 
--- 排名变化通知(中心->数据服)
-WL_PEAK_BATTLEFIELD_RANK_CHANGE_C2D = {
-  {"uuid",                  "string"},    -- 玩家uuid
-  {"name",                  "string"},    -- 玩家名字
-  {"head",                  "int"},       -- 玩家头像ID
-  {"headFrame",             "int"},       -- 玩家头像框
-  {"nSrcServerID",          "int"},       -- 服务器ID
-  {"nRank",                 "int"},       -- 当前排名
-  {"nValue",                "int"},       -- 排名值
-  {"servername",            "string"},    -- 服务器名称
-}
+-- -- 排名变化通知(中心->数据服)
+-- WL_PEAK_BATTLEFIELD_RANK_CHANGE_C2D = {
+--   {"uuid",                  "string"},    -- 玩家uuid
+--   {"name",                  "string"},    -- 玩家名字
+--   {"head",                  "int"},       -- 玩家头像ID
+--   {"headFrame",             "int"},       -- 玩家头像框
+--   {"nSrcServerID",          "int"},       -- 服务器ID
+--   {"nRank",                 "int"},       -- 当前排名
+--   {"nValue",                "int"},       -- 排名值
+--   {"servername",            "string"},    -- 服务器名称
+-- }
+
+-- -- 发送排名奖励(数据服->中心)
+-- LW_PEAK_BATTLEFIELD_SEND_RANK_REWARD_D2C = {
+--   {"uuid",                  "string"},    -- 玩家uid
+--   {"nServerID",             "int"},       -- 服务器ID
+--   {"nRank",                 "int"},       -- 排名
+--   {"tReward",               "table"},     -- 奖励列表
+-- }
+
+-- -- 发送排名奖励(中心->普通)
+-- WL_PEAK_BATTLEFIELD_SEND_RANK_REWARD_C2O = {
+--   {"uuid",                  "string"},    -- 玩家uid
+--   {"nRank",                 "int"},       -- 排名
+--   {"tReward",               "table"},     -- 奖励列表
+-- }
+
+-- 查询玩家排名
+-- LW_BATTLEGROUND_PLAYER_RANK_QUERY = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+-- }
+-- WL_BATTLEGROUND_PLAYER_RANK_QUERY = {
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"playerRank",              "int"},        -- 玩家排名
+-- }
+
+-- -- 查询对手列表信息
+-- LW_BATTLEGROUND_MATCHLIST_QUERY = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"matchList",               "table"},      -- 对手列表
+-- }
+-- WL_BATTLEGROUND_MATCHLIST_QUERY = {
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"playerInfoList",          "table"},      -- 对手列表信息
+-- }
+
+
+-- -- 查询玩家信息
+-- LW_BATTLEGROUND_PLAYER_DATA_QUERY = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"playerRank",               "int"},       -- 玩家排名
+-- }
+-- WL_BATTLEGROUND_PLAYER_DATA_QUERY = {
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"playerInfo",              "table"},      -- 玩家信息
+-- }
+
+
+-- -- 查询排行榜数据
+-- LW_BATTLEGROUND_RANKLIST_QUERY = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+-- }
+-- WL_BATTLEGROUND_RANKLIST_QUERY = {
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"rankList",                "table"},      -- 排行榜信息
+-- }
+
+
+-- -- 请求挑战玩家
+-- LW_BATTLEGROUND_CHALLENGE_QUERY = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"rank",                    "int"},        -- 排名数组
+-- }
+-- WL_BATTLEGROUND_CHALLENGE_QUERY = {
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"playerInfo",              "table"},      -- 排名数组
+-- }
+
+-- -- 挑战结束
+-- LW_BATTLEGROUND_CHALLENGE_END = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"challengeRes",            "int"},        -- 挑战结果
+--   {"playerShowData",          "table"},      -- 玩家展示信息
+-- }
+
+-- -- 通知玩家被打落排名了
+-- WL_BATTLEGROUND_NOTIFY_PLAYER = {
+--   {"playerUuid",              "string"},     -- 玩家uuid
+-- }
+
+-- -- 玩家更新阵容
+-- LW_BATTLEGROUND_LINEUP_UPDATE = {
+--   {"sourceServerId",          "int"},        -- 请求服服务器Id
+--   {"playerUuid",              "string"},     -- 玩家uuid
+--   {"heroArr",                 "table"},      -- 阵容英雄列表
+-- }
+
+-- -- 通知发奖
+-- WL_BATTLEGROUND_NOTIFY_PRIZEAWARD = {
+--   {"playerArr",              "table"},     -- 要发奖的玩家信息列表
+-- }
 
--- 发送排名奖励(数据服->中心)
-LW_PEAK_BATTLEFIELD_SEND_RANK_REWARD_D2C = {
-  {"uuid",                  "string"},    -- 玩家uid
-  {"nServerID",             "int"},       -- 服务器ID
-  {"nRank",                 "int"},       -- 排名
-  {"tReward",               "table"},     -- 奖励列表
-}
 
--- 发送排名奖励(中心->普通)
-WL_PEAK_BATTLEFIELD_SEND_RANK_REWARD_C2O = {
-  {"uuid",                  "string"},    -- 玩家uid
-  {"nRank",                 "int"},       -- 排名
-  {"tReward",               "table"},     -- 奖励列表
-}
 --------------------- 巅峰战场跨服协议结束 -----------------------------------
 
 ---------------------------------异界之战----------------------------
@@ -1044,12 +1127,14 @@ WL_ANOTHERWORLDBATTLE_TIPS = {
 LW_ANOTHERWORLDBATTLE_GET_STATE = {
   {"sourceServerId",          "int"},        -- 请求服服务器Id
   {"playerUuid",              "string"},     -- 玩家uuid
-  {"myUnionId",                 "string"},      -- 公会信息
+  {"myUnionId",               "string"},     -- 公会信息
+  {"isTips",                  "int"},        -- 是否用于提示
 }
 WL_ANOTHERWORLDBATTLE_GET_STATE = {
   {"playerUuid",              "string"},   -- 玩家uuid
   {"systemState",              "int"},     -- 活动状态
   {"joinState",              "int"},       -- 报名状态
+  {"isTips",                  "int"},        -- 是否用于提示
 }
 
 

+ 0 - 8
script/common/ProtoID.lua

@@ -1648,22 +1648,17 @@ _ENV[1685]="CG_UNLIMITDRAW_DO"
 _ENV[1686]="GC_UNLIMITDRAW_DO"
 _ENV[1687]="CG_UNLIMITDRAW_ABANDON"
 _ENV[1688]="CG_UNLIMITDRAW_GET"
-
 _ENV[1689]="CG_COMMON_HUMAN_INFO"       -- 玩家通用的一些数据请求(是否首充等)
 _ENV[1690]="GC_COMMON_HUMAN_INFO"
-
 _ENV[1691]="CG_HEROSEED_QUERY_ALL"
 _ENV[1692]="GC_HEROSEED_QUERY_ALL"
 _ENV[1693]="CG_HEROSEED_QUERY_SINGLE"
 _ENV[1694]="GC_HEROSEED_QUERY_SINGLE"
 _ENV[1695]="CG_HEROSEED_UPGRADE"
-
 _ENV[1697]="CG_OPENSERVER_GIFTPACKAGE_QUERY"
 _ENV[1698]="GC_OPENSERVER_GIFTPACKAGE_QUERY"
-
 _ENV[1699]="CG_ZHUANPAN_GIFT_QUERY"
 _ENV[1700]="GC_ZHUANPAN_GIFT_QUERY"
-
 _ENV[1701]="CG_DUIHUANG_QUERY"
 _ENV[1702]="GC_DUIHUANG_QUERY"
 _ENV[1703]="CG_DUIHUANG_DO"
@@ -1673,10 +1668,8 @@ _ENV[1706]="GC_EQUIP_REFINING_DO"
 _ENV[1707]="CG_EQUIP_REFINING_ABANDON"
 _ENV[1708]="CG_EQUIP_REFINING_SAVE"
 _ENV[1709]="CG_EQUIP_REFINING_QUERY"
-
 _ENV[1710]="CG_CYCLMAKEFOOD_SELECT_MUL"
 _ENV[1711]="GC_ITEM_BAG_LIST_CHANGE"
-
 _ENV[1712]="CG_ELF_SIMPLEDATA_QUERY"
 _ENV[1713]="GC_ELF_SIMPLEDATA_QUERY"
 _ENV[1714]="CG_ELF_SINGLE_QUERY"
@@ -1688,7 +1681,6 @@ _ENV[1719]="GC_ELF_POS_QUERY"
 _ENV[1720]="CG_COMBAT_ELFPOS_QUERY"
 _ENV[1721]="GC_COMBAT_ELFPOS_QUERY"
 _ENV[1722]="CG_COMBAT_ELFPOS_DO"
-
 _ENV[1723]="CG_AB_JOIN"
 _ENV[1724]="GC_AB_JOIN"
 _ENV[1725]="CG_AB_ALLCITY_QUERY"

+ 4 - 0
script/core/ObjHuman.lua

@@ -103,6 +103,7 @@ local JjcNewLadderLogic = require("jjcnewladder.jjcNewLadderLogic")
 local WinnerRelicLogic = require("winnerRelic.WinnerRelicLogic")
 local commonOperate = require("core.commonOperate")
 local AnotherWorldBattleNS = require("anotherWorldBattle.AnotherWorldBattleNS")
+local RoleStorageBox = require("roleSystem.RoleStorageBox")
 
 
 local Json = require("common.Json")
@@ -498,6 +499,8 @@ function onLvUpCB(human, oldLv, newLv)
 
 	commonOperate.LvChange(human, oldLv, newLv)
 
+	GiftLogic.trigger(human, GiftLogic.GIFT_UPGRADE_LV_EVENT, {currentVal = newLv}, GiftLogic.GIFT_SEC_TYPE1)
+
 	--升级日志
 	--Log.write(Log.LOGID_OSS_LEVELUP, human.db._id, human.db.account, human.db.name, oldLv, newLv)
 	Log.write(Log.LOGID_OSS_LEVELUP, human.db._id, human.db.newUniqueTag, human.db.name, oldLv, newLv)
@@ -820,6 +823,7 @@ function onLogin(human, isNew)
 	WinnerRelicLogic.onLogin(human)
 	commonOperate.onLogin(human)
 	AnotherWorldBattleNS.onLogin(human)
+	RoleStorageBox.onLogin(human)
 
     HeroLogLogic.finishTaskCB(human,HeroLogLogic.HERO_LOG_TYPE_1,1)
 	-- 红点 检测   需要放在通用的onLogin 后面  保持数据的正确性 再检测红点

+ 1 - 0
script/module/anotherWorldBattle/AnotherWorldBattleCS.lua

@@ -814,6 +814,7 @@ function N2C_GetState_Req(msg)
     msgData.playerUuid = msg.playerUuid
     msgData.systemState = 0
     msgData.joinState = 0
+    msgData.isTips = msg.isTips or 0
 
     local stage = AnotherWorldBattleDB.GetStage()
     msgData.systemState = stage

+ 103 - 23
script/module/anotherWorldBattle/AnotherWorldBattleNS.lua

@@ -343,6 +343,21 @@ local function sendDefHeroArr(human)
     Msg.send(msgRet, human.fd)
 end
 
+-- 如果活动开启时间范围内, 则通知客户端弹出提示框
+local function notifyTips(human)
+    if not baseCondCheck(human) then
+        return
+    end
+
+    local wDay = getWDay()
+    if wDay > AnotherWorldBattleDefine.AB_OPEN_WDAY_AREA[2] and wDay < AnotherWorldBattleDefine.AB_OPEN_WDAY_AREA[1] then
+        return
+    end
+
+    AB_GetState(human, true)
+end
+
+
 -- 是否是防守阵容中的英雄
 local function isDefHero(human, checkHeroUuid, excludeCityId, excludePointIdx)
     if not human.db.anotherWorlBattle then
@@ -411,18 +426,16 @@ end
 local function getRankGenericAwadId(rankRewardList, rankType)
     for _, v in ipairs(rankRewardList) do
         if v.nRankType == rankType then
-            return v.Prize[1]
+            return v.Prize
         end
     end
-
-    return 101
 end
 
 -- 获取排名奖励
 local function getRankAward(rankRewardList, rankType, targetRank)
     for _, v in ipairs(rankRewardList) do
         if v.nRankType == rankType and (targetRank >= v.nOrder[1] and targetRank <= v.nOrder[2]) then
-            return v.Prize[1], v.Prize[2]
+            return v.Prize
         end
     end
 end
@@ -513,15 +526,40 @@ local function genAwardObjArr(unionOccupyInfo)
         awardObjArr[#awardObjArr+1] = {playerUuid, itemArr, AnotherWorldBattleDefine.AB_AWARD_MAIL_ID}
 
         -- 公会排名奖励
-        local itemId, itemNum = getRankAward(rankReward, 1, unionOccupyInfo.unionRank)
-        if itemId and itemNum then
-            awardObjArr[#awardObjArr+1] = {playerUuid, {{itemId, itemNum}}, AnotherWorldBattleDefine.AB_UNIONRANK_AWARD_MAIL_ID, unionOccupyInfo.unionRank}
+        -- local itemId, itemNum = getRankAward(rankReward, 1, unionOccupyInfo.unionRank)
+        -- if itemId and itemNum then
+        --     awardObjArr[#awardObjArr+1] = {playerUuid, {{itemId, itemNum}}, AnotherWorldBattleDefine.AB_UNIONRANK_AWARD_MAIL_ID, unionOccupyInfo.unionRank}
+        -- end
+        local itemTb = getRankAward(rankReward, 1, unionOccupyInfo.unionRank)
+        if itemTb then
+            local items
+            for _, itemInfo in ipairs(itemTb) do
+                items = items or {}
+                items[#items+1] = {itemInfo[1], itemInfo[2]}
+            end
+            if items then
+                awardObjArr[#awardObjArr+1] = {playerUuid, items, AnotherWorldBattleDefine.AB_UNIONRANK_AWARD_MAIL_ID, unionOccupyInfo.unionRank}
+            end
         end
 
+
         -- 玩家排名奖励
-        local itemId2, itemNum2 = getRankAward(rankReward, 2, playerRank)
-        if itemId2 and itemNum2 then
-            awardObjArr[#awardObjArr+1] = {playerUuid, {{itemId2, itemNum2}}, AnotherWorldBattleDefine.AB_PLAYERRANK_AWARD_MAIL_ID, playerRank}
+        -- local itemId2, itemNum2 = getRankAward(rankReward, 2, playerRank)
+        -- if itemId2 and itemNum2 then
+        --     awardObjArr[#awardObjArr+1] = {playerUuid, {{itemId2, itemNum2}}, AnotherWorldBattleDefine.AB_PLAYERRANK_AWARD_MAIL_ID, playerRank}
+        -- end
+
+        itemTb = getRankAward(rankReward, 2, playerRank)
+        if itemTb then
+            local items
+            for _, itemInfo in ipairs(itemTb) do
+                items = items or {}
+                items[#items+1] = {itemInfo[1], itemInfo[2]}
+            end
+
+            if items then
+                awardObjArr[#awardObjArr+1] = {playerUuid, items, AnotherWorldBattleDefine.AB_PLAYERRANK_AWARD_MAIL_ID, playerRank}
+            end
         end
     end
 
@@ -693,17 +731,19 @@ end
 function onLogin(human)
     formationDataCheeck(human)
     sendDefHeroArr(human)
+    notifyTips(human)
 end
 
 
 
 
 -- 请求状态
-function AB_GetState(human)
+function AB_GetState(human, isTips)
     local msgData = InnerMsg.lw.LW_ANOTHERWORLDBATTLE_GET_STATE
     msgData.sourceServerId = Config.SVR_INDEX
     msgData.playerUuid = human.db._id
     msgData.myUnionId = human.db.unionUuid or ""
+    msgData.isTips = isTips and 1 or 0
 
     InnerMsg.sendMsg(0, msgData)
 end
@@ -1193,6 +1233,16 @@ function C2N_State_Response(msg)
         return
     end
 
+    -- 只用于弹出提示框
+    if msg.isTips and msg.isTips == 1 then
+        if (msg.systemState > 0 and msg.systemState <= 2) and msg.joinState <= 1 then
+            local msgRet = Msg.gc.GC_AB_TIPS
+            Msg.send(msgRet, human.fd)
+        end
+
+        return
+    end
+
     local msgRet = Msg.gc.GC_AB_GetState
     msgRet.joinState = msg.joinState
     msgRet.systemState = msg.systemState
@@ -1505,9 +1555,9 @@ function C2N_UnionRank_Response(msg)
         return
     end
 
-    local itemId, itemNum = 184, 0
+    -- local itemId, itemNum = 184, 0
     local rankReward = AnotherWorldBattleConfig.rankReward
-    itemId = getRankGenericAwadId(rankReward, 1)
+    local itemArr = getRankGenericAwadId(rankReward, 1)
 
     local msgRet = Msg.gc.GC_AB_UNION_RANK_QUERY
     msgRet.unionRankArr[0] = 0
@@ -1516,7 +1566,12 @@ function C2N_UnionRank_Response(msg)
     msgRet.myData.power = 0
     msgRet.myData.cityNum = 0
     msgRet.myData.pointNum = 0
-    Grid.makeItem(msgRet.myData.rankAward, itemId, itemNum)
+
+    msgRet.myData.rankAward[0] = 0
+    for i, itemInfo in ipairs(itemArr or {}) do
+        msgRet.myData.rankAward[0] = i
+        Grid.makeItem(msgRet.myData.rankAward[i], itemInfo[1], itemInfo[2])
+    end
 
     for rank, rankdData in ipairs(msg.unionRankArr) do
         msgRet.unionRankArr[0] = rank
@@ -1525,15 +1580,25 @@ function C2N_UnionRank_Response(msg)
         msgRet.unionRankArr[rank].cityNum = rankdData.cityNum
         msgRet.unionRankArr[rank].pointNum = rankdData.pointNum
 
-        itemId, itemNum = getRankAward(rankReward, 1, rank)
-        Grid.makeItem(msgRet.unionRankArr[rank].rankAward, itemId, itemNum)
+        -- Grid.makeItem(msgRet.unionRankArr[rank].rankAward, itemId, itemNum)
+        msgRet.unionRankArr[rank].rankAward[0] = 0
+        itemArr = getRankAward(rankReward, 1, rank)
+        for i, itemInfo in ipairs(itemArr or {}) do
+            msgRet.unionRankArr[rank].rankAward[0] = i
+            Grid.makeItem(msgRet.unionRankArr[rank].rankAward[i], itemInfo[1], itemInfo[2])
+        end
 
         if rank == msg.myUnionRank then
             msgRet.myData.name = rankdData.name
             msgRet.myData.power = rankdData.power
             msgRet.myData.cityNum = rankdData.cityNum
             msgRet.myData.pointNum = rankdData.pointNum
-            Grid.makeItem(msgRet.myData.rankAward, itemId, itemNum)
+            -- Grid.makeItem(msgRet.myData.rankAward, itemId, itemNum)
+            msgRet.myData.rankAward[0] = 0
+            for i, itemInfo in ipairs(itemArr or {}) do
+                msgRet.myData.rankAward[0] = i
+                Grid.makeItem(msgRet.myData.rankAward[i], itemInfo[1], itemInfo[2])
+            end
         end
     end
 
@@ -1555,9 +1620,9 @@ function C2N_PlayerRank_Response(msg)
         return
     end
 
-    local itemId, itemNum = 184, 0
+    -- local itemId, itemNum = 184, 0
     local rankReward = AnotherWorldBattleConfig.rankReward
-    itemId = getRankGenericAwadId(rankReward, 2)
+    local itemArr = getRankGenericAwadId(rankReward, 2)
 
     local msgRet = Msg.gc.GC_AB_PLAYER_RANK_QUERY
     msgRet.playerRankArr[0] = 0
@@ -1566,7 +1631,11 @@ function C2N_PlayerRank_Response(msg)
     msgRet.myData.power = human.db.zhandouli
     msgRet.myData.pointNum = 0
     msgRet.myData.pointWeight = 0
-    Grid.makeItem(msgRet.myData.rankAward, itemId, itemNum)
+    msgRet.myData.rankAward[0] = 0
+    for i, itemInfo in ipairs(itemArr or {}) do
+        msgRet.myData.rankAward[0] = i
+        Grid.makeItem(msgRet.myData.rankAward[i], itemInfo[1], itemInfo[2])
+    end
 
     for rank, rankdData in ipairs(msg.playerRankArr) do
         msgRet.playerRankArr[0] = rank
@@ -1575,13 +1644,24 @@ function C2N_PlayerRank_Response(msg)
         msgRet.playerRankArr[rank].pointNum = rankdData.pointNum
         msgRet.playerRankArr[rank].pointWeight = rankdData.pointWeight
 
-        itemId, itemNum = getRankAward(rankReward, 2, rank)
-        Grid.makeItem(msgRet.playerRankArr[rank].rankAward, itemId, itemNum)
+        -- itemId, itemNum = getRankAward(rankReward, 2, rank)
+        -- Grid.makeItem(msgRet.playerRankArr[rank].rankAward, itemId, itemNum)
+        itemArr = getRankAward(rankReward, 2, rank)
+        msgRet.playerRankArr[rank].rankAward[0] = 0
+        for i, itemInfo in ipairs(itemArr or {}) do
+            msgRet.playerRankArr[rank].rankAward[0] = i
+            Grid.makeItem(msgRet.playerRankArr[rank].rankAward[i], itemInfo[1], itemInfo[2])
+        end
 
         if rank == msg.myRank then
             msgRet.myData.pointNum = rankdData.pointNum
             msgRet.myData.pointWeight = rankdData.pointWeight
-            Grid.makeItem(msgRet.myData.rankAward, itemId, itemNum)
+            -- Grid.makeItem(msgRet.myData.rankAward, itemId, itemNum)
+            msgRet.myData.rankAward[0] = 0
+            for i, itemInfo in ipairs(itemArr or {}) do
+                msgRet.myData.rankAward[0] = i
+                Grid.makeItem(msgRet.myData.rankAward[i], itemInfo[1], itemInfo[2])
+            end
         end
     end
 

+ 7 - 2
script/module/anotherWorldBattle/Proto.lua

@@ -50,7 +50,7 @@ AB_UNION_RANK_INFO = {
     {"power",           1,      "double"},  -- 战力
     {"cityNum",         1,      "short"},   -- 城池数量
     {"pointNum",        1,      "short"},   -- 据点数量
-    {"rankAward",       1,      ItemData},  -- 排行奖励
+    {"rankAward",       2,      ItemData},  -- 排行奖励
 }
 
 AB_PLAYER_RANK_INFO = {
@@ -58,7 +58,7 @@ AB_PLAYER_RANK_INFO = {
     {"power",           1,      "double"},  -- 战力
     {"pointNum",        1,      "short"},   -- 据点数量
     {"pointWeight",     1,      "short"},   -- 据点权重
-    {"rankAward",       1,      ItemData},  -- 排行奖励
+    {"rankAward",       2,      ItemData},  -- 排行奖励
 }
 
 
@@ -268,6 +268,11 @@ CG_AB_ELF_UPDATE = {
 }
 
 
+-- 玩家登录时, 如果活动处于开启时间范围, 弹出提示
+GC_AB_TIPS = {}
+
+
+
 -- 异界寻宝查询
 CG_AB_TREASURE_QUERY = {}
 GC_AB_TREASURE_QUERY = {

+ 35 - 14
script/module/areaBattle/AreaBattleCS.lua

@@ -185,10 +185,12 @@ local function genMacthArr()
             joinSrvArr_cp[srvCnt] = nil
             srvCnt = srvCnt - 1
 
+            -- 新修改: 被移除的服务器为轮空服, 轮空服改为发战败服奖励, 发奖励时需要用到参战玩家数据, 所以不删除轮空服数据
+
             -- 把被移除的服务器从服务器数据列表中移除
-            local serverList = AreaBattleDB.GetServerList()
-            serverList[deleteSrvId] = nil
-            AreaBattleDB.UpdateServerList(serverList)
+            -- local serverList = AreaBattleDB.GetServerList()
+            -- serverList[deleteSrvId] = nil
+            -- AreaBattleDB.UpdateServerList(serverList)
 
             -- 记录日志
             local logStr = "移除多余的区服, 区服Id:  " .. deleteSrvId
@@ -358,24 +360,43 @@ local function updateDBBattleResult(battleInfo)
     AreaBattleDB.UpdateServerList(serverList)
 end
 
+-- 检查服务器是否是轮空服
+local function isByeServer(matchSrvArr, serverId)
+    local isBye = true
+    for _, serverTb in ipairs(matchSrvArr or {}) do
+        if table.find(serverTb, serverId) then
+            isBye = false
+            break
+        end
+    end
+
+    return isBye
+end
 
 -- 生成用于通知各个普通服发奖的数据
-local function genNotifyInfo(serverInfo)
+local function genNotifyInfo(serverInfo, matchSrvArr, serverId)
     local info = {
         isWin = 0,
         winPlayerArr = {},
-        defeatPlayerArr = {}
+        defeatPlayerArr = {},
+        isBye = 0, -- 是否轮空
     }
 
-    if (serverInfo.winTimes or 0) > (serverInfo.defeatTimes or 0) then
-        info.isWin = 1
-    end
+    local isBye = isByeServer(matchSrvArr, serverId)
 
-    for _, playerInfo in ipairs(serverInfo.playerInfoArr) do
-        if table.find(serverInfo.winPlayerArr, playerInfo.uuid) then
-            table.insert(info.winPlayerArr, playerInfo.uuid)
-        else
-            table.insert(info.defeatPlayerArr, playerInfo.uuid)
+    if isBye then
+        info.isBye = 1
+    else
+        if (serverInfo.winTimes or 0) > (serverInfo.defeatTimes or 0) then
+            info.isWin = 1
+        end
+
+        for _, playerInfo in ipairs(serverInfo.playerInfoArr) do
+            if table.find(serverInfo.winPlayerArr, playerInfo.uuid) then
+                table.insert(info.winPlayerArr, playerInfo.uuid)
+            else
+                table.insert(info.defeatPlayerArr, playerInfo.uuid)
+            end
         end
     end
 
@@ -773,7 +794,7 @@ function BatchServer()
         local fd = MiddleManager.getFDBySvrIndex(serverId)
         if fd then
             len = len + 1
-            local notifyInfo = genNotifyInfo(serverInfo)
+            local notifyInfo = genNotifyInfo(serverInfo, matchSrvArr, serverId)
             srvTb[fd] = notifyInfo
         end
 

+ 0 - 1
script/module/areaBattle/AreaBattleDB.lua

@@ -24,7 +24,6 @@ AreaBattleData = nil
 
 --     serverList = {    -- 服务器信息列表
 --         [serverId] = {
---             serverId = serverId,
 --             winTimes = 0,
 --             defeatTimes = 0,
 --             winPlayerArr = {playerUuid1, playerUuid2,},

+ 3 - 0
script/module/areaBattle/AreaBattleDefine.lua

@@ -47,6 +47,8 @@ AWARD_TYPE4 = 4 -- 战败服个人战胜奖励
 AWARD_TYPE5 = 5 -- 战败服个人战败奖励
 AWARD_TYPE6 = 6 -- 战败服全服奖励
 
+AWARD_TYPE7 = 7 -- 轮空服奖励
+
 -- 奖励类型 —— 邮件ID映射表
 AWARDTYPE2MAILID = {
     [AWARD_TYPE1] = 7026,
@@ -55,6 +57,7 @@ AWARDTYPE2MAILID = {
     [AWARD_TYPE4] = 7028,
     [AWARD_TYPE5] = 7029,
     [AWARD_TYPE6] = 7025,
+    [AWARD_TYPE7] = 7034,
 }
 
 GET_BATTLE_MAX_CNT_TIMES = 20  -- 一次最多请求30名玩家的战斗数据

+ 10 - 4
script/module/areaBattle/AreaBattleNS.lua

@@ -163,11 +163,17 @@ local function genPlayerAwardArray(srvBattleRes)
     local len = 0
     local playerAwardArray = {}
 
-    insertPlayerAward(playerAwardArray, srvBattleRes.winPlayerArr, battleWinAwward, winAwardType)
-    insertPlayerAward(playerAwardArray, srvBattleRes.winPlayerArr, normalAward, normalAwardType)
+    if srvBattleRes.isBye == 0 then -- 非轮空服
+        insertPlayerAward(playerAwardArray, srvBattleRes.winPlayerArr, battleWinAwward, winAwardType)
+        insertPlayerAward(playerAwardArray, srvBattleRes.winPlayerArr, normalAward, normalAwardType)
 
-    insertPlayerAward(playerAwardArray, srvBattleRes.defeatPlayerArr, battleDefeatAward, defeateAwardType)
-    insertPlayerAward(playerAwardArray, srvBattleRes.defeatPlayerArr, normalAward, normalAwardType)
+        insertPlayerAward(playerAwardArray, srvBattleRes.defeatPlayerArr, battleDefeatAward, defeateAwardType)
+        insertPlayerAward(playerAwardArray, srvBattleRes.defeatPlayerArr, normalAward, normalAwardType)
+    else
+        -- 轮空服
+        normalAwardType = AreaBattleDefine.AWARD_TYPE7
+        normalAward = AreaBattleConfig[1].defeatAward3
+    end
 
 
     len = #playerAwardArray

+ 6 - 1
script/module/bag/Grid.lua

@@ -191,6 +191,11 @@ function makeItemEquip(net, itemConfig, equipGrid, equipIndex, shuijingAttrID, e
 			baseAttrInfo = equipGrid.baseAttr
 		end
 
+		local baseAttrRandVal = 1
+		if equipGrid and equipGrid.baseRandVal then
+			baseAttrRandVal = equipGrid.baseRandVal
+		end
+
 		net.equip[1].effects[0] = 0
 		net.equip[1].heroExclusive = ""
 
@@ -234,7 +239,7 @@ function makeItemEquip(net, itemConfig, equipGrid, equipIndex, shuijingAttrID, e
 		for i=1, net.equip[1].baseAttr[0] do
 			net.equip[1].baseAttr[i] = net.equip[1].baseAttr[i] or {}
 			net.equip[1].baseAttr[i].key = baseAttrInfo[i][1]
-			net.equip[1].baseAttr[i].value = math.floor(baseAttrInfo[i][2] * baseRate)
+			net.equip[1].baseAttr[i].value = math.floor(baseAttrInfo[i][2] * baseRate * baseAttrRandVal)
 
 
 			if gemBonus then

+ 21 - 1
script/module/chengjiu/ChengjiuLogic.lua

@@ -19,6 +19,8 @@ local Util = require("common.Util")
 local LuaMongo = _G.lua_mongo
 local RoleDefine = require("role.RoleDefine")
 
+local GiftLogic
+
 --[[
 human.db.chengjiu = {}
 human.db.chengjiu.task = {}
@@ -295,9 +297,27 @@ function getReward(human,taskID)
     GuideLogic.setDoSpecialGuide(human, GuideLogic.SKIPTYPE_JUMP_JIEFENG_BAOJU)
     RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_2001)
 
-    if taskID == 406 then
+
+    local giftType
+    GiftLogic = GiftLogic or require("topup.GiftLogic")
+
+    if taskID == 404 then
+        giftType = GiftLogic.GIFT_TALISMAN_OPEN
+    elseif taskID == 405 then
+        giftType = GiftLogic.GIFT_WINNERRELIC_OPEN
+    elseif taskID == 406 then
+        giftType = GiftLogic.GIFT_ELF_OPEN
         RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_2031)
     end
+
+    if giftType then
+        GiftLogic.trigger(human, giftType, nil, GiftLogic.GIFT_SEC_TYPE2)
+    end
+
+
+    -- if taskID == 406 then
+    --     RoleSystemLogic.onDot(human, RoleSystemDefine.ROLE_SYS_ID_2031)
+    -- end
 end
 
 -- 回调

+ 1 - 0
script/module/combat/BeSkill.lua

@@ -280,6 +280,7 @@ NO_CHECK_COMBO_LIST = {
     [BESKILL_TYPE37] = 1,
 	[BESKILL_TYPE38] = 1,
 	[BESKILL_TYPE43] = 1,
+	[BESKILL_TYPE15] = 1,
 }
 
 local function checkLimit(data)

+ 106 - 7
script/module/combat/CombatBuff.lua

@@ -478,7 +478,7 @@ function onBufferChange(obj)
 			RoleAttr.updateValue(RoleDefine.FANSHE_ZHUANGJIA,conf.args[1],bufferAttr,true)
 		end
 
-		if conf.cmd == "hudun" then
+		if conf.cmd == "hudun" or conf.cmd == "hudun1" then
 			RoleAttr.updateValue(RoleDefine.HUDUN_COMBAT, buffer.arg, bufferAttr,true)
 		end
 		--标识,给其他英雄加属性的buff
@@ -588,12 +588,12 @@ local function canAdd(obj, id, random, attacker, skillConfig)
 	-- end
 
 	--连击 BUFF率为 本来的一半
-	if CombatImpl.comboType > 0 then
-		local random = math.random(1, 10000)
-		if random < 5000 then
-			return
-		end
-	end
+	-- if CombatImpl.comboType > 0 then
+	-- 	local random = math.random(1, 10000)
+	-- 	if random < 5000 then
+	-- 		return
+	-- 	end
+	-- end
 	return true
 end
 --根据cmd删除buff
@@ -1131,6 +1131,35 @@ function getSkillBuffers(skillConfig)
 	return ret
 end
 
+local function calcCorrectObjCnt(targets, skillConfig, bufferConf, bufferID, attacker, randomVal)
+	local cnt = 0
+	if not targets then
+		return 	cnt
+	end
+
+	local statusBuffRate = skillConfig.otherArgs.statusBuffRate
+	local jobRateAdd = skillConfig.otherArgs.jobRateAdd
+
+	for _, target in ipairs(targets) do
+		local r = randomVal
+		if statusBuffRate and bufferConf.cmd == statusBuffRate[1] and isStatus(target, statusBuffRate[2]) then
+			r = r + statusBuffRate[3]
+		end
+
+		if jobRateAdd and bufferConf.cmd == jobRateAdd[1] and isJob(target, jobRateAdd[2]) then
+			r = r + jobRateAdd[3]
+		end
+
+		if canAdd(target, bufferID, r, attacker, skillConfig) then
+			cnt = cnt + 1
+		end
+	end
+
+	return 	cnt
+end
+
+
+
 
 -- 战斗回合中触发buffer
 function onHit(attacker, skillTargets, skillConfig, cmdTargets, otherArgs, cmdChangeArgs)
@@ -1253,6 +1282,9 @@ function onHit(attacker, skillTargets, skillConfig, cmdTargets, otherArgs, cmdCh
         if #targets > 0 then
             --local bufferConf = BufferExcel.buffer[bufferID]
 			local bufferConf = GetBuffConfig(bufferID)
+
+			local targetObjCnt
+
             for _, target in ipairs(targets) do
                 local r = random
                 if statusBuffRate and bufferConf.cmd == statusBuffRate[1] and isStatus(target, statusBuffRate[2]) then
@@ -1308,6 +1340,19 @@ function onHit(attacker, skillTargets, skillConfig, cmdTargets, otherArgs, cmdCh
 						end
 					end
 
+					if bufferConf.cmd == "hudun1" then
+						if not targetObjCnt then
+							targetObjCnt = calcCorrectObjCnt(targets, skillConfig, bufferConf, bufferID, attacker, random)
+						end
+
+						if targetObjCnt <= 0 then
+							arg = 0
+						else
+							local val = CombatObj.getValue(attacker, RoleDefine.HURT_COMBAT_NOW)
+							arg = math.floor(val / targetObjCnt)
+						end
+					end
+
 
                     if delayRound and delayRound > 0 then
                         target.delayAddBuffer = target.delayAddBuffer or { }
@@ -1458,9 +1503,25 @@ function onUseBeSkill(attacker,skillConfig,objs,cmdTargets, arg, arg2)--attacker
 			if bufferConf.roundOne == 1 and objs and objs[1] then
 				attackPos = objs[1].pos
 			end
+
+			local targetObjCnt
 			for _,target in ipairs(targets) do
 				if canAdd(target,bufferID,buffers[i][3], attacker, skillConfig) then
 					isAddSkillFrame = true
+
+					if bufferConf.cmd == "hudun1" then
+						if not targetObjCnt then
+							targetObjCnt = calcCorrectObjCnt(targets, skillConfig, bufferConf, bufferID, attacker, buffers[i][3])
+						end
+
+						if targetObjCnt <= 0 then
+							bufferArg1 = 0  -- 基于现在的判断条件正常来说程序执行到这里后, targetObjCnt 应该不会为0
+						else
+							local val = CombatObj.getValue(attacker, RoleDefine.HURT_COMBAT_NOW)
+							bufferArg1 = math.floor(val / targetObjCnt)
+						end
+					end
+
 					if bufferConf.cmd == "shaqi" then
 						for k=1, addBuffTimes do
 							addBuffer(attackPos,target, bufferID,skillConfig.id,bufferArg1 or bufferArg)
@@ -2132,7 +2193,19 @@ function BuffHandleAfterSkill(obj)
 		handle2.liuxue3(obj, buffer)
 		CombatImpl.addFrameBuffer(obj,buffer,CombatDefine.BUFFER_OP_HOLD)
 	end
+end
 
+-- 通过 xishou buff吸收伤害回血
+function AddHPByXishouBuff(obj, targetBuffCMD, hpVal)
+	local res, _, idx = isStatus(obj, {targetBuffCMD})
+	if res then
+		CombatImpl.addFrame()
+		CombatImpl.setSkillID(obj, 3001) -- 没实际效,用于柔化表现
+		local buffer = obj.buffer[idx]
+		local d = CombatObj.updateHp(obj, hpVal, nil, nil, obj.pos,CombatObj.BUFFER_HP_TYPE)
+		buffer.attrs[RoleDefine.HP_COMBAT] = hpVal
+		CombatImpl.addFrameBuffer(obj,buffer,CombatDefine.BUFFER_OP_HOLD)
+	end
 end
 
 
@@ -2237,6 +2310,7 @@ function updateHuDun(obj, value)
 		if newValue <= 0 then
 			obj.bufferAttr[RoleDefine.HUDUN_COMBAT] = 0
 			delBufferByCmd(obj,"hudun", 0)
+			delBufferByCmd(obj,"hudun1", 0)
 		else
 			obj.bufferAttr[RoleDefine.HUDUN_COMBAT] = newValue
 		end
@@ -2244,6 +2318,7 @@ function updateHuDun(obj, value)
 		-- 护盾值没有全部去除  则 在对应英雄上吧 BUF 上的arg 数值 减除  防止 BufChange 重置 属性
 		if value < 0 and newValue > 0 then
 			if obj and obj.buffer then
+				local subVal = value
 				for j =  1 , obj.buffer[0] do       -- 从最早的护盾BUF中删除arg 
 					local buffer = obj.buffer[j]
 					--local conf = BufferExcel.buffer[buffer.id]
@@ -2254,6 +2329,7 @@ function updateHuDun(obj, value)
 						if have > -value then
 							buffer.arg = have + value
 						else
+							subVal = subVal + have
 							buffer.arg = 0
 							local bufferCnt = delBuffer(obj, j)
 							CombatImpl.addFrameBuffer(obj,buffer,CombatDefine.BUFFER_OP_DEL)
@@ -2261,6 +2337,29 @@ function updateHuDun(obj, value)
 						end
 					end 
 				end
+
+				if subVal < 0 then
+					for j =  1 , obj.buffer[0] do
+						local buffer = obj.buffer[j]
+						--local conf = BufferExcel.buffer[buffer.id]
+						local conf = GetBuffConfig(buffer.id)
+						local bufferCmd = conf.cmd
+
+						if bufferCmd == "hudun1" then
+							buffer.arg = buffer.arg + subVal
+							subVal = buffer.arg
+							if subVal >= 0 then
+								break
+							end
+
+							if subVal < 0 then
+								buffer.arg = 0
+								local bufferCnt = delBuffer(obj, j)
+								CombatImpl.addFrameBuffer(obj,buffer,CombatDefine.BUFFER_OP_DEL)
+							end
+						end
+					end
+				end
 			end
 		end
 	end	

+ 40 - 8
script/module/combat/CombatObj.lua

@@ -420,8 +420,34 @@ function updateHp(obj, value,isRevive,delayCB, attackPos,type)
 	end
 	local sum = obj.hp + value
 
+	-- 判断伤害能否被吸收
+	local extraHandle
+	local targetBuffCMD
+	local isAbsorb = false
+	if value < 0 and (type == SKILL_HP_TYPE or type == EXTRA_HP_TYPE) then -- 是扣血, 并且来自技能本身或技能特殊效果
+		local attacker = CombatImpl.objList[attackPos]
+		if attacker and attacker.side ~= obj.side then -- 攻击方是英雄, 并且是敌方英雄
+			local isHaveTargetBuff = false
+			if attacker.hurtType and attacker.hurtType == CombatDefine.PHY_HURT_TYPE then
+				isHaveTargetBuff = CombatBuff.isStatus(obj, {"xishouPhysical"})
+				targetBuffCMD = "xishouPhysical"
+			elseif attacker.hurtType and attacker.hurtType == CombatDefine.MAGIC_HURT_TYPE then
+				isHaveTargetBuff = CombatBuff.isStatus(obj, {"xishouMagic"})
+				targetBuffCMD = "xishouMagic"
+			end
+
+			if isHaveTargetBuff then -- 拥有指定buff
+				isAbsorb = true
+				extraHandle = extraHandle or {}
+				extraHandle[1] = 1
+				extraHandle[2] = targetBuffCMD
+			end
+		end
+	end
+
+
 	-- 护盾值检测
-	if value < 0 then
+	if value < 0 and not isAbsorb then
 		local huDunValue = CombatBuff.getBuffHuDun(obj)
 		if huDunValue > 0 then
 			if huDunValue >= (-value) then
@@ -432,8 +458,10 @@ function updateHp(obj, value,isRevive,delayCB, attackPos,type)
 				CombatBuff.updateHuDun(obj, -huDunValue)
 			end
 		end
+
 	end
 
+	-- 加血上限判断
 	local hpMax = getHpMax(obj) 
 	if sum > hpMax then
 		sum = hpMax
@@ -449,11 +477,20 @@ function updateHp(obj, value,isRevive,delayCB, attackPos,type)
 	end
 
 
+	if isAbsorb then
+		extraHandle[3] = d
+		d = 0
+
+		-- CombatBuff.AddHPByXishouBuff(obj, -d, targetBuffCMD)
+		-- return 0, value
+	end
+
+
 	if sum < 0 then
 		sum = 0
 	end
 	local ret = beforeUpdateHp(obj, sum, value, d)
-	if ret then
+	if ret and not isAbsorb then
 		obj.hp = sum
 	end
 
@@ -500,12 +537,7 @@ function updateHp(obj, value,isRevive,delayCB, attackPos,type)
 		end
 	end
 
-	-- if d == 0 then
-	-- 	value = 0
-	-- end
-
-
-	return d,value
+	return d,value, extraHandle
 end
 
 function updateMp(obj, value)

+ 1 - 1
script/module/combat/JibanLogic.lua

@@ -404,7 +404,7 @@ function getJibanHero(human,combatHeroDB)
 						icon = heroCfg and heroCfg.head or 0,
 						star = 1,
 						camp = heroCfg and heroCfg.camp or 1,
-						grade = heroCfg.grade or 1,
+						grade = heroCfg and heroCfg.grade or 1,
 					}
 				else
 					local heroGrid = HeroLogic.getHeroGridByUuid(human, uuid)

+ 172 - 43
script/module/combat/Skill.lua

@@ -28,7 +28,6 @@ local function calcHp(attacker,target,calcType,rate,limit,isAddHp,param, flag)
 end
 
 
-
 function GetSkillConfig(skillId)
 	return weaponSkillConfig[skillId] or SkillExcel[skillId]
 end
@@ -43,8 +42,6 @@ function GetFinalSkillConfig(obj, skillId)
 	return GetSkillConfig(targetId)
 end
 
-
-
 -- 技能释放条件检测表
 COND_CMD = {}
 
@@ -52,8 +49,6 @@ COND_CMD = {}
 COST_CMD = {}
 
 
-
-
 -- 技能释放条件检查
 function COND_CMD.attrCheck(obj, skillId)
 	local skillConfig = GetSkillConfig(skillId)
@@ -86,11 +81,6 @@ function COST_CMD.attrCheck(obj, skillConfig)
 	end
 end
 
-
-
-
-
-
 -- 技能释放条件检测
 function SkillCondCheck(obj, skillId)
 	local skillConfig = GetSkillConfig(skillId)
@@ -108,6 +98,18 @@ function SkillCondCheck(obj, skillId)
 end
 
 
+-- 等释放技能完后的处理
+function ExtraHandle(obj, args)
+	if not args then
+		return
+	end
+
+	local hanleType = args[1]
+	if hanleType == 1 then
+		CombatBuff.AddHPByXishouBuff(obj, args[2], -args[3])
+	end
+end
+
 
 function setSkill(obj,config,skinConfig)
 	config = skinConfig or config	
@@ -349,8 +351,10 @@ function attack(obj, targets, skillConfig)
             obj.isAllCalcHp = index == #targets
 			target.attackSkillID = skillConfig.id
 			local hpValue,srcValue = 0,0
+
+			local extraHandleTb
 			if hurt > 0 then
-				hpValue,srcValue = CombatObj.updateHp(target, -hurt,nil,true, obj.pos,CombatObj.SKILL_HP_TYPE)
+				hpValue,srcValue, extraHandleTb = CombatObj.updateHp(target, -hurt,nil,true, obj.pos,CombatObj.SKILL_HP_TYPE)
 
 				if hpValue < 0 and CombatBuff.isStatus(target, {"chenshui"}) then
 					CombatBuff.DelBuffByCmd(target, "chenshui", 999)
@@ -361,6 +365,11 @@ function attack(obj, targets, skillConfig)
 
 			CombatImpl.setSkillHit(target,srcValue,nil,flag,obj,skillConfig.id)
 
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
+
+
             if target.isShanBi ~= 1 then
                len = len + 1
                hitList[len] = target
@@ -418,13 +427,17 @@ function CMD.hp(obj,skillConfig,skillTargets)
                 if target.side == CombatDefine.DEFEND_SIDE then
 					hp = CombatImpl.commonArgs.hpLimit or hp
 				end
-				local d = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
                 local showHp = hp
                 if hp > 0 and d < 0 then
                    showHp = hp * (-1)
                 end
 				CombatImpl.setExtraHit(target, showHp, CMD2ID["hp"])
 				CombatObj.onHpCB(target,d, obj.pos)
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
             end 
 
 			if hp < 0 then
@@ -501,12 +514,16 @@ function CMD.shixueHP(obj,skillConfig,skillTargets, hitObj)
 				if target.side == CombatDefine.DEFEND_SIDE then
 					hp = CombatImpl.commonArgs.hpLimit or hp
 				end
-				local d = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
                 if hp > 0 and d < 0 then
                    hp = hp * (-1)
                 end
 				CombatImpl.setExtraHit(target, hp, CMD2ID["hp"])
 				CombatObj.onHpCB(target,d, obj.pos)
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 			ret[#ret+1] = target
 		end
@@ -552,12 +569,16 @@ function CMD.speedHp(obj,skillConfig,skillTargets)
 				if target.side == CombatDefine.DEFEND_SIDE then
 					hp = CombatImpl.commonArgs.hpLimit or hp
 				end
-				local d = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
                 if hp > 0 and d < 0 then
                    hp = hp * (-1)
                 end
 				CombatImpl.setExtraHit(target,hp,CMD2ID["hp"])
 				CombatObj.onHpCB(target,d, obj.pos)
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 			ret[#ret+1] = target
 		end
@@ -597,12 +618,16 @@ function CMD.statusHp(obj,skillConfig,skillTargets)
 			if target.side == CombatDefine.DEFEND_SIDE then
 				hp = CombatImpl.commonArgs.hpLimit or hp
 			end
-			local d = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,hp,nil,true,obj.pos,CombatObj.EXTRA_HP_TYPE)
             if hp > 0 and d < 0 then
                hp = hp * (-1)
             end
 			CombatImpl.setExtraHit(target,hp,CMD2ID["hp"])
 			CombatObj.onHpCB(target,d, obj.pos)
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 		ret[#ret+1] = target
 	end
@@ -627,12 +652,16 @@ function CMD.hurt(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurt"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -661,12 +690,16 @@ function CMD.behurt(obj,skillConfig,skillTargets)
 		end
 		
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["behurt"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -690,12 +723,16 @@ function CMD.hurtwalun(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtwalun"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -734,12 +771,16 @@ function CMD.posHurt(obj,skillConfig,skillTargets)
 	    if bfind  then
 			hp = calcHp(obj,target,calcType,rate,limit, false)
 			if hp ~= 0 then
-				local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 				if d ~= 0 then
 					CombatImpl.setExtraHit(target,-hp,CMD2ID["hurt"])
 					CombatObj.onHpCB(target,d, obj.pos)
 					ret[#ret+1] = target
 				end
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 		end
 	end
@@ -764,12 +805,16 @@ function CMD.hurtxia(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtxia"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -793,12 +838,16 @@ function CMD.hurtmingli(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtmingli"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -822,12 +871,16 @@ function CMD.hurtmingli2(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtmingli2"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -851,12 +904,16 @@ function CMD.hurtguangci(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtguangci"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -882,12 +939,16 @@ function CMD.jobHurt(obj,skillConfig,skillTargets)
 		if target.job == job then
 			hp = calcHp(obj,target,calcType,rate,limit)
 			if hp and hp ~= 0 then
-				local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 				if d ~= 0 then
 					CombatImpl.setExtraHit(target,-hp,CMD2ID["jobHurt"])
 					CombatObj.onHpCB(target,d, obj.pos)
 					ret[#ret+1] = target
 				end
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 		end
 	end	
@@ -915,12 +976,16 @@ function CMD.statusHurt(obj,skillConfig,skillTargets)
 		if CombatBuff.isStatus(target, status)  then
 			hp = calcHp(obj,target,calcType,rate,limit)
 			if hp and hp ~= 0 then
-				local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 				if d ~= 0 then
 					CombatImpl.setExtraHit(target,-hp,CMD2ID["jobHurt"])
 					CombatObj.onHpCB(target,d, obj.pos)
 					ret[#ret+1] = target
 				end
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 		end
 	end	
@@ -938,12 +1003,16 @@ function CMD.hpHurt(obj,skillConfig,skillTargets)
 		if target.hp >= hpLimit then
 			hp = calcHp(obj,target,calcType,rate)
 			if hp and hp ~= 0 then
-				local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 				if d ~= 0 then
 					CombatImpl.setExtraHit(target,-hp,CMD2ID["hpHurt"])
 					CombatObj.onHpCB(target,d, obj.pos)
 					ret[#ret+1] = target
 				end
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 		end
 	end	
@@ -995,10 +1064,14 @@ function CMD.qingsuan(obj,skillConfig,skillTargets)
 			if limit and hurt > limit then
 				hurt = limit
 			end
-			local d = CombatObj.updateHp(target,-hurt,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hurt,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			CombatImpl.setExtraHit(target,-hurt,CMD2ID["qingsuan"])
 			CombatObj.onHpCB(target,d, obj.pos)
 			ret[#ret+1] = target
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -1092,11 +1165,15 @@ function CMD.qusan(obj,skillConfig,skillTargets)
 		if calcType and rate and cnt > 0 then
 			local hp = calcHp(obj,target,calcType,rate * cnt, limit, nil, nil, args[6])
 			if hp ~= 0 then
-				local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d,_, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 				if d ~= 0 then
 					CombatImpl.setExtraHit(target,-hp,CMD2ID["qusan"])
 					CombatObj.onHpCB(target,d, obj.pos)
 				end
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 		end
 	end
@@ -1157,12 +1234,16 @@ function CMD.hurtwalun2(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtwalun2"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -1187,12 +1268,16 @@ function CMD.hurtxia2(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtxia2"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -1217,12 +1302,16 @@ function CMD.hurtxia3(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurtxia3"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -1258,12 +1347,16 @@ function CMD.attributehurt(obj, skillConfig, skillTargets)
 		-- print("[attributehurt] 计算出来的最终伤害 nEndHurt = "..nEndHurt.." 获取到的敌方 属性ID = "
 		-- 	..nEnemyAttrID.." 敌方属性值 "..nEnemyValue.." 开始 nBeginHurt "..nBeginHurt)
 
-		local d = CombatObj.updateHp(tEnemyObj, -nEndHurt, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
+		local d, _, extraHandleTb = CombatObj.updateHp(tEnemyObj, -nEndHurt, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
 		if d ~= 0 then
 			CombatImpl.setExtraHit(tEnemyObj, -nEndHurt, CMD2ID["attributehurt"])
 			CombatObj.onHpCB(tEnemyObj, d, obj.pos, skillConfig.id)
 			ret[#ret+1] = tEnemyObj
 		end
+
+		if extraHandleTb then
+			ExtraHandle(tEnemyObj, extraHandleTb)
+		end
 	end
 	
 	return ret
@@ -1542,13 +1635,17 @@ function CMD.zhuiji(obj,skillConfig,skillTargets)
 end
 
 function CMD.baohu(obj,skillConfig,skillTargets)
-	local d = CombatObj.updateHp(obj,obj.baohuHurt,nil,nil, obj.pos,CombatObj.BUFFER_HP_TYPE)
+	local d, _, extraHandleTb = CombatObj.updateHp(obj,obj.baohuHurt,nil,nil, obj.pos,CombatObj.BUFFER_HP_TYPE)
 	if d ~= 0 then
 		local attrs = {}
 		attrs[RoleDefine.BAOHU_COMBAT] = skillTargets[1].pos
 		CombatImpl.setExtraHit(obj,d,CMD2ID["baohu"],attrs)
 		CombatObj.onHpCB(obj,d, skillTargets[1].pos)
-	end	
+	end
+
+	if extraHandleTb then
+		ExtraHandle(obj, extraHandleTb)
+	end
 end
 
 function CMD.yinbaoZuzhou(obj,skillConfig,skillTargets)
@@ -1573,11 +1670,15 @@ function CMD.zuzhouHurt(obj, skillConfig, skillTargets)
 	local zuzhouHurt = obj.args * confArgs[2] / 10000
 	for _,target in ipairs(targets) do
 		if zuzhouHurt ~= 0 then
-			local d = CombatObj.updateHp(target,-zuzhouHurt,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-zuzhouHurt,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-zuzhouHurt,CMD2ID["zuzhouHurt"])
 				CombatObj.onHpCB(target,d, obj.pos)
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
     obj.args = nil
@@ -1594,11 +1695,15 @@ function CMD.zuzhouTrueHurt(obj, skillConfig, skillTargets)
 			local nZuZhouNum = #target.bufferCmd["zuzhou"]
 			local nHurt = baseHurt * nZuZhouNum
 			if nHurt > 0  then
-				local d = CombatObj.updateHp(target, -nHurt,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+				local d, _, extraHandleTb = CombatObj.updateHp(target, -nHurt,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 				if d ~= 0 then
 					CombatImpl.setExtraHit(target,-nHurt,CMD2ID["zuzhouTrueHurt"])
 					CombatObj.onHpCB(target,d, obj.pos)
 				end
+
+				if extraHandleTb then
+					ExtraHandle(target, extraHandleTb)
+				end
 			end
 		end
 	end
@@ -1626,12 +1731,16 @@ function CMD.dnexhurt(obj,skillConfig,skillTargets)
 	for _,target in ipairs(targets) do
 		hp = calcHp(obj,target,calcType,rate,limit)
 		if hp ~= 0 then
-			local d = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hp,nil,true, obj.pos,CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hp,CMD2ID["hurt"])
 				CombatObj.onHpCB(target,d, obj.pos)
 				ret[#ret+1] = target
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 	return ret
@@ -1682,11 +1791,15 @@ function CMD.targetBuffExtraEffect(obj,skillConfig,skillTargets)
 		end
 		hurt = calcHp(obj, target, calcType, rate, limitVale)
 		if hurt ~= 0 then
-			local d = CombatObj.updateHp(target,-hurt,nil,true, obj.pos,CombatObj.SKILL_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(target,-hurt,nil,true, obj.pos,CombatObj.SKILL_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(target,-hurt,CMD2ID["hurt"])
 				CombatObj.onHpCB(target,d, obj.pos)
 			end
+
+			if extraHandleTb then
+				ExtraHandle(target, extraHandleTb)
+			end
 		end
 	end
 
@@ -1720,11 +1833,15 @@ function CMD.buffDiffHurt(obj,skillConfig,skillTargets)
 				hurt = maxHurt
 			end
 
-			local d = CombatObj.updateHp(targetObj, -hurt, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(targetObj, -hurt, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(targetObj, -hurt, CMD2ID["hurt"])
 				CombatObj.onHpCB(targetObj, d, obj.pos)
 			end
+
+			if extraHandleTb then
+				ExtraHandle(targetObj, extraHandleTb)
+			end
 		end
 
 	end
@@ -1762,33 +1879,45 @@ function CMD.exchangeHP(obj,skillConfig,skillTargets)
 		-- 施放者血量不能最低
 		if subHP > 0 then
 			-- 给目标加血量差值
-			local d = CombatObj.updateHp(targetObj, subHP, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
+			local d, _, extraHandleTb = CombatObj.updateHp(targetObj, subHP, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
 
 			if d ~= 0 then
 				CombatImpl.setExtraHit(targetObj, subHP,  CMD2ID["hp"])
 				CombatObj.onHpCB(targetObj, d, obj.pos)
 			end
 
+			if extraHandleTb then
+				ExtraHandle(targetObj, extraHandleTb)
+			end
+
 			-- 给施放者扣血量差值
-			d = CombatObj.updateHp(obj, -subHP, nil, true, targetObj.pos, CombatObj.EXTRA_HP_TYPE)
+			d, _, extraHandleTb = CombatObj.updateHp(obj, -subHP, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
 			if d ~= 0 then
 				CombatImpl.setExtraHit(obj, -subHP,  CMD2ID["hp"])
-				CombatObj.onHpCB(obj, d, targetObj.pos)
+				CombatObj.onHpCB(obj, d, obj.pos)
 			end
 
+			if extraHandleTb then
+				ExtraHandle(targetObj, extraHandleTb)
+			end
 
 			-- 给施放者恢复一定血量
 			local calcType = args[1]
 			local rate = args[2]
 			local hp = calcHp(obj,targetObj, calcType, rate, nil, true)
 
-			d = CombatObj.updateHp(obj,hp, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
+			d, _, extraHandleTb = CombatObj.updateHp(obj,hp, nil, true, obj.pos, CombatObj.EXTRA_HP_TYPE)
 			local showHp = hp
 			if hp > 0 and d < 0 then
 			   showHp = hp * (-1)
 			end
 			CombatImpl.setExtraHit(obj, showHp, CMD2ID["hp"])
 			CombatObj.onHpCB(obj,d, obj.pos)
+
+			if extraHandleTb then
+				ExtraHandle(targetObj, extraHandleTb)
+			end
+
 		end
 	end
 end

+ 14 - 1
script/module/combat/TargetMode.lua

@@ -192,7 +192,10 @@ local function handlerStatus(attacker,status)
 	end
 end
 --
-CAN_EMPTY_TARGET = { [22] = 1 }
+CAN_EMPTY_TARGET = { 
+	[22] = 1,
+	[37] = 1,
+}
 function getTargets(attacker, targetMode, skillTargets, cmdTargets, checkChaofen, isNormalAtk, skillID, isNeedFentanObjs)
     for i = 1, #targets do
         targets[i] = nil
@@ -1382,6 +1385,15 @@ local function handler36(attacker,targetMode)
 	end
 end
 
+-- 在技能目标中选择友方/敌方
+local function handler37(attacker,targetMode,skillTargets)
+	local targetSide = getTargetSide(attacker,targetMode)
+	for _,obj in ipairs (skillTargets) do
+		if obj.side == targetSide then
+			targets[#targets + 1] = obj
+		end
+	end
+end
 
 
 ------------------------------ 分界线 ------------------------------
@@ -1428,5 +1440,6 @@ handler[0] = handler0 -- 技能选取 和11相同
 handler[34] = handler34	--从技能目标中根据职业选择
 handler[35] = handler35	--从全体中根据种族选择
 handler[36] = handler36	--按照属性X排序,选择前Y个目标, 且排除技能施放者,属性值需要比技能施放者高/低
+handler[37] = handler37 -- 在技能目标中选择友方/敌方
 
 --对象选择器end

+ 20 - 0
script/module/dailyTask/HonorJourney.lua

@@ -105,6 +105,26 @@ local function HonorJourneyTask_GetTaskDB(human)
         HonorJourneyTask_CreateDB(human)
     end
 
+    local tTaskPointPrize = human.db.HonorJourney and human.db.HonorJourney.TaskPointPrize
+
+    if tTaskPointPrize and #tTaskPointPrize < #HonorJourneyExcel.Reward then
+        -- local nNowPoint = HonorJourney_GetTaskPoint(human)
+        local nNowPoint = human.db.HonorJourney and human.db.HonorJourney.nPoint or 0
+
+        for i=#tTaskPointPrize+1, #HonorJourneyExcel.Reward do
+            if not tTaskPointPrize[i] then
+                tTaskPointPrize[i] = CommonDefine.COMMON_PRIZE_STATE_NOGET
+                local boxCfg = HonorJourneyExcel.Reward[i]
+                if boxCfg and nNowPoint >= boxCfg.needcnt then
+                    tTaskPointPrize[i] = CommonDefine.COMMON_PRIZE_STATE_CANGET
+                end
+            else
+                print("[HonorJourneyTask_CreateDB] 配置了重复的奖励ID nID = "..i)
+            end
+        end
+    end
+
+
     return human.db.HonorJourney
 end
 

+ 1 - 1
script/module/drawCard/UnlimitDrawLogic.lua

@@ -266,7 +266,7 @@ function onLogin(human)
 
         if not Util.isSameDay(nLastTime) then
             local beforeLoginDay = UnlimitDraw_GetLoginDay(human)
-            if beforeLoginDay < LOGINDAY7 then
+            if beforeLoginDay <= LOGINDAY7 then
                 UnlimitDraw_AddLoginDay(human, 1)
                 UnlimitDraw_SetLoginTime(human, os.time())
 

+ 5 - 0
script/module/elf/ElfLogic.lua

@@ -34,6 +34,8 @@ local TriggerLogic = require("trigger.TriggerLogic")
 local TriggerDefine = require("trigger.TriggerDefine")
 local ChengjiuLogic = require("chengjiu.ChengjiuLogic")
 
+local GiftLogic
+
 local ELF_UPGRADELV_LOG_TAG = "elfUpGradeLv"
 local ELF_UPGRADESTAR_LOG_TAG = "elfUpGradeStar"
 -- local ELF_COND_TOWER_LEVEL =  600
@@ -579,6 +581,9 @@ function Elf_UpGradeStar(human, elfId)
 
     local nAllStar = calcAllElfStar(elfData)
     TriggerLogic.PublishEvent(TriggerDefine.EVENT_TYPE_JINGLING_STAR, human.db._id, 1)
+
+    GiftLogic = GiftLogic or require("topup.GiftLogic")
+    GiftLogic.trigger(human, GiftLogic.GIFT_ELF_UPGRADE_STAR, {currentVal = nAllStar}, GiftLogic.GIFT_SEC_TYPE1)
 end
 
 -- 布阵界面, 请求精灵数据

+ 6 - 0
script/module/equip/EquipLogic.lua

@@ -50,6 +50,8 @@ function AttrHashToArray(attrData)
 end
 
 
+
+
 -- 随出单条洗练属性的品质 和 倍数
 local function randAttrValMul()
     local totalWeight = 0
@@ -520,6 +522,10 @@ function sendEquipBagList(human)
         if equipGrid then
             equipGrid.putUuid = nil --处理数据异常的装备
 
+            if not equipGrid.baseRandVal then
+                equipGrid.baseRandVal = EquipLogicGrid.GenEquipBaseRandomVal(equipGrid.id)
+            end
+
             len = len + 1
             Grid.makeItem(msgRet.list[len], equipGrid.id, 1, nil, equipGrid, index, Grid.getOpflagAtBag(equipGrid.id))
             if len >= 30 then

+ 15 - 0
script/module/equip/EquipLogicGrid.lua

@@ -47,6 +47,20 @@ local function generateNewBaseAttr(equipId)
    return baseAttr
 end
 
+-- 生成装备基础属性的随机加成值
+function GenEquipBaseRandomVal(equipId)
+   local val = 1
+   local equipCfg = EquipExcel[equipId]
+   if equipCfg and equipCfg.baserandom then
+      local valAreaTb = equipCfg.baserandom[1]
+      local minVal = (valAreaTb[1] or 1) * 100
+      local maxVal = (valAreaTb[2] or 1) * 100
+      val = math.random(minVal, maxVal)
+      val = val / 100
+   end
+
+   return val
+end
 
 
 
@@ -68,6 +82,7 @@ function createGrid(id)
    equip.putUuid = nil              --装备者
    equip.washAttr = nil             --当前洗练属性
    equip.washQuality = nil          --当前洗练品质
+   equip.baseRandVal = GenEquipBaseRandomVal(id)
 
    if equipCfg.subType == ItemDefine.EQUIP_SUBTYPE_RING or equipCfg.subType == ItemDefine.EQUIP_SUBTYPE_AMULET then
       equip.quality = equipCfg.quality

+ 4 - 2
script/module/hero/HeroEquip.lua

@@ -342,9 +342,11 @@ function doCalcHero(obj,attrs)
 				--装备基础属性
 				for _,v in ipairs(baseAttrs) do
 					if equipConfig.subType == ItemDefine.EQUIP_SUBTYPE_RING or equipConfig.subType == ItemDefine.EQUIP_SUBTYPE_AMULET then
-						RoleAttr.updateValue(v[1],mathFloor(v[2] * baseRate * (1 + (suitBonusTbl and suitBonusTbl.sp_base or 0))),attrs)
+						local val = mathFloor(v[2] * baseRate * (1 + (suitBonusTbl and suitBonusTbl.sp_base or 0)) * (equipGrid.baseRandVal or 1) )
+						RoleAttr.updateValue(v[1], val ,attrs)
 					else
-						RoleAttr.updateValue(v[1],mathFloor(v[2] * baseRate * (1 + (suitBonusTbl and suitBonusTbl.base or 0))),attrs)
+						local val = mathFloor(v[2] * baseRate * (1 + (suitBonusTbl and suitBonusTbl.base or 0)) * (equipGrid.baseRandVal or 1) )
+						RoleAttr.updateValue(v[1], val, attrs)
 					end
 				end
 

+ 41 - 28
script/module/hero/HeroLogic.lua

@@ -304,8 +304,24 @@ local function deleteErrHero(human)
 	human.db.delErrHeroTag = true
 end
 
+-- 处理老数据, 英雄身上装备检查
+local function equipCheck(human)
+	if human.db.equipCheck then
+		return
+	end
 
+	local EquipLogicGrid = require("equip.EquipLogicGrid")
+	for index = 1, human.db.heroBag[0] do
+		local heroGrid = human.db.heroBag[index]
+		if heroGrid and heroGrid.equip and next(heroGrid.equip) then
+			for _, equipGrid in pairs(heroGrid.equip) do
+				equipGrid.baseRandVal = EquipLogicGrid.GenEquipBaseRandomVal(equipGrid.id)
+			end
+		end
+	end
 
+	human.db.equipCheck = true
+end
 
 
 
@@ -313,7 +329,9 @@ end
 
 -- 下发英雄背包列表
 function sendHeroBagList(human)
-	local msgRet = Msg.gc.GC_HERO_BAG_LIST	
+	equipCheck(human)
+
+	local msgRet = Msg.gc.GC_HERO_BAG_LIST
 	local cnt = 0
 	human.maxZDL = {}
 	human.maxZDL.maxHero = {}
@@ -326,31 +344,14 @@ function sendHeroBagList(human)
 	for index = 1, human.db.heroBag[0] do
         local heroGrid = human.db.heroBag[index]
         if heroGrid then
-			-- 对皮肤进行一下检测
-			-- if heroGrid.body then
-			-- 	local nHeroID = heroGrid.id or heroGrid.heroID
-			-- 	local tHeroCof = HeroExcel.hero[heroGrid.heroID]
-			-- 	if tHeroCof and heroGrid.body ~= tHeroCof.body then
-			-- 		local tSkinCof = SkillExcel.skin[heroGrid.body]
-			-- 		if tSkinCof then
-			-- 			if tSkinCof.heroId ~= heroGrid.heroID then
-			-- 				print("[sendHeroBagList] 玩家当前英雄身体数据不正确 heroID = "..heroGrid.heroID.." OldBodyID = "..heroGrid.body.." NewBodyID = "..tHeroCof.body)
-			-- 				heroGrid.body = tHeroCof.body
-			-- 			end
-			-- 		end
-			-- 	else 
-			-- 		print("[sendHeroBagList] 不存在对应的配置 nHeroID = "..nHeroID)
-			-- 	end
-			-- end
-
-		cnt = cnt + 1
-		HeroGrid.makeHeroSimple(msgRet.list[cnt], heroGrid, index, human)
-
-		if cnt >= HeroDefine.PAGE_HERO_COUNT then
-			msgRet.list[0] = cnt
-			Msg.send(msgRet, human.fd)
-			cnt = 0
-		end
+			cnt = cnt + 1
+			HeroGrid.makeHeroSimple(msgRet.list[cnt], heroGrid, index, human)
+
+			if cnt >= HeroDefine.PAGE_HERO_COUNT then
+				msgRet.list[0] = cnt
+				Msg.send(msgRet, human.fd)
+				cnt = 0
+			end
 			list[#list + 1] = {heroGrid.uuid or "", heroGrid.zhandouli or 0}
         end
 
@@ -1270,8 +1271,14 @@ function heroJueXingDo(human, heroID, heroIndex, inputIDList, inputIndexList)
 	-- 弹窗礼包相关触发
 	if human.db.heroLevelUpgrade < heroGrid.star then 
 		human.db.heroLevelUpgrade = heroGrid.star
-		GiftLogic.trigger(human,Upgrade_HERO_EVENT,{star = heroGrid.star})
+		-- GiftLogic.trigger(human,Upgrade_HERO_EVENT,{star = heroGrid.star})
+
+		-- GiftLogic.trigger(human, GiftLogic.GIFT_HERO_UPGRADE_STAR_DAILY, {currentVal = heroGrid.star}, GiftLogic.GIFT_SEC_TYPE1)
 	end
+
+	GiftLogic.trigger(human,Upgrade_HERO_EVENT,{star = heroGrid.star})
+	GiftLogic.trigger(human, GiftLogic.GIFT_HERO_UPGRADE_STAR_DAILY, {currentVal = heroGrid.star}, GiftLogic.GIFT_SEC_TYPE1)
+
     for i in ipairs(yunYingActParam) do
         yunYingActParam[i] = nil
     end
@@ -2967,8 +2974,14 @@ function heroJueXingOneClickDo(human, tHeroData)
 		-- 弹窗礼包相关触发
 		if human.db.heroLevelUpgrade < heroGrid.star then 
 			human.db.heroLevelUpgrade = heroGrid.star
-			GiftLogic.trigger(human,Upgrade_HERO_EVENT,{star = heroGrid.star})
+			-- GiftLogic.trigger(human,Upgrade_HERO_EVENT,{star = heroGrid.star})
+
+			-- GiftLogic.trigger(human, GiftLogic.GIFT_HERO_UPGRADE_STAR_DAILY, {currentVal = heroGrid.star}, GiftLogic.GIFT_SEC_TYPE1)
 		end
+
+		GiftLogic.trigger(human,Upgrade_HERO_EVENT,{star = heroGrid.star})
+		GiftLogic.trigger(human, GiftLogic.GIFT_HERO_UPGRADE_STAR_DAILY, {currentVal = heroGrid.star}, GiftLogic.GIFT_SEC_TYPE1)
+
 		for j in ipairs(yunYingActParam) do
 			yunYingActParam[j] = nil
 		end

+ 35 - 4
script/module/roleSystem/RoleStorageBox.lua

@@ -16,12 +16,17 @@ local CommonDB = require("common.CommonDB")
 local LogDefine = require("common.LogDefine")
 local ItemDefine = require("bag.ItemDefine")
 local BagLogic = require("bag.BagLogic")
+local Util = require("common.Util")
 
 
 local ITEM_LOG_TYPE = "storageBox" --本模块的道具日志标识
 
 local GET_EXTEA_REWARD_RATE = 50  -- 额外获得一份奖励的几率
 
+local STORAGEBOXID = 1038         -- 月光宝盒Id
+
+local LOGOUT_DAY_COND = 2       -- 至少离线两天, 才可以获得离线奖励
+
 
 -- 月光宝盒道具列表
 -- key 为道具ID, value为基础数量
@@ -115,7 +120,7 @@ local function updateData(human, itemId, itemCnt, isAdd)
     local storageBoxData = getData(human)
 
     if isAdd then
-        storageBoxData[itemId] = itemCnt
+        storageBoxData[itemId] = (storageBoxData[itemId] or 0) + itemCnt
     else
         local num = storageBoxData[itemId] - itemCnt
         storageBoxData[itemId] = math.max(num, 0)
@@ -127,11 +132,31 @@ local function writeLog(human, sourceType, itemId, itemSubCnt, randRate)
     local logStr = string.format("角色: [%s] 通过: %s 扣除月光宝盒中道具, 道具ID: %d, 道具数量: %d, 随机值: %d",
     human.db._id, sourceType, itemId, itemSubCnt, randRate)
 
-
     Log.write(Log.LOGID_OSS_STORAGEBOx, logStr)
 end
 
 
+local function addStorageBoxItem(human)
+    if human.db.lastLogoutTime and human.db.lastLogoutTime > 0 then
+        local logoutDay = Util.diffDay(human.db.lastLogoutTime)
+        if logoutDay >= LOGOUT_DAY_COND then
+            if BagLogic.getItemCnt(human, STORAGEBOXID) <= 0 then
+                BagLogic.addItem(human, STORAGEBOXID, 1, ITEM_LOG_TYPE)
+            end
+
+            local storageBoxData = getData(human)
+            if not storageBoxData then
+                initData(human)
+            end
+
+            for itemId, itemBaseCnt in pairs(ITEM_LIST) do
+                updateData(human, itemId, itemBaseCnt * logoutDay , true)
+            end
+        end
+    end
+end
+
+
 
 -- 创角时给角色的月光宝盒中添加一定数量道具
 function FillStorageBox(human)
@@ -148,10 +173,10 @@ function FillStorageBox(human)
     end
 
     -- 新修改: 需要获得一个道具
-    BagLogic.addItem(human, 1038, 1, ITEM_LOG_TYPE)
+    BagLogic.addItem(human, STORAGEBOXID, 1, ITEM_LOG_TYPE)
 
     initData(human)
-    storageBoxData = getData(human)
+    -- storageBoxData = getData(human)
 
     for itemId, itemBaseCnt in pairs(ITEM_LIST) do
         updateData(human, itemId, itemBaseCnt * openSvrDay , true)
@@ -159,6 +184,12 @@ function FillStorageBox(human)
 end
 
 
+
+function onLogin(human)
+    addStorageBoxItem(human)
+end
+
+
 -- 从月光宝盒中额外获取一份道具
 function GetExtraItem(human, itemId, itemCnt, sourceType)
     local sourceId = LogDefine.DEFINE[sourceType]

+ 2 - 2
script/module/scene/Handler.lua

@@ -40,14 +40,14 @@ end
 
 --在线玩家列表不再从 ObjHuman.onlineAccount 获取, 改为从 ObjHuman.onlineNewUniqueTag 获取
 function CG_TEST_PROTO(fd,msg)
-	print("============ CG_TEST_PROTO============", msg.param)
+	-- print("============ CG_TEST_PROTO============", msg.param)
 	local param = Json.Decode(msg.param)
 	local uTag
 	if param.type == "UseCDKV2" or param.type == "UseCDK" or param.type == "UseFixCDK" then
 		uTag = RoleDBLogic.Generateuuid(param.channel_id, msg.account, msg.serverTag)
 	end
 
-	Util.printTable(param)
+	-- Util.printTable(param)
 
 	if param.type == "UseCDKV2" then
 		-- local human = ObjHuman.onlineAccount[msg.account]

+ 6 - 0
script/module/talisman/TalismanLogic.lua

@@ -23,6 +23,8 @@ local RoleSystemDefine = require("roleSystem.RoleSystemDefine")
 local TriggerDefine = require("trigger.TriggerDefine")
 local TriggerLogic = require("trigger.TriggerLogic")
 
+local GiftLogic
+
 local LOGTYPE = "talisman"
 local COND_TOWER_LEVEL = 100 --开启本系统需要通关恶魔之塔的层数
 
@@ -344,6 +346,10 @@ function UpGrade(human, id)
     -- 总星数
     TriggerLogic.PublishEvent(TriggerDefine.MIBAO_ALLSTAR, human.db._id, 1)
 
+    local allStar = talismanLogic_GetAllStar(human)
+    GiftLogic = GiftLogic or require("topup.GiftLogic")
+    GiftLogic.trigger(human, GiftLogic.GIFT_TALISMAN_UPGRADE_STAR, {currentVal = allStar}, GiftLogic.GIFT_SEC_TYPE1)
+
     --刷新红点
     nLevel = nLevel + 1
     costItemCnt = nLevel -- math.ceil(nLevel/3)

+ 1 - 1
script/module/topup/BuyLogic.lua

@@ -188,7 +188,7 @@ function fontBuyItem(human, net, buyID)
 		net.voucher = conf.Voucher
 		-- net.productId = conf.productid
 		net.productId = getRealProductId(human, buyID, region)
-		if conf.module then
+		if conf.module and conf.module ~= "" then
 			local tModule = load("return require(\"" .. conf.module .. "\")")()
 			if tModule and tModule.GetRemainNum then
 				net.nCanBuyNum = tModule.GetRemainNum(human, buyID)

+ 149 - 6
script/module/topup/GiftLogic.lua

@@ -8,17 +8,37 @@ local Grid = require("bag.Grid")
 local NewLogic = require("role.NewLogic")
 local BagLogic = require("bag.BagLogic")
 local BuyLogic = require("topup.BuyLogic")
+local Util = require("common.Util")
 
 -- 推送协议类型
 local GC_GIFT_GENERATE = 50 
 local GC_QUERY_GIFT = 51
 
+-- 次类型
+GIFT_SEC_TYPE1 = 1
+GIFT_SEC_TYPE2 = 2
 
 -- 礼包触发事件
 local PRINCIPAL_LINE_EVNET = 1 -- 主线推图
 local EVAL_TOWER_EVENT = 2 -- 恶魔之塔
 local Upgrade_HERO_EVENT = 3 -- 英雄升星
 
+GIFT_UPGRADE_LV_EVENT = 4           -- 玩家升级
+GIFT_TALISMAN_UPGRADE_STAR = 5      -- 秘宝升星
+GIFT_WINNERRELIC_UPGRADE_STAR = 6   -- 圣遗物升星
+GIFT_ELF_UPGRADE_STAR = 7           -- 精灵升星
+GIFT_HERO_UPGRADE_STAR_DAILY = 8    -- 英雄升星, 每日只可触发一次, 可与 Upgrade_HERO_EVENT 同时触发
+
+GIFT_TALISMAN_OPEN = 15           -- 开启秘宝玩法
+GIFT_WINNERRELIC_OPEN = 16        -- 开启圣遗物玩法
+GIFT_ELF_OPEN = 17                -- 开启精灵玩法
+
+-- 每日只触发一次的礼包类型
+local dailyEventList = {
+    [GIFT_HERO_UPGRADE_STAR_DAILY] = 1,
+}
+
+
 --[[
     human.db.gift = {
         [id] = startTimeStamp
@@ -56,9 +76,13 @@ local function genGiftData(human, id,startTime,region,isNew)
     }
 end
 
-local function genGift(human,id) 
+local function genGift(human,id)
+    local giftCfg = GiftExcel[id]
+    local gitftType = giftCfg and giftCfg.trigger or 0
+
     -- 已经触发过礼包了
-    if human.db.gift.unlock[id] then 
+    -- 新修改: 类型8不受这个影响
+    if human.db.gift.unlock[id] and GIFT_HERO_UPGRADE_STAR_DAILY ~= gitftType then
         return
     end
     human.db.gift.unlock[id] = true
@@ -101,6 +125,29 @@ local function sendHumanGift(human)
     })
 end
 
+-- 每日只触发一次的礼包
+local function dailyEventCheck(human, giftId)
+    if not human.db.gift or not human.db.gift.dailyEventRecord then
+        return true
+    end
+
+    local dailyEventRecord = human.db.gift.dailyEventRecord
+    if dailyEventRecord[giftId] then
+        local lastGenGiftTime = dailyEventRecord[giftId]
+        if Util.isSameDay(lastGenGiftTime) then
+            return false
+        end
+    end
+
+    return true
+end
+
+local function updateDailyEventRecord(human, giftId)
+    human.db.gift.dailyEventRecord = human.db.gift.dailyEventRecord or {}
+    human.db.gift.dailyEventRecord[giftId] = os.time()
+end
+
+
 local handler = {
     [PRINCIPAL_LINE_EVNET] = function(human,param)
         for id,cfg in pairs(GiftExcel) do 
@@ -136,6 +183,95 @@ local handler = {
             end
         end
     end,
+
+    -- [GIFT_UPGRADE_LV_EVENT] = function (human,param)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_UPGRADE_LV_EVENT and cfg.param[1] == param.newLv then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+    -- [GIFT_TALISMAN_UPGRADE_STAR] = function (human,param)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_TALISMAN_UPGRADE_STAR and cfg.param[1] == param.newLv then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+    -- [GIFT_WINNERRELIC_UPGRADE_STAR] = function (human,param)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_WINNERRELIC_UPGRADE_STAR and cfg.param[1] == param.newLv then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+    -- [GIFT_ELF_UPGRADE_STAR] = function (human,param)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_ELF_UPGRADE_STAR and cfg.param[1] == param.newLv then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+
+    -- [GIFT_TALISMAN_OPEN] = function (human)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_TALISMAN_OPEN then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+    -- [GIFT_WINNERRELIC_OPEN] = function (human)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_WINNERRELIC_OPEN then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+    -- [GIFT_ELF_OPEN] = function (human)
+    --     for id,cfg in pairs(GiftExcel) do
+    --         -- 触发新礼包
+    --         if cfg.trigger == GIFT_ELF_OPEN then
+    --             genGift(human,id)
+    --         end
+    --     end
+    -- end,
+
+}
+
+local handler2 = {
+    [GIFT_SEC_TYPE1] = function (human, param, eventType)
+        for id,cfg in pairs(GiftExcel) do
+            -- 触发新礼包
+            if cfg.trigger == eventType and cfg.param[1] == param.currentVal and (not dailyEventList[eventType] or dailyEventCheck(human, id)) then
+                genGift(human,id)
+
+                if dailyEventList[eventType] then
+                    updateDailyEventRecord(human, id)
+                end
+            end
+        end
+    end,
+
+    [GIFT_SEC_TYPE2] = function (human, param, eventType)
+        for id,cfg in pairs(GiftExcel) do
+            -- 触发新礼包
+            if cfg.trigger == eventType then
+                genGift(human,id)
+            end
+        end
+    end,
 }
 
 ---------------------------------------------------------------
@@ -147,12 +283,19 @@ function onLogin(human)
     return sendHumanGift(human)
 end
 
-function trigger(human,type,param)
-    local f = handler[type]
-    if not f then 
+function trigger(human,type,param, extraType)
+    local f
+    if not extraType then
+        f = handler[type]
+    else
+        f = handler2[extraType]
+    end
+
+    if not f then
         return
     end
-    return f(human,param)
+
+    return f(human, param, type)
 end
 
 function buy(human,giftId) 

+ 76 - 25
script/module/treasurechest/TreasureChestLogic.lua

@@ -31,6 +31,8 @@ local TREASURECHEST_OPEN_AUTO_NUM   =   2       -- 开两次
 local TREASURECHEST_OPEN_ONETOUCH_NUM = 10       -- 激活一键开启宝箱功能, 需要开启钻石宝箱数量
 
 ----------------------------------------- 内部处理开始 -------------------------------------
+
+
 -- 写日志
 local function TreasureChestLogic_WriteLog(human, szText)
     Log.write(Log.LOGID_OSS_COMMON, "name = "..human.db.name.." id = "..human.db._id..szText)
@@ -46,9 +48,55 @@ local function TreasureChestLogic_GetPointConf()
     return TreasureConf.boxpoint
 end
 
+
+-- 重置积分奖励数据
+local function TreasureChestLogic_ResetDBPointPrize(human)
+    local tBoxPoint = TreasureChestLogic_GetPointConf()
+    
+    for key, v in pairs(tBoxPoint) do
+        human.db.TreasureChest.tPointPrize[key] = CommonDefine.COMMON_PRIZE_STATE_NOGET
+    end
+end
+
+-- 创建DB数据
+local function TreasureChestLogic_CreateDB(human)
+    human.db.TreasureChest = {
+        nPoint = 0,
+        nOpenNum = 0,
+        tPointPrize  = {},
+        tItem = {}, -- 宝箱数量
+        openBoxSumNum = 0, -- 开启宝箱总数量
+    }
+
+    TreasureChestLogic_ResetDBPointPrize(human)
+end
+
+-- 获取开启总宝箱数量
+local function TreasureChestLogic_GetDBOpenBoxSumNum(human)
+    if not human.db.TreasureChest then
+        TreasureChestLogic_CreateDB(human)
+    end
+
+    return human.db.TreasureChest.openBoxSumNum
+end
+
+-- 增加宝箱开启数量
+local function TreasureChestLogic_AddDBOpenBoxSumNum(human, nValue)
+    if not human.db.TreasureChest then
+        TreasureChestLogic_CreateDB(human)
+    end
+
+    human.db.TreasureChest.openBoxSumNum = (human.db.TreasureChest.openBoxSumNum or 0) + nValue
+end
+
+
+
+
 -- 获取宝箱奖励配置
-local function TreasureChestLogic_GetPointPrizeConf()
-    if not tCacheBoxPrize then
+local function TreasureChestLogic_GetPointPrizeConf(human)
+    local openBoxSumNum = TreasureChestLogic_GetDBOpenBoxSumNum(human)
+
+    --if not tCacheBoxPrize then
         tCacheBoxPrize = {}
         for _, v in pairs(TreasureConf.boxprize) do
             local nType = v.nType
@@ -56,7 +104,10 @@ local function TreasureChestLogic_GetPointPrizeConf()
                 tCacheBoxPrize[nType] = {}
             end
 
-            table.insert(tCacheBoxPrize[nType], v)
+            -- table.insert(tCacheBoxPrize[nType], v)
+            if openBoxSumNum >= v.count then
+                table.insert(tCacheBoxPrize[nType], v)
+            end
         end
 
         -- 变成有序
@@ -79,31 +130,14 @@ local function TreasureChestLogic_GetPointPrizeConf()
                 v.nAllPro = nAllWeight
             end
         end
-    end
+    --end
 
     return tCacheBoxPrize
 end
 
--- 重置积分奖励数据
-local function TreasureChestLogic_ResetDBPointPrize(human)
-    local tBoxPoint = TreasureChestLogic_GetPointConf()
-    
-    for key, v in pairs(tBoxPoint) do
-        human.db.TreasureChest.tPointPrize[key] = CommonDefine.COMMON_PRIZE_STATE_NOGET
-    end
-end
 
--- 创建DB数据
-local function TreasureChestLogic_CreateDB(human)
-    human.db.TreasureChest = {
-        nPoint = 0,
-        nOpenNum = 0,
-        tPointPrize  = {},
-        tItem = {}, -- 宝箱数量
-    }
 
-    TreasureChestLogic_ResetDBPointPrize(human)
-end
+
 
 -- 获取当前积分
 local function TreasureChestLogic_GetDBPoint(human)
@@ -165,9 +199,9 @@ local function TreasureChestLogic_UpdatePointPrize(human)
 end
 
 -- 打开宝箱操作
-local function TreasureChestLogic_OpenBox(nType, nBoxNum)
+local function TreasureChestLogic_OpenBox(human, nType, nBoxNum)
     local tBoxTypeConf = TreasureChestLogic_GetBoxTypeConf()
-    local tBoxPrize = TreasureChestLogic_GetPointPrizeConf()
+    local tBoxPrize = TreasureChestLogic_GetPointPrizeConf(human)
     if not tBoxTypeConf[nType] or not tBoxPrize[nType] then
         return nil
     end
@@ -189,9 +223,15 @@ local function TreasureChestLogic_OpenBox(nType, nBoxNum)
                 if nRandNum <= v.nAllPro then
                     table.insert(tOpenPrize, v.tPrize)
                     --print("[TreasureChestLogic_OpenBox] 获得的道具 nID = "..v.tPrize[1].." nNum = "..v.tPrize[2].." nKey "..v.nAllPro)
+                    TreasureChestLogic_AddDBOpenBoxSumNum(human, 1)
                     break
                 end
             end
+
+            tBoxPrize = TreasureChestLogic_GetPointPrizeConf(human)
+            tBoxTypePrize = tBoxPrize[nType]
+            nLen = #tBoxTypePrize
+            nAllWeight = tBoxTypePrize[nLen].nAllPro
         end
     end
 
@@ -263,6 +303,16 @@ local function TreasureChestLogic_AddAutoNum(human, nType, nNum)
     end
 end
 
+
+-- 处理老数据
+local function openBoxSumNumCheck(human)
+    local num = TreasureChestLogic_GetDBOpenBoxSumNum(human)
+    if not num then
+        TreasureChestLogic_AddDBOpenBoxSumNum(human, 2300)
+    end
+end
+
+
 ----------------------------------------- 客户端请求 -------------------------------------
 -- 请求宝箱界面信息
 function TreasureChestLogic_Query(human)
@@ -350,6 +400,7 @@ end
 
 -- 请求打开宝箱
 function TreasureChestLogic_Open(human, nBoxType, nBoxNum)
+    openBoxSumNumCheck(human)
 
     print("[TreasureChestLogic_Open]玩家当前请求打开的宝箱类型 ntyepe = "..nBoxType)
     local szText = "[TreasureChestLogic_Open] 玩家请求打开宝箱 nBoxType = "..nBoxType.." nBoxNum = "..nBoxNum
@@ -381,7 +432,7 @@ function TreasureChestLogic_Open(human, nBoxType, nBoxNum)
         return
     end
 
-    local tPrize = TreasureChestLogic_OpenBox(nBoxType, nBoxNum)
+    local tPrize = TreasureChestLogic_OpenBox(human, nBoxType, nBoxNum)
 
     -- 发送奖励
     BagLogic.addItemList(human, tPrize, "treasurechest")

+ 1 - 1
script/module/voucher/VoucherShopDefine.lua

@@ -34,4 +34,4 @@ VOUCHERSTATUS_GET = 1           -- 已获取
 -- VOUCHER_NO_OPEN_TAG = "iostishen"   -- 当标识是这个的时候, 代金券活动不开启
 
 -- 当标识是这些时,代金券活动不开启
-VOUCHER_NO_OPEN_TAG_ARR = {"ios", "iostishen", "minigame", "minigame_ts"}
+VOUCHER_NO_OPEN_TAG_ARR = {"minigame_ts_ios", "iostishen", "minigame_ts"}

+ 6 - 0
script/module/winnerRelic/WinnerRelicLogic.lua

@@ -19,6 +19,8 @@ local TriggerLogic = require("trigger.TriggerLogic")
 local TalismanLogic = require("talisman.TalismanLogic")
 
 
+local GiftLogic
+
 local LOGTYPE = "WinnerRelic"
 local relicUpgradeConfig = relicModule.RelicUpgrade  -- 获取升级配置表
 local relicDataConfig = relicModule.RelicData       -- 获取遗物数据表
@@ -297,6 +299,10 @@ function ActiveandUpgrade(human,relicId)
     -- 总星数
     TriggerLogic.PublishEvent(TriggerDefine.YIWU_ALLSTAR, human.db._id, 1)
 
+    local allStars = WinnerRelic_GetAllStar(human)
+    GiftLogic = GiftLogic or require("topup.GiftLogic")
+    GiftLogic.trigger(human, GiftLogic.GIFT_WINNERRELIC_UPGRADE_STAR, {currentVal = allStars}, GiftLogic.GIFT_SEC_TYPE1)
+
     --更新英雄身上穿的遗物的星级
 
     -- tMsgData = relicData

+ 2 - 1
script/module/xianzhi/Proto.lua

@@ -21,7 +21,8 @@ CG_XIANZHI_ZHAOHUAN_DO = {
 
 GC_XIANZHI_ZHAOHUAN_DO = {
 	{"camp",             1,          "byte"},
-    {"list",      		 30,         HeroNiceNet}  -- 最大支持30个
+    {"list",      		 30,         HeroNiceNet},  -- 最大支持30个
+	{"isEnd",             1,          "byte"}, -- 0-没有发完 ,1-发完
 }
 
 -- 置换

+ 74 - 32
script/module/xianzhi/XianzhiLogic.lua

@@ -168,20 +168,20 @@ function zhaohuanDo(human, camp, cnt, skip)
 	-- 填充协议数据(协议最大支持30个)
 	local listIndex = 0
 	local maxListCount = 30
-	for id, data in pairs(mergedData) do
-		if listIndex >= maxListCount then
-			break
-		end
-		listIndex = listIndex + 1
-		HeroGrid.makeHeroNice(msgRet.list[listIndex], id, data.cnt, data.isNew)
-		-- 日志:添加到协议显示列表
-		local heroConfig = HeroExcel.hero[id]
-		-- if heroConfig then
-		-- 	Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(英雄): uuid="..human.db._id..", heroID="..id..", cnt="..data.cnt..", isNew="..(data.isNew and 1 or 0))
-		-- else
-		-- 	Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(物品): uuid="..human.db._id..", itemID="..id..", cnt="..data.cnt)
-		-- end
-	end
+	-- for id, data in pairs(mergedData) do
+	-- 	if listIndex >= maxListCount then
+	-- 		break
+	-- 	end
+	-- 	listIndex = listIndex + 1
+	-- 	HeroGrid.makeHeroNice(msgRet.list[listIndex], id, data.cnt, data.isNew)
+	-- 	-- 日志:添加到协议显示列表
+	-- 	-- local heroConfig = HeroExcel.hero[id]
+	-- 	-- if heroConfig then
+	-- 	-- 	Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(英雄): uuid="..human.db._id..", heroID="..id..", cnt="..data.cnt..", isNew="..(data.isNew and 1 or 0))
+	-- 	-- else
+	-- 	-- 	Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(物品): uuid="..human.db._id..", itemID="..id..", cnt="..data.cnt)
+	-- 	-- end
+	-- end
 	
 	BagLogic.updateMomentItem(2, xianzhiOutPutId, cnt * 1) --每次召唤获得精华1个
 	-- 额外增加的物品 30珠子 1转轴
@@ -193,35 +193,77 @@ function zhaohuanDo(human, camp, cnt, skip)
 	-- 英雄碎片已经通过 BagLogic.updateMomentItem 和 BagLogic.addMomentItemList 添加到背包
 
 	-- 添加天命结晶到协议(如果还有空间)
-	if listIndex < maxListCount then
-		listIndex = listIndex + 1
-		HeroGrid.makeHeroNice(msgRet.list[listIndex], ItemDefine.ITEM_XIANZHI_ZHUFU_ID, exItemCnt1, nil)
-		--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(天命结晶): uuid="..human.db._id..", itemID="..ItemDefine.ITEM_XIANZHI_ZHUFU_ID..", cnt="..exItemCnt1)
-	else
-		--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 协议已满,天命结晶未添加到显示列表: uuid="..human.db._id..", itemID="..ItemDefine.ITEM_XIANZHI_ZHUFU_ID..", cnt="..exItemCnt1..", listIndex="..listIndex)
-	end
+	-- if listIndex < maxListCount then
+	-- 	listIndex = listIndex + 1
+	-- 	HeroGrid.makeHeroNice(msgRet.list[listIndex], ItemDefine.ITEM_XIANZHI_ZHUFU_ID, exItemCnt1, nil)
+	-- 	--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(天命结晶): uuid="..human.db._id..", itemID="..ItemDefine.ITEM_XIANZHI_ZHUFU_ID..", cnt="..exItemCnt1)
+	-- else
+	-- 	--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 协议已满,天命结晶未添加到显示列表: uuid="..human.db._id..", itemID="..ItemDefine.ITEM_XIANZHI_ZHUFU_ID..", cnt="..exItemCnt1..", listIndex="..listIndex)
+	-- end
 	
 	-- 添加珠子到协议(如果还有空间)
-	if listIndex < maxListCount then
-		listIndex = listIndex + 1
-		HeroGrid.makeHeroNice(msgRet.list[listIndex], xianzhiOutPutId, cnt * 1, nil)
-		--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(珠子): uuid="..human.db._id..", itemID="..xianzhiOutPutId..", cnt="..(cnt * 1))
-	else
-		--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 协议已满,珠子未添加到显示列表: uuid="..human.db._id..", itemID="..xianzhiOutPutId..", cnt="..(cnt * 1)..", listIndex="..listIndex)
-	end
+	-- if listIndex < maxListCount then
+	-- 	listIndex = listIndex + 1
+	-- 	HeroGrid.makeHeroNice(msgRet.list[listIndex], xianzhiOutPutId, cnt * 1, nil)
+	-- 	--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 添加到协议显示列表(珠子): uuid="..human.db._id..", itemID="..xianzhiOutPutId..", cnt="..(cnt * 1))
+	-- else
+	-- 	--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 协议已满,珠子未添加到显示列表: uuid="..human.db._id..", itemID="..xianzhiOutPutId..", cnt="..(cnt * 1)..", listIndex="..listIndex)
+	-- end
 	
-	msgRet.list[0] = listIndex
+	-- msgRet.list[0] = listIndex
 	--Log.write(Log.LOGID_DEBUG, "[zhaohuanDo] 协议填充完成: uuid="..human.db._id..", 最终listIndex="..listIndex..", 协议list[0]="..msgRet.list[0])
 
-	Msg.send(msgRet, human.fd)
 
+	-- 修改: 增加50连抽功能, 协议数据改为分段发
 	ChengjiuLogic.onCallback(human,ChengjiuDefine.CJ_TASK_TYPE_14,cnt)
-
 	HeroGrowUp.onCallback(human, HeroGrowUp.TASKTYPE2, cnt)
 	YunYingLogic.onCallBack(human, "onTMDrawCard", cnt)
 	WeekTaskLogic.recordWeekTaskFinishCnt(human, WeekTaskLogic.WEEK_TASK_ID_3, cnt)
-
 	TriggerLogic.PublishEvent(TriggerDefine.EVENT_TYPE_FATESUMMON, human.db._id, cnt)
+
+
+	local itemArr = {}
+	for id, data in pairs(mergedData) do
+		itemArr[#itemArr+1] = {id = id, cnt = data.cnt, isNew = data.isNew }
+	end
+
+	itemArr[#itemArr+1] = {id = ItemDefine.ITEM_XIANZHI_ZHUFU_ID, cnt = exItemCnt1}
+	itemArr[#itemArr+1] = {id = xianzhiOutPutId, cnt = cnt}
+
+	msgRet.isEnd = 0
+	local itemNum = #itemArr
+
+	for _, itemInfo in ipairs(itemArr) do
+		listIndex = listIndex + 1
+		msgRet.list[0] = listIndex
+		HeroGrid.makeHeroNice(msgRet.list[listIndex], itemInfo.id, itemInfo.cnt, itemInfo.isNew)
+
+		if listIndex >= maxListCount then
+			itemNum = itemNum - listIndex
+
+			if itemNum <= 0 then
+				msgRet.isEnd = 1
+				return Msg.send(msgRet, human.fd)
+			end
+			
+			Msg.send(msgRet, human.fd)
+			listIndex = 0
+		end
+	end
+
+	if listIndex > 0 then
+		msgRet.isEnd = 1
+		Msg.send(msgRet, human.fd)
+	end
+
+
+	-- ChengjiuLogic.onCallback(human,ChengjiuDefine.CJ_TASK_TYPE_14,cnt)
+
+	-- HeroGrowUp.onCallback(human, HeroGrowUp.TASKTYPE2, cnt)
+	-- YunYingLogic.onCallBack(human, "onTMDrawCard", cnt)
+	-- WeekTaskLogic.recordWeekTaskFinishCnt(human, WeekTaskLogic.WEEK_TASK_ID_3, cnt)
+
+	-- TriggerLogic.PublishEvent(TriggerDefine.EVENT_TYPE_FATESUMMON, human.db._id, cnt)
 end
 
 -- 置换查询

+ 216 - 116
script/module/zhuanpan/ZhuanpanLogic.lua

@@ -22,6 +22,8 @@ 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
 
 
 DEFAULT_ZHUANPAN_TYPE_NORMAL 		= 1     -- 基础转盘
@@ -45,7 +47,7 @@ function getLuckConfig(mainType)
 	elseif mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
 	   config = ZhuanpanExcel.luck2
 	end
-	
+
 	return config
 end
 
@@ -59,10 +61,10 @@ function red(human, type)
        local luckStatus = zhuanpan.luckStatus
        for k, v in pairs(config) do
            if luck >= k then
-              if luckStatus[k] == 1 then		
+              if luckStatus[k] == 1 then
 	       		    return true
 	       		 end
-           end 	
+           end
        end
     end
     if zhuanpan.free == 1 then
@@ -86,23 +88,23 @@ function query(human, mainType)
 
 	local oldCnt = getCntByType(human, mainType)
 
-	local oldLuck 
+	local oldLuck
 	local tOldStatus = nil
-	if	human.db.zhuanpan and human.db.zhuanpan[mainType] then 
+	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)			
+			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 zuanpan = human.db.zhuanpan[mainType]
+
 	local config = ZhuanpanExcel.define[mainType]
 	local msgRet = Msg.gc.GC_ZHUANPAN_QUERY
 	msgRet.type = mainType
@@ -128,10 +130,10 @@ function query(human, mainType)
        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)
@@ -139,13 +141,13 @@ function query(human, mainType)
 		msgRet.list[i].chance = tempConfig.chance
 		msgRet.list[i].maxCnt = tempConfig.getCnt
 		msgRet.list[i].getCnt = 0
-		if data.getCnt then 
+		if data.getCnt then
 			msgRet.list[i].getCnt = data.getCnt
 		end
 	end
-	
-    local luckConfig = getLuckConfig(mainType)	
-	
+
+    local luckConfig = getLuckConfig(mainType)
+
 	msgRet.luck = zuanpan.luck or 0
 	local len = 0
 	for k, v in pairs(luckConfig) do
@@ -156,9 +158,9 @@ function query(human, mainType)
 	   Grid.makeItem(net.item, v.reward[1] , v.reward[2])
 	end
 	msgRet.luckList[0] = len
-	
+
     len = 0
-    if REWARD_RECORD[mainType] then 
+    if REWARD_RECORD[mainType] then
 	   for k, v in ipairs(REWARD_RECORD[mainType]) do
 	      len = len + 1
 	      local net = msgRet.record[len]
@@ -183,7 +185,7 @@ function query(human, mainType)
        end
        chanceList[k].totalWeight = rewardWeight
 	end
-    
+
     -- 大全重占比
     local temp = {}
     for k, v in pairs(zhuanpanConfig) do
@@ -203,14 +205,14 @@ function query(human, mainType)
                net.chance = weight * scale * 100
                temp[itemID] = 1
            end
-        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
 
@@ -220,24 +222,24 @@ end
 function refresh(human, mainType, needTime, bRefreshLuck)
 	if not ZhuanpanExcel[mainType] then
 		return
-	end	
-	
+	end
+
     local oldCnt = getCntByType(human, mainType)
-	local oldLuck 
+	local oldLuck
 	local tOldStatus = nil
-	if	human.db.zhuanpan and human.db.zhuanpan[mainType] then 
+	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)			
+			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]
@@ -248,7 +250,7 @@ function refresh(human, mainType, needTime, bRefreshLuck)
 	    if leftTime > 0 then
 	    	needCost = config.refreshCost[1][2]
 	    end
-	    
+
 	    -- 判断消耗
 	    local cnt = BagLogic.getItemCnt(human, config.refreshCost[1][1])
 	    if cnt < needCost then
@@ -258,7 +260,7 @@ function refresh(human, mainType, needTime, bRefreshLuck)
 	    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
@@ -266,7 +268,7 @@ function refresh(human, mainType, needTime, bRefreshLuck)
     end
 
 	addRandomReward(human, mainType)
-	
+
 	-- 通知客户端
     if needTime then
 	    query(human, mainType)
@@ -290,7 +292,7 @@ function getReward(human, mainType, cnt)
 	local config = ZhuanpanExcel.define[mainType]
 	if not ZhuanpanExcel[mainType] then
 		return
-	end	
+	end
 	local needItemID = config.useItemID
 	local needItemCnt = nil
 
@@ -307,23 +309,23 @@ function getReward(human, mainType, cnt)
 	if needItemCnt == nil then
 		return
 	end
-	
+
 	local oldCnt = getCntByType(human, mainType)
-	local oldLuck 
+	local oldLuck
 	local tOldStatus = nil
-	if human.db.zhuanpan and human.db.zhuanpan[mainType] then 
+	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)			
+			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
@@ -345,7 +347,7 @@ function getReward(human, mainType, cnt)
 	-- 扣消耗
 	BagLogic.delItem(human, needItemID, needItemCnt, "zhuanpan_get")
 
-	
+
 	local msgRet = Msg.gc.GC_ZHUANPAN_GET_REWARD
 	msgRet.type = mainType
 	msgRet.list[0] = cnt
@@ -355,7 +357,7 @@ function getReward(human, mainType, cnt)
 		if result == nil then
 			assert(nil)
 		end
-		
+
 		-- 改db
 		local data = zhuanpan[result]
 		local zhuanpanConfig = ZhuanpanExcel[mainType]
@@ -403,7 +405,7 @@ function getReward(human, mainType, cnt)
             REWARD_RECORD.order[mainType] = order + 1
             REWARD_RECORD.index[mainType] = index
 		end
-	    
+
 		msgRet.list[i] = data.id
 
         len = len + 1
@@ -414,31 +416,31 @@ function getReward(human, mainType, cnt)
         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.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		
+            if luckStatus[k] == nil then
 			   luckStatus[k] = 1
 			end
-        end 	
+        end
 	end
 
     Msg.send(msgRet, human.fd)
@@ -456,24 +458,24 @@ 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)
@@ -487,11 +489,11 @@ function addRandomReward(human, mainType)
 	local itemID = nil
 	local itemCnt = nil
 	local cntIndex = 0
-	for i = 1, #zhuanpanConfig do 
+	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 
+		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
@@ -501,7 +503,7 @@ function addRandomReward(human, mainType)
 				   human.db.zhuanpan[mainType][cntIndex].itemCnt = itemCnt
                 end
 			end
-		elseif mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then 
+		elseif mainType == DEFAULT_ZHUANPAN_TYPE_GAOJI then
 			itemID, itemCnt = getRandomItem(human, tempConfig)
             if itemID and itemCnt then
 			   cntIndex = cntIndex + 1
@@ -519,7 +521,7 @@ function getLuckDrawSingle(human, 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
@@ -530,7 +532,7 @@ function getLuckDrawSingle(human, mainType)
 			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]
@@ -545,14 +547,14 @@ function getLuckDrawSingle(human, mainType)
 			end
 			randNum = randNum - tempWeight
 		end
-	end	
+	end
 end
 
 function initDB(human, mainType, cnt, oldluck, tOldStatus)
-	if human.db.zhuanpan ~= nil and 
-       human.db.zhuanpan[mainType] ~= nil then 
-		return 
-	end 
+	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 {}
@@ -569,20 +571,20 @@ function initDB(human, mainType, cnt, oldluck, 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 
+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 
+	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 
+		if leftTime <= 0 then
 			human.db.zhuanpan[mainType] = nil
 		end
 	end
@@ -603,7 +605,7 @@ function getRandomItem(human, tempConfig)
           return itemID, itemCnt
        end
        random = random - weight
-    end	
+    end
 end
 
 function zhuanpanByGm(human,mainType,val)
@@ -633,47 +635,47 @@ function getLuck(human, msg)
     local config = getLuckConfig(mainType)
 	if not config then
 		return
-	end	
-	
+	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
 		-- 计算是否需要重新扣除幸运值 重新激活奖励
@@ -684,24 +686,24 @@ function getLuck(human, msg)
 				calc = false
 			end
 		end
-		
+
 		-- 如果所有奖励都已领取,扣除幸运值并重新激活
 		if calc == true then
-			zhuanpan.luckStatus = {}	
+			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		
+					if luckStatus[k] == nil then
 						luckStatus[k] = 1
 					end
-				end 	
+				end
 			end
-			
+
 			-- 如果重新激活后还有可领取的奖励,继续循环领取
 			local hasMore = false
 			for k, v in pairs(config) do
@@ -714,7 +716,7 @@ function getLuck(human, msg)
 					itemList[itemListLen] = {v.reward[1], v.reward[2]}
 				end
 			end
-			
+
 			-- 如果没有更多可领取的奖励,退出循环
 			if not hasMore then
 				break
@@ -724,12 +726,12 @@ function getLuck(human, msg)
 			break
 		end
 	end
-	
+
 	-- 发送所有奖励列表
 	if itemListLen > 0 then
 		BagLogic.sendItemGetList(human, itemList, "zhuanpan_back")
 	end
-	
+
 	query(human, msg.type)
 end
 
@@ -740,17 +742,17 @@ function isDot(human)
 
    --local red2 = red(human, DEFAULT_ZHUANPAN_TYPE_GAOJI)
    --if red2 then return true end
- 
+
    return false
 end
 
 function getFree(human)
-	local oldLuck 
+	local oldLuck
 	local tOldStatus = nil
-	if human.db.zhuanpan and human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL] then 
+	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)			
+			tOldStatus = Util.copyTable(human.db.zhuanpan[DEFAULT_ZHUANPAN_TYPE_NORMAL].luckStatus)
 		end
 	end
     initDB(human,DEFAULT_ZHUANPAN_TYPE_NORMAL,nil, oldLuck,tOldStatus)
@@ -787,6 +789,60 @@ function getFree(human)
     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 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
+
 -- 每日固定奖励查询
 function dailyFixedRewardQuery(human)
 	local msgRet = Msg.gc.GC_ZHUANPAN_DAILY_FIXED_QUERY
@@ -801,18 +857,29 @@ function dailyFixedRewardQuery(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
-		msgRet.status = 2  -- 已领取
+		isReceived = true
 	else
-		msgRet.status = 1  -- 可领取
+		-- 再检查账号下其他角色(数据库)
+		isReceived = checkAccountDailyFixedReward(account)
 	end
 	
+	if isReceived then
+		msgRet.status = 2  -- 已领取(账号级别)
+	else
+		msgRet.status = 1  -- 可领取
+	end
+
 	-- 设置奖励物品
 	Grid.makeItem(msgRet.reward, 102, 50)
-	
+
 	if human.fd then
 		Msg.send(msgRet, human.fd)
 	else
@@ -834,14 +901,25 @@ function dailyFixedRewardGet(human, channelId)
 	-- 初始化数据库
 	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
-		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 今日已领取: getTime="..getTime)
-		return Broadcast.sendErr(human, "今日已领取")
+		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
+
 	BagLogic.cleanMomentItemList()
 
 	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 102, 50)
@@ -878,15 +956,26 @@ function onceRewardQuery(human)
 	-- 初始化数据库
 	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
-		msgRet.status = 2  -- 已领取
+		isReceived = true
 	else
-		msgRet.status = 1  -- 可领取
+		-- 再检查账号下其他角色(数据库)
+		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)
@@ -894,12 +983,12 @@ function onceRewardQuery(human)
 		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
@@ -916,21 +1005,32 @@ function onceRewardGet(human, channelId)
 	-- 初始化数据库
 	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
-		Log.write(Log.LOGID_DEBUG, "[onceRewardGet] 已领取过,无法重复领取: getTime="..getTime)
-		return Broadcast.sendErr(human, "已领取过,无法重复领取")
+		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)

+ 2 - 0
webServer/src/channels/factory/ChannelFactory.ts

@@ -14,6 +14,7 @@ import { MianyouChannelHandler } from "../handlers/MianyouChannelHandler";
 import { HupuChannelHandler } from "../handlers/HupuChannelHandler";
 import { SevenTwoZeroChannelHandler } from "../handlers/SevenTwoZeroChannelHandler";
 import { MeituanChannelHandler } from "../handlers/MeituanChannelHandler";
+import { HuaweiChannelHandler } from "../handlers/HuaweiChannelHandler";
 
 
 const logger = require("../../utils/log");
@@ -51,6 +52,7 @@ class ChannelFactory {
     this.registerHandler(14, new HupuChannelHandler()); // 虎扑渠道
     this.registerHandler(15, new SevenTwoZeroChannelHandler()); // 720渠道
     this.registerHandler(16, new MeituanChannelHandler()); // 美团渠道
+    this.registerHandler(17, new HuaweiChannelHandler()); // 华为渠道
   }
 
   /**

+ 439 - 0
webServer/src/channels/handlers/HuaweiChannelHandler.ts

@@ -0,0 +1,439 @@
+import {Context} from "koa";
+import axios from "axios";
+import * as crypto from 'crypto';
+import {ChannelHandler, LoginResult, PaymentResult} from "../interfaces/ChannelHandler";
+import {ChannelConfig} from "../../config/channelConfig";
+import {PaymentHelper} from "../../utils/PaymentHelper";
+
+const logger = require("../../utils/log");
+
+/**
+ * 华为渠道处理器
+ * 负责登录验证与支付回调处理
+ */
+export class HuaweiChannelHandler implements ChannelHandler {
+    /**
+     * 获取HTTPS代理配置
+     * @returns HTTPS代理配置
+     */
+    private getHttpsAgent() {
+        const https = require('https');
+        return new https.Agent({
+            rejectUnauthorized: false,
+            secureProtocol: 'TLSv1_2_method',
+            timeout: 10000
+        });
+    }
+
+    /**
+     * 生成签名
+     * 根据文档:移除空值参数 -> 按字母顺序排序 -> 拼接参数 -> 拼接secret -> MD5
+     * @param params 请求参数对象
+     * @param secret 应用密钥
+     * @returns MD5签名字符串(32位小写)
+     */
+    private generateSign(params: any, secret: string): string {
+        try {
+            // 1. 移除空值参数
+            const filteredParams: any = {};
+            Object.keys(params).forEach(key => {
+                if (params[key] !== null && params[key] !== undefined && params[key] !== '') {
+                    filteredParams[key] = params[key].toString();
+                }
+            });
+
+            // 2. 按字母顺序排序
+            const sortedKeys = Object.keys(filteredParams).sort();
+
+            // 3. 构建签名字符串:key=value&key=value
+            const queryString = sortedKeys.map(key => {
+                return `${key}=${filteredParams[key]}`;
+            }).join('&');
+
+            // 4. 拼接secret key
+            const signStr = `${queryString}&key=${secret}`;
+
+            logger.info("华为渠道签名生成详情:", {
+                filteredParams,
+                queryString,
+                signStr
+            });
+
+            // 5. MD5哈希(32位小写)
+            const signature = crypto.createHash('md5').update(signStr).digest('hex').toLowerCase();
+
+            return signature;
+        } catch (error) {
+            logger.error("华为渠道签名生成出错:", error);
+            return '';
+        }
+    }
+
+    /**
+     * 华为渠道登录鉴权
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     * @returns 登录结果
+     */
+    async handleLogin(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        try {
+            const data = ctx.request.body as any;
+            const { token } = data;
+
+            // 验证必要参数
+            if (!token) {
+                logger.error("华为渠道登录鉴权失败 - 缺少token参数");
+                return {
+                    code: -1,
+                    msg: "缺少必要参数: token",
+                    data: null
+                };
+            }
+
+            // 获取配置
+            const appId = config.loginConfig?.appId;
+            const appSecret = config.loginConfig?.appSecret;
+
+            if (!appId || !appSecret) {
+                logger.error("华为渠道登录鉴权失败 - 缺少配置参数", { appId, appSecret: !!appSecret });
+                return {
+                    code: -1,
+                    msg: "服务器配置错误: 缺少appId或appSecret",
+                    data: null
+                };
+            }
+
+            // 构建请求参数
+            const timestamp = Math.floor(Date.now() / 1000).toString();
+            const requestBody = {
+                appId: appId,
+                timestamp: timestamp
+            };
+
+            // 生成签名
+            const sign = this.generateSign(requestBody, appSecret);
+
+            if (!sign) {
+                logger.error("华为渠道登录鉴权失败 - 签名生成失败");
+                return {
+                    code: -1,
+                    msg: "签名生成失败",
+                    data: null
+                };
+            }
+
+            // 构建请求头
+            const headers = {
+                'Accept': 'application/json;',
+                'Content-Type': 'application/json;charset=utf-8;',
+                'Authorization': token,
+                'sign': sign,
+                'Server-Plat-From': 'minigame_server' // 可选,但建议传递
+            };
+
+            // API地址
+            const apiUrl = 'https://sdkapi.haotianhuyu.com/api/user/getLoginStatus';
+
+            logger.info("华为渠道登录鉴权请求", {
+                url: apiUrl,
+                headers: { ...headers, Authorization: token ? '***' : undefined },
+                body: requestBody
+            });
+
+            // 发送请求
+            const response = await axios.post(apiUrl, requestBody, {
+                headers: headers,
+                timeout: 10000,
+                httpsAgent: this.getHttpsAgent(),
+                validateStatus: (status) => {
+                    // 根据文档,失败时HTTP状态码是401,但我们也接受200
+                    return status === 200 || status === 401;
+                }
+            });
+
+            logger.info("华为渠道登录鉴权响应", {
+                status: response.status,
+                data: response.data
+            });
+
+            const responseData = response.data;
+
+            // 判断登录结果
+            // 根据文档:status=20000 表示成功,其他都是失败
+            if (responseData.status === 20000) {
+                // 登录成功
+                logger.info("华为渠道登录鉴权成功", {
+                    user_id: responseData.data?.user_id,
+                    token: responseData.data?.token ? '***' : undefined
+                });
+
+                return {
+                    code: 0,
+                    msg: responseData.message || "登录成功",
+                    data: {
+                        token: responseData.data?.token,
+                        user_id: responseData.data?.user_id,
+                        ali_user_id: responseData.data?.ali_user_id,
+                        channel_open_id: responseData.data?.channel_open_id
+                    }
+                };
+            } else {
+                // 登录失败
+                logger.warn("华为渠道登录鉴权失败", {
+                    status: responseData.status,
+                    errCode: responseData.errCode,
+                    message: responseData.message,
+                    msg: responseData.msg
+                });
+
+                return {
+                    code: -1,
+                    msg: responseData.msg || responseData.message || "登录验证失败",
+                    data: null
+                };
+            }
+
+        } catch (error: any) {
+            logger.error("华为渠道登录鉴权出错:", error);
+            
+            // 处理axios错误
+            if (error.response) {
+                const responseData = error.response.data;
+                logger.error("华为渠道登录鉴权API错误响应", {
+                    status: error.response.status,
+                    data: responseData
+                });
+
+                // 如果返回了错误数据,尝试解析
+                if (responseData && responseData.status !== undefined) {
+                    return {
+                        code: -1,
+                        msg: responseData.msg || responseData.message || "登录验证失败",
+                        data: null
+                    };
+                }
+            }
+
+            return {
+                code: -1,
+                msg: error.message || "登录验证失败",
+                data: null
+            };
+        }
+    }
+
+    /**
+     * 验证支付回调签名
+     * 根据文档:移除sign参数和空值 -> 按ASCII码升序排序 -> 拼接成key=value格式 -> 拼接&key={callback secret} -> MD5
+     * @param data 回调参数对象
+     * @param callbackSecret 回调密钥
+     * @returns 验证结果
+     */
+    private verifyPaymentSign(data: any, callbackSecret: string): boolean {
+        try {
+            // 1. 移除sign参数和空值字段
+            const filteredParams: any = {};
+            Object.keys(data).forEach(key => {
+                if (key !== 'sign' && data[key] !== null && data[key] !== undefined && data[key] !== '') {
+                    filteredParams[key] = data[key].toString();
+                }
+            });
+
+            // 2. 按ASCII码升序排序
+            const sortedKeys = Object.keys(filteredParams).sort();
+
+            // 3. 构建签名字符串:key1=value1&key2=value2
+            const queryString = sortedKeys.map(key => {
+                return `${key}=${filteredParams[key]}`;
+            }).join('&');
+
+            // 4. 拼接回调密钥
+            const signStr = `${queryString}&key=${callbackSecret}`;
+
+            logger.info("华为渠道支付回调签名验证详情:", {
+                filteredParams,
+                queryString,
+                signStr: signStr.substring(0, 100) + '...' // 只显示前100个字符
+            });
+
+            // 5. MD5哈希(32位小写)
+            const calculatedSign = crypto.createHash('md5').update(signStr).digest('hex').toLowerCase();
+            const receivedSign = (data.sign || '').toLowerCase();
+
+            const isValid = calculatedSign === receivedSign;
+
+            logger.info("华为渠道支付回调签名验证结果:", {
+                calculatedSign,
+                receivedSign,
+                isValid
+            });
+
+            return isValid;
+        } catch (error) {
+            logger.error("华为渠道支付回调签名验证出错:", error);
+            return false;
+        }
+    }
+
+    /**
+     * 华为渠道支付回调处理
+     * POST请求,处理支付成功后的发货通知
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     * @returns 支付结果(注意:返回格式为 {errCode: 0} 或 {errCode: 1, errMsg: "..."})
+     */
+    async handlePayment(ctx: Context, config: ChannelConfig): Promise<PaymentResult> {
+        const startTime = Date.now();
+        
+        try {
+            const data = ctx.request.body as any;
+            logger.info("华为渠道支付回调参数:", { url: ctx.href, params: data });
+
+            // 1. 验证必要参数
+            const requiredParams = ['app_id', 'user_id', 'server_id', 'role_id', 'cp_order_id', 
+                                   'cs_order_no', 'cs_trade_no', 'pay_amount', 'swap_coin_num', 
+                                   'order_create_time', 'order_pay_time', 'order_status', 'sign'];
+            
+            const missingParams = requiredParams.filter(param => !data[param]);
+            if (missingParams.length > 0) {
+                logger.warn("华为渠道支付回调失败: 缺少必要参数", { missingParams });
+                return {
+                    code: 1,
+                    msg: `缺少必要参数: ${missingParams.join(', ')}`
+                };
+            }
+
+            // 2. 验证订单状态(只有SUCCESS状态才发货)
+            if (data.order_status !== 'SUCCESS') {
+                logger.warn("华为渠道支付回调失败: 订单状态非成功", { order_status: data.order_status });
+                return {
+                    code: 1,
+                    msg: `订单状态非成功: ${data.order_status}`
+                };
+            }
+
+            // 3. 获取回调密钥并验证签名
+            const callbackSecret = config.paymentConfig?.callbackKey;
+            if (!callbackSecret) {
+                logger.error("华为渠道支付回调失败: 未配置回调密钥");
+                return {
+                    code: 1,
+                    msg: "服务器配置错误: 未配置回调密钥"
+                };
+            }
+
+            // 验证签名(必须验证,只有签名正确的订单才可发货)
+            if (!this.verifyPaymentSign(data, callbackSecret)) {
+                logger.error("华为渠道支付回调失败: 签名验证失败");
+                return {
+                    code: 1,
+                    msg: "签名验证失败"
+                };
+            }
+
+            // 4. 验证订单(使用cp_order_id作为订单ID)
+            const orderId = data.cp_order_id;
+            const validation = await PaymentHelper.validateOrder(orderId);
+            
+            if (!validation.valid) {
+                // 如果是重复发货,需要返回成功响应,但不重复发货
+                if (validation.message?.includes("重复发货")) {
+                    logger.info("华为渠道支付回调: 订单已发货,返回成功响应", { orderId });
+                    return {
+                        code: 0,
+                        msg: "订单已发货"
+                    };
+                }
+                
+                logger.warn("华为渠道支付回调订单验证失败", {
+                    orderId,
+                    validation
+                });
+                return {
+                    code: 1,
+                    msg: validation.message || "订单验证失败"
+                };
+            }
+
+            const orderInfo = validation.orderInfo;
+
+            // 5. 验证金额(pay_amount单位是分,需要转换为元进行比较)
+            const paymentAmount = parseInt(data.pay_amount) / 100; // 转换为元
+            if (Math.abs(Number(orderInfo.amount) - paymentAmount) > 0.01) {
+                logger.warn("华为渠道支付回调金额不匹配", {
+                    orderId,
+                    orderAmount: orderInfo.amount,
+                    paymentAmount: paymentAmount
+                });
+                return {
+                    code: 1,
+                    msg: `订单金额不一致: 订单金额${orderInfo.amount}元,支付金额${paymentAmount}元`
+                };
+            }
+
+            // 6. 发货处理
+            logger.info(`华为渠道支付订单${orderId}开始发货`, {
+                cs_order_no: data.cs_order_no,
+                cs_trade_no: data.cs_trade_no,
+                amount: paymentAmount,
+                swap_coin_num: data.swap_coin_num
+            });
+
+            const result = await PaymentHelper.deliverOrder(
+                orderInfo,
+                ctx.request.ip,
+                validation.url,
+                data.cs_order_no // 使用传盛订单号作为out_trade_no
+            );
+
+            // 检查是否超时(必须在7秒内响应)
+            const elapsedTime = Date.now() - startTime;
+            if (elapsedTime > 7000) {
+                logger.warn("华为渠道支付回调响应超时", {
+                    orderId,
+                    elapsedTime: `${elapsedTime}ms`
+                });
+            }
+
+            // 7. 返回响应(注意格式:成功返回 {errCode: 0},失败返回 {errCode: 1, errMsg: "..."})
+            // PaymentHelper.deliverOrder 成功返回 code=1,失败返回 code=0
+            // 华为渠道要求:成功返回 errCode=0,失败返回 errCode=1
+            if (result.code === 1) {
+                // 发货成功
+                logger.info(`华为渠道支付订单${orderId}发货成功`, {
+                    elapsedTime: `${elapsedTime}ms`
+                });
+                // 返回 code=0 表示成功,ApiController会转换为 {errCode: 0}
+                return {
+                    code: 0,
+                    msg: "发货成功"
+                };
+            } else {
+                // 发货失败
+                logger.error(`华为渠道支付订单${orderId}发货失败`, {
+                    result,
+                    elapsedTime: `${elapsedTime}ms`
+                });
+                // 返回 code=1 表示失败,ApiController会转换为 {errCode: 1, errMsg: "..."}
+                return {
+                    code: 1,
+                    msg: `发放失败,原因:${result.msg || "未知错误"}`
+                };
+            }
+
+        } catch (error: any) {
+            const elapsedTime = Date.now() - startTime;
+            logger.error("华为渠道支付回调异常", { 
+                error: error.message, 
+                stack: error.stack,
+                elapsedTime: `${elapsedTime}ms`
+            });
+            
+            return {
+                code: 1,
+                msg: `发放失败,原因:${error.message || "未知错误"}`
+            };
+        }
+    }
+}
+

+ 386 - 53
webServer/src/channels/handlers/MiniappChannelHandler.ts

@@ -36,6 +36,10 @@ export class MiniappChannelHandler implements ChannelHandler {
                     ...baseConfig.paymentConfig,
                     signKey: DOUYIN_MINI_APP_PRIVATE_KEY,
                     gameId: DOUYIN_MINI_APP_GAME_ID,
+                },
+                loginConfig: {
+                    ...baseConfig.loginConfig,
+                    apiKey: DOUYIN_MINI_APP_PRIVATE_KEY, // 抖音使用privateKey作为apiKey
                 }
             };
         } else {
@@ -517,85 +521,261 @@ export class MiniappChannelHandler implements ChannelHandler {
     }
 
     /**
-     * 内容安全审核接口
-     * @param ctx Koa上下文
+     * 执行小程序内容安全审核
+     * @param params 审核参数
      * @param config 渠道配置
+     * @returns 审核结果
      */
-    async contentSecurityCheck(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+    private async performMiniappContentSecurityCheck(params: {
+        uid: string;
+        content: string;
+        scene?: string;
+        nickname?: string;
+        title?: string;
+        usersign?: string;
+    }, config: ChannelConfig): Promise<{
+        success: boolean;
+        error?: number;
+        errmsg?: string;
+        result?: any;
+        trace_id?: string;
+        detail?: any;
+    }> {
         try {
-            const data = ctx.request.body as any;
-            logger.info("内容安全审核请求参数:", data);
+            const timestamp = Math.floor(Date.now() / 1000);
+            const nonce = Math.floor(Math.random() * 1000000);
 
-            // 验证必要参数
-            const requiredParams = ['uid', 'scene', 'content'];
-            for (const param of requiredParams) {
-                if (!data[param]) {
-                    logger.error(`缺少必要参数: ${param}`);
-                    return {
-                        code: -1,
-                        msg: `缺少必要参数: ${param}`,
-                        data: null
-                    };
-                }
+            // 使用小程序配置
+            let actualConfig = config;
+            if (config.channelId === 11) {
+                actualConfig = this.getChannel11Config('miniapp');
             }
-            data['timestamp'] = Math.floor(Date.now() / 1000); // 添加时间戳参数
-            data['gameid'] = config.paymentConfig.gameId; // 添加游戏ID参数
-            data['nonce'] = Math.floor(Math.random() * 1000000); // 添加随机数参数
-            //生成签名
-            data['signature'] = this.generateContentSecuritySignature(data, config);
-            if (!data['signature']) {
+
+            // 生成签名
+            const signature = this.generateContentSecuritySignature({ timestamp, nonce }, actualConfig);
+            if (!signature) {
                 return {
-                    code: -1,
-                    msg: "签名生成失败",
-                    data: null
+                    success: false,
+                    error: -1,
+                    errmsg: "签名生成失败"
                 };
             }
+
             // 构建请求参数
             const requestData = {
-                uid: data.uid,
-                gameid: data.gameid,
-                signature: data.signature,
-                timestamp: data.timestamp,
-                nonce: data.nonce,
-                scene: data.scene,
-                content: data.content,
-                nickname: data.nickname || '',
-                title: data.title || '',
-                usersign: data.usersign || ''
+                uid: params.uid,
+                gameid: actualConfig.paymentConfig.gameId,
+                signature: signature,
+                timestamp: timestamp,
+                nonce: nonce,
+                scene: params.scene || '1',
+                content: params.content,
+                nickname: params.nickname || '',
+                title: params.title || '',
+                usersign: params.usersign || ''
             };
 
-            // 调用内容安全API
-            const apiUrl = `https://api.${config.paymentConfig.apiUrl}/mpcommon/?cmd=wxaMsgSecCheck`;
-            logger.info("调用内容安全API:", {url: apiUrl, data: requestData});
+            // 调用小程序内容安全API
+            const apiUrl = `https://api.${actualConfig.paymentConfig.apiUrl}/mpcommon/?cmd=wxaMsgSecCheck`;
+            logger.info("调用小程序内容安全API:", { 
+                url: apiUrl, 
+                content: params.content.substring(0, 50) + '...' 
+            });
 
             const response = await axios.post(apiUrl, requestData, {
                 httpsAgent: this.getHttpsAgent(),
                 headers: this.getCommonHeaders()
             });
 
-            logger.info("内容安全API响应:", response.data);
+            logger.info("小程序内容安全API响应:", response.data);
 
             // 处理响应结果
             if (response.data && response.data.error === 0) {
-                const result = response.data.result;
+                return {
+                    success: true,
+                    error: 0,
+                    result: response.data.result,
+                    trace_id: response.data.trace_id,
+                    detail: response.data.detail
+                };
+            } else {
+                return {
+                    success: false,
+                    error: response.data?.error || -1,
+                    errmsg: response.data?.errmsg || "内容安全检测失败"
+                };
+            }
+        } catch (error) {
+            logger.error("小程序内容安全审核出错:", error);
+            return {
+                success: false,
+                error: -1,
+                errmsg: "内容安全审核失败"
+            };
+        }
+    }
+
+    /**
+     * 执行抖音内容安全审核
+     * @param params 审核参数
+     * @param config 渠道配置
+     * @returns 审核结果
+     */
+    private async performByteDanceContentSecurityCheck(params: {
+        uid: string;
+        content: string;
+        scene?: string;
+        nickname?: string;
+        title?: string;
+        usersign?: string;
+    }, config: ChannelConfig): Promise<{
+        success: boolean;
+        error?: number;
+        errmsg?: string;
+        result?: any;
+        trace_id?: string;
+        detail?: any;
+    }> {
+        try {
+            const timestamp = Math.floor(Date.now() / 1000);
+            const nonce = Math.floor(Math.random() * 1000000);
+
+            // 使用抖音小程序配置
+            let actualConfig = config;
+            if (config.channelId === 11) {
+                actualConfig = this.getChannel11Config('ttminiapp');
+            }
+
+            // 生成签名
+            const signature = this.generateContentSecuritySignature({ timestamp, nonce }, actualConfig);
+            if (!signature) {
+                return {
+                    success: false,
+                    error: -1,
+                    errmsg: "签名生成失败"
+                };
+            }
+
+            // 构建请求参数
+            const requestData = {
+                uid: params.uid,
+                gameid: actualConfig.paymentConfig.gameId,
+                signature: signature,
+                timestamp: timestamp,
+                nonce: nonce,
+                scene: params.scene || '1',
+                content: params.content,
+                nickname: params.nickname || '',
+                title: params.title || '',
+                usersign: params.usersign || ''
+            };
+
+            // 调用抖音内容安全API
+            const apiUrl = `https://api.${actualConfig.paymentConfig.apiUrl}/mpcommon/?cmd=byteDanceMsgSecCheck`;
+            logger.info("调用抖音内容安全API:", { 
+                url: apiUrl, 
+                content: params.content.substring(0, 50) + '...' 
+            });
+
+            const response = await axios.post(apiUrl, requestData, {
+                httpsAgent: this.getHttpsAgent(),
+                headers: this.getCommonHeaders()
+            });
+
+            logger.info("抖音内容安全API响应:", response.data);
+
+            // 处理抖音响应结果(抖音返回结构:{error: 0, msg: "ok", data: {}})
+            // error: 0 代表通过,其他错误码代表检测不通过
+            if (response.data && response.data.error === 0) {
+                // 抖音API返回通过,返回统一的格式
+                return {
+                    success: true,
+                    error: 0,
+                    result: {
+                        suggest: 'pass' // 抖音返回error=0表示通过
+                    }
+                };
+            } else {
+                // 抖音API返回不通过
+                return {
+                    success: false,
+                    error: response.data?.error || -1,
+                    errmsg: response.data?.msg || response.data?.errmsg || "内容安全检测失败"
+                };
+            }
+        } catch (error) {
+            logger.error("抖音内容安全审核出错:", error);
+            return {
+                success: false,
+                error: -1,
+                errmsg: "内容安全审核失败"
+            };
+        }
+    }
+
+    /**
+     * 内容安全审核接口
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async contentSecurityCheck(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        try {
+            const data = ctx.request.body as any;
+            // 支持从GET query参数或POST body中获取platform参数
+            const platform = ctx.request.query.platform || data.platform || 'miniapp';
+            logger.info("内容安全审核请求参数:", { ...data, platform });
+
+            // 验证必要参数
+            const requiredParams = ['uid', 'scene', 'content'];
+            for (const param of requiredParams) {
+                if (!data[param]) {
+                    logger.error(`缺少必要参数: ${param}`);
+                    return {
+                        code: -1,
+                        msg: `缺少必要参数: ${param}`,
+                        data: null
+                    };
+                }
+            }
+
+            // 根据platform调用对应的内容安全审核方法
+            const checkResult = platform === 'ttminiapp' 
+                ? await this.performByteDanceContentSecurityCheck({
+                    uid: data.uid,
+                    content: data.content,
+                    scene: data.scene,
+                    nickname: data.nickname,
+                    title: data.title,
+                    usersign: data.usersign
+                }, config)
+                : await this.performMiniappContentSecurityCheck({
+                    uid: data.uid,
+                    content: data.content,
+                    scene: data.scene,
+                    nickname: data.nickname,
+                    title: data.title,
+                    usersign: data.usersign
+                }, config);
+
+            // 处理审核结果
+            if (checkResult.success && checkResult.result) {
                 return {
                     code: 0,
                     msg: "success",
                     data: {
-                        suggest: result.suggest, // pass/review/reject
-                        label: result.label,
-                        trace_id: response.data.trace_id,
-                        detail: response.data.detail
+                        suggest: checkResult.result.suggest, // pass/review/reject
+                        label: checkResult.result.label,
+                        trace_id: checkResult.trace_id,
+                        detail: checkResult.detail
                     }
                 };
             } else {
                 return {
                     code: -1,
-                    msg: response.data?.errmsg || "内容安全检测失败",
+                    msg: checkResult.errmsg || "内容安全检测失败",
                     data: null
                 };
             }
-
         } catch (error) {
             logger.error("内容安全审核出错:", error);
             return {
@@ -606,6 +786,99 @@ export class MiniappChannelHandler implements ChannelHandler {
         }
     }
 
+    /**
+     * 内部内容安全检查方法(用于角色名称等场景)
+     * @param content 待检查内容
+     * @param uid 用户ID
+     * @param config 渠道配置
+     * @param scene 场景类型,默认为'1'(角色名称)
+     * @param platform 平台类型:miniapp(小程序)或 ttminiapp(抖音小程序),默认为 miniapp
+     * @returns 检查结果,suggest: pass/review/reject/risky
+     */
+    private async checkContentSecurity(
+        content: string, 
+        uid: string, 
+        config: ChannelConfig,
+        scene: string = '1',
+        platform: string = 'miniapp'
+    ): Promise<{ success: boolean; suggest?: string; message: string }> {
+        // 根据platform调用对应的内容安全审核方法
+        const checkResult = platform === 'ttminiapp'
+            ? await this.performByteDanceContentSecurityCheck({
+                uid: uid,
+                content: content,
+                scene: scene
+            }, config)
+            : await this.performMiniappContentSecurityCheck({
+                uid: uid,
+                content: content,
+                scene: scene
+            }, config);
+
+        console.log("checkResult:", checkResult);
+        // 处理审核结果
+        if (checkResult.success && checkResult.result) {
+            const suggest = checkResult.result.suggest; // pass/review/reject/risky
+            
+            // 拒绝状态:reject 和 risky 都应该拒绝
+            if (suggest === 'reject' || suggest === 'risky') {
+                logger.warn("内容安全审核未通过:", { 
+                    content: content.substring(0, 20) + '...', 
+                    uid, 
+                    suggest,
+                    detail: checkResult.detail
+                });
+                return {
+                    success: false,
+                    suggest: suggest,
+                    message: suggest === 'risky' ? "内容存在风险,无法使用" : "内容包含敏感信息,无法使用"
+                };
+            } else if (suggest === 'review') {
+                // review状态可以选择允许或拒绝,这里选择允许但记录日志
+                logger.warn("内容需要人工审核:", { 
+                    content: content.substring(0, 20) + '...', 
+                    uid, 
+                    suggest,
+                    detail: checkResult.detail
+                });
+                return {
+                    success: true,
+                    suggest: suggest,
+                    message: "内容通过审核"
+                };
+            } else if (suggest === 'pass') {
+                // pass状态,允许通过
+                return {
+                    success: true,
+                    suggest: suggest,
+                    message: "内容通过审核"
+                };
+            } else {
+                // 未知状态,记录日志并允许通过(容错处理)
+                logger.warn("内容安全审核返回未知状态:", { 
+                    content: content.substring(0, 20) + '...', 
+                    uid, 
+                    suggest,
+                    detail: checkResult.detail
+                });
+                return {
+                    success: true,
+                    suggest: suggest,
+                    message: "内容通过审核"
+                };
+            }
+        } else {
+            logger.error("内容安全审核API返回错误:", checkResult.errmsg);
+            // API错误时,为了不影响用户体验,可以选择允许通过或拒绝
+            // 这里选择允许通过,但记录错误日志
+            return {
+                success: false, 
+                suggest: "reject",
+                message: "内容安全审核失败,已拒绝"
+            };
+        }
+    }
+
     /**
      * 角色名称修改上报
      * @param ctx Koa上下文
@@ -614,7 +887,10 @@ export class MiniappChannelHandler implements ChannelHandler {
     async editUserRoleInfo(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
         try {
             const data = ctx.request.body as any;
-            logger.info("角色名称修改上报请求参数:", data);
+            // 支持从GET query参数或POST body中获取platform参数
+            const platform = ctx.request.query.platform || data.platform || 'miniapp';
+            logger.info("角色名称修改上报请求参数:", { ...data, platform });
+            
             // 验证必要参数
             const requiredParams = ['openId', 'serverid', 'playerName'];
             for (const param of requiredParams) {
@@ -656,6 +932,27 @@ export class MiniappChannelHandler implements ChannelHandler {
             data.playerName = decodedPlayerName;
             console.log("playerName for signature:", data.playerName);
 
+            // 内容安全审核:验证角色名称是否包含敏感内容
+            const securityCheck = await this.checkContentSecurity(decodedPlayerName, data.openId, config, '1', platform as string);
+            if (!securityCheck.success) {
+                logger.warn("角色名称内容安全审核未通过:", {
+                    playerName: decodedPlayerName,
+                    openId: data.openId,
+                    suggest: securityCheck.suggest,
+                    message: securityCheck.message
+                });
+                return {
+                    code: -1,
+                    msg: securityCheck.message || "角色名称包含敏感内容,无法使用",
+                    data: null
+                };
+            }
+            logger.info("角色名称内容安全审核通过:", {
+                playerName: decodedPlayerName,
+                openId: data.openId,
+                suggest: securityCheck.suggest
+            });
+
             //生成签名
             data['sign'] = this.generateRoleInfoSignature(data, config);
 
@@ -819,17 +1116,53 @@ export class MiniappChannelHandler implements ChannelHandler {
     }
 
     /**
-     * 生成内容安全签名(供客户端使用)
-     * @param params 签名参数
+     * 生成内容安全签名(签名算法1:SHA1)
+     * 根据文档:将apiKey、timestamp、nonce按字典序排序后拼接,然后SHA1加密
+     * @param params 签名参数(需包含timestamp和nonce)
      * @param config 渠道配置
      * @returns 签名结果
      */
     generateContentSecuritySignature(params: any, config: ChannelConfig): string | null {
         try {
-            let arr = [config.loginConfig.apiKey, params.timestamp, params.nonce];
-            arr.sort();
-            let str = arr.join('');
-            return crypto.createHash('sha1').update(str).digest('hex');
+            // 检查loginConfig和apiKey是否存在
+            if (!config.loginConfig || !config.loginConfig.apiKey) {
+                logger.error("生成内容安全签名出错: loginConfig或apiKey未配置", {
+                    channelId: config.channelId,
+                    hasLoginConfig: !!config.loginConfig,
+                    hasApiKey: !!(config.loginConfig && config.loginConfig.apiKey)
+                });
+                return null;
+            }
+
+            const apiKey = config.loginConfig.apiKey;
+            const timestamp = params.timestamp;
+            const nonce = params.nonce;
+
+            // 验证必要参数
+            if (!timestamp || !nonce) {
+                logger.error("生成内容安全签名出错: 缺少timestamp或nonce参数", {
+                    hasTimestamp: !!timestamp,
+                    hasNonce: !!nonce
+                });
+                return null;
+            }
+
+            // 签名算法1:将apiKey、timestamp、nonce按字典序排序后拼接,然后SHA1加密
+            let arr = [apiKey, timestamp.toString(), nonce.toString()];
+            arr.sort(); // 字典序排序
+            let str = arr.join(''); // 拼接
+            let signature = crypto.createHash('sha1').update(str).digest('hex'); // SHA1加密
+
+            logger.info("内容安全签名生成详情(签名算法1):", {
+                apiKey: apiKey.substring(0, 10) + '...', // 只显示前10个字符,保护密钥
+                timestamp,
+                nonce,
+                sortedArray: arr,
+                signString: str.substring(0, 50) + '...', // 只显示前50个字符
+                signature
+            });
+
+            return signature;
         } catch (error) {
             logger.error("生成内容安全签名出错:", error);
             return null;

+ 18 - 0
webServer/src/config/account_status_table.sql

@@ -0,0 +1,18 @@
+-- ----------------------------
+-- Table structure for account_status
+-- 账号状态上报表
+-- ----------------------------
+DROP TABLE IF EXISTS `account_status`;
+CREATE TABLE `account_status` (
+  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
+  `account` varchar(50) NOT NULL COMMENT '账号',
+  `channel_id` int(11) NOT NULL COMMENT '渠道ID',
+  `status` int(11) NOT NULL DEFAULT '0' COMMENT '上报状态',
+  `created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
+  `updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
+  PRIMARY KEY (`id`) USING BTREE,
+  UNIQUE KEY `account_channel_unique` (`account`, `channel_id`) USING BTREE COMMENT '账号和渠道ID唯一索引',
+  KEY `account_index` (`account`) USING BTREE,
+  KEY `channel_id_index` (`channel_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='账号状态上报表';
+

+ 21 - 1
webServer/src/config/channelConfig.ts

@@ -37,8 +37,12 @@ import {
     SEVEN_TWO_ZERO_QUICK_CALLBACK_KEY,
     SEVEN_TWO_ZERO_QUICK_PRODUCT_CODE,
     MINI_APP_DOMAIN,
+    MINI_APP_API_KEY,
     MEITUAN_APP_ID,
-    MEITUAN_APP_SECRET
+    MEITUAN_APP_SECRET,
+    HUAWEI_APP_ID,
+    HUAWEI_APP_SECRET,
+    HUAWEI_CALLBACK_SECRET
 } from "./thirdParams";
 
 // 渠道配置接口定义
@@ -216,6 +220,9 @@ export const channelConfigs: Record<number, ChannelConfig> = {
             signKey: MINI_APP_PRIVATE_KEY,
             gameId: MINI_APP_GAME_ID,
             apiUrl: MINI_APP_DOMAIN,
+        },
+        loginConfig: {
+            apiKey: MINI_APP_API_KEY,
         }
     },
     12: {
@@ -282,4 +289,17 @@ export const channelConfigs: Record<number, ChannelConfig> = {
             appSecret: MEITUAN_APP_SECRET,
         },
     },
+    17: {
+        // 华为渠道
+        channelId: 17,
+        name: "华为",
+        platform: "hwminiapp",
+        paymentConfig: {
+            callbackKey: HUAWEI_CALLBACK_SECRET,
+        },
+        loginConfig: {
+            appId: HUAWEI_APP_ID,
+            appSecret: HUAWEI_APP_SECRET,
+        },
+    },
 };

+ 1 - 1
webServer/src/config/dbConfig.ts

@@ -19,7 +19,7 @@ export const redisConfig = {
     host: 'localhost',
     port: 6379,
     // 其他可选配置...
-}
+};
 
 export const mongoConfig = {
   url: "localhost",

+ 7 - 3
webServer/src/config/thirdParams.ts

@@ -96,7 +96,7 @@ export const QIAI_QUICK_H5_PRODUCT_CODE = "80939198711722706880933346971719";
 export const MINI_APP_PRIVATE_KEY = "lxq05Tka0pXR8IxK5Imz61qF1XZVwhEj";
 export const MINI_APP_GAME_ID = "1540";
 export const MINI_APP_DOMAIN = "wefunol.com";
-export const MINI_APP_API_KEY = "your_api_key_here";
+export const MINI_APP_API_KEY = "lxq05Tka0pXR8IxK5Imz61qF1XZVwhEj";
 export const MINI_APP_STAT_API_KEY = "your_stat_api_key_here";
 
 //抖音小程序
@@ -128,7 +128,11 @@ export const SEVEN_TWO_ZERO_QUICK_PRODUCT_CODE = "920332684063151776920581716758
 export const SEVEN_TWO_ZERO_QUICK_PRODUCT_KEY = "02845520";
 
 // 美团渠道
-export const MEITUAN_APP_ID = ""; // 需要配置实际的应用ID
-export const MEITUAN_APP_SECRET = ""; // 需要配置实际的应用密钥
+export const MEITUAN_APP_ID = "mgcb9w7mswari84t"; // 需要配置实际的应用ID
+export const MEITUAN_APP_SECRET = "e56ebfd0ac5d47911aea3d6b55383b32c710a83415db4c5c0c5fa9d269d543b5"; // 需要配置实际的应用密钥
 
+// 华为渠道
+export const HUAWEI_APP_ID = "251208633431"; // 需要配置实际的传盛应用ID
+export const HUAWEI_APP_SECRET = "7B04DC515110A88336FD3DEDBF650F3F"; // 需要配置实际的应用密钥
+export const HUAWEI_CALLBACK_SECRET = "55B5E96B295F07BB858FE7D90018AEAB"; // 需要配置实际的回调密钥(用于支付回调签名验证)
 

+ 12 - 1
webServer/src/controller/ApiController.ts

@@ -558,6 +558,16 @@ class ApiController {
                 //{"status":0,"msg":"status 非 0 时,传入失败信息"}
                 ctx.body = {status: result.code === 1 ? 0 : -1, msg: result.msg};
                 break;
+            case 17:
+                // 华为渠道:成功返回 {"errCode": 0},失败返回 {"errCode": 1, "errMsg": "..."}
+                if (result.code === 0 || result.code === 1) {
+                    // 发货成功
+                    ctx.body = {errCode: 0};
+                } else {
+                    // 发货失败
+                    ctx.body = {errCode: 1, errMsg: result.msg || "发放失败"};
+                }
+                break;
             case 14:
                 ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
                 break;
@@ -733,7 +743,6 @@ class ApiController {
                         logger.error(`查询服务器${element.server_id}角色信息失败:`, error);
                     }
                 }
-
                 data.push({
                     channel: channelConfig.name, //渠道固定
                     minSid: 1, //最小服务器
@@ -1081,6 +1090,8 @@ class ApiController {
                 "gf100",
                 "gf200",
                 "gf300",
+                "YDKL888",
+                "SDKL888",
             ];
             let param: string = "";
 

+ 140 - 10
webServer/src/controller/MiniAppController.ts

@@ -53,7 +53,7 @@ class MiniappController {
     try {
       const handler = MiniappController.getChannelHandler();
       const config = MiniappController.getChannelConfig();
-      
+      console.log("MiniappController contentSecurityCheck config:", config);
       if (!handler) {
         ctx.body = {
           code: -1,
@@ -96,13 +96,27 @@ class MiniappController {
 
       // 调用渠道处理器的角色名称修改上报方法
       const result = await (handler as any).editUserRoleInfo(ctx, config);
-      ctx.body = result;
+      
+      // 转换返回格式:handler返回 { code, msg, data },需要转换为 { error, errmsg }
+      if (result.code === -1) {
+        // 失败情况(包括内容安全审核失败)
+        ctx.body = {
+          code: -1,
+          msg: result.msg || "角色名称修改失败"
+        };
+      } else {
+        // 成功情况
+        ctx.body = {
+          code: 0,
+          msg: result.msg || "修改成功"
+        };
+      }
 
     } catch (error) {
       logger.error("角色名称修改上报出错:", error);
       ctx.body = {
-        error: 0,
-        errmsg: "修改成功"
+        code: -1,
+        msg: error.message || "角色名称修改失败"
       };
     }
   }
@@ -216,8 +230,8 @@ class MiniappController {
       if (expectedSignature !== data.sign) {
         logger.error("解除禁言接口签名验证失败");
         ctx.body = {
-          error: -1,
-          errmsg: "签名验证失败"
+          code: -1,
+          msg: "签名验证失败"
         };
         return;
       }
@@ -227,13 +241,13 @@ class MiniappController {
       
       if (gameResult) {
         ctx.body = {
-          error: 0,
-          errmsg: ""
+          code: 0,
+          msg: ""
         };
       } else {
         ctx.body = {
-          error: -1,
-          errmsg: "解除禁言失败"
+          code: -1,
+          msg: "解除禁言失败"
         };
       }
 
@@ -484,6 +498,122 @@ class MiniappController {
       };
     }
   }
+
+  /**
+   * 账号状态上报接口
+   * GET请求:只处理查询,如果不存在则自动创建一条记录(默认status=0)
+   * POST请求:只处理存储/更新状态
+   * @param ctx Koa上下文
+   */
+  async accountStatusReport(ctx: Context) {
+    try {
+      const AccountStatusModel = require("../model/AccountStatusModel");
+      const isGet = ctx.method === 'GET';
+      
+      // GET请求从query参数获取,POST请求从body获取
+      const account = isGet ? ctx.query.account : (ctx.request.body as any).account;
+      const channelId = isGet ? ctx.query.channelId : (ctx.request.body as any).channelId;
+      const status = isGet ? undefined : (ctx.request.body as any).status;
+
+      logger.info("账号状态上报接口请求参数:", { 
+        method: ctx.method,
+        account, 
+        channelId, 
+        status 
+      });
+
+      // 验证必要参数
+      if (!account) {
+        ctx.body = {
+          code: -1,
+          msg: "缺少必要参数: account"
+        };
+        return;
+      }
+
+      if (!channelId) {
+        ctx.body = {
+          code: -1,
+          msg: "缺少必要参数: channelId"
+        };
+        return;
+      }
+
+      // GET请求:只处理查询,如果不存在则自动创建
+      if (isGet) {
+        let accountStatus = await AccountStatusModel.getAccountStatus(account, parseInt(channelId as string));
+        
+        // 如果不存在,自动创建一条记录(默认status=0)
+        if (!accountStatus) {
+          logger.info("查询记录不存在,自动创建:", { account, channelId });
+          await AccountStatusModel.upsertAccountStatus(account, parseInt(channelId as string), 0);
+          // 重新查询获取创建后的记录
+          accountStatus = await AccountStatusModel.getAccountStatus(account, parseInt(channelId as string));
+        }
+        
+        if (accountStatus) {
+          ctx.body = {
+            code: 0,
+            msg: "查询成功",
+            data: {
+              account: accountStatus.account,
+              channelId: accountStatus.channel_id,
+              status: accountStatus.status,
+              createdAt: accountStatus.created_at,
+              updatedAt: accountStatus.updated_at
+            }
+          };
+        } else {
+          ctx.body = {
+            code: -1,
+            msg: "查询失败"
+          };
+        }
+      } 
+      // POST请求:只处理存储/更新
+      else {
+        if (status === undefined || status === null) {
+          ctx.body = {
+            code: -1,
+            msg: "POST请求缺少必要参数: status"
+          };
+          return;
+        }
+
+        // 验证status参数(应该是数字)
+        const statusNum = parseInt(status);
+        if (isNaN(statusNum)) {
+          ctx.body = {
+            code: -1,
+            msg: "参数错误: status必须是数字"
+          };
+          return;
+        }
+
+        // 保存或更新状态
+        await AccountStatusModel.upsertAccountStatus(account, parseInt(channelId), statusNum);
+        
+        logger.info("账号状态保存成功:", { account, channelId, status: statusNum });
+        
+        ctx.body = {
+          code: 0,
+          msg: "保存成功",
+          data: {
+            account,
+            channelId: parseInt(channelId),
+            status: statusNum
+          }
+        };
+      }
+
+    } catch (error: any) {
+      logger.error("账号状态上报接口出错:", error);
+      ctx.body = {
+        code: -1,
+        msg: error.message || "操作失败"
+      };
+    }
+  }
   
 }
 

+ 91 - 0
webServer/src/model/AccountStatusModel.ts

@@ -0,0 +1,91 @@
+import { query } from "../sql/query";
+
+/**
+ * 账号状态上报模型
+ * 用于存储和查询账号的上报状态
+ */
+class AccountStatusModel {
+    /**
+     * 查询账号状态
+     * @param account 账号
+     * @param channelId 渠道ID
+     * @returns 账号状态记录
+     */
+    async getAccountStatus(account: string, channelId: number) {
+        try {
+            const result = await query(
+                `SELECT * FROM account_status WHERE account = ? AND channel_id = ?`,
+                [account, channelId]
+            );
+            return Array.isArray(result) && result.length > 0 ? result[0] : null;
+        } catch (error) {
+            const logger = require("../utils/log");
+            logger.error("查询账号状态失败:", error);
+            throw error;
+        }
+    }
+
+    /**
+     * 创建或更新账号状态
+     * @param account 账号
+     * @param channelId 渠道ID
+     * @param status 上报状态
+     * @returns 操作结果
+     */
+    async upsertAccountStatus(account: string, channelId: number, status: number) {
+        try {
+            const now = new Date().toISOString().slice(0, 19).replace('T', ' ');
+            
+            // 先查询是否存在
+            const existing = await this.getAccountStatus(account, channelId);
+            
+            if (existing) {
+                // 更新
+                const result = await query(
+                    `UPDATE account_status SET status = ?, updated_at = ? WHERE account = ? AND channel_id = ?`,
+                    [status, now, account, channelId]
+                );
+                return result;
+            } else {
+                // 插入
+                const result = await query(
+                    `INSERT INTO account_status (account, channel_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`,
+                    [account, channelId, status, now, now]
+                );
+                return result;
+            }
+        } catch (error) {
+            const logger = require("../utils/log");
+            logger.error("保存账号状态失败:", error);
+            throw error;
+        }
+    }
+
+    /**
+     * 批量查询账号状态
+     * @param accounts 账号数组
+     * @param channelId 渠道ID
+     * @returns 账号状态记录数组
+     */
+    async getAccountStatusBatch(accounts: string[], channelId: number) {
+        try {
+            if (!accounts || accounts.length === 0) {
+                return [];
+            }
+            
+            const placeholders = accounts.map(() => '?').join(',');
+            const result = await query(
+                `SELECT * FROM account_status WHERE account IN (${placeholders}) AND channel_id = ?`,
+                [...accounts, channelId]
+            );
+            return Array.isArray(result) ? result : [];
+        } catch (error) {
+            const logger = require("../utils/log");
+            logger.error("批量查询账号状态失败:", error);
+            throw error;
+        }
+    }
+}
+
+module.exports = new AccountStatusModel();
+

+ 6 - 2
webServer/src/router/miniapp.ts

@@ -3,10 +3,10 @@ const MiniAppController = require("../controller/MiniappController");
 
 const miniAppRouter = new Router();
 
-// 内容安全审核
+// 内容安全审核(支持platform query参数)
 miniAppRouter.post("/contentSecurityCheck", MiniAppController.contentSecurityCheck);
 
-// 角色名称修改上报
+// 角色名称修改上报(支持platform query参数)
 miniAppRouter.post("/editUserRoleInfo", MiniAppController.editUserRoleInfo);
 
 // 禁言接口
@@ -22,4 +22,8 @@ miniAppRouter.post("/freeChat", MiniAppController.freeChat);
 // 角色查询接口
 miniAppRouter.post("/roleQuery", MiniAppController.roleQuery);
 
+// 账号状态上报接口(支持查询和存储)
+miniAppRouter.post("/accountStatusReport", MiniAppController.accountStatusReport);
+miniAppRouter.get("/accountStatusReport", MiniAppController.accountStatusReport);
+
 export default miniAppRouter;

+ 27 - 2
webServer/src/sql/query.ts

@@ -2,17 +2,42 @@ import {
     mysqlConfig,
 } from '../config/dbConfig'
 const mysql = require("mysql2");
+const logger = require("../utils/log");
+
+//创建连接池,添加错误处理和重试配置
+const pool = mysql.createPool({
+    ...mysqlConfig,
+    waitForConnections: true,
+    connectionLimit: 10,
+    queueLimit: 0,
+    enableKeepAlive: true,
+    keepAliveInitialDelay: 0,
+    reconnect: true,
+    // 连接池错误处理
+    acquireTimeout: 60000,
+});
+
+// 监听连接池错误
+pool.on('error', (err: any) => {
+    if (err.code === 'PROTOCOL_CONNECTION_LOST') {
+        logger.error('MySQL连接丢失,正在尝试重连...', err);
+    } else if (err.code === 'ECONNREFUSED') {
+        logger.error(`MySQL连接被拒绝,请检查MySQL服务是否运行在 ${mysqlConfig.host}:${mysqlConfig.port}`, err);
+    } else {
+        logger.error('MySQL连接池错误:', err);
+    }
+});
 
-//创建连接池
-const pool = mysql.createPool(mysqlConfig);
 const query = (sql, val) => {
     return new Promise((resolve, reject) => {
         pool.getConnection(function (err, connection) {
             if (err) {
+                logger.error('MySQL获取连接失败:', err);
                 reject(err);
             } else {
                 connection.query(sql, val, (err: any, fields: unknown) => {
                     if (err) {
+                        logger.error('MySQL查询错误:', { sql, err });
                         reject(err);
                     } else {
                         resolve(fields);

+ 51 - 2
webServer/src/utils/serverManager.ts

@@ -34,6 +34,26 @@ export class ServerManager {
         console.log(`服务器成功启动在端口: ${port}`);
       });
       
+      // 添加服务器错误处理
+      server.on('error', (err: any) => {
+        if (err.code === 'EADDRINUSE') {
+          console.error(`端口 ${port} 已被占用`);
+        } else {
+          console.error(`服务器错误 (端口 ${port}):`, err);
+        }
+      });
+      
+      // 添加客户端连接错误处理
+      server.on('clientError', (err: any, socket: any) => {
+        if (err.message && err.message.includes('Parse Error')) {
+          console.warn('客户端HTTP解析错误:', err.message);
+          socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
+        } else {
+          console.error('客户端连接错误:', err);
+          socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
+        }
+      });
+      
       // 存储服务器实例
       this.servers.set(port, { app, server });
       return true;
@@ -70,6 +90,26 @@ export class ServerManager {
   
   // 配置中间件
   private setupMiddlewares(app: Koa): void {
+    // 全局错误处理中间件(必须在最前面)
+    app.use(async (ctx, next) => {
+      try {
+        await next();
+      } catch (err: any) {
+        // 处理HTTP解析错误
+        if (err.message && err.message.includes('Parse Error')) {
+          console.warn('HTTP解析错误(可能是无效请求):', err.message);
+          ctx.status = 400;
+          ctx.body = { error: 'Bad Request', message: 'Invalid HTTP request' };
+          return;
+        }
+        
+        // 其他错误
+        console.error('服务器错误:', err);
+        ctx.status = err.status || 500;
+        ctx.body = { error: 'Internal Server Error', message: err.message };
+      }
+    });
+
     // CORS中间件
     app.use(async (ctx, next) => {
       ctx.set('Access-Control-Allow-Origin', '*');
@@ -84,8 +124,17 @@ export class ServerManager {
       }
     });
     
-    // Body解析中间件
-    app.use(bodyParser());
+    // Body解析中间件(添加错误处理选项)
+    app.use(bodyParser({
+      enableTypes: ['json', 'form'],
+      jsonLimit: '10mb',
+      formLimit: '10mb',
+      onerror: (err: Error, ctx: Koa.Context) => {
+        console.warn('Body解析错误:', err.message);
+        ctx.status = 400;
+        ctx.body = { error: 'Bad Request', message: 'Invalid request body' };
+      }
+    }));
     
     // 路由中间件
     app.use(this.router.routes());