瀏覽代碼

1.同步360test分支本周修改 2.修改部分运营活动协议中金额相关字段的类型

gitxsm 3 月之前
父節點
當前提交
66ba55b6bb
共有 38 個文件被更改,包括 1803 次插入162 次删除
  1. 3 0
      script/common/Lang.lua
  2. 2 0
      script/common/LogDefine.lua
  3. 15 3
      script/common/ProtoID.lua
  4. 4 0
      script/core/ObjHuman.lua
  5. 7 0
      script/core/Timer.lua
  6. 二進制
      script/file3.tar.gz
  7. 1 1
      script/merge/MergeServerCS.lua
  8. 54 56
      script/merge/MergeServerDefine.lua
  9. 2 2
      script/merge/MergeServerLogic.lua
  10. 3 3
      script/module/WeekendLoopActivity/Proto.lua
  11. 7 1
      script/module/absAct/AbsActLogic.lua
  12. 275 0
      script/module/absAct/CommonActLoginGift.lua
  13. 279 0
      script/module/absAct/CommonActMoneyTree.lua
  14. 28 0
      script/module/absAct/Handler.lua
  15. 56 5
      script/module/absAct/Proto.lua
  16. 9 4
      script/module/anotherWorldBattle/AnotherWorldBattleCS.lua
  17. 4 1
      script/module/anotherWorldBattle/AnotherWorldBattleDefine.lua
  18. 61 4
      script/module/anotherWorldBattle/AnotherWorldBattleNS.lua
  19. 1 1
      script/module/anotherWorldBattle/Proto.lua
  20. 17 8
      script/module/combat/CombatLogic.lua
  21. 12 6
      script/module/combat/Proto.lua
  22. 2 2
      script/module/copy/CopyLogic.lua
  23. 10 1
      script/module/dailyTask/DailyTaskLogic.lua
  24. 30 30
      script/module/drill/DrillLogic.lua
  25. 5 0
      script/module/hero/HeroSeed.lua
  26. 5 0
      script/module/hero/HeroTianYuan.lua
  27. 2 2
      script/module/hero/Proto.lua
  28. 3 3
      script/module/present/Proto.lua
  29. 19 0
      script/module/scene/Handler.lua
  30. 1 1
      script/module/talisman/TalismanLogic.lua
  31. 17 0
      script/module/topup/GiftLogic.lua
  32. 10 0
      script/module/zhuanpan/Handler.lua
  33. 23 4
      script/module/zhuanpan/Proto.lua
  34. 474 23
      script/module/zhuanpan/ZhuanpanLogic.lua
  35. 2 0
      webServer/src/channels/factory/ChannelFactory.ts
  36. 327 0
      webServer/src/channels/handlers/HongKongTaiwanChannelHandler.ts
  37. 20 1
      webServer/src/config/channelConfig.ts
  38. 13 0
      webServer/src/config/thirdParams.ts

+ 3 - 0
script/common/Lang.lua

@@ -586,6 +586,8 @@ YUNYING_BUY_ERR_CNT = [[剩余购买次数不足]]
 YUNYING_BUY_ERR_VIP = [[VIP等级不足]]
 YUNYING_BUY_ERR_HAD = [[已购买]]
 
+MONEYTREE_IS_MAX = [[篮子已撑破, 不能再获得奖励了]]
+
 QQAPI_ERR_TIMEOUT = [[登录状态超时,请刷新游戏后再次尝试]]
 
 PRESENT_TUISONGLIBAO_NOTBUY = [[不可购买]]
@@ -845,6 +847,7 @@ AB_OCCUPY_POINT_MAX = [[当前占据的据点已达上限]]
 AB_HERO_IN_OTHER_POINT= [[英雄在其他据点上阵了]]
 AB_UPDATE_LINEUP_SUCC= [[更换防守阵容成功]]
 AB_POINT_CHALLENGING= [[已有玩家在进攻该据点]]
+AB_CHALLENGING_TIMES_NOT_ENOUGH= [[行动力不足]]
 
 AB_MIYAO_NOT_ENOUG = [[秘钥不足]]
 

+ 2 - 0
script/common/LogDefine.lua

@@ -272,6 +272,8 @@ DEFINE = {
 	UnlimitDrawCard			= 275,			 -- 无限高抽
 	OpenServerGiftPackage   = 276,			 -- 开服礼包
 	item_hecheng  			= 277,			 -- 物品合成
+	CommonActLoginGift		= 278,			 -- 通用节日活动 - 登录豪礼
+	CommonActMoneyTree		= 279,			 -- 通用节日活动 - 摇钱树
 
 	abs_totalReach          = 500,           -- 周期性活动累计
     abs_singleReach         = 501,           -- 周期性活动单次

+ 15 - 3
script/common/ProtoID.lua

@@ -1752,12 +1752,10 @@ _ENV[1793]="CG_SERVEERCOMMERCE_ACT_BATTLEGROUND_UPDATE_MATCHLIST"
 _ENV[1794]="CG_SERVEERCOMMERCE_ACT_BATTLEGROUND_ONECLICK_SWEEP"
 _ENV[1795]="GC_AB_TIPS"
 _ENV[1796]="CG_SERVEERCOMMERCE_ACT_BATTLEGROUND_ALREAFY_KNOW"
-
 _ENV[1797]="CG_HEROTY_QUERY"
 _ENV[1798]="GC_HEROTY_QUERY"
 _ENV[1799]="CG_HEROTY_POINT_UPGRADE"
 _ENV[1800]="CG_HEROTY_STAGE_UPGRADE"
-
 _ENV[1801]="CG_BREATHROUGHTHEME_LINEUP_QUERY"
 _ENV[1802]="GC_BREATHROUGHTHEME_LINEUP_QUERY"
 _ENV[1803]="CG_BREATHROUGHTHEME_BREAKTHROUGHREWARD_QUERY"
@@ -1767,4 +1765,18 @@ _ENV[1806]="GC_BREATHROUGHTHEME_ADVANCEDREWARD_QUERY"
 _ENV[1807]="CG_BREATHROUGHTHEME_GET_REWARD"
 _ENV[1808]="GC_BREATHROUGHTHEME_REDDOT"
 _ENV[1809]="CG_BREATHROUGHTHEME_SHOW_TIPS"
-_ENV[1810]="GC_BREATHROUGHTHEME_SHOW_TIPS"
+_ENV[1810]="GC_BREATHROUGHTHEME_SHOW_TIPS"
+_ENV[1811]="CG_ABS_COMONACT_LOGINGIFT_QUERY"
+_ENV[1812]="GC_ABS_COMONACT_LOGINGIFT_QUERY"
+_ENV[1813]="CG_ABS_COMONACT_LOGINGIFT_GET_REWARD"
+_ENV[1814]="GC_COMBAT_JiBan_DESC"
+_ENV[1815]="CG_ABS_COMONACT_MONEYTREE_QUERY"
+_ENV[1816]="GC_ABS_COMONACT_MONEYTREE_QUERY"
+_ENV[1817]="CG_ABS_COMONACT_MONEYTREE_LOTTERY"
+_ENV[1818]="GC_ABS_COMONACT_MONEYTREE_LOTTERY"
+_ENV[1819]="CG_ABS_COMONACT_MONEYTREE_GET_REWARD"
+
+_ENV[1896]="CG_ZHUANPAN_SUBSCRIBE_REWARD_QUERY"
+_ENV[1897]="GC_ZHUANPAN_SUBSCRIBE_REWARD_QUERY"
+_ENV[1898]="CG_ZHUANPAN_SUBSCRIBE_REWARD_GET"
+

+ 4 - 0
script/core/ObjHuman.lua

@@ -882,6 +882,10 @@ function updateDaily(human, isGm)
     ZhuanpanLogic.updateDaily(human)
     LostTempleLogic.updateDaily(human)
 
+	-- 美团2楼标记:每天重置一次
+	human.db.mtFrom2floor = 0
+	human.db.mtFrom2floorDay = nil
+
 	human.db.update_daily_time = os.time()
 	human.db.dailyTask = nil
 	human.db.dailyShareTask = nil

+ 7 - 0
script/core/Timer.lua

@@ -159,6 +159,13 @@ function doZhengDian(hour)
     UnionWarDBLogic.onHour(hour)
     AbsActLogic.onHour(hour)
     MoZhuMiddleLogic.onHour(hour)
+    
+    -- 晚上11点检查美团2楼玩家每日固定奖励领取情况
+    if hour == 23 then
+        local ZhuanpanLogic = require("zhuanpan.ZhuanpanLogic")
+        ZhuanpanLogic.checkMtFrom2floorDailyReward()
+    end
+    
     _G.collectgarbage("step", 100000)
 end
 

二進制
script/file3.tar.gz


+ 1 - 1
script/merge/MergeServerCS.lua

@@ -67,7 +67,7 @@ function AfterStart()
     end
 
     local updateTb, removeTb = generateArray()
-    if (not updateTb or not next(updateTb)) and (not removeTb or not next(removeTb)) then
+    if (not updateTb and not removeTb) or (not next(updateTb) and not next(removeTb)) then
         return
     end
 

+ 54 - 56
script/merge/MergeServerDefine.lua

@@ -74,6 +74,7 @@ CHANNEL_ID_SANLI_4399 = 6
 CHANNEL_ID_SANLI_360 = 7
 
 CHANNEL_ID_SANLI_SHIYOU = 10
+CHANNEL_ID_TAP_WX = 11
 
 -- 数据库名中字符串部分的格式
 DB_NAME_STR = "ckwy_fy_S"
@@ -98,6 +99,9 @@ CHANNEL_2_DBNUMBER = {
 
     -- 三狸视游
     [CHANNEL_ID_SANLI_SHIYOU] = 750001,
+
+    -- 微信/抖音小游戏
+    [CHANNEL_ID_TAP_WX] = 1150001,
 }
 
 -- 要进行合并的数据库所属渠道ID, 用于检测合错数据库的情况, 与下面的 MERGE_DB_TB 的值一一对应
@@ -105,50 +109,38 @@ CHANNEL_2_DBNUMBER = {
 MERGE_CHECK_TB = {
     -- 木子/Tap
     [CHANNEL_ID_TAP] = {
-        { {CHANNEL_ID_TAP, 101, 0},  {CHANNEL_ID_TAP, 111, 1} },
-        { {CHANNEL_ID_TAP, 103, 0},  {CHANNEL_ID_TAP, 113, 1} },
-        { {CHANNEL_ID_TAP, 105, 0},  {CHANNEL_ID_TAP, 115, 1} },
-        { {CHANNEL_ID_TAP, 107, 0},  {CHANNEL_ID_TAP, 117, 1} },
-        { {CHANNEL_ID_TAP, 109, 0},  {CHANNEL_ID_TAP, 119, 1} },
-
-        { {CHANNEL_ID_TAP, 121, 0},  {CHANNEL_ID_TAP, 131, 1} },
-        { {CHANNEL_ID_TAP, 123, 0},  {CHANNEL_ID_TAP, 133, 1} },
-        { {CHANNEL_ID_TAP, 125, 0},  {CHANNEL_ID_TAP, 135, 1} },
-        { {CHANNEL_ID_TAP, 127, 0},  {CHANNEL_ID_TAP, 137, 1} },
-        { {CHANNEL_ID_TAP, 129, 0},  {CHANNEL_ID_TAP, 139, 1} },
-
-        { {CHANNEL_ID_TAP, 141, 0},  {CHANNEL_ID_TAP, 151, 1} },
-        { {CHANNEL_ID_TAP, 143, 0},  {CHANNEL_ID_TAP, 153, 1} },
-        { {CHANNEL_ID_TAP, 145, 0},  {CHANNEL_ID_TAP, 155, 1} },
-        { {CHANNEL_ID_TAP, 147, 0},  {CHANNEL_ID_TAP, 157, 1} },
-        { {CHANNEL_ID_TAP, 149, 0},  {CHANNEL_ID_TAP, 159, 1} },
-
-        { {CHANNEL_ID_TAP, 201, 0},  {CHANNEL_ID_TAP, 202, 1} },
-        { {CHANNEL_ID_TAP, 203, 0},  {CHANNEL_ID_TAP, 204, 1} },
-        { {CHANNEL_ID_TAP, 205, 0},  {CHANNEL_ID_TAP, 206, 1} },
-        { {CHANNEL_ID_TAP, 207, 0},  {CHANNEL_ID_TAP, 208, 1} },
-        { {CHANNEL_ID_TAP, 209, 0},  {CHANNEL_ID_TAP, 210, 1} },
+        { {CHANNEL_ID_TAP, 161, 0},  {CHANNEL_ID_TAP, 171, 1} },
+        { {CHANNEL_ID_TAP, 163, 0},  {CHANNEL_ID_TAP, 173, 1} },
+        { {CHANNEL_ID_TAP, 165, 0},  {CHANNEL_ID_TAP, 175, 1} },
+        { {CHANNEL_ID_TAP, 167, 0},  {CHANNEL_ID_TAP, 177, 1} },
+        { {CHANNEL_ID_TAP, 169, 0},  {CHANNEL_ID_TAP, 179, 1} },
+
+        { {CHANNEL_ID_TAP, 211, 0},  {CHANNEL_ID_TAP, 212, 1} },
+        { {CHANNEL_ID_TAP, 213, 0},  {CHANNEL_ID_TAP, 214, 1} },
+        { {CHANNEL_ID_TAP, 215, 0},  {CHANNEL_ID_TAP, 216, 1} },
+        { {CHANNEL_ID_TAP, 217, 0},  {CHANNEL_ID_TAP, 218, 1} },
+        { {CHANNEL_ID_TAP, 219, 0},  {CHANNEL_ID_TAP, 220, 1} },
 
     },
 
     -- 三狸功夫
-    [CHANNEL_ID_SANLI_ANDOIRD] = {
-        { {CHANNEL_ID_SANLI_ANDOIRD, 121, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 123, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 125, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 127, 1}, {CHANNEL_ID_SANLI_ANDOIRD, 129, 1} },
+    -- [CHANNEL_ID_SANLI_ANDOIRD] = {
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 121, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 123, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 125, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 127, 1}, {CHANNEL_ID_SANLI_ANDOIRD, 129, 1} },
 
-        { {CHANNEL_ID_SANLI_ANDOIRD, 131, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 133, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 135, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 137, 1}, {CHANNEL_ID_SANLI_ANDOIRD, 139, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 131, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 133, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 135, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 137, 1}, {CHANNEL_ID_SANLI_ANDOIRD, 139, 1} },
 
-        { {CHANNEL_ID_SANLI_ANDOIRD, 141, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 143, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 145, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 147, 1}, {CHANNEL_ID_SANLI_ANDOIRD, 149, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 141, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 143, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 145, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 147, 1}, {CHANNEL_ID_SANLI_ANDOIRD, 149, 1} },
 
-        { {CHANNEL_ID_SANLI_ANDOIRD, 191, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 192, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 193, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 194, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 195, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 196, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 197, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 198, 1} },
-        { {CHANNEL_ID_SANLI_ANDOIRD, 199, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 200, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 191, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 192, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 193, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 194, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 195, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 196, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 197, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 198, 1} },
+    --     { {CHANNEL_ID_SANLI_ANDOIRD, 199, 0}, {CHANNEL_ID_SANLI_ANDOIRD, 200, 1} },
         
-    },
+    -- },
 
 
     -- 三狸H5
@@ -171,26 +163,32 @@ MERGE_CHECK_TB = {
 
 
     -- 三狸视游
-    [CHANNEL_ID_SANLI_SHIYOU] = {
-        { {CHANNEL_ID_SANLI_SHIYOU, 1, 0},  {CHANNEL_ID_SANLI_SHIYOU, 3, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 5, 0},  {CHANNEL_ID_SANLI_SHIYOU, 7, 1}, {CHANNEL_ID_SANLI_SHIYOU, 9, 1} },
-
-        { {CHANNEL_ID_SANLI_SHIYOU, 11, 0},  {CHANNEL_ID_SANLI_SHIYOU, 12, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 13, 0},  {CHANNEL_ID_SANLI_SHIYOU, 14, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 15, 0},  {CHANNEL_ID_SANLI_SHIYOU, 16, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 17, 0},  {CHANNEL_ID_SANLI_SHIYOU, 18, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 19, 0},  {CHANNEL_ID_SANLI_SHIYOU, 20, 1} },
-
-        { {CHANNEL_ID_SANLI_SHIYOU, 81, 0},  {CHANNEL_ID_SANLI_SHIYOU, 82, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 83, 0},  {CHANNEL_ID_SANLI_SHIYOU, 84, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 85, 0},  {CHANNEL_ID_SANLI_SHIYOU, 86, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 87, 0},  {CHANNEL_ID_SANLI_SHIYOU, 88, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 89, 0},  {CHANNEL_ID_SANLI_SHIYOU, 90, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 91, 0},  {CHANNEL_ID_SANLI_SHIYOU, 92, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 93, 0},  {CHANNEL_ID_SANLI_SHIYOU, 94, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 95, 0},  {CHANNEL_ID_SANLI_SHIYOU, 96, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 97, 0},  {CHANNEL_ID_SANLI_SHIYOU, 98, 1} },
-        { {CHANNEL_ID_SANLI_SHIYOU, 99, 0},  {CHANNEL_ID_SANLI_SHIYOU, 100, 1} },
+    -- [CHANNEL_ID_SANLI_SHIYOU] = {
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 1, 0},  {CHANNEL_ID_SANLI_SHIYOU, 3, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 5, 0},  {CHANNEL_ID_SANLI_SHIYOU, 7, 1}, {CHANNEL_ID_SANLI_SHIYOU, 9, 1} },
+
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 11, 0},  {CHANNEL_ID_SANLI_SHIYOU, 12, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 13, 0},  {CHANNEL_ID_SANLI_SHIYOU, 14, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 15, 0},  {CHANNEL_ID_SANLI_SHIYOU, 16, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 17, 0},  {CHANNEL_ID_SANLI_SHIYOU, 18, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 19, 0},  {CHANNEL_ID_SANLI_SHIYOU, 20, 1} },
+
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 81, 0},  {CHANNEL_ID_SANLI_SHIYOU, 82, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 83, 0},  {CHANNEL_ID_SANLI_SHIYOU, 84, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 85, 0},  {CHANNEL_ID_SANLI_SHIYOU, 86, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 87, 0},  {CHANNEL_ID_SANLI_SHIYOU, 88, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 89, 0},  {CHANNEL_ID_SANLI_SHIYOU, 90, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 91, 0},  {CHANNEL_ID_SANLI_SHIYOU, 92, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 93, 0},  {CHANNEL_ID_SANLI_SHIYOU, 94, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 95, 0},  {CHANNEL_ID_SANLI_SHIYOU, 96, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 97, 0},  {CHANNEL_ID_SANLI_SHIYOU, 98, 1} },
+    --     { {CHANNEL_ID_SANLI_SHIYOU, 99, 0},  {CHANNEL_ID_SANLI_SHIYOU, 100, 1} },
+    -- },
+
+    -- 微信/抖音小游戏
+    [CHANNEL_ID_TAP_WX] = {
+        { {CHANNEL_ID_TAP_WX, 1, 0},  {CHANNEL_ID_TAP_WX, 2, 1}, {CHANNEL_ID_TAP_WX, 3, 1}, {CHANNEL_ID_TAP_WX, 4, 1}, {CHANNEL_ID_TAP_WX, 5, 1}},
+        { {CHANNEL_ID_TAP_WX, 6, 0},  {CHANNEL_ID_TAP_WX, 7, 1}, {CHANNEL_ID_TAP_WX, 8, 1}, {CHANNEL_ID_TAP_WX, 9, 1}, {CHANNEL_ID_TAP_WX, 10, 1}},
     },
 }
 

+ 2 - 2
script/merge/MergeServerLogic.lua

@@ -1,7 +1,7 @@
 --合服逻辑
 
 --[=[
-    0.一般在跨服上进行合服, 所以需要把merge目录下用到文件更新到跨服上
+    0.一般在跨服上进行合服, 所以需要把merge目录下所有文件更新到跨服上
 
     1.更新MergeServerDefine.MERGE_DB_TB中要合并的数据库, 并备份数据库, 方法见 MergeServerDefine
 
@@ -14,7 +14,7 @@
 
     5.查看日志oss_merge, 是否有报错, 如果有报错则还原数据, 恢复见方法见 MergeServerDefine
 
-    6.修改mysql区服列表的 port, dbName, megre_server字段, 被合服的 port 和 dbname 要改为目标服一样, 被合服的 megre_server字段更新为宿主服的sid
+    6.修改mysql区服列表的 port, dbName, megre_server字段, 被合服的 port 和 dbname 要改为目标服一样, 被合服的 megre_server字段更新为1
         sql见sdk数据库下查询中的updateMergeServerData(需要修改数据库范围)
 
     7.修改linux上被合服的bin*文件名, 防止 start.sh 启动时会把被合服也启动起来。脚本见/server/changebinName.sh  (需要修改脚本中范围)

+ 3 - 3
script/module/WeekendLoopActivity/Proto.lua

@@ -115,18 +115,18 @@ CG_WEEKLOOP_ACT_CARDGETPRIZE = {}
 ----------------------------------------- 累计充值相关协议开始 -------------------------------------
 WeekLoopActRankList = {
     {"rank",                1,         "int"},      -- 名次(-1未上榜)
-    {"rankNeedValue",       1,         "int"},      -- 上榜条件值
+    {"rankNeedValue",       1,         "double"},      -- 上榜条件值
     {"items",               5,         ItemData},   -- 排行名次奖励列表    
     {"uid",                 1,         "string"},   -- 用户id(未上榜为"")
     {"name",                1,         "string"},   -- 角色名(未上榜为"")
     {"head",                1,         "int"},      -- 头像(未上榜为-1)  
-    {"rankValue",           1,         "int"},      -- 当前排名值     
+    {"rankValue",           1,         "double"},      -- 当前排名值     
     {"headFrame",           1,         "int"},      -- 头像框(未上榜为-1)     
 }
 
 WeekLoopActOnwerData = {
     {"rank",             1,          "int"},        -- 名次(-1未上榜)
-    {"rankValue",        1,          "int"},        -- 当前排名值
+    {"rankValue",        1,          "double"},        -- 当前排名值
     {"items",            5,         ItemData},      -- 档位奖励列表(未上榜没有奖励) 
 }
 

+ 7 - 1
script/module/absAct/AbsActLogic.lua

@@ -85,7 +85,7 @@ function onLogin(human)
     end
     -- 是否有新增活动
     for k,v in pairs(absActConfig) do 
-        local ok,realEndTime = isStarted(human,k)
+        local ok,realEndTime, realStartTime = isStarted(human,k)
         if ok and  human.db.absAct[k] then
             --- 同步结束时间
             local nowTime = os.time()
@@ -93,6 +93,10 @@ function onLogin(human)
             if  finishTime >= nowTime and  finishTime ~= realEndTime then
                 human.db.absAct[k].finish = realEndTime
             end
+
+            if not human.db.absAct[k].realStartTime or human.db.absAct[k].realStartTime ~= realStartTime then
+                human.db.absAct[k].realStartTime = realStartTime
+            end
         end
         if ok and not human.db.absAct[k] then 
             human.db.absAct[k] = {}
@@ -101,6 +105,8 @@ function onLogin(human)
                 human.db.absAct[k] = f.genAbsActData(v)
             end
             human.db.absAct[k].finish = realEndTime -- 记录活动结束时间
+
+            human.db.absAct[k].realStartTime = realStartTime
         end
     end
     -- 登录奖励重置

+ 275 - 0
script/module/absAct/CommonActLoginGift.lua

@@ -0,0 +1,275 @@
+-- 通用节日活动 - 登录豪礼
+-- db
+--[=[
+    human.db.absAct[actId] = {
+        getRecordData = {
+            [gitftType] = {
+                [day1] = true,
+                [day3] = true,
+            },
+        },
+
+        buyGiftType = nil, -- 已经购买的礼包类型
+    }   
+]=]--
+
+local Msg = require("core.Msg")
+local Grid = require("bag.Grid")
+local BagLogic = require("bag.BagLogic")
+local AbsActLogic = require("absAct.AbsActLogic")
+local Broadcast = require("broadcast.Broadcast")
+local Lang = require("common.Lang")
+local LoginGiftConfig = require("excel.commonact").loginreward
+local CommonDefine = require("common.CommonDefine")
+local Util = require("common.Util")
+local YunYingLogic = require("yunying.YunYingLogic")
+local AbsActExcel = require("excel.absAct")
+local BuyLogic = require("topup.BuyLogic")
+
+local LOGTYPE = "CommonActLoginGift"
+local COMMONACT_LOGINGIFT_ID = 7505
+
+
+local function getActData(human)
+    return human.db.absAct[COMMONACT_LOGINGIFT_ID]
+end
+
+local function updateGetRecordData(human, giftType, dayIdx)
+    human.db.absAct[COMMONACT_LOGINGIFT_ID].getRecordData = human.db.absAct[COMMONACT_LOGINGIFT_ID].getRecordData or {}
+    human.db.absAct[COMMONACT_LOGINGIFT_ID].getRecordData[giftType] = human.db.absAct[COMMONACT_LOGINGIFT_ID].getRecordData[giftType] or {}
+    human.db.absAct[COMMONACT_LOGINGIFT_ID].getRecordData[giftType][dayIdx] = true
+end
+
+local function updateBuyGiftType(human, giftType)
+    human.db.absAct[COMMONACT_LOGINGIFT_ID].buyGiftType = giftType
+end
+
+
+
+local function isOpenAct(human, funcID)
+    return AbsActLogic.isStarted(human, funcID)
+end
+
+local function transformCfg()
+    local tb = {}
+    for _, giftCfg in ipairs(LoginGiftConfig) do
+        if not tb[giftCfg.giftType] then
+            tb[giftCfg.giftType] = {}
+        end
+        local t = tb[giftCfg.giftType]
+        t[#t+1] = giftCfg
+    end
+
+    return tb
+end
+
+local function getAwardState(human, giftType, dayIdx)
+    local actData = getActData(human)
+    local diffDays = Util.diffDay(actData.realStartTime) + 1
+    local getRecordData = actData and actData.getRecordData or {}
+    local nowGiftTypeGetRecord = getRecordData[giftType] or {}
+    local buyGiftType = actData and actData.buyGiftType
+
+    local isBuy = true
+    local state = CommonDefine.COMMON_PRIZE_STATE_CANGET
+
+    if giftType ~= 1 and (not buyGiftType or buyGiftType ~= giftType) then -- 需要充值的礼包类型
+        state = CommonDefine.COMMON_PRIZE_STATE_NOGET
+        isBuy = false
+    end
+
+    if isBuy and diffDays >= dayIdx and nowGiftTypeGetRecord[dayIdx] then
+        state = CommonDefine.COMMON_PRIZE_STATE_GET
+    end
+
+    return state
+end
+
+-- 检查是否有可领取的奖励
+local function isCanGet(human)
+    local type_2_cfg = transformCfg()
+
+    for giftType, giftCfgArr in pairs(type_2_cfg) do
+        for _, giftCfg in ipairs(giftCfgArr) do
+            local state = getAwardState(human, giftType, giftCfg.dayIdx)
+            if state == CommonDefine.COMMON_PRIZE_STATE_CANGET then
+                return true
+            end
+        end
+    end
+
+    return false
+end
+-- 填充协议结构
+local function populateGiftMsg(human, net, giftCfg, nowGiftType)
+    net.reward[0] = #giftCfg.reward
+    for i, itemInfo in ipairs(giftCfg.reward) do
+        Grid.makeItem(net.reward[i], itemInfo[1], itemInfo[2])
+    end
+
+    net.reallyDays = giftCfg.dayIdx
+    net.rewardState = getAwardState(human, nowGiftType, giftCfg.dayIdx)
+    BuyLogic.fontBuyItem(human, net.buyMsg, giftCfg.buyId)
+    net.giftType = nowGiftType
+end
+-- 主动刷新一次红点
+local function updateRedDot(human)
+    YunYingLogic.sendBanner(human)
+    local config = AbsActExcel.absActivity[COMMONACT_LOGINGIFT_ID]
+    YunYingLogic.sendGroupUpdate(YYInfo[COMMONACT_LOGINGIFT_ID], human, config.panelID)
+end
+
+
+
+
+function isRed(human, funcConfig)
+    local state = isOpenAct(human, funcConfig and funcConfig.funcID)
+    if not state then
+        return false
+    end
+
+    return isCanGet(human)
+end
+
+function isOpen(human, YYInfo, funcConfig)
+    return isOpenAct(human, funcConfig and funcConfig.funcID)
+end
+
+function isActive(human, YYInfo, funcConfig)
+    return not isOpen(human, YYInfo, funcConfig)
+end
+
+function onCharge(human, price, funcID, buyID, buyNum)
+    local actData = getActData(human)
+    local buyGiftType = actData and actData.buyGiftType
+    if buyGiftType then
+        return
+    end
+
+    local type_2_cfg = transformCfg()
+
+    for giftType, giftCfgArr in pairs(type_2_cfg) do
+        local singleCfg = giftCfgArr[1]
+        if singleCfg.buyId == buyID then
+            updateBuyGiftType(human, giftType)
+            updateRedDot(human)
+            CommonActLoginGift_Query(human)
+            break
+        end
+    end
+end
+
+function updateDaily(human, funcID)
+    if isOpenAct(human, COMMONACT_LOGINGIFT_ID) then
+        return
+    end
+
+    updateRedDot(human)
+end
+
+
+
+
+-- 查询
+function CommonActLoginGift_Query(human)
+    local msgRet = Msg.gc.GC_ABS_COMONACT_LOGINGIFT_QUERY
+    msgRet.giftDatafree[0] = 0
+    msgRet.giftData68[0] = 0
+    msgRet.giftData128[0] = 0
+    msgRet.giftData328[0] = 0
+    msgRet.giftData648[0] = 0
+    msgRet.isEnd = 0
+    msgRet.isStart = 1
+
+    local len, msgOnceLen = 0, 5
+    local type_2_cfg = transformCfg()
+    local configNum = type_2_cfg[1] and #type_2_cfg[1] or 0
+    local num = configNum
+
+    for i=1, configNum do
+        len = len + 1
+
+        if type_2_cfg[1] then
+            msgRet.giftDatafree[0] = len
+            populateGiftMsg(human, msgRet.giftDatafree[len], type_2_cfg[1][i], 1)
+        end
+
+        if type_2_cfg[2] then
+            msgRet.giftData68[0] = len
+            populateGiftMsg(human, msgRet.giftData68[len], type_2_cfg[2][i], 2)
+        end
+
+        if type_2_cfg[3] then
+            msgRet.giftData128[0] = len
+            populateGiftMsg(human, msgRet.giftData128[len], type_2_cfg[3][i], 3)
+        end
+
+        if type_2_cfg[4] then
+            msgRet.giftData328[0] = len
+            populateGiftMsg(human, msgRet.giftData328[len], type_2_cfg[4][i], 4)
+        end
+
+        if type_2_cfg[5] then
+            msgRet.giftData648[0] = len
+            populateGiftMsg(human, msgRet.giftData648[len], type_2_cfg[5][i], 5)
+        end
+
+        if len >= msgOnceLen then
+            num = num - len
+            if num <= 0 then
+                msgRet.isEnd = 1
+                return Msg.send(msgRet, human.fd)
+            end
+
+            Msg.send(msgRet, human.fd)
+            len = 0
+            msgRet.isStart = 0
+        end
+    end
+
+    if len > 0 then
+        msgRet.isEnd = 1
+        Msg.send(msgRet, human.fd)
+    end
+end
+
+-- 领取奖励
+function CommonActLoginGift_GetReward(human, targetGiftType)
+    local type_2_cfg = transformCfg()
+    local giftArr = type_2_cfg[targetGiftType]
+    if not giftArr then
+        return Broadcast.sendErr(human, Lang.COMMON_ARGUMENT_ERROR)
+    end
+
+    local actData = getActData(human)
+    local buyGiftType = actData and actData.buyGiftType
+    if targetGiftType ~= 1 and (not buyGiftType or buyGiftType ~= targetGiftType) then
+        return Broadcast.sendErr(human, Lang.YUNYING_GET_ERR_CONDITION)
+    end
+
+    local itemArr = {}
+    for _, giftCfg in ipairs(giftArr) do
+        local state = getAwardState(human, targetGiftType, giftCfg.dayIdx)
+        if state == CommonDefine.COMMON_PRIZE_STATE_CANGET then
+            for _, itemInfo in ipairs(giftCfg.reward) do
+                itemArr[#itemArr+1] = { itemInfo[1], itemInfo[2] }
+            end
+
+            -- 记录领取
+            updateGetRecordData(human, targetGiftType, giftCfg.dayIdx)
+        end
+    end
+
+    if #itemArr == 0 then
+        return Broadcast.sendErr(human, Lang.HO_NO_CAN_GET)
+    end
+
+    -- 发放道具
+    BagLogic.addItemList(human, itemArr, LOGTYPE)
+
+    -- 更红点
+    updateRedDot(human)
+
+    -- 推送最新数给客户端
+    CommonActLoginGift_Query(human)
+end

+ 279 - 0
script/module/absAct/CommonActMoneyTree.lua

@@ -0,0 +1,279 @@
+-- 通用节日活动 - 摇钱树
+-- db
+--[=[
+    human.db.absAct[actId] = {
+        basketItemArr = nil, --篮子里的道具idx列表
+
+        lotteryTimes = nil, -- 抽奖次数
+    }   
+]=]--
+
+local Msg = require("core.Msg")
+local Grid = require("bag.Grid")
+local BagLogic = require("bag.BagLogic")
+local AbsActLogic = require("absAct.AbsActLogic")
+local Broadcast = require("broadcast.Broadcast")
+local Lang = require("common.Lang")
+local MoneyTreeConfig = require("excel.commonact").treereward
+local YunYingLogic = require("yunying.YunYingLogic")
+local AbsActExcel = require("excel.absAct")
+
+
+local LOGTYPE = "CommonActMoneyTree" -- 日志标识
+local COMMONACT_MONEYTREE_ID = 7506  -- 活动Id
+local BASKET_WEIGHT = 100           -- 篮子可承受的总重量
+
+
+local function getData(human)
+    return human.db.absAct[COMMONACT_MONEYTREE_ID]
+end
+
+local function updateLotteryTimes(human, val)
+    local actData = getData(human)
+    actData.lotteryTimes = (actData.lotteryTimes or 0) + val
+end
+
+local function insertBasketItemArr(human, itemIdx)
+    local actData = getData(human)
+    actData.basketItemArr = actData.basketItemArr or {}
+    table.insert(actData.basketItemArr, itemIdx)
+end
+
+local function updateBasketItemArr(human, newBasketItemArr)
+    local actData = getData(human)
+    actData.basketItemArr = newBasketItemArr
+end
+
+local function resetBasketArr(human)
+    local actData = getData(human)
+    actData.basketItemArr = nil
+end
+
+
+
+local function isOpenAct(human, funcID)
+    return AbsActLogic.isStarted(human, funcID)
+end
+
+-- 计算篮子中所有道具的总重量
+local function calcBasketWeight(basketItemArr)
+    local weightSum = 0
+
+    for _, cfgIdx in ipairs(basketItemArr or {}) do
+        local itemCfg = MoneyTreeConfig[cfgIdx]
+        weightSum = weightSum + itemCfg.value
+    end
+
+    return weightSum
+end
+
+-- 抽奖
+local function lottery()
+    local totalWeight = 0
+    for _, itemCfg in ipairs(MoneyTreeConfig) do
+        totalWeight = totalWeight + itemCfg.nWeight
+    end
+
+    local finalIdx = 0
+    local weight = 0
+    local randWeight = math.random(0, totalWeight)
+    for i, itemCfg in ipairs(MoneyTreeConfig) do
+        weight = weight + itemCfg.nWeight
+        if randWeight <= weight then
+            finalIdx = i
+            break
+        end
+    end
+
+    return finalIdx
+end
+
+-- 将篮子中每个道具都随机一次, 获得最新的篮子道具
+local function randBasketItem(basketItemArr)
+    local val = 50
+    local newBasketItemArr = {}
+    for _,itemCfgIdx in ipairs(basketItemArr) do
+        local randVal = math.random(1, 100)
+        if randVal >= val then
+            newBasketItemArr[#newBasketItemArr+1] = itemCfgIdx
+        end
+    end
+
+    return newBasketItemArr
+end
+
+-- 主动刷新一次红点
+local function updateRedDot(human)
+    YunYingLogic.sendBanner(human)
+    local config = AbsActExcel.absActivity[COMMONACT_MONEYTREE_ID]
+    YunYingLogic.sendGroupUpdate(YYInfo[COMMONACT_MONEYTREE_ID], human, config.panelID)
+end
+
+
+
+function isRed(human, funcConfig)
+    local state = isOpenAct(human, funcConfig and funcConfig.funcID)
+    if not state then
+        return false
+    end
+
+    local actData = getData(human)
+    if (actData.lotteryTimes or 0) > 0 then
+        return true
+    end
+
+    return false
+end
+
+function isOpen(human, YYInfo, funcConfig)
+    return isOpenAct(human, funcConfig and funcConfig.funcID)
+end
+
+function isActive(human, YYInfo, funcConfig)
+    return not isOpen(human, YYInfo, funcConfig)
+end
+
+-- 日常任务活跃值达标
+function CompleteHuoYueTask(human)
+    if not isOpenAct(human, COMMONACT_MONEYTREE_ID) then
+        return
+    end
+
+    updateLotteryTimes(human, 1)
+
+    updateRedDot(human)
+end
+
+function updateDaily(human, funcID)
+    if not isOpenAct(human, funcID) then
+        return
+    end
+
+    updateLotteryTimes(human, 1)
+
+    updateRedDot(human)
+end
+
+
+
+-- 查询
+function CommonActMoneyTree_Query(human)
+    local msgRet = Msg.gc.GC_ABS_COMONACT_MONEYTREE_QUERY
+    msgRet.rewardBasket[0] = 0
+    msgRet.weighMax = BASKET_WEIGHT
+    msgRet.weighNow = 0
+    msgRet.lotteryTimes = 0
+    msgRet.isStart = 1
+    msgRet.isEnd = 0
+
+    local actData = getData(human)
+
+    if actData.lotteryTimes then
+        msgRet.lotteryTimes = actData.lotteryTimes
+    end
+
+    if actData.basketItemArr then
+        msgRet.weighNow = calcBasketWeight(actData.basketItemArr)
+    end
+
+    local itemNum = actData.basketItemArr and #actData.basketItemArr or 0
+    if itemNum == 0 then
+        return Msg.send(msgRet, human.fd)
+    end
+
+    local len, msgOneceMaxLen = 0, 20
+
+    for _, itemCfgIdx in ipairs(actData.basketItemArr) do
+        len = len + 1
+        msgRet.rewardBasket[0] = len
+
+        local itemCfg = MoneyTreeConfig[itemCfgIdx].reward
+        Grid.makeItem(msgRet.rewardBasket[len], itemCfg[1], itemCfg[2])
+
+        if len >= msgOneceMaxLen then
+            itemNum = itemNum - len
+            if itemNum <= 0 then
+                msgRet.isEnd = 1
+                return Msg.send(msgRet, human.fd)
+            end
+
+            Msg.send(msgRet, human.fd)
+            len = 0
+            msgRet.isStart = 0
+        end
+    end
+
+    if len > 0 then
+        msgRet.isEnd = 1
+        Msg.send(msgRet, human.fd)
+    end
+end
+
+-- 抽奖
+function CommonActMoneyTree_Lottery(human)
+    if not isOpenAct(human, COMMONACT_MONEYTREE_ID) then
+        return Broadcast.sendErr(human, Lang.YUNYING_ERR_TIME)
+    end
+
+    local actData = getData(human)
+    if (actData.lotteryTimes or 0) <= 0 then
+        return Broadcast.sendErr(human, Lang.JINBI_EXCHANGE_ERR_CNT)
+    end
+
+    if actData.basketItemArr then
+        local weightNow = calcBasketWeight(actData.basketItemArr)
+        if weightNow > BASKET_WEIGHT then
+            return Broadcast.sendErr(human, Lang.MONEYTREE_IS_MAX)
+        end
+    end
+
+    -- 扣除次数
+    updateLotteryTimes(human, -1)
+
+    local finalIdx = lottery()
+    if finalIdx == 0 then
+        return Broadcast.sendErr(human, Lang.COMMON_COMFIG_ERROR)
+    end
+
+    -- 检查当前篮子中道具重量是否超过限定重量, 如果超过则当前篮子中每个道具都有50%几率失去
+    insertBasketItemArr(human, finalIdx)
+    local weightNow = calcBasketWeight(actData.basketItemArr)
+    if weightNow > BASKET_WEIGHT then
+        local newBasketItemArr = randBasketItem(actData.basketItemArr)
+        updateBasketItemArr(human, newBasketItemArr)
+        CommonActMoneyTree_Query(human)
+    end
+
+    local msgRet = Msg.gc.GC_ABS_COMONACT_MONEYTREE_LOTTERY
+    local itemCfg = MoneyTreeConfig[finalIdx]
+    Grid.makeItem(msgRet.reward, itemCfg.reward[1], itemCfg.reward[2])
+    Msg.send(msgRet, human.fd)
+end
+
+-- 领取奖励
+function CommonActMoneyTree_GetReward(human)
+    if not isOpenAct(human, COMMONACT_MONEYTREE_ID) then
+        return Broadcast.sendErr(human, Lang.YUNYING_ERR_TIME)
+    end
+
+    local actData = getData(human)
+
+    if not actData.basketItemArr or not next(actData.basketItemArr) then
+        return Broadcast.sendErr(human, Lang.SHARE_GROUP_GET_ERR_CNT)
+    end
+
+    local itemList = {}
+    for _, itemCfgIdx in ipairs(actData.basketItemArr) do
+        local itemCfg = MoneyTreeConfig[itemCfgIdx].reward
+        local itemId, itemNum = itemCfg[1], itemCfg[2]
+        itemList[itemId] = (itemList[itemId] or 0) + itemNum
+    end
+
+    -- 重置篮子中道具数据
+    resetBasketArr(human)
+
+    -- 发放道具
+    BagLogic.addItemList(human, itemList, LOGTYPE)
+
+    CommonActMoneyTree_Query(human)
+end

+ 28 - 0
script/module/absAct/Handler.lua

@@ -50,6 +50,8 @@ local CommonActBoss = require("absAct.CommonActBoss")
 local CommonActShop = require("absAct.CommonActShop")
 local CommonActCharge = require("absAct.CommonActCharge")
 local CommonActFindTreasure = require("absAct.CommonActFindTreasure")
+local CommonActLoginGift = require("absAct.CommonActLoginGift")
+local CommonActMoneyTree = require("absAct.CommonActMoneyTree")
 
 function CG_ABS_ACT_DETAIL_QUERY(human, msg)
     -- local Log = require("common.Log")
@@ -505,4 +507,30 @@ end
 
 function CG_ABS_FESTIVAL_SEVENDAY_CARD_DO(human, msg)
     CommonActFindTreasure.CommonActFindTreasure_Do(human, msg.nType)
+end
+
+
+----------------------------------------------------通用节日活动 — 登录豪礼 --------------------------------------------------
+
+function CG_ABS_COMONACT_LOGINGIFT_QUERY(human, msg)
+    CommonActLoginGift.CommonActLoginGift_Query(human)
+end
+
+function CG_ABS_COMONACT_LOGINGIFT_GET_REWARD(human, msg)
+    CommonActLoginGift.CommonActLoginGift_GetReward(human, msg.giftType)
+end
+
+
+----------------------------------------------------通用节日活动 — 摇钱树 --------------------------------------------------
+
+function CG_ABS_COMONACT_MONEYTREE_QUERY(human, msg)
+    CommonActMoneyTree.CommonActLoginGift_Query(human)
+end
+
+function CG_ABS_COMONACT_MONEYTREE_LOTTERY(human, msg)
+    CommonActMoneyTree.CommonActMoneyTree_Lottery(human)
+end
+
+function CG_ABS_COMONACT_MONEYTREE_GET_REWARD(human, msg)
+    CommonActMoneyTree.CommonActMoneyTree_GetReward(human)
 end

+ 56 - 5
script/module/absAct/Proto.lua

@@ -625,7 +625,7 @@ NdCustomNet = {
     {"id",1,"int"},            -- id
     {"fixed",2,ItemData},       -- 固定道具
     {"buyItem",2,BuyItem},      -- 购买信息  由于需求文档中表示有可购买和免费两种,故用数组,方便不填数据
-    {"first",10,ItemData},      -- 道具库1
+    {"first",15,ItemData},      -- 道具库1
     {"second",10,ItemData},     -- 道具库2
     {"third",10,ItemData},      -- 道具库3
     {"one",2,ItemData},         -- 道具1
@@ -1571,7 +1571,7 @@ CG_ABS_FESTIVAL_BOSS_REWARD = {}
 
 -- 回复奖励信息
 GC_ABS_FESTIVAL_BOSS_REWARD = {
-    {"tPrize",      25,     BOSSPRIZEINFO},      -- 奖励信息
+    {"tPrize",      30,     BOSSPRIZEINFO},      -- 奖励信息
 }
 
 -- 领取奖励
@@ -1583,7 +1583,7 @@ CG_ABS_FESTIVAL_SHOP_QUERY = {}
 
 -- 回复商店数据
 GC_ABS_FESTIVAL_SHOP_QUERY = {
-    {"tGoodInfo",   20,      GOODS},         -- 物品信息
+    {"tGoodInfo",   45,      GOODS},         -- 物品信息
 }
 
 -- 购买物品
@@ -1606,7 +1606,7 @@ CG_ABS_FESTIVAL_RECHARGE_QUERY = {}
 -- 回复累充数据
 GC_ABS_FESTIVAL_RECHARGE_QUERY = {
     {"nNowCharge",  1,      "double"},         -- 当前充值金额
-    {"tPrize",      25,     FESTIVALRECHARGEINFO},      -- 奖励信息
+    {"tPrize",      30,     FESTIVALRECHARGEINFO},      -- 奖励信息
 }
 
 -- 领取奖励
@@ -1636,4 +1636,55 @@ GC_ABS_FESTIVAL_SEVENDAY_CARD_QUERY = {
 -- 抽取
 CG_ABS_FESTIVAL_SEVENDAY_CARD_DO = {
     {"nType",       1,      "byte"},        -- 抽取类型 1-单抽 2-十连
-}
+}
+
+
+---------------------节日活动 - 登录豪礼 -------------------
+
+COMONACT_LOGINGIFT_INFO = {
+    {"reward",          2,      ItemData},      -- 奖励信息
+    {"reallyDays",      1,      "short"},       -- 第几天
+    {"rewardState",     1,      "byte"},        -- 奖励状态, 0-不可领, 1-可领, 2-已领
+    {"buyMsg",	        1,      BuyItem},       -- buyID
+    {"giftType",        1,      "byte"},        -- 礼包类型, 1-登录豪礼, 2-68礼包, 3-128礼包, 4-328礼包, 5-648礼包
+}
+
+-- 查询
+CG_ABS_COMONACT_LOGINGIFT_QUERY = {}
+GC_ABS_COMONACT_LOGINGIFT_QUERY = {
+    {"giftDatafree",    5,     COMONACT_LOGINGIFT_INFO},      -- 奖励信息
+    {"giftData68",      5,     COMONACT_LOGINGIFT_INFO},      -- 奖励信息
+    {"giftData128",     5,     COMONACT_LOGINGIFT_INFO},      -- 奖励信息
+    {"giftData328",     5,     COMONACT_LOGINGIFT_INFO},      -- 奖励信息
+    {"giftData648",     5,     COMONACT_LOGINGIFT_INFO},      -- 奖励信息
+    {"isEnd",           1,      "byte"},                      -- 是否发完数据, 0-否, 1-是
+    {"isStart",         1,      "byte"},                      -- 是否是第一段数据, 0-否, 1-是
+}
+
+-- 领奖
+CG_ABS_COMONACT_LOGINGIFT_GET_REWARD= {
+    {"giftType",        1,      "byte"},        -- 礼包类型, 1-登录豪礼, 2-68礼包, 3-128礼包, 4-328礼包, 5-648礼包
+}
+
+
+---------------------节日活动 - 摇钱树 -------------------
+
+-- 查询
+CG_ABS_COMONACT_MONEYTREE_QUERY = {}
+GC_ABS_COMONACT_MONEYTREE_QUERY = {
+    {"rewardBasket",        20,      ItemData},      -- 篮子中道具
+    {"weighMax",            1,      "short"},        -- 篮子可承受总重量
+    {"weighNow",            1,      "short"},        -- 当前篮子中重量
+    {"lotteryTimes",        1,      "short"},        -- 可抽取次数
+    {"isStart",             1,      "byte"},         -- 是否是第一段数据, 0-否, 1-是
+    {"isEnd",               1,      "byte"},         -- 是否发完数据, 0-否, 1-是
+}
+
+-- 抽奖
+CG_ABS_COMONACT_MONEYTREE_LOTTERY = {}
+GC_ABS_COMONACT_MONEYTREE_LOTTERY = {
+    {"reward",          1,      ItemData},      -- 获得的道具
+}
+
+-- 领奖
+CG_ABS_COMONACT_MONEYTREE_GET_REWARD = {}

+ 9 - 4
script/module/anotherWorldBattle/AnotherWorldBattleCS.lua

@@ -1342,6 +1342,11 @@ function N2C_TryChallengdePoint_Req(msg)
     local targetCityId = msg.targetCityId
     local targetPointIdx = msg.targetPointIdx
 
+    local pChallengeTimes = getPlayerChallengeTimesInfo(playerUuid)
+    if pChallengeTimes <= 0 then
+        return errTips(msg.sourceServerId, playerUuid, AnotherWorldBattleDefine.ERR_CODE_12)
+    end
+
     local state = isCanChallengePoint(targetCityId, msg.targetPointIdx, msg.myUnionId, playerUuid)
     if state ~= 1 then
         return
@@ -1521,8 +1526,8 @@ local function challenge_Win(msg)
     -- end
     local pChallengeTimes, pLastTime = getPlayerChallengeTimesInfo(playerUuid)
     atkPlayerData.challengeTimes = pChallengeTimes - 1
-    if pLastTime == 0 then
-        pLastTime=  os.time()
+    if pLastTime <= 0 then
+        pLastTime = os.time()
     end
     atkPlayerData.lastTime = pLastTime
 
@@ -1597,8 +1602,8 @@ local function challenge_Fail(msg)
     -- end
     local pChallengeTimes, pLastTime = getPlayerChallengeTimesInfo(playerUuid)
     playerData.challengeTimes = pChallengeTimes - 1
-    if pLastTime == 0 then
-        pLastTime=  os.time()
+    if pLastTime <= 0 then
+        pLastTime = os.time()
     end
     playerData.lastTime = pLastTime
 

+ 4 - 1
script/module/anotherWorldBattle/AnotherWorldBattleDefine.lua

@@ -19,7 +19,7 @@ AB_OPEN_DAYS = 6                -- 每轮活动天数
 AB_JOIN_WDAY_AREA = {7, 1}      -- 报名时间范围, 周六 ~ 周日
 
 -- AB_BATTLE_WDAY_AREA = {2, 4}    -- 战斗时间范围, 周一 ~ 周三
-AB_BATTLE_WDAY_AREA = {2, 5}    -- 战斗时间范围, 周一 ~ 周四。(修改后需要同步修改 AB_BATTLE_DAYS )
+AB_BATTLE_WDAY_AREA = {2, 5}    -- 战斗时间范围, 周一 ~ 周四。(修改后需要同步修改 AB_BATTLE_DAYS, calcRewardNow() )
 
 AB_BATTLE_DAYS = 4              -- 战斗阶段天数
 
@@ -51,6 +51,8 @@ AB_POINT_BATTLE_TIME = 30        -- 挑战据点从发起战斗 ~ 结束时间,
 
 AB_MORALE_DURATION = 7200       -- 士气加成持续时间
 
+AB_MIN_ITEM_MAX_NUM = 800       -- 公会每分钟获得道具数量最大值
+
 
 AB_LOSE_POINT_MAIL_ID = 7031          -- 玩家失去据点后通知邮件Id
 
@@ -78,6 +80,7 @@ ERR_CODE_8 = 8      -- 公会已报名
 ERR_CODE_9 = 9      -- 没有在本城池发起集结
 ERR_CODE_10 = 10    -- 当前占据的据点已达上限
 ERR_CODE_11 = 11    -- 当前据点被挑战中, 请稍后再试
+ERR_CODE_12 = 12    -- 行动力不足
 
 
 

+ 61 - 4
script/module/anotherWorldBattle/AnotherWorldBattleNS.lua

@@ -200,7 +200,7 @@ local function calcPointAward(cityIdArr)
         end
     end
 
-    return myUnionAwardNum
+    return math.min(myUnionAwardNum, AnotherWorldBattleDefine.AB_MIN_ITEM_MAX_NUM)
 end
 
 -- 获取展示部位的固定Id
@@ -440,6 +440,32 @@ local function getRankAward(rankRewardList, rankType, targetRank)
     end
 end
 
+-- 计算整个战斗阶段占据据点的最大产出数量
+local function calcRewardMax()
+    local totalSec = AnotherWorldBattleDefine.AB_BATTLE_DAYS * 86400
+    totalSec = totalSec - AnotherWorldBattleDefine.AB_START_SEC -- 第一天0:10分才开始
+    totalSec = totalSec - (86400 - AnotherWorldBattleDefine.AB_BATTLE_END_SEC) -- 最后一天只到23点
+
+    local itemCntMax = math.floor(totalSec / 60) * AnotherWorldBattleDefine.AB_MIN_ITEM_MAX_NUM
+    return itemCntMax
+end
+
+-- 计算当前占领的据点最大产出数量
+local function calcRewardNow()
+    local now = os.time()
+    local toDayStartTime = Util.getDayStartTime(now)
+
+    local wDay = getWDay()
+    local subDay = wDay - AnotherWorldBattleDefine.AB_BATTLE_WDAY_AREA[1]
+
+    local battleStageStartTime = toDayStartTime - (subDay * 86400)
+    battleStageStartTime = battleStageStartTime + AnotherWorldBattleDefine.AB_START_SEC
+
+    local subSec = now - battleStageStartTime
+    local itemCntMax = math.floor(subSec / 60) * AnotherWorldBattleDefine.AB_MIN_ITEM_MAX_NUM
+    return itemCntMax
+end
+
 -- 生成奖励发放对象列表
 local function genAwardObjArr(unionOccupyInfo)
     local function calcMinute(timeArr)
@@ -493,6 +519,8 @@ local function genAwardObjArr(unionOccupyInfo)
     end
 
     -- 占领据点每分钟的奖励
+    local minuteAwardId, minuteAwardNum = 0, 0
+
     for _, pointInfo in ipairs(unionOccupyInfo.point2CityIdArr) do
         local cityId = pointInfo[1]
         local occupyTimeArr = pointInfo[2]
@@ -501,10 +529,18 @@ local function genAwardObjArr(unionOccupyInfo)
         local totalMinVal = calcMinute(occupyTimeArr)
         if totalMinVal > 0 then
             local itemId, itemNum = cityCfg.cityAward[1], cityCfg.cityAward[2]
-            itemList[itemId] = (itemList[itemId] or 0) + totalMinVal * itemNum
+            minuteAwardId = itemId
+            minuteAwardNum = minuteAwardNum + totalMinVal * itemNum
+            -- itemList[itemId] = (itemList[itemId] or 0) + totalMinVal * itemNum
         end
     end
 
+    local maxNum = calcRewardMax()
+    if maxNum > 0 then
+        minuteAwardNum = math.min(minuteAwardNum, maxNum)
+    end
+    itemList[minuteAwardId] = (itemList[minuteAwardId] or 0) + minuteAwardNum
+
 
     if item_185_Num > 0 then
         itemList[item_185_Id] = item_185_Num
@@ -634,6 +670,10 @@ end
 
 -- 玩家战力更新
 function PlayerPowerChange(human)
+    if not human.db.anotherWorlBattle then
+        return
+    end
+
     if not baseCondCheck(human) then
         return false
     end
@@ -687,6 +727,10 @@ end
 
 -- 玩家改名
 function PlayerChangeName(human)
+    if not human.db.anotherWorlBattle then
+        return
+    end
+
     if not baseCondCheck(human) then
         return false
     end
@@ -1221,6 +1265,8 @@ function C2N_ErrTips(msg)
         tips = Lang.AB_OCCUPY_POINT_MAX
     elseif errCode == AnotherWorldBattleDefine.ERR_CODE_11 then
         tips = Lang.AB_POINT_CHALLENGING
+    elseif errCode == AnotherWorldBattleDefine.ERR_CODE_12 then
+        tips = Lang.AB_CHALLENGING_TIMES_NOT_ENOUGH
     end
 
     Broadcast.sendErr(human, tips)
@@ -1323,7 +1369,7 @@ function C2N_AllCity_Response(msg)
     end
 
 
-    -- 据点每分钟的奖励
+    -- 占领据点奖励
     local itemId, itemNum = 0, 0
     local cityCfg = AnotherWorldBattleConfig.city[1]
     itemId = cityCfg.cityAward[1]
@@ -1345,6 +1391,17 @@ function C2N_AllCity_Response(msg)
 
         itemNum = itemNum + minuteVal * cityCfg.cityAward[2]
     end
+
+    local itemNumMax = calcRewardMax()
+    local isRun = actStartTimeCheck()
+    local isBattle = isBattleStage()
+    if isRun and isBattle then
+        itemNumMax = calcRewardNow()
+    end
+    if itemNumMax > 0 then
+        itemNum = math.min(itemNum, itemNumMax)
+    end
+
     Grid.makeItem(msgRet.myUnionAward, itemId, itemNum)
 
     Msg.send(msgRet, human.fd)
@@ -1485,7 +1542,7 @@ function C2N_BaseCity_Response(msg)
     local myUnionAwardNum = calcPointAward(baseCityInfo.myUnionOccupyArr)
     Grid.makeItem(msgRet.myUnionAward, cityCfg.cityAward[1], myUnionAwardNum)
 
-    msgRet.cityIconId = baseCityInfo.occupyPointNum
+    -- msgRet.cityIconId = baseCityInfo.occupyPointNum
     msgRet.occupyPointNum = baseCityInfo.occupyPointNum
     msgRet.occupyCityLv2Num = baseCityInfo.occupyCityLv2Num
     msgRet.occupyCityLv3Num = baseCityInfo.occupyCityLv3Num

+ 1 - 1
script/module/anotherWorldBattle/Proto.lua

@@ -133,7 +133,7 @@ GC_AB_BASECITY_QUERY = {
     {"cityIconId",      1,      "int"},     -- 城池造型 Id
     {"cityAward",       1,      ItemData},  -- 当前城池每分钟产出道具信息
     {"myUnionAward",    1,      ItemData},  -- 本公会每分钟产出道具信息
-    {"occupyPointNum",  1,      "byte"},    -- 占领据点数量
+    {"occupyPointNum",  1,      "short"},   -- 占领据点数量
     {"occupyCityLv2Num",1,      "byte"},    -- 占领2级城池数量
     {"occupyCityLv3Num",1,      "byte"},    -- 占领3级城池数量
     {"occupyCityLv4Num",1,      "byte"},    -- 占领4级城池数量

+ 17 - 8
script/module/combat/CombatLogic.lua

@@ -876,19 +876,19 @@ local function sendCombatBegin(human, combatInfo, isLogin)
 	msgRet.defUuid = combatInfo.defender and combatInfo.defender.uuid or ""
 	msgRet.maxRound = combatInfo.maxRound
  
-	msgRet.petCD = CombatDefine.PET_CD
+	-- msgRet.petCD = CombatDefine.PET_CD
 	msgRet.heros[0] = 0
     msgRet.atkFormation = combatInfo.atkFormation
     msgRet.defFormation = combatInfo.defFormation
-    msgRet.backup[0] = 2
+    -- msgRet.backup[0] = 2
 	for i = 1, CombatDefine.COMBAT_HERO_ALL_CNT do
 		local obj = combatInfo.objList[i]
 		if fontCombatHeroNet(msgRet.heros[msgRet.heros[0] + 1], obj) then
 			msgRet.heros[0] = msgRet.heros[0] + 1
 		end
-		if i % CombatDefine.COMBAT_HERO_CNT == 0 then
-			msgRet.backup[i/CombatDefine.COMBAT_HERO_CNT] = obj and obj.backupPos or 0
-		end
+		-- if i % CombatDefine.COMBAT_HERO_CNT == 0 then
+		-- 	msgRet.backup[i/CombatDefine.COMBAT_HERO_CNT] = obj and obj.backupPos or 0
+		-- end
 	end
 	msgRet.helps[0] = 0
 
@@ -924,10 +924,18 @@ local function sendCombatBegin(human, combatInfo, isLogin)
 			msgRet.bufferList[0] = msgRet.bufferList[0] + 1
 		end
 	end	
-	fontCombatPosAttr(msgRet.attrsAtk, combatInfo.posAttr[1])
-	fontCombatPosAttr(msgRet.attrsDef, combatInfo.posAttr[2])	
+	-- fontCombatPosAttr(msgRet.attrsAtk, combatInfo.posAttr[1])
+	-- fontCombatPosAttr(msgRet.attrsDef, combatInfo.posAttr[2])	
+	-- msgRet.atkJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.ATTACK_SIDE])
+	-- msgRet.defJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.DEFEND_SIDE])
+	Msg.send(msgRet, human.fd)
+end
+
+-- 发送羁绊描述
+local function sendJibanDesc(human, combatInfo)
+	local msgRet = Msg.gc.GC_COMBAT_JiBan_DESC
 	msgRet.atkJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.ATTACK_SIDE])
-	msgRet.defJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.DEFEND_SIDE]) 
+	msgRet.defJibanDesc = JibanLogic.getDesc(combatInfo.jiban[CombatDefine.DEFEND_SIDE])
 	Msg.send(msgRet, human.fd)
 end
 
@@ -1079,6 +1087,7 @@ end
 -- 发送战斗开始
 function sendCombatData(human, combatInfo, isLogin)
 	sendCombatBegin(human, combatInfo, isLogin)
+	sendJibanDesc(human, combatInfo)
 	sendCombatFrame(human, combatInfo, isLogin)
 end
 

+ 12 - 6
script/module/combat/Proto.lua

@@ -210,24 +210,30 @@ GC_COMBAT_BEGIN = {
 	{"cmdList",			16,		CombatCmdConf},	    -- 战斗中使用过的作用
 	{"bufferList",		60,	    CombatBufferConf},	-- 战斗中使用过的技能
 	{"speed",			1,		"byte"},
-	{"attrsAtk",	    10,	     Attr},
-	{"attrsDef",	    10,	     Attr},
+	-- {"attrsAtk",	    10,	     Attr},
+	-- {"attrsDef",	    10,	     Attr},
 	{"atkName",		    1,	     "string"},
 	{"defName",		    1,	     "string"},
     {"atkUuid",		    1,	     "string"},
 	{"defUuid",		    1,	     "string"},
 	{"maxRound",		1,		"short"},
     {"isVideo",		    1,		"byte"},
-	{"petCD",		    1,		"short"},
-	{"atkJibanDesc",	1,		"string"},
-	{"defJibanDesc",	1,		"string"},
+	-- {"petCD",		    1,		"short"},
+	-- {"atkJibanDesc",	1,		"string"},
+	-- {"defJibanDesc",	1,		"string"},
     {"atkFormation",	1,		"short"},
     {"defFormation",	1,		"short"},
     {"isQuick",	        1,		"byte"},
-	{"backup",			2,		"byte"},	--第一个是攻方援军的站位,第二个是守方援军的站位。0值表示援军还没上
+	-- {"backup",			2,		"byte"},	--第一个是攻方援军的站位,第二个是守方援军的站位。0值表示援军还没上
 	--{"isBoss",			1,		"byte"},		-- 敌方是否是boss
 }
 
+GC_COMBAT_JiBan_DESC = {
+	{"atkJibanDesc",	1,		"string"},
+	{"defJibanDesc",	1,		"string"},
+}
+
+
 CombatHitNet = {
 	{"pos",			1,		"byte"},
 	{"flag",		1,		"byte"},

+ 2 - 2
script/module/copy/CopyLogic.lua

@@ -570,7 +570,7 @@ function checkCombatPos(human, args)
 	    	human.db.copy[copyType].buyCnt = human.db.copy[copyType].buyCnt + 1
 	    end
     else
-		if leftCnt < 1 then
+		--if leftCnt < 1 then
 			local canBuy = getCanBuyCnt(human, copyType)
 			if canBuy < 1 then
 				return Broadcast.sendErr(human, Lang.COPY_FIGHT_ERR_NO_CNT) 
@@ -588,7 +588,7 @@ function checkCombatPos(human, args)
 			human.db.copy[copyType] = human.db.copy[copyType] or {}
 			human.db.copy[copyType].buyCnt =  human.db.copy[copyType].buyCnt or 0
 			human.db.copy[copyType].buyCnt = human.db.copy[copyType].buyCnt + canBuy
-		end
+		--end
     end
 
 	return true, id, copyConfig

+ 10 - 1
script/module/dailyTask/DailyTaskLogic.lua

@@ -15,6 +15,8 @@ local YunYingLogic = require("yunying.YunYingLogic")
 local WeekTaskLogic = require("dailyTask.WeekTaskLogic")
 local HonorTaskLogic = require("dailyTask.HonorJourney")
 
+local CommonActMoneyTree
+
 DAILY_TASK_ID_1  = 1  --日常登陆1次 o
 DAILY_TASK_ID_2  = 2  --赠送友情点5次 o
 DAILY_TASK_ID_3  = 3  --进行3次招将 o
@@ -372,7 +374,14 @@ function onDailyTaskHuoYue(human,sum)
 
 	human.db.dailyTask.nowHuoYue = (human.db.dailyTask.nowHuoYue or 0) + sum
 
-    YunYingLogic.onCallBack(human, "onDailyTask",sum)   
+    YunYingLogic.onCallBack(human, "onDailyTask",sum)
+
+	local finallyIdx = #DailyTaskExcel.huoYueBox
+	local finallyCfg = DailyTaskExcel.huoYueBox[finallyIdx]
+	if human.db.dailyTask.nowHuoYue >= finallyCfg.needHuoYue then
+		CommonActMoneyTree = CommonActMoneyTree or require("absAct.CommonActMoneyTree")
+		CommonActMoneyTree.CompleteHuoYueTask(human)
+	end
 end
 
 function onLogin(human)	

+ 30 - 30
script/module/drill/DrillLogic.lua

@@ -111,8 +111,8 @@ local CHALLENGE_DIAMONS = 500  -- 使用钻石通过当前关卡需要的钻石
 
 --秘宝加成
 local function getTalismanAdd(human)
-	local jinbiAdd = (TalismanLogic.getTalismanAdd(human, TalismanLogic.OTHER_EFFECT_TBL.DRILL_JINBI) or 0) / 100
-	return jinbiAdd
+	local heroExpMul = (TalismanLogic.getTalismanAdd(human, TalismanLogic.OTHER_EFFECT_TBL.DRILL_HERO_EXP) or 0) / 100
+	return heroExpMul
 end
 
 
@@ -308,7 +308,7 @@ function checkSaoDang(human, diff, lastDiff, lastId)
     local rewardCnt = double and 2 or 1
 
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 
 	if lastDiff and lastDiff == diff and lastId > 10 then
 		id = lastId - 10
@@ -320,8 +320,8 @@ function checkSaoDang(human, diff, lastDiff, lastId)
 			for _, item in ipairs(drillItems) do
 				local itemID = item[1]
 				local itemCnt = item[2] * rewardCnt
-				if itemID == ItemDefine.ITEM_JINBI_ID then
-					itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+				if itemID == ItemDefine.ITEM_GREEN_EXP_ID then
+					itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 				end
 
 				drillDB.dayGet = drillDB.dayGet or {}
@@ -479,7 +479,7 @@ function queryDrillId(human, drillData, openPanel)
 	local boxCount = math.min(#DrillExcel.box, 5)
 	msgRet.box[0] = boxCount
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 	for k = 1, boxCount do
 		local config = DrillExcel.box[k]
 		local net = msgRet.box[k]	
@@ -496,14 +496,14 @@ function queryDrillId(human, drillData, openPanel)
 		net.state = getBoxState(drill,  k)
 		local itemID1  = config["items"..diff][1][1]
 		local itemCnt1 = config["items"..diff][1][2]
-		if itemID1 == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-			itemCnt1 = itemCnt1 + math.ceil(itemCnt1 * talismanAdd_jinbi)
+		if itemID1 == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+			itemCnt1 = itemCnt1 + math.ceil(itemCnt1 * heroExpMul)
 		end
 
 		local itemID2  = config["items"..diff][2][1]
 		local itemCnt2 = config["items"..diff][2][2]
-		if itemID2 == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-			itemCnt2 = itemCnt1 + math.ceil(itemCnt2 * talismanAdd_jinbi)
+		if itemID2 == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+			itemCnt2 = itemCnt1 + math.ceil(itemCnt2 * heroExpMul)
 		end
 
 		Grid.makeItem(net.reward[1], itemID1,  itemCnt1 )
@@ -513,8 +513,8 @@ function queryDrillId(human, drillData, openPanel)
 		if config["items"..diff][3] then
 			local itemID3  = config["items"..diff][3][1]
 			local itemCnt3 = config["items"..diff][3][2]
-			if itemID3 == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-				itemCnt3 = itemCnt1 + math.ceil(itemCnt3 * talismanAdd_jinbi)
+			if itemID3 == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+				itemCnt3 = itemCnt1 + math.ceil(itemCnt3 * heroExpMul)
 			end
 
 			Grid.makeItem(net.reward[3], itemID3,  itemCnt3 )
@@ -614,7 +614,7 @@ function query(human, drillId)
 	end
 
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 
 	local double = RoleSystemLogic.isDouble(human, RoleSystemDefine.ROLE_SYS_ID_1204)
 	local rewardCnt = double and 2 or 1
@@ -636,8 +636,8 @@ function query(human, drillId)
 	for _, item in pairs(itemConfig) do
 		msgRet.reward[0] = msgRet.reward[0] + 1
 		local itemCnt = item[2] * rewardCnt
-		if item[1] == ItemDefine.ITEM_JINBI_ID then
-			itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+		if item[1] == ItemDefine.ITEM_GREEN_EXP_ID then
+			itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 		end
 		Grid.makeItem(msgRet.reward[msgRet.reward[0]], item[1], itemCnt)
 		--Grid.makeItem(msgRet.reward[msgRet.reward[0]], item[1], item[2] * rewardCnt)
@@ -786,13 +786,13 @@ function drillBoxGet(human, index)
 	end
 
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 	local awardVec = {}
 	for i, v in ipairs(itemConfig) do
 		local itemID = v[1]
 		local itemCnt = v[2]
-		if itemID == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-			itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+		if itemID == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+			itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 		end
 		awardVec[i]= {itemID, itemCnt}
 	end
@@ -813,7 +813,7 @@ function rewardPreview(human)
     msgRet.list[0] = 0
 
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 
     for drillId, cf in pairs(DrillExcel.drill) do 
     	msgRet.list[0] = msgRet.list[0] + 1
@@ -823,8 +823,8 @@ function rewardPreview(human)
 		for i, item in ipairs(cf.items) do
 		    local itemID = item[1]
 		    local itemCnt = item[2]
-			if itemID == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-				itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+			if itemID == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+				itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 			end
 	        Grid.makeItem(net.items[i], itemID, itemCnt)
 		end
@@ -1609,15 +1609,15 @@ function onFightEnd(human, result, combatType, cbParam, combatInfo)
 	combatInfo.double = double and 2 or 0
 
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 
 	-- 关卡奖励		
 	local itemList = {}
 	for k, item in ipairs(drillItems) do
 		local itemID = item[1]
 		local itemCnt = item[2] * rewardCnt
-		if itemID == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-			itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+		if itemID == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+			itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 		end
 
 		itemList[k] = {}
@@ -1766,15 +1766,15 @@ function ChallengeLevelByDiamond(human, levelId)
 	local rewardCnt = double and 2 or 1
 
 	--秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 
 	-- 关卡奖励		
 	local itemArray = {}
 	for k, item in ipairs(drillItems) do
 		local itemID = item[1]
 		local itemCnt = item[2] * rewardCnt
-		if itemID == ItemDefine.ITEM_JINBI_ID and talismanAdd_jinbi > 0 then
-			itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+		if itemID == ItemDefine.ITEM_GREEN_EXP_ID and heroExpMul > 0 then
+			itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 		end
 
 		itemArray[k] = {itemID, itemCnt}
@@ -1903,7 +1903,7 @@ function oneClickSaodang(human)
 	local rewardCnt = double and 2 or 1
 	
 	-- 秘宝加成
-	local talismanAdd_jinbi = getTalismanAdd(human)
+	local heroExpMul = getTalismanAdd(human)
 	
 	-- 初始化今日奖励
 	drill.dayGet = drill.dayGet or {}
@@ -1918,8 +1918,8 @@ function oneClickSaodang(human)
 			for _, item in ipairs(drillItems) do
 				local itemID = item[1]
 				local itemCnt = item[2] * rewardCnt
-				if itemID == ItemDefine.ITEM_JINBI_ID then
-					itemCnt = itemCnt + math.ceil(itemCnt * talismanAdd_jinbi)
+				if itemID == ItemDefine.ITEM_GREEN_EXP_ID then
+					itemCnt = itemCnt + math.ceil(itemCnt * heroExpMul)
 				end
 				
 				-- 累加奖励到列表

+ 5 - 0
script/module/hero/HeroSeed.lua

@@ -11,6 +11,7 @@ local Broadcast = require("broadcast.Broadcast")
 local RoleDefine = require("role.RoleDefine")
 local TalismanLogic = require("talisman.TalismanLogic")
 
+local GiftLogic
 
 local LOGTAG = "HeroSeed" --日志标识
 local HAVE_SEED_HERO_MIN_STAR = 10 --英雄获得种子的最低星级要求
@@ -342,4 +343,8 @@ function HeroSeed_UpGrade(human, heroID, heroIndex, seedIdx, opType)
     HeroLogic.sendHeroBagDynamic(human, heroID, heroIndex)
     ObjHuman.sendAttr(human, RoleDefine.ZHANDOULI)
     HeroLogic.refreshDot(human, heroGrid.uuid)
+
+    -- 弹窗礼包
+    GiftLogic = GiftLogic or require("topup.GiftLogic")
+    GiftLogic.trigger(human, GiftLogic.GIFT_HEROSEED_UPGRADE_STAR, {currentVal = heroGrid.seedData[seedIdx]}, GiftLogic.GIFT_SEC_TYPE3)
 end

+ 5 - 0
script/module/hero/HeroTianYuan.lua

@@ -11,6 +11,7 @@ local HeroTianYuanCfg = require("excel.heroTianYuan")
 local Broadcast = require("broadcast.Broadcast")
 local RoleDefine = require("role.RoleDefine")
 
+local GiftLogic
 
 local LOGTAG = "HeroTianYuan" --日志标识
 
@@ -403,4 +404,8 @@ function HeroTianYuan_StageUpGrade(human, heroID, heroIndex)
 
     -- 刷新红点
     HeroLogic.refreshDot(human, heroGrid.uuid)
+
+    -- 弹窗礼包
+    GiftLogic = GiftLogic or require("topup.GiftLogic")
+    GiftLogic.trigger(human, GiftLogic.GIFT_HEROTIANYUAN_UPGRADE_STAR, {currentVal = nextStage}, GiftLogic.GIFT_SEC_TYPE1)
 end

+ 2 - 2
script/module/hero/Proto.lua

@@ -1005,10 +1005,10 @@ GC_HEROTY_QUERY = {
 	{"pointIdx",	1,		"short"}, 	-- 当前已点亮的天元点索引
 	{"stageIdx",	1,		"short"}, 	-- 当前已突破的重数
 	{"stageMax",	1,		"short"}, 	-- 最大可突破重数
-	{"attrs",		6,		Attr}, 		-- 总加成属性
+	{"attrs",		8,		Attr}, 		-- 总加成属性
 	{"cost", 		1,     ItemData},	-- 点亮/突破消耗的道具,没法升级后数量为0
 	{"maxPoint",	1,		"short"}, 	-- 最大可点亮天元点数量
-	{"nextAttrs",	6,		Attr}, 		-- 下一次提升的属性
+	{"nextAttrs",	8,		Attr}, 		-- 下一次提升的属性
 }
 
 -- 点亮天元

+ 3 - 3
script/module/present/Proto.lua

@@ -831,17 +831,17 @@ CG_OPEN_SERVER_RANK_QUERY = {                   -- 开服排名活动
 }             
 OpenServerRankList = {
     {"rank",             1,          "int"},    -- 名次(-1未上榜)
-    {"rankNeedValue",      1,        "int"},    -- 上榜条件值
+    {"rankNeedValue",      1,        "double"},    -- 上榜条件值
     {"items",        5,         ItemData},      -- 排行名次奖励列表    
     {"uid",             1,          "string"},  -- 用户id(未上榜为"")
     {"name",            1,          "string"},  -- 角色名(未上榜为"")
     {"head",            1,          "int"},     -- 头像(未上榜为-1)  
-    {"rankValue",        1,          "int"},    -- 当前排名值     
+    {"rankValue",        1,          "double"},    -- 当前排名值     
     {"headFrame",        1,          "int"},    -- 头像框(未上榜为-1)     
 }
 OpenServerOnwerData = {
     {"rank",             1,          "int"},    -- 名次(-1未上榜)
-    {"rankValue",        1,          "int"},    -- 当前排名值
+    {"rankValue",        1,          "double"},    -- 当前排名值
     {"items",           5,         ItemData},   -- 档位奖励列表(未上榜没有奖励) 
 }
 GC_OPEN_SERVER_RANK_QUERY = {                   -- 开服排名活动发送

+ 19 - 0
script/module/scene/Handler.lua

@@ -276,6 +276,25 @@ function CG_ASK_LOGIN(fd, msg)
 		ObjHuman.onLogin(human, nil)
 		HumanLogin_Handle(human, params)
 	end
+
+	-- 存储美团相关参数(如果存在)
+	if human.db then
+		-- mtSubscribe 改为由专门协议处理,这里不再从登录参数写入
+		if params.mtFrom2floor ~= nil then
+			local from2floor = tonumber(params.mtFrom2floor) or 0
+			-- 只在“当天第一次从2楼进入”时,将 mtFrom2floor 置为 1
+			-- 如果当天已经被服务器逻辑(领取奖励或11点邮件)置为 0,则本日内前端不能再次覆盖为 1
+			if from2floor == 1 then
+				local now = os.time()
+				local todayStart = Util.getDayStartTime(now)
+				human.db.mtFrom2floorDay = human.db.mtFrom2floorDay or 0
+				if not Util.isSameDay(human.db.mtFrom2floorDay) then
+					human.db.mtFrom2floor = 1
+					human.db.mtFrom2floorDay = todayStart
+				end
+			end
+		end
+	end
 end
 
 function CG_HEART_BEAT(human, msg)

+ 1 - 1
script/module/talisman/TalismanLogic.lua

@@ -42,7 +42,7 @@ OTHER_EFFECT_TBL = {
     ["NW_JB"] = "NW_JB",                            --女巫森林金币类型
     ["NW_HERO_EXP"] = "NW_HERO_EXP",                --女巫森林英雄经验类型
     ["NW_LHS"] = "NW_LHS",                          --女巫森林龙魂石
-    ["DRILL_JINBI"] = "DRILL_JINBI",                --勇者试炼金币类型
+    ["DRILL_HERO_EXP"] = "DRILL_HERO_EXP",          --勇者试炼英雄经验
     ["ZHANBU_MAX_POINTS"] = "ZHANBU_MAX_POINTS",    --占卜最大积分类型
     ["JYZH_LOTTERY10"] = "JYZH_LOTTERY10",          --精英召唤10连类型
     ["NW_FWJH"] = "NW_FWJH",                        --女巫森林符文精华

+ 17 - 0
script/module/topup/GiftLogic.lua

@@ -17,6 +17,7 @@ local GC_QUERY_GIFT = 51
 -- 次类型
 GIFT_SEC_TYPE1 = 1
 GIFT_SEC_TYPE2 = 2
+GIFT_SEC_TYPE3 = 3
 
 -- 礼包触发事件
 local PRINCIPAL_LINE_EVNET = 1 -- 主线推图
@@ -29,6 +30,9 @@ GIFT_WINNERRELIC_UPGRADE_STAR = 6   -- 圣遗物升星
 GIFT_ELF_UPGRADE_STAR = 7           -- 精灵升星
 GIFT_HERO_UPGRADE_STAR_DAILY = 8    -- 英雄升星, 每日只可触发一次, 可与 Upgrade_HERO_EVENT 同时触发
 
+GIFT_HEROSEED_UPGRADE_STAR = 9      -- 英雄种子
+GIFT_HEROTIANYUAN_UPGRADE_STAR = 10 -- 英雄天元
+
 GIFT_TALISMAN_OPEN = 15           -- 开启秘宝玩法
 GIFT_WINNERRELIC_OPEN = 16        -- 开启圣遗物玩法
 GIFT_ELF_OPEN = 17                -- 开启精灵玩法
@@ -272,6 +276,19 @@ local handler2 = {
             end
         end
     end,
+
+    [GIFT_SEC_TYPE3] = 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,
 }
 
 ---------------------------------------------------------------

+ 10 - 0
script/module/zhuanpan/Handler.lua

@@ -57,4 +57,14 @@ end
 -- 抖音渠道一次性奖励领取
 function CG_ZHUANPAN_ONCE_REWARD_GET(human, msg)
 	ZhuanpanLogic.onceRewardGet(human, msg.channelId)
+end
+
+-- 美团订阅奖励查询(同时上报订阅状态)
+function CG_ZHUANPAN_SUBSCRIBE_REWARD_QUERY(human, msg)
+	ZhuanpanLogic.subscribeRewardQuery(human, msg)
+end
+
+-- 美团订阅奖励领取
+function CG_ZHUANPAN_SUBSCRIBE_REWARD_GET(human, msg)
+	ZhuanpanLogic.subscribeRewardGet(human, msg.channelId)
 end

+ 23 - 4
script/module/zhuanpan/Proto.lua

@@ -119,8 +119,8 @@ GC_ZHUANPAN_GIFT_QUERY = {
 CG_ZHUANPAN_DAILY_FIXED_QUERY = {}
 
 GC_ZHUANPAN_DAILY_FIXED_QUERY = {
-	{"reward",      1,          ItemData},  -- 奖励物品
-	{"status",      1,          "byte"},   -- 状态 0-不可领取 1-可领取 2-已领取
+	{"reward",      2,          ItemData},  -- 奖励物品列表(最多2个,渠道11返回1个,渠道18返回2个)
+	{"status",      1,          "byte"},   -- 状态 0-不可领取 1-可领取 2-已领取 3-未激活
 }
 
 -- 每日固定奖励领取
@@ -129,7 +129,9 @@ CG_ZHUANPAN_DAILY_FIXED_GET = {
 }
 
 -- 抖音渠道一次性奖励查询
-CG_ZHUANPAN_ONCE_REWARD_QUERY = {}
+CG_ZHUANPAN_ONCE_REWARD_QUERY = {
+
+}
 
 GC_ZHUANPAN_ONCE_REWARD_QUERY = {
 	{"reward",      3,          ItemData},  -- 奖励物品列表(3个:102,500; 118,10; 111,100000)
@@ -139,4 +141,21 @@ GC_ZHUANPAN_ONCE_REWARD_QUERY = {
 -- 抖音渠道一次性奖励领取
 CG_ZHUANPAN_ONCE_REWARD_GET = {
 	{"channelId",   1,          "int"},  -- 渠道ID
-}
+}
+
+-- 美团订阅奖励查询(同时可上报订阅状态)
+CG_ZHUANPAN_SUBSCRIBE_REWARD_QUERY = {
+	{"mtSubscribe", 1, "int"},  -- 订阅状态:0未订阅;1已订阅
+}
+
+GC_ZHUANPAN_SUBSCRIBE_REWARD_QUERY = {
+	{"reward",      2,          ItemData},  -- 奖励物品列表(2个:101,50000; 112,100)
+	{"status",      1,          "byte"},   -- 状态 0-不可领取 1-可领取 2-已领取 
+}
+
+-- 美团订阅奖励领取
+CG_ZHUANPAN_SUBSCRIBE_REWARD_GET = {
+	{"channelId",   1,          "int"},  -- 渠道ID
+}
+
+

+ 474 - 23
script/module/zhuanpan/ZhuanpanLogic.lua

@@ -24,11 +24,12 @@ local ZhuanpanGift = require("zhuanpan.ZhuanpanGift")
 local Log = require("common.Log")
 local DB = require("common.DB")
 local LuaMongo = _G.lua_mongo
+local MailManager = require("mail.MailManager")
 
 -- 允许的渠道
 local ALLOW_CHANNELS = {
 	[11] = true,
-	[16] = true,
+	[17] = true, -- 美团2楼
 }
 
 DEFAULT_ZHUANPAN_TYPE_NORMAL 		= 1     -- 基础转盘
@@ -799,17 +800,17 @@ 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
@@ -817,10 +818,55 @@ local function checkAccountDailyFixedReward(account)
 			end
 		end
 	end
-
+	
 	return false  -- 账号下没有角色今日领取过
 end
 
+-- 获取账号下最早创建的角色注册日期(账号级别判断)
+local function getAccountEarliestCreateTime(account)
+	if not account then
+		return nil
+	end
+	
+	local QueryByAccount = {account = account}
+	local fields = {createTime = 1}
+	LuaMongo.find(DB.db_char, QueryByAccount, fields)
+	
+	local earliestTime = nil
+	while true do
+		local data = {}
+		if not LuaMongo.next(data) then
+			break
+		end
+		
+		if data.createTime then
+			if not earliestTime or data.createTime < earliestTime then
+				earliestTime = data.createTime
+			end
+		end
+	end
+	
+	return earliestTime
+end
+
+-- 检查渠道17是否已超过注册后15天(基于当前角色注册时间)
+local function isChannel18Over15Days(human)
+	if not human or not human.db or not human.db.createTime then
+		return false
+	end
+	
+	local createTime = human.db.createTime
+	local registerDayStart = Util.getDayStartTime(createTime)
+	local now = os.time()
+	local todayDayStart = Util.getDayStartTime(now)
+	
+	-- 计算从注册日期到今天已经过了多少天(注册日期是第1天)
+	local daysSinceRegister = math.floor((todayDayStart - registerDayStart) / 86400) + 1
+	
+	-- 如果超过15天,则不能领取
+	return daysSinceRegister > 15
+end
+
 -- 检查账号下是否有任何角色已领取一次性奖励(账号级别判断)
 local function checkAccountOnceReward(account)
 	if not account then
@@ -848,6 +894,64 @@ local function checkAccountOnceReward(account)
 	return false  -- 账号下没有角色领取过
 end
 
+-- 检查账号下是否有任何角色已领取订阅奖励(账号级别判断)
+local function checkAccountSubscribeReward(account)
+	if not account then
+		return false
+	end
+
+	local QueryByAccount = {account = account}
+	local fields = {zhuanpan = 1}
+	LuaMongo.find(DB.db_char, QueryByAccount, fields)
+
+	while true do
+		local data = {}
+		if not LuaMongo.next(data) then
+			break
+		end
+
+		if data.zhuanpan and data.zhuanpan.subscribeReward then
+			local getTime = data.zhuanpan.subscribeReward.getTime
+			if getTime then
+				return true  -- 账号下已有角色领取过
+			end
+		end
+	end
+
+	return false  -- 账号下没有角色领取过
+end
+
+-- 从newUniqueTag解析渠道ID (格式: "channelID|serverTag|account")
+local function getChannelIdFromNewUniqueTag(newUniqueTag)
+	if not newUniqueTag then
+		return nil
+	end
+	local parts = {}
+	for part in string.gmatch(newUniqueTag, "([^|]+)") do
+		table.insert(parts, part)
+	end
+	if #parts >= 1 then
+		return tonumber(parts[1])
+	end
+	return nil
+end
+
+-- 获取每日固定奖励配置(根据渠道ID)
+local function getDailyFixedRewardConfig(channelId)
+	if channelId == 17 then
+		-- 渠道17(美图2楼):[[101,20000],[111,5000]]
+		return {
+			{101, 20000},
+			{111, 5000}
+		}
+	else
+		-- 渠道11(默认):[[102, 50]]
+		return {
+			{102, 50}
+		}
+	end
+end
+
 -- 每日固定奖励查询
 function dailyFixedRewardQuery(human)
 	local msgRet = Msg.gc.GC_ZHUANPAN_DAILY_FIXED_QUERY
@@ -859,31 +963,83 @@ function dailyFixedRewardQuery(human)
 	-- 每日更新检查
 	ObjHuman.updateDaily(human)
 
-	-- 初始化数据库
-	human.db.zhuanpan = human.db.zhuanpan or {}
-	human.db.zhuanpan.dailyFixedReward = human.db.zhuanpan.dailyFixedReward or {}
-
-	-- 账号级别判断:先检查当前角色的内存数据,再检查数据库
 	local account = human.db.account
 	local isReceived = false
-	
-	-- 先检查当前角色的内存数据
-	local getTime = human.db.zhuanpan.dailyFixedReward.getTime
+	-- 先读取当日是否已领取(必须在 or {} 初始化之前读,否则可能被空表覆盖导致刚写入的 getTime 丢失)
+	local getTime = nil
+	if human.db.zhuanpan and human.db.zhuanpan.dailyFixedReward then
+		getTime = human.db.zhuanpan.dailyFixedReward.getTime
+	end
 	if getTime and Util.isSameDay(getTime) then
 		isReceived = true
 	else
-		-- 再检查账号下其他角色(数据库)
 		isReceived = checkAccountDailyFixedReward(account)
 	end
-	
-	if isReceived then
-		msgRet.status = 2  -- 已领取(账号级别)
+
+	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] getTime=" .. tostring(getTime) .. ", isReceived=" .. tostring(isReceived) .. ", account=" .. tostring(account))
+
+	-- 初始化数据库(放在读取 getTime 之后,避免覆盖已有 dailyFixedReward 导致 isReceived 错误)
+	human.db.zhuanpan = human.db.zhuanpan or {}
+	human.db.zhuanpan.dailyFixedReward = human.db.zhuanpan.dailyFixedReward or {}
+
+	-- 渠道17(美团2楼)需要满足:
+	-- 1) 当日从2楼登录(mtFrom2floor == 1,且 mtFrom2floorDay 是今天)
+	-- 2) 订阅奖励已领取且在15日有效期内
+	local newUniqueTag = human.db.newUniqueTag or human.newUniqueTag
+	local channelId = getChannelIdFromNewUniqueTag(newUniqueTag)
+	if channelId == 17 then
+		-- 2楼登录状态(mtFrom2floor 可能为字符串;Util.isSameDay 同天返回 now 时间戳,否则 nil,转为布尔)
+		local from2floorOk = false
+		if tonumber(human.db.mtFrom2floor) == 1 and human.db.mtFrom2floorDay then
+			from2floorOk = (Util.isSameDay(human.db.mtFrom2floorDay) and true or false)
+		end
+
+		-- 订阅相关状态(mtSubscribe 可能为字符串 "1",用 tonumber 统一判断)
+		local isSubscribed = (tonumber(human.db.mtSubscribe) == 1)
+		local zhuanpan = human.db.zhuanpan
+		local hasSubscribeReward = zhuanpan and zhuanpan.subscribeReward and zhuanpan.subscribeReward.getTime
+		local inValidPeriod = hasSubscribeReward and isSubscribeRewardInValidPeriod(human)
+
+		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] channelId=17 from2floorOk=" .. tostring(from2floorOk) .. ", mtFrom2floor=" .. tostring(human.db.mtFrom2floor) .. ", mtFrom2floorDay=" .. tostring(human.db.mtFrom2floorDay) .. ", isSubscribed=" .. tostring(isSubscribed) .. ", hasSubscribeReward=" .. tostring(hasSubscribeReward) .. ", inValidPeriod=" .. tostring(inValidPeriod))
+
+		if not isSubscribed then
+			-- 未订阅:不可领取
+			msgRet.status = 3 -- 未激活(未订阅)
+		elseif isReceived then
+			-- 当日已领取:固定为 2,不因 mtFrom2floor 被置 0 而变成 0
+			msgRet.status = 2  -- 已领取(今日已领取)
+		elseif not from2floorOk then
+			-- 当天未从2楼进入:不可领取
+			msgRet.status = 0
+		else
+			if not hasSubscribeReward or not inValidPeriod then
+				-- 已订阅,但订阅奖励尚未领取,或已超过15日有效期
+				msgRet.status = 3  -- 未激活(订阅奖励未领或15天已满)
+			else
+				msgRet.status = 1  -- 可领取(已激活且在15日内,且今日未领)
+			end
+		end
 	else
-		msgRet.status = 1  -- 可领取
+		-- 其他渠道保持原有逻辑
+		if isReceived then
+			msgRet.status = 2  -- 已领取(账号级别)
+		else
+			msgRet.status = 1  -- 可领取
+		end
+	end
+
+	-- 根据渠道ID设置奖励物品
+	local newUniqueTag = human.db.newUniqueTag or human.newUniqueTag
+	local channelId = getChannelIdFromNewUniqueTag(newUniqueTag)
+	local rewardConfig = getDailyFixedRewardConfig(channelId or 11)  -- 默认渠道11
+	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] 查询奖励信息: rewardConfig",rewardConfig)
+	-- 循环处理所有奖励物品
+	for i = 1, #rewardConfig do
+		Grid.makeItem(msgRet.reward[i], rewardConfig[i][1], rewardConfig[i][2])
 	end
+	msgRet.reward[0] = #rewardConfig  -- 设置数组长度
 
-	-- 设置奖励物品
-	Grid.makeItem(msgRet.reward, 102, 50)
+	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardQuery] channelId=" .. tostring(channelId) .. ", status=" .. tostring(msgRet.status))
 
 	if human.fd then
 		Msg.send(msgRet, human.fd)
@@ -897,7 +1053,7 @@ function dailyFixedRewardGet(human, channelId)
 	if not ALLOW_CHANNELS[channelId] then
 		Log.write(
 				Log.LOGID_DEBUG,
-				"[onceRewardGet] 渠道不匹配: channelId=" .. (channelId or "nil") .. ", 允许=11,12"
+				"[dailyFixedRewardGet] 渠道不匹配: channelId=" .. (channelId or "nil") .. ", 允许=11,16,18"
 		)
 		return Broadcast.sendErr(human, "渠道不匹配")
 	end
@@ -926,10 +1082,38 @@ function dailyFixedRewardGet(human, channelId)
 		Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 账号今日已领取: account="..(account or "nil"))
 		return Broadcast.sendErr(human, "今日已领取")
 	end
+	
+	-- 渠道17(美团2楼)额外条件:
+	-- 1) 当日从2楼登录(mtFrom2floor == 1 且 mtFrom2floorDay 为今天)
+	-- 2) 订阅奖励已领取且在15日有效期内
+	if channelId == 17 then
+		-- 2楼登录状态(mtFrom2floor 可能为字符串,Util.isSameDay 同天返回时间戳,转为布尔)
+		local from2floorOk = false
+		if tonumber(human.db.mtFrom2floor) == 1 and human.db.mtFrom2floorDay then
+			from2floorOk = (Util.isSameDay(human.db.mtFrom2floorDay) and true or false)
+		end
+
+		if not from2floorOk then
+			Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 渠道17当日未从2楼进入,无法领取: account="..(account or "nil"))
+			return Broadcast.sendErr(human, "条件不足,无法领取")
+		end
+
+		-- 订阅有效期检查(mtSubscribe 可能为字符串 "1")
+		if tonumber(human.db.mtSubscribe) ~= 1 or not isSubscribeRewardInValidPeriod(human) then
+			Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 渠道17订阅未激活或已过期,无法领取: account="..(account or "nil"))
+			return Broadcast.sendErr(human, "订阅未激活或已过期")
+		end
+	end
 
 	BagLogic.cleanMomentItemList()
 
-	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 102, 50)
+	-- 根据渠道ID发放不同的奖励(循环处理所有奖励)
+	local rewardConfig = getDailyFixedRewardConfig(channelId)
+	for i = 1, #rewardConfig do
+		BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, rewardConfig[i][1], rewardConfig[i][2])
+	end
+	
+	-- 复用已有的日志类型,避免未定义的logType导致断言
 	local success, err = pcall(function()
 		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
 	end)
@@ -939,7 +1123,15 @@ function dailyFixedRewardGet(human, channelId)
 	end
 
 	-- 记录领取时间
-	human.db.zhuanpan.dailyFixedReward.getTime = os.time()
+	local nowTime = os.time()
+	human.db.zhuanpan.dailyFixedReward.getTime = nowTime
+
+	-- 渠道17:领取成功后,消耗当天的2楼登录资格,防止同日重复触发
+	if channelId == 17 then
+		human.db.mtFrom2floor = 0
+	end
+
+	Log.write(Log.LOGID_DEBUG, "[dailyFixedRewardGet] 领取成功 account=" .. tostring(human.db.account) .. " channelId=" .. tostring(channelId) .. " getTime=" .. tostring(nowTime) .. " mtFrom2floor=" .. tostring(human.db.mtFrom2floor))
 
 	-- 领取成功后下发查询协议
 	local querySuccess, queryErr = pcall(function()
@@ -1059,4 +1251,263 @@ function onceRewardGet(human, channelId)
 		Log.write(Log.LOGID_DEBUG, "[onceRewardGet] 错误: 发送查询协议失败: "..(queryErr or "unknown"))
 		-- 即使查询协议发送失败,奖励已经发放,所以不返回错误
 	end
+end
+
+-- 检查当前角色订阅奖励是否在15日有效期内(从订阅奖励领取时间起算)
+function isSubscribeRewardInValidPeriod(human)
+	if not human or not human.db then
+		return false
+	end
+	local zhuanpan = human.db.zhuanpan
+	if not zhuanpan or not zhuanpan.subscribeReward or not zhuanpan.subscribeReward.getTime then
+		return false
+	end
+	local activateTime = zhuanpan.subscribeReward.getTime
+	local activateDayStart = Util.getDayStartTime(activateTime)
+	local nowTs = os.time()
+	local todayDayStart = Util.getDayStartTime(nowTs)
+	-- 激活日为第1天,15日内有效
+	local daysSinceActivate = math.floor((todayDayStart - activateDayStart) / 86400) + 1
+	return daysSinceActivate <= 15
+end
+
+-- 检查美团2楼玩家每日固定奖励领取情况(晚上11点调用)
+function checkMtFrom2floorDailyReward()
+	if _G.is_middle == true then
+		return
+	end
+	
+	local QueryMtFrom2floor = {mtFrom2floor = 1}
+	local fields = {_id = 1, zhuanpan = 1, createTime = 1, mtSubscribe = 1, account = 1}
+	LuaMongo.find(DB.db_char, QueryMtFrom2floor, fields)
+	
+	local now = os.time()
+	local todayDayStart = Util.getDayStartTime(now)
+	local dailyMailSentCount = 0
+	local subscribeMailSentCount = 0
+	
+	while true do
+		local data = {}
+		if not LuaMongo.next(data) then
+			break
+		end
+		
+		-- 检查是否在订阅有效期内(如果从未激活订阅奖励,视为未激活,不发每日固定奖励)
+		if data.mtSubscribe ~= 1 then
+			goto continue
+		end
+
+		local inValidPeriod = false
+		if data.zhuanpan and data.zhuanpan.subscribeReward and data.zhuanpan.subscribeReward.getTime then
+			local activateTime = data.zhuanpan.subscribeReward.getTime
+			local activateDayStart = Util.getDayStartTime(activateTime)
+			local daysSinceActivate = math.floor((todayDayStart - activateDayStart) / 86400) + 1
+			inValidPeriod = (daysSinceActivate <= 15)
+		end
+
+		-- 订阅奖励未激活或已过期,则不发每日固定奖励
+		if not inValidPeriod then
+			goto continue
+		end
+		
+		-- 检查今天是否已领取每日固定奖励
+		local hasReceivedToday = false
+		if data.zhuanpan and data.zhuanpan.dailyFixedReward then
+			local getTime = data.zhuanpan.dailyFixedReward.getTime
+			if getTime and Util.isSameDay(getTime) then
+				hasReceivedToday = true
+			end
+		end
+		
+		-- 如果今天没领取,发送邮件,并将 mtFrom2floor 置为0,防止当天再次通过登录触发
+		if not hasReceivedToday then
+			local title = "每日固定奖励提醒"
+			local content = "您今日尚未领取每日固定奖励,请及时领取!"
+			local rewardItems = {{101, 20000}, {111, 5000}}  -- 渠道17的奖励
+			
+			local success, err = pcall(function()
+				MailManager.add(MailManager.SYSTEM, data._id, title, content, rewardItems)
+				-- 更新数据库:标记今日已通过邮件发放,并清除当天的2楼标记
+				local updateFields = {
+					["zhuanpan.dailyFixedReward.getTime"] = now,
+					["mtFrom2floor"] = 0,
+				}
+				LuaMongo.update(DB.db_char, {_id = data._id}, updateFields, false, false)
+			end)
+			
+			if success then
+				dailyMailSentCount = dailyMailSentCount + 1
+				Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送每日固定奖励邮件并更新状态成功: uuid="..data._id)
+			else
+				Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送每日固定奖励邮件或更新状态失败: uuid="..data._id..", err="..tostring(err))
+			end
+		end
+		
+		-- 检查订阅奖励:如果已订阅(mtSubscribe == 1)且未领取,发送邮件
+		if data.mtSubscribe == 1 then
+			-- 检查账号下是否已领取订阅奖励
+			local hasReceivedSubscribe = checkAccountSubscribeReward(data.account)
+			
+			if not hasReceivedSubscribe then
+				local title = "订阅奖励提醒"
+				local content = "您已订阅但尚未领取订阅奖励,请及时领取!"
+				local rewardItems = {{101, 50000}, {112, 100}}  -- 订阅奖励
+				
+				local success, err = pcall(function()
+					MailManager.add(MailManager.SYSTEM, data._id, title, content, rewardItems)
+				end)
+				
+				if success then
+					subscribeMailSentCount = subscribeMailSentCount + 1
+					Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送订阅奖励邮件成功: uuid="..data._id)
+				else
+					Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 发送订阅奖励邮件失败: uuid="..data._id..", err="..tostring(err))
+				end
+			end
+		end
+		
+		::continue::
+	end
+	
+	Log.write(Log.LOGID_DEBUG, "[checkMtFrom2floorDailyReward] 检查完成,每日固定奖励邮件数量: "..dailyMailSentCount..", 订阅奖励邮件数量: "..subscribeMailSentCount)
+end
+
+-- 美团订阅奖励查询(同时接收并存储订阅状态)
+function subscribeRewardQuery(human, msg)
+	local msgRet = Msg.gc.GC_ZHUANPAN_SUBSCRIBE_REWARD_QUERY
+	if not msgRet then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardQuery] 错误: msgRet为nil")
+		return
+	end
+
+	-- 如果前端上传了订阅状态,则进行存储
+	if msg and msg.mtSubscribe ~= nil then
+		local val = tonumber(msg.mtSubscribe) or 0
+		-- 只从 0 -> 1,避免重复覆盖或回退
+		if val == 1 and human.db then
+			human.db.mtSubscribe = human.db.mtSubscribe or 0
+			if human.db.mtSubscribe ~= 1 then
+				human.db.mtSubscribe = 1
+				Log.write(Log.LOGID_DEBUG, "[subscribeRewardQuery] 更新订阅状态为1, account="..tostring(human.db.account))
+			end
+		end
+	end
+
+	-- 初始化数据库
+	human.db.zhuanpan = human.db.zhuanpan or {}
+	human.db.zhuanpan.subscribeReward = human.db.zhuanpan.subscribeReward or {}
+
+	-- 检查是否订阅(mtSubscribe == 1)
+	local isSubscribed = (human.db.mtSubscribe == 1)
+	
+	-- 账号级别判断:先检查当前角色的内存数据,再检查数据库
+	local account = human.db.account
+	local isReceived = false
+	
+	if isSubscribed then
+		-- 先检查当前角色的内存数据
+		local getTime = human.db.zhuanpan.subscribeReward.getTime
+		if getTime then
+			isReceived = true
+		else
+			-- 再检查账号下其他角色(数据库)
+			isReceived = checkAccountSubscribeReward(account)
+		end
+	end
+	
+	if not isSubscribed then
+		msgRet.status = 0  -- 不可领取(未订阅)
+	elseif isReceived then
+		msgRet.status = 2  -- 已领取
+	else
+		msgRet.status = 1  -- 可领取
+	end
+
+	-- 设置奖励物品列表:[[101,50000],[112,100]]
+	local success, err = pcall(function()
+		Grid.makeItem(msgRet.reward[1], 101, 50000)
+		Grid.makeItem(msgRet.reward[2], 112, 100)
+		msgRet.reward[0] = 2  -- 设置数组长度
+	end)
+	
+	if not success then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardQuery] 错误: 设置奖励物品失败: "..tostring(err))
+		return
+	end
+
+	if human.fd then
+		Msg.send(msgRet, human.fd)
+	end
+end
+
+-- 美团订阅奖励领取
+function subscribeRewardGet(human, channelId)
+	-- 检查渠道,只有渠道ID=17(美团2楼)才能领取
+	if not channelId or channelId ~= 17 then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 渠道不匹配: channelId="..(channelId or "nil")..", 需要=18")
+		return Broadcast.sendErr(human, "渠道不匹配")
+	end
+
+	-- 检查是否订阅(mtSubscribe == 1)
+	if human.db.mtSubscribe ~= 1 then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 未订阅: mtSubscribe="..(human.db.mtSubscribe or "nil"))
+		return Broadcast.sendErr(human, "未订阅,无法领取")
+	end
+
+	-- 初始化数据库
+	human.db.zhuanpan = human.db.zhuanpan or {}
+	human.db.zhuanpan.subscribeReward = human.db.zhuanpan.subscribeReward or {}
+
+	-- 账号级别判断:先检查当前角色的内存数据,再检查数据库
+	local account = human.db.account
+	local isReceived = false
+	
+	-- 先检查当前角色的内存数据
+	local getTime = human.db.zhuanpan.subscribeReward.getTime
+	if getTime then
+		isReceived = true
+	else
+		-- 再检查账号下其他角色(数据库)
+		isReceived = checkAccountSubscribeReward(account)
+	end
+	
+	if isReceived then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 账号已领取过,无法重复领取: account="..(account or "nil"))
+		return Broadcast.sendErr(human, "已领取过,无法重复领取")
+	end
+
+	BagLogic.cleanMomentItemList()
+
+	-- 发放奖励:[[101,50000],[112,100]]
+	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 101, 50000)
+	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, 112, 100)
+	
+	-- 使用已存在的日志类型,避免日志定义缺失导致的断言
+	local success, err = pcall(function()
+		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
+	end)
+	if not success then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 错误: 添加到背包失败: "..(err or "unknown"))
+		return Broadcast.sendErr(human, "发放奖励失败")
+	end
+
+	-- 记录领取时间(永久记录)
+	human.db.zhuanpan.subscribeReward.getTime = os.time()
+
+	local querySuccess, queryErr = pcall(function()
+		subscribeRewardQuery(human)
+	end)
+	if not querySuccess then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 错误: 发送订阅奖励查询协议失败: "..(queryErr or "unknown"))
+		-- 即使查询协议发送失败,奖励已经发放,所以不返回错误
+	end
+
+	-- 领取订阅奖励后,同时下发每日固定奖励查询协议
+	local dailyQuerySuccess, dailyQueryErr = pcall(function()
+		dailyFixedRewardQuery(human)
+	end)
+	if not dailyQuerySuccess then
+		Log.write(Log.LOGID_DEBUG, "[subscribeRewardGet] 错误: 发送每日固定奖励查询协议失败: "..(dailyQueryErr or "unknown"))
+		-- 即使查询协议发送失败,奖励已经发放,所以不返回错误
+	end
 end

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

@@ -15,6 +15,7 @@ import { HupuChannelHandler } from "../handlers/HupuChannelHandler";
 import { SevenTwoZeroChannelHandler } from "../handlers/SevenTwoZeroChannelHandler";
 import { MeituanChannelHandler } from "../handlers/MeituanChannelHandler";
 import { HuaweiChannelHandler } from "../handlers/HuaweiChannelHandler";
+import { HongKongTaiwanChannelHandler } from "../handlers/HongKongTaiwanChannelHandler";
 
 
 const logger = require("../../utils/log");
@@ -53,6 +54,7 @@ class ChannelFactory {
     this.registerHandler(15, new SevenTwoZeroChannelHandler()); // 720渠道
     this.registerHandler(16, new MeituanChannelHandler()); // 美团渠道
     this.registerHandler(17, new HuaweiChannelHandler()); // 华为渠道
+    this.registerHandler(18, new HongKongTaiwanChannelHandler()); // 港台渠道
   }
 
   /**

+ 327 - 0
webServer/src/channels/handlers/HongKongTaiwanChannelHandler.ts

@@ -0,0 +1,327 @@
+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";
+import {
+    HKT_ANDROID_PRODUCT_CODE,
+    HKT_ANDROID_CALLBACK_KEY,
+    HKT_IOS_PRODUCT_CODE,
+    HKT_IOS_CALLBACK_KEY,
+    HKT_DOMAIN
+} from "../../config/thirdParams";
+
+const logger = require("../../utils/log");
+
+/**
+ * 港台渠道处理器(Quick海外SDK)
+ * 负责登录验证与支付回调处理
+ * 支持安卓和iOS平台
+ */
+export class HongKongTaiwanChannelHandler implements ChannelHandler {
+    /**
+     * 根据平台获取配置
+     * @param platform 平台:android 或 ios
+     * @param config 渠道配置
+     * @returns 平台特定配置
+     */
+    private getPlatformConfig(platform: string, config: ChannelConfig) {
+        const isIOS = platform === 'ios';
+        
+        return {
+            productCode: isIOS ? HKT_IOS_PRODUCT_CODE : HKT_ANDROID_PRODUCT_CODE,
+            callbackKey: isIOS ? HKT_IOS_CALLBACK_KEY : HKT_ANDROID_CALLBACK_KEY,
+            domain: HKT_DOMAIN
+        };
+    }
+
+    /**
+     * QuickSDK 登录验证
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async handleLogin(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        const data = ctx.request.body as any;
+        const {token, uid, platform} = data || {};
+        
+        // 默认使用android,如果没有指定platform
+        const platformType = platform || 'android';
+        const platformConfig = this.getPlatformConfig(platformType, config);
+        const finalProductCode = data.product_code || platformConfig.productCode;
+
+        if (!token || !uid) {
+            logger.warn("港台渠道登录验证失败: 缺少必要参数", {token, uid, platform: platformType});
+            return {code: 0, msg: "缺少必要参数 token 或 uid"};
+        }
+
+        if (!finalProductCode) {
+            logger.error("港台渠道登录验证失败: 未配置productCode", {platform: platformType});
+            return {code: 0, msg: "服务器未配置productCode"};
+        }
+
+        // 使用海外QuickSDK的域名
+        // 安卓和iOS都使用相同的登录验证接口
+        const requestUrl = `${platformConfig.domain}/webapi/checkUserInfo`;
+        const params: Record<string, string> = {
+            token,
+            uid,
+            product_code: finalProductCode
+        };
+
+        logger.info("港台渠道登录验证请求", {
+            platform: platformType,
+            url: requestUrl,
+            params: {...params, token: token.substring(0, 20) + '...'}
+        });
+
+        try {
+            const response = await axios.get(requestUrl, {params, timeout: 8000});
+            logger.info("港台渠道登录验证响应", {
+                platform: platformType,
+                data: response.data
+            });
+
+            // 根据响应格式判断成功
+            // 海外QuickSDK可能返回JSON格式:{"status":true,"message":""}
+            // 或者返回字符串 "1" 表示成功
+            if (typeof response.data === 'string' && response.data === "1") {
+                return {code: 1, msg: "success"};
+            } else if (typeof response.data === 'object' && response.data.status === true) {
+                return {code: 1, msg: "success"};
+            }
+
+            logger.warn("港台渠道登录验证失败: 接口返回非成功", {
+                platform: platformType,
+                data: response.data
+            });
+            return {code: 0, msg: "登录验证失败"};
+        } catch (error: any) {
+            logger.error("港台渠道登录验证异常", {
+                platform: platformType,
+                error: error.message
+            });
+            return {code: 0, msg: "登录验证异常"};
+        }
+    }
+
+    /**
+     * QuickSDK 支付回调(海外版本,字段明文)
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async handlePayment(ctx: Context, config: ChannelConfig): Promise<PaymentResult> {
+        const data = ctx.request.body as any;
+        logger.info("港台渠道支付回调参数", {url: ctx.href, params: data});
+
+        // 海外版本直接使用明文字段,不需要nt_data和XML解密
+        const {
+            uid,
+            username,
+            cpOrderNo,
+            orderNo,
+            payTime,
+            payType,
+            payAmount,
+            payCurrency,
+            usdAmount,
+            payStatus,
+            actRate,
+            extrasParams,
+            subscriptionStatus,
+            subReason,
+            sign,
+            platform
+        } = data || {};
+
+        // 验证必要参数
+        if (!uid || !username || !orderNo || !payTime || !payType || !payAmount || 
+            !payCurrency || !usdAmount || typeof payStatus === "undefined" || 
+            !actRate || !sign) {
+            logger.warn("港台渠道支付回调失败: 缺少必要参数", {
+                uid: !!uid,
+                username: !!username,
+                orderNo: !!orderNo,
+                payTime: !!payTime,
+                payType: !!payType,
+                payAmount: !!payAmount,
+                payCurrency: !!payCurrency,
+                usdAmount: !!usdAmount,
+                payStatus: typeof payStatus !== "undefined",
+                actRate: !!actRate,
+                sign: !!sign
+            });
+            return {code: 0, msg: "缺少必要参数"};
+        }
+
+        // 从请求中获取platform,如果没有则尝试从数据中推断
+        // 默认使用android
+        const platformType = platform || 'android';
+        const platformConfig = this.getPlatformConfig(platformType, config);
+
+        const callbackKey = platformConfig.callbackKey;
+
+        if (!callbackKey) {
+            logger.error("港台渠道支付回调失败: 未配置回调密钥", {platform: platformType});
+            return {code: 0, msg: "服务器未配置回调密钥"};
+        }
+
+        // 验证sign签名(使用callbackKey)
+        // 根据文档:移除sign字段 -> 按首字母升序排序 -> 拼接成key=val&格式 -> 拼接callbackKey -> MD5
+        if (!this.verifyCallbackSign(data, callbackKey)) {
+            logger.warn("港台渠道支付回调失败: sign签名验证失败", {platform: platformType});
+            return {code: 0, msg: "签名验证失败"};
+        }
+
+        try {
+            // 检查订阅状态(如果有subscriptionStatus字段,且值为2,表示订阅取消,不需要发货)
+            if (subscriptionStatus !== undefined && subscriptionStatus === "2") {
+                logger.info("港台渠道支付回调: 订阅取消,不需要发货", {
+                    platform: platformType,
+                    orderNo,
+                    subscriptionStatus,
+                    subReason
+                });
+                return {code: 1, msg: "订阅取消,不需要发货"};
+            }
+
+            // 验证支付状态(0表示成功,1表示失败)
+            if (payStatus !== "0") {
+                logger.warn("港台渠道支付状态非成功", {
+                    platform: platformType,
+                    orderNo,
+                    payStatus
+                });
+                return {code: 0, msg: `支付状态失败: ${payStatus}`};
+            }
+
+            // 使用cpOrderNo作为订单ID(如果为空则使用orderNo)
+            const orderId = cpOrderNo || orderNo;
+            
+            if (!orderId) {
+                logger.error("港台渠道支付回调失败: 缺少订单号", {
+                    platform: platformType,
+                    cpOrderNo,
+                    orderNo
+                });
+                return {code: 0, msg: "缺少订单号"};
+            }
+
+            // 验证订单
+            const validation = await PaymentHelper.validateOrder(orderId);
+            if (!validation.valid) {
+                return {
+                    code: validation.message?.includes("重复发货") ? 1 : 0,
+                    msg: validation.message || "订单验证失败"
+                };
+            }
+
+            const orderInfo = validation.orderInfo;
+            
+            // 验证金额(payAmount单位是元)
+            const paymentAmount = parseFloat(payAmount);
+            if (Math.abs(Number(orderInfo.amount) - paymentAmount) > 0.01) {
+                logger.warn("港台渠道支付金额不匹配", {
+                    platform: platformType,
+                    orderId,
+                    requestAmount: paymentAmount,
+                    orderAmount: orderInfo.amount
+                });
+                return {code: 0, msg: `订单金额不一致: 订单金额${orderInfo.amount}元,支付金额${paymentAmount}元`};
+            }
+
+            logger.info(`港台渠道支付订单${orderId}开始发货`, {
+                platform: platformType,
+                orderNo,
+                uid,
+                username,
+                payAmount: paymentAmount,
+                payCurrency,
+                actRate
+            });
+            
+            const result = await PaymentHelper.deliverOrder(
+                orderInfo,
+                ctx.request.ip,
+                validation.url,
+                orderNo // 使用SDK订单号作为out_trade_no
+            );
+            
+            logger.info(`港台渠道支付订单${orderId}发货完成`, {
+                platform: platformType,
+                result
+            });
+            
+            return result;
+        } catch (error: any) {
+            logger.error("港台渠道支付回调处理异常", {
+                platform: platformType,
+                error: error.message,
+                stack: error.stack
+            });
+            return {code: 0, msg: "回调处理异常"};
+        }
+    }
+
+    /**
+     * 验证回调签名(使用callbackKey)
+     * 根据文档签名算法:
+     * 1. 移除sign字段
+     * 2. 按首字母升序排序
+     * 3. 拼接成 key1=val1&key2=val2&key3=val3& 格式
+     * 4. 拼接callbackKey
+     * 5. MD5编码
+     * 6. 与收到的sign对比
+     * @param data 回调数据
+     * @param callbackKey 回调密钥
+     * @returns 验证结果
+     */
+    private verifyCallbackSign(data: any, callbackKey: string): boolean {
+        try {
+            const receivedSign = data.sign;
+            if (!receivedSign) {
+                logger.error("港台渠道支付回调: 缺少sign字段");
+                return false;
+            }
+
+            // 1. 移除sign字段
+            const params = {...data};
+            delete params.sign;
+
+            // 2. 按首字母升序排序
+            const sortedKeys = Object.keys(params).sort();
+
+            // 3. 拼接成 key1=val1&key2=val2&key3=val3& 格式
+            const signStr = sortedKeys.map(key => {
+                const value = params[key];
+                // 处理null和undefined
+                if (value === null || value === undefined) {
+                    return '';
+                }
+                return `${key}=${value}&`;
+            }).join('');
+
+            // 4. 拼接callbackKey
+            const stringToSign = signStr + callbackKey;
+
+            // 5. MD5编码(32位小写)
+            const calculatedSign = crypto.createHash('md5').update(stringToSign).digest('hex').toLowerCase();
+
+            logger.info("港台渠道支付回调签名验证详情:", {
+                sortedKeys,
+                signStr: signStr.substring(0, 200) + '...', // 只显示前200个字符
+                stringToSign: stringToSign.substring(0, 200) + '...',
+                calculatedSign,
+                receivedSign,
+                isValid: calculatedSign === receivedSign.toLowerCase()
+            });
+
+            // 6. 与收到的sign对比(不区分大小写)
+            return calculatedSign === receivedSign.toLowerCase();
+        } catch (error: any) {
+            logger.error("港台渠道支付回调签名验证出错:", error);
+            return false;
+        }
+    }
+}

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

@@ -42,7 +42,12 @@ import {
     MEITUAN_APP_SECRET,
     HUAWEI_APP_ID,
     HUAWEI_APP_SECRET,
-    HUAWEI_CALLBACK_SECRET
+    HUAWEI_CALLBACK_SECRET,
+    HKT_ANDROID_PRODUCT_CODE,
+    HKT_ANDROID_CALLBACK_KEY,
+    HKT_IOS_PRODUCT_CODE,
+    HKT_IOS_CALLBACK_KEY,
+    HKT_DOMAIN
 } from "./thirdParams";
 
 // 渠道配置接口定义
@@ -302,4 +307,18 @@ export const channelConfigs: Record<number, ChannelConfig> = {
             appSecret: HUAWEI_APP_SECRET,
         },
     },
+    18: {
+        // 港台渠道(Quick海外SDK)
+        channelId: 18,
+        name: "港台",
+        platform: "hkt",
+        paymentConfig: {
+            // 回调密钥会根据platform参数动态选择(android/ios)
+            callbackKey: HKT_ANDROID_CALLBACK_KEY, // 默认使用安卓的
+        },
+        loginConfig: {
+            // ProductCode会根据platform参数动态选择(android/ios)
+            productCode: HKT_ANDROID_PRODUCT_CODE, // 默认使用安卓的
+        },
+    },
 };

+ 13 - 0
webServer/src/config/thirdParams.ts

@@ -136,3 +136,16 @@ export const HUAWEI_APP_ID = "251208633431"; // 需要配置实际的传盛应
 export const HUAWEI_APP_SECRET = "7B04DC515110A88336FD3DEDBF650F3F"; // 需要配置实际的应用密钥
 export const HUAWEI_CALLBACK_SECRET = "55B5E96B295F07BB858FE7D90018AEAB"; // 需要配置实际的回调密钥(用于支付回调签名验证)
 
+// 港台渠道(Quick海外SDK)
+// 安卓配置
+export const HKT_ANDROID_PRODUCT_CODE = "34455834768674171358576585112815";
+export const HKT_ANDROID_CALLBACK_KEY = "35184531118869583168823674803966";
+// iOS配置
+export const HKT_IOS_PRODUCT_CODE = "42084290837922679039451825484488";
+export const HKT_IOS_CALLBACK_KEY = "79721739009336027422030285139917";
+// 通用配置
+export const HKT_DOMAIN = "https://mia.hkhappygame.com";
+// Facebook配置(安卓和iOS共用)
+export const HKT_FB_ID = "176358215454264";
+export const HKT_FB_SECRET = "f437717865e8f40d988deedcc577f833";
+export const HKT_FB_CLIENT_TOKEN = "8df8bed44f30bea52a89a77e5bfc69b7";