pigflower 1 сар өмнө
parent
commit
4b18a0facb

+ 4 - 0
script/common/ProtoID.lua

@@ -1791,3 +1791,7 @@ _ENV[1900]="CG_ZHUANPAN_TB_QUERY"
 _ENV[1891]="GC_ZHUANPAN_TB_QUERY"
 _ENV[1892]="CG_ZHUANPAN_TB_REWARD_GET"
 
+_ENV[1901]="CG_ZHUANPAN_YOUXI_TASK_QUERY"
+_ENV[1902]="GC_ZHUANPAN_YOUXI_TASK_QUERY"
+_ENV[1903]="CG_ZHUANPAN_YOUXI_TASK_GET"
+

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

@@ -85,4 +85,17 @@ end
 -- 活动奖励领取
 function CG_ZHUANPAN_TB_REWARD_GET(human, msg)
 	ZhuanpanLogic.tbminiappRewardGet(human, msg.status)
+end
+
+
+-----------------------游戏圈任务-----------------------
+
+-- 游戏圈任务查询(前端上传SDK数据)
+function CG_ZHUANPAN_YOUXI_TASK_QUERY(human, msg)
+	ZhuanpanLogic.youxiTaskQuery(human, msg)
+end
+
+-- 游戏圈任务领取
+function CG_ZHUANPAN_YOUXI_TASK_GET(human, msg)
+	ZhuanpanLogic.youxiTaskGetReward(human, msg.taskId)
 end

+ 27 - 0
script/module/zhuanpan/Proto.lua

@@ -184,3 +184,30 @@ CG_ZHUANPAN_TB_REWARD_GET = {
 	{"status",  1,  "byte"},  -- 1=桌面进入奖励 2=二楼进入奖励 3=添加桌面奖励(仅1次)
 }
 
+
+-----------------------游戏圈任务-----------------------
+
+YOUXI_TASK_DATA = {
+	{"taskId",   1,  "byte"},   -- 任务ID (1-5)
+	{"status",   1,  "byte"},   -- 0=未完成 1=待领取 2=已领取
+	{"progress", 1,  "short"},  -- 当前进度
+	{"target",   1,  "short"},  -- 目标数量
+	{"reward",   1,  ItemData}, -- 奖励物品
+}
+
+-- 游戏圈任务查询(前端上传SDK数据,后端计算进度)
+CG_ZHUANPAN_YOUXI_TASK_QUERY = {
+	{"joinTime",     1,  "int"},   -- SDK field1: 加入游戏圈时间戳(0表示未加入)
+	{"likeCount",    1,  "short"}, -- SDK field4: 当天点赞贴子数
+	{"commentCount", 1,  "short"}, -- SDK field5: 当天评论贴子数
+}
+
+GC_ZHUANPAN_YOUXI_TASK_QUERY = {
+	{"tasks",  5,  YOUXI_TASK_DATA},  -- 任务列表(共5个)
+}
+
+-- 游戏圈任务领取
+CG_ZHUANPAN_YOUXI_TASK_GET = {
+	{"taskId",  1,  "byte"},  -- 任务ID (1-5)
+}
+

+ 273 - 8
script/module/zhuanpan/ZhuanpanLogic.lua

@@ -1520,14 +1520,18 @@ local TB_ENTER_REWARD        = {{112, 100}}   -- 桌面进入奖励(每日1次
 local TB_SECOND_FLOOR_REWARD = {{127, 10}}    -- 二楼进入奖励(每日1次)
 local TB_DESKTOP_REWARD      = {{118, 5}}     -- 添加桌面奖励(仅1次)
 
--- 渠道校验:仅 hanman.pf == "tbminiapp" 的玩家可参与
+-- 渠道校验:仅 pf == "tbMiniGame" 的玩家可参与
 local function isTbMiniApp(human)
-	return human.pf == "tbminiapp"
+	return human.pf == "tbMiniGame"
 end
 
 -- 淘宝小程序桌面活动查询
 -- status: 1=桌面进入 2=二楼进入
 function tbminiappQuery(human, status)
+	Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 收到请求 account=" .. tostring(human and human.db and human.db.account)
+		.. " pf=" .. tostring(human and human.pf)
+		.. " status=" .. tostring(status))
+
 	local msgRet = Msg.gc.GC_ZHUANPAN_TB_QUERY
 	if not msgRet then
 		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: msgRet为nil")
@@ -1535,6 +1539,7 @@ function tbminiappQuery(human, status)
 	end
 
 	if not isTbMiniApp(human) then
+		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 渠道不匹配 pf=" .. tostring(human and human.pf))
 		return Broadcast.sendErr(human, "渠道不匹配")
 	end
 
@@ -1559,21 +1564,28 @@ function tbminiappQuery(human, status)
 	msgRet.isFloorRewardReceived = (floorTime and Util.isSameDay(floorTime)) and 1 or 0
 
 	-- 填充奖励道具信息
-	Grid.makeItem(msgRet.enterReward,   TB_ENTER_REWARD[1][1],        TB_ENTER_REWARD[1][2])
-	Grid.makeItem(msgRet.floorReward,   TB_SECOND_FLOOR_REWARD[1][1], TB_SECOND_FLOOR_REWARD[1][2])
-	Grid.makeItem(msgRet.desktopReward, TB_DESKTOP_REWARD[1][1],      TB_DESKTOP_REWARD[1][2])
+	local ok, err = pcall(function()
+		Grid.makeItem(msgRet.enterReward,   TB_ENTER_REWARD[1][1],        TB_ENTER_REWARD[1][2])
+		Grid.makeItem(msgRet.floorReward,   TB_SECOND_FLOOR_REWARD[1][1], TB_SECOND_FLOOR_REWARD[1][2])
+		Grid.makeItem(msgRet.desktopReward, TB_DESKTOP_REWARD[1][1],      TB_DESKTOP_REWARD[1][2])
+	end)
+	if not ok then
+		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: Grid.makeItem失败: " .. tostring(err))
+		return
+	end
 
-	Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] status=" .. tostring(status)
+	Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 准备发送"
 		.. " isDesktopAdded=" .. tostring(msgRet.isDesktopAdded)
 		.. " isDesktopRewardReceived=" .. tostring(msgRet.isDesktopRewardReceived)
 		.. " isEnterRewardReceived=" .. tostring(msgRet.isEnterRewardReceived)
 		.. " isFloorRewardReceived=" .. tostring(msgRet.isFloorRewardReceived)
-		.. " account=" .. tostring(human.db.account))
+		.. " fd=" .. tostring(human.fd))
 
 	if human.fd then
 		Msg.send(msgRet, human.fd)
+		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 发送成功 account=" .. tostring(human.db.account))
 	else
-		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: human.fd为nil,无法发送协议")
+		Log.write(Log.LOGID_DEBUG, "[tbminiappQuery] 错误: human.fd为nil,无法发送协议 account=" .. tostring(human.db.account))
 	end
 end
 
@@ -1681,4 +1693,257 @@ function tbminiappRewardGet(human, status)
 	if not querySuccess then
 		Log.write(Log.LOGID_DEBUG, "[tbminiappRewardGet] 错误: 发送查询协议失败: "..(queryErr or "unknown"))
 	end
+end
+
+
+-----------------------游戏圈任务-----------------------
+
+-- 游戏圈任务配置
+-- reward: {itemId, count}  target: 完成目标数量  type: "once"永久/"daily"每日/"weekly"每周
+local YOUXI_TASK_CONFIG = {
+	[1] = { reward = {112, 10},  target = 1,  taskType = "once"   },  -- 首次加入游戏圈: 10连抽
+	[2] = { reward = {101, 100}, target = 1,  taskType = "daily"  },  -- 今日点赞1次: 金币100
+	[3] = { reward = {101, 100}, target = 1,  taskType = "daily"  },  -- 今日发表评论1次: 金币100
+	[4] = { reward = {101, 100}, target = 10, taskType = "weekly" },  -- 每周点赞10次: 金币100
+	[5] = { reward = {101, 100}, target = 20, taskType = "weekly" },  -- 每周发表评论20次: 金币100
+}
+
+-- 获取本周一0点时间戳
+local function getWeekStartTime()
+	local now = os.time()
+	local t = os.date("*t", now)
+	local daysFromMonday = (t.wday - 2) % 7  -- wday: 1=Sun, 2=Mon
+	local todayStart = Util.getDayStartTime(now)
+	return todayStart - daysFromMonday * 86400
+end
+
+-- 判断时间戳是否在本周(本周一0点)之后
+local function isCurrentWeek(timestamp)
+	if not timestamp then return false end
+	return timestamp >= getWeekStartTime()
+end
+
+-- 更新每日 SDK 进度(当天自然日,同天取最大值;跨天累加入本周合计)
+local function updateDailyCount(field, newCount)
+	field = field or {}
+	local now = os.time()
+	if field.dayTime and Util.isSameDay(field.dayTime) then
+		-- 同一天:SDK 数据只增不减,取最大值
+		field.count = math.max(field.count or 0, newCount)
+	else
+		-- 新的一天:把今天的数量累加到本周合计,重置今天的记录
+		local weekTotal = field.weekTotal or 0
+		if field.weekTime and isCurrentWeek(field.weekTime) then
+			field.weekTotal = weekTotal + newCount
+		else
+			-- 新的一周:重置
+			field.weekTotal = newCount
+		end
+		field.count    = newCount
+		field.dayTime  = now
+		field.weekTime = now
+	end
+	return field
+end
+
+-- 获取本周累计进度(当天数据已合并进 weekTotal)
+local function getWeekProgress(field)
+	if not field then return 0 end
+	local weekTotal = 0
+	if field.weekTime and isCurrentWeek(field.weekTime) then
+		weekTotal = field.weekTotal or 0
+	end
+	-- 加上今天尚未合并的部分(同天更新不写入 weekTotal,直接取 count)
+	if field.dayTime and Util.isSameDay(field.dayTime) then
+		-- 今天的 count 已是最新值,weekTotal 里还没有今天的,需加上
+		-- 但如果今天是新的一天(跨天时已合并),weekTotal 已含今天
+		-- 区分:dayTime 与 weekTime 同天时说明今天还未跨天合并
+		if field.weekTime and Util.isSameDay(field.weekTime) then
+			-- weekTime 也是今天,说明今天刚重置过(跨天时),今天已计入 weekTotal
+			return weekTotal
+		else
+			return weekTotal + (field.count or 0)
+		end
+	end
+	return weekTotal
+end
+
+-- 游戏圈任务查询(前端上传 SDK 数据,后端存储并计算进度)
+function youxiTaskQuery(human, msg)
+	local msgRet = Msg.gc.GC_ZHUANPAN_YOUXI_TASK_QUERY
+	if not msgRet then
+		Log.write(Log.LOGID_DEBUG, "[youxiTaskQuery] 错误: msgRet为nil")
+		return
+	end
+
+	human.db.zhuanpan = human.db.zhuanpan or {}
+	human.db.zhuanpan.youxiTask = human.db.zhuanpan.youxiTask or {}
+	local yt = human.db.zhuanpan.youxiTask
+
+	-- 存储 SDK 上报数据并更新进度
+	if msg then
+		-- 任务1:加入游戏圈(joinTime > 0 表示已加入)
+		if msg.joinTime and msg.joinTime > 0 and not yt.joined then
+			yt.joined = true
+		end
+		-- 任务2/4:点赞
+		if msg.likeCount and msg.likeCount > 0 then
+			yt.dailyLike = updateDailyCount(yt.dailyLike, msg.likeCount)
+		end
+		-- 任务3/5:评论
+		if msg.commentCount and msg.commentCount > 0 then
+			yt.dailyComment = updateDailyCount(yt.dailyComment, msg.commentCount)
+		end
+	end
+
+	local ok, err = pcall(function()
+		for taskId = 1, 5 do
+			local config = YOUXI_TASK_CONFIG[taskId]
+			local taskData = msgRet.tasks[taskId]
+			taskData.taskId = taskId
+			taskData.target = config.target
+			Grid.makeItem(taskData.reward, config.reward[1], config.reward[2])
+
+			if config.taskType == "once" then
+				taskData.progress = yt.joined and 1 or 0
+				if yt.joinGetTime then
+					taskData.status = 2
+				elseif yt.joined then
+					taskData.status = 1
+				else
+					taskData.status = 0
+				end
+
+			elseif config.taskType == "daily" then
+				local field    = (taskId == 2) and yt.dailyLike or yt.dailyComment
+				local dayCount = (field and field.dayTime and Util.isSameDay(field.dayTime)) and (field.count or 0) or 0
+				taskData.progress = math.min(dayCount, config.target)
+				local getTime = field and field.getTime
+				if getTime and Util.isSameDay(getTime) then
+					taskData.status = 2
+				elseif dayCount >= config.target then
+					taskData.status = 1
+				else
+					taskData.status = 0
+				end
+
+			elseif config.taskType == "weekly" then
+				local field     = (taskId == 4) and yt.dailyLike or yt.dailyComment
+				local weekCount = getWeekProgress(field)
+				taskData.progress = math.min(weekCount, config.target)
+				local getTime = field and field.getTime
+				if getTime and isCurrentWeek(getTime) then
+					taskData.status = 2
+				elseif weekCount >= config.target then
+					taskData.status = 1
+				else
+					taskData.status = 0
+				end
+			end
+		end
+		msgRet.tasks[0] = 5
+	end)
+
+	if not ok then
+		Log.write(Log.LOGID_DEBUG, "[youxiTaskQuery] 错误: " .. tostring(err))
+		return
+	end
+
+	if human.fd then
+		Msg.send(msgRet, human.fd)
+	end
+end
+
+-- 游戏圈任务领取(前端协议调用)
+-- taskId: 1-5
+function youxiTaskGetReward(human, taskId)
+	local config = YOUXI_TASK_CONFIG[taskId]
+	if not config then
+		Log.write(Log.LOGID_DEBUG, "[youxiTaskGetReward] 无效taskId=" .. tostring(taskId))
+		return Broadcast.sendErr(human, "无效的任务ID")
+	end
+
+	human.db.zhuanpan = human.db.zhuanpan or {}
+	human.db.zhuanpan.youxiTask = human.db.zhuanpan.youxiTask or {}
+	local yt = human.db.zhuanpan.youxiTask
+	local now = os.time()
+
+	-- 校验条件 & 重复领取
+	if config.taskType == "once" then
+		if yt.joinGetTime then
+			return Broadcast.sendErr(human, "已领取过")
+		end
+		if not yt.joined then
+			return Broadcast.sendErr(human, "尚未加入游戏圈")
+		end
+
+	elseif config.taskType == "daily" then
+		local field = (taskId == 2) and yt.dailyLike or yt.dailyComment
+		if field and field.getTime and Util.isSameDay(field.getTime) then
+			return Broadcast.sendErr(human, "今日已领取")
+		end
+		local dayCount = (field and field.dayTime and Util.isSameDay(field.dayTime)) and (field.count or 0) or 0
+		if dayCount < config.target then
+			return Broadcast.sendErr(human, "任务未完成")
+		end
+
+	elseif config.taskType == "weekly" then
+		local field = (taskId == 4) and yt.dailyLike or yt.dailyComment
+		if field and field.getTime and isCurrentWeek(field.getTime) then
+			return Broadcast.sendErr(human, "本周已领取")
+		end
+		if getWeekProgress(field) < config.target then
+			return Broadcast.sendErr(human, "任务未完成")
+		end
+	end
+
+	-- 发放奖励
+	BagLogic.cleanMomentItemList()
+	BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, config.reward[1], config.reward[2])
+	local success, err = pcall(function()
+		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
+	end)
+	if not success then
+		Log.write(Log.LOGID_DEBUG, "[youxiTaskGetReward] 发放奖励失败 task=" .. taskId .. " err=" .. tostring(err))
+		return Broadcast.sendErr(human, "发放奖励失败")
+	end
+
+	-- 记录领取时间
+	if config.taskType == "once" then
+		yt.joinGetTime = now
+	elseif config.taskType == "daily" then
+		local key = (taskId == 2) and "dailyLike" or "dailyComment"
+		yt[key] = yt[key] or {}
+		yt[key].getTime = now
+	elseif config.taskType == "weekly" then
+		local key = (taskId == 4) and "dailyLike" or "dailyComment"
+		yt[key] = yt[key] or {}
+		yt[key].getTime = now
+	end
+
+	Log.write(Log.LOGID_DEBUG, "[youxiTaskGetReward] 领取成功 task=" .. taskId .. " account=" .. tostring(human.db.account))
+
+	-- 领取后下发最新查询(不传 msg,不更新 SDK 数据)
+	youxiTaskQuery(human, nil)
+end
+
+-- SDK 回调奖励发放(内部调用,与任务奖励独立)
+-- items: {{itemId, count}, ...}
+function youxiSdkReward(human, items)
+	if not items or #items == 0 then
+		Log.write(Log.LOGID_DEBUG, "[youxiSdkReward] items为空 account=" .. tostring(human.db.account))
+		return
+	end
+	BagLogic.cleanMomentItemList()
+	for _, v in ipairs(items) do
+		BagLogic.updateMomentItem(BagLogic.ADDITEM_TYPE_1, v[1], v[2])
+	end
+	local success, err = pcall(function()
+		BagLogic.addMomentItemList(human, "zhuanpan_once_reward")
+	end)
+	if not success then
+		Log.write(Log.LOGID_DEBUG, "[youxiSdkReward] 发放失败 err=" .. tostring(err) .. " account=" .. tostring(human.db.account))
+	else
+		Log.write(Log.LOGID_DEBUG, "[youxiSdkReward] 发放成功 cnt=" .. #items .. " account=" .. tostring(human.db.account))
+	end
 end

+ 76 - 0
webServer/src/channels/handlers/MiniappChannelHandler.ts

@@ -1475,6 +1475,82 @@ export class MiniappChannelHandler implements ChannelHandler {
         }
     }
 
+    /**
+     * 游戏圈发货接口
+     * 微信SDK回调,发货礼包给玩家
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async deliverGoods(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        try {
+            const body = ctx.request.body as any;
+            const { timestamp, nonce, signature, wxMsg } = body;
+
+            // 验证签名(签名算法1:SHA1)
+            const verifyConfig = this.getChannel11Config('miniapp');
+            const expectedSignature = this.generateContentSecuritySignature({ timestamp, nonce }, verifyConfig);
+            if (!expectedSignature || expectedSignature !== signature) {
+                logger.error("游戏圈发货签名验证失败", { received: signature, expected: expectedSignature });
+                return { code: -1, msg: "签名验证失败" };
+            }
+
+            const miniGame = wxMsg?.MiniGame;
+            if (!miniGame) {
+                logger.error("游戏圈发货:缺少MiniGame参数");
+                return { code: 0, msg: "ok" };
+            }
+
+            const { OrderId, ToUserOpenid, GiftTypeId, GiftId, GoodsList, Zone, IsPreview } = miniGame;
+            if (!OrderId || !ToUserOpenid || !GoodsList) {
+                logger.error("游戏圈发货:缺少必要参数", { OrderId, ToUserOpenid });
+                return { code: 0, msg: "ok" };
+            }
+
+            logger.info("游戏圈发货请求", { OrderId, ToUserOpenid, GiftTypeId, GiftId, Zone, IsPreview });
+
+            // 查询玩家最近登录的服务器
+            const Server = require("../../model/ServerModel");
+            const serverList = await Server.getEnterServerListByUid(ToUserOpenid, 11);
+            if (!serverList || serverList.length === 0) {
+                logger.warn("游戏圈发货:未找到玩家服务器记录", { ToUserOpenid });
+                return { code: 0, msg: "ok" };
+            }
+
+            // 取最近登录的服务器
+            const serverInfo = serverList[0];
+            const url = serverInfo.wss || `ws://${serverInfo.ip}:${serverInfo.port}`;
+
+            const params = JSON.stringify({
+                type: "deliverGoods",
+                account: ToUserOpenid,
+                channel_id: 11,
+                orderId: OrderId,
+                giftTypeId: GiftTypeId,
+                giftId: GiftId,
+                goods: GoodsList,
+                zone: Zone,
+            });
+
+            logger.info("游戏圈发货 - 通知游戏服务器", { url, params });
+
+            const sendMsg = new Msg();
+            sendMsg.connect(url, Account);
+            await new Promise<void>((resolve) => {
+                setTimeout(() => {
+                    sendMsg.CG_TEST_PROTO(ToUserOpenid, params, serverInfo.server_id);
+                    resolve();
+                }, 1000);
+            });
+
+            logger.info("游戏圈发货成功", { OrderId, ToUserOpenid });
+            return { code: 0, msg: "ok" };
+
+        } catch (error) {
+            logger.error("游戏圈发货出错:", error);
+            return { code: 0, msg: "ok" };
+        }
+    }
+
     /**
      * 生成iOS支付签名(签名算法3)
      * @param parameters 参数数组

+ 6 - 6
webServer/src/channels/handlers/QingtianChannelHandler.ts

@@ -66,14 +66,14 @@ export class QingtianChannelHandler implements ChannelHandler {
         const data = ctx.request.body as any;
         logger.info("晴天SDK支付回调参数", {url: ctx.href, params: data,headerSign:ctx.request.headers["x-platform-sign"]});
 
-        const {game_order, channel_order_id, status, amount} = data || {};
+        const {extras_params, channel_order_id, status, amount} = data || {};
         if (!channel_order_id || !status || !amount) {
-            logger.warn("晴天SDK支付回调失败: 缺少必要参数", {game_order, channel_order_id, status, amount});
+            logger.warn("晴天SDK支付回调失败: 缺少必要参数", {extras_params, channel_order_id, status, amount});
             return {code: 0, msg: "缺少必要参数"};
         }
-        if (!game_order) {
-            logger.warn("晴天SDK支付回调失败: 缺少game_order", {channel_order_id});
-            return {code: 0, msg: "缺少game_order"};
+        if (!extras_params) {
+            logger.warn("晴天SDK支付回调失败: 缺少extras_params", {channel_order_id});
+            return {code: 0, msg: "缺少extras_params"};
         }
 
         const md5Key = config.paymentConfig?.callbackKey;
@@ -100,7 +100,7 @@ export class QingtianChannelHandler implements ChannelHandler {
 
         try {
             const parsedAmount = parseFloat(amount);
-            const orderId = game_order;
+            const orderId = extras_params;
             const outTradeNo = channel_order_id;
 
             const validation = await PaymentHelper.validateOrder(orderId);

+ 2 - 2
webServer/src/controller/ApiController.ts

@@ -13,7 +13,7 @@ import {
     generateOrderNumber,
     formatDate,
     getServerList,
-    getClientIp,
+    getClientIp, getServerAddr,
 } from "../utils/common";
 import {PaymentHelper} from "../utils/PaymentHelper";
 import {SignatureVerifier} from "../utils/SignatureVerifier";
@@ -1076,7 +1076,7 @@ class ApiController {
             data.userId = body.uuid;
         }
 
-        let serverUrl = await getServerList(body.serverId, body.channel_id);
+        let serverUrl = await getServerAddr(body.serverId, body.channel_id);
         let err = "";
         if (!serverUrl) {
             ctx.body = ApiController.fail(`区服id错误: serverId ${body.serverId}`, 1);

+ 27 - 0
webServer/src/controller/MiniAppController.ts

@@ -469,6 +469,33 @@ class MiniappController {
     }
   }
 
+  /**
+   * 游戏圈发货接口
+   * @param ctx Koa上下文
+   */
+  async deliverGoods(ctx: Context) {
+    try {
+      const handler = MiniappController.getChannelHandler();
+      const config = MiniappController.getChannelConfig();
+
+      if (!handler) {
+        ctx.body = { errcode: -1, errmsg: "渠道处理器未找到" };
+        return;
+      }
+
+      const result = await (handler as any).deliverGoods(ctx, config);
+      if (result.code === -1) {
+        ctx.body = { errcode: -1, errmsg: result.msg || "发货失败" };
+      } else {
+        ctx.body = { errcode: 0, errmsg: "ok" };
+      }
+
+    } catch (error) {
+      logger.error("游戏圈发货出错:", error);
+      ctx.body = { errcode: 0, errmsg: "ok" };
+    }
+  }
+
   /**
    * 订单校验接口
    * @param ctx Koa上下文

+ 3 - 0
webServer/src/router/miniapp.ts

@@ -26,4 +26,7 @@ miniAppRouter.post("/roleQuery", MiniAppController.roleQuery);
 miniAppRouter.post("/accountStatusReport", MiniAppController.accountStatusReport);
 miniAppRouter.get("/accountStatusReport", MiniAppController.accountStatusReport);
 
+// 游戏圈发货接口
+miniAppRouter.post("/deliverGoods", MiniAppController.deliverGoods);
+
 export default miniAppRouter;

+ 11 - 8
webServer/src/utils/SignatureVerifier.ts

@@ -154,19 +154,22 @@ export class SignatureVerifier {
    * @returns 验证结果
    */
   static verifyQingtianSign(data: any, secretKey: string, headerSign: string): boolean {
-    const sortedKeys = Object.keys(data).sort().filter((key) => {
-      if (key === "sign") return false;
-      const val = data[key];
-      return val !== null && val !== undefined && val !== "";
+    const sortedKeys = Object.keys(data)
+        .filter((key) => key !== "sign")
+        .sort();
+
+    const pairs = sortedKeys.map((key) => {
+      const val = data[key] == null ? "" : String(data[key]);
+      return `${key}=${val}`;
     });
-    const pairs = sortedKeys.map((key) => `${key}=${data[key]}`);
+
     const signStr = pairs.join("&") + "&key=" + secretKey;
     const newSign = CryptoJS.MD5(signStr).toString();
 
     logger.info("晴天SDK签名验证:", {
-      signStr: signStr,
-      newSign: newSign,
-      headerSign: headerSign,
+      signStr,
+      newSign,
+      headerSign,
     });
 
     return newSign === headerSign;