Selaa lähdekoodia

小程序接入

flowerpig 7 kuukautta sitten
vanhempi
sitoutus
48d0bcd290

+ 233 - 0
webServer/serverListApi.md

@@ -0,0 +1,233 @@
+# 服务器列表接口 API 文档
+
+## 接口说明
+
+`getAllServerList` 和 `getLastServerList` 接口已升级,支持查询玩家在各个服务器的角色信息。
+
+---
+
+## 1. 获取所有服务器列表
+
+### 接口地址
+- **GET** `/getAllServerList`
+- **POST** `/getAllServerList`
+
+### 请求参数
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| channel_id | number | 否 | 渠道ID,默认为1 |
+| uid | string | 否 | 玩家ID,提供时会查询角色信息。不提供时只返回基本服务器信息 |
+
+### 请求示例
+
+#### 不查询角色信息
+```bash
+GET /getAllServerList?channel_id=11
+```
+
+#### 查询角色信息
+```bash
+GET /getAllServerList?channel_id=11&uid=12345678
+```
+
+#### POST请求
+```bash
+POST /getAllServerList
+Content-Type: application/json
+
+{
+  "channel_id": 11,
+  "uid": "12345678"
+}
+```
+
+### 响应参数
+
+#### 不提供uid时的响应
+```json
+[
+  {
+    "id": 1,
+    "name": "1区",
+    "ip": "192.168.1.100",
+    "port": 8080,
+    "status": 0,
+    "tips": "正常"
+  }
+]
+```
+
+#### 提供uid时的响应
+```json
+[
+  {
+    "id": 1,
+    "name": "1区",
+    "ip": "192.168.1.100",
+    "port": 8080,
+    "status": 0,
+    "tips": "正常",
+    "roleName": "玩家昵称",
+    "roleLevel": 50,
+    "head": "avatar_001.png",
+    "hasRole": true
+  },
+  {
+    "id": 2,
+    "name": "2区",
+    "ip": "192.168.1.101",
+    "port": 8080,
+    "status": 0,
+    "tips": "正常",
+    "roleName": null,
+    "roleLevel": null,
+    "head": null,
+    "hasRole": false
+  }
+]
+```
+
+---
+
+## 2. 获取最近登录服务器列表
+
+### 接口地址
+- **POST** `/getLastServerList`
+
+### 请求参数
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| uid | string | 否 | 玩家ID,用于查询角色信息。不提供时只返回基本服务器信息 |
+| channel_id | number | 否 | 渠道ID,默认为1 |
+
+### 请求示例
+
+#### 查询角色信息
+```bash
+POST /getLastServerList
+Content-Type: application/json
+
+{
+  "uid": "12345678",
+  "channel_id": 11
+}
+```
+
+#### 不查询角色信息
+```bash
+POST /getLastServerList
+Content-Type: application/json
+
+{
+  "channel_id": 11
+}
+```
+
+### 响应参数
+```json
+[
+  {
+    "channel": "小程序",
+    "minSid": 1,
+    "maxSid": 10,
+    "isNewAccount": 0,
+    "sid": 1,
+    "id": 1,
+    "name": "1区",
+    "tips": "正常",
+    "server": "ws://192.168.1.100:8080",
+    "status": 0,
+    "roleName": "玩家昵称",
+    "roleLevel": 50,
+    "head": "avatar_001.png",
+    "hasRole": true
+  }
+]
+```
+
+---
+
+## 3. 角色信息字段说明
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| roleName | string/null | 角色名称,无角色时为null |
+| roleLevel | number/null | 角色等级,无角色时为null |
+| head | string/null | 角色头像,无角色时为null |
+| hasRole | boolean | 是否有角色,true表示有角色,false表示无角色 |
+
+---
+
+## 4. 数据库查询逻辑
+
+### 数据库名称生成规则
+```
+数据库名称 = "ckwy_fy_S" + (350001 + 服务器ID).padStart(3, '0')
+```
+
+### 查询条件
+- 集合名称:`char`
+- 查询条件:`{uid: 玩家ID}`
+
+### 返回字段
+- `name`: 角色名称
+- `lv`: 角色等级
+- `roleId`: 角色ID
+- `head`: 角色头像
+
+---
+
+## 5. 错误处理
+
+- 如果查询角色信息失败,会记录错误日志,但不会影响服务器列表的返回
+- 查询失败时,角色相关字段会设置为null或false
+- 数据库连接失败时会返回默认值
+
+---
+
+## 6. 性能优化
+
+- 使用 `Promise.all` 并行查询多个服务器的角色信息
+- 查询失败时不会阻塞其他服务器的查询
+- 只查询有玩家ID的请求,避免不必要的数据库查询
+
+---
+
+## 7. 使用示例
+
+### JavaScript示例
+```javascript
+// 获取所有服务器列表(带角色信息)
+const getAllServers = async (uid) => {
+  const response = await fetch(`/getAllServerList?uid=${uid}&channel_id=11`);
+  const servers = await response.json();
+  
+  servers.forEach(server => {
+    if (server.hasRole) {
+      console.log(`服务器${server.name}: ${server.roleName} (等级${server.roleLevel}) 头像: ${server.head}`);
+    } else {
+      console.log(`服务器${server.name}: 无角色`);
+    }
+  });
+};
+
+// 获取最近登录服务器列表
+const getLastServers = async (uid) => {
+  const response = await fetch('/getLastServerList', {
+    method: 'POST',
+    headers: { 'Content-Type': 'application/json' },
+    body: JSON.stringify({ uid, channel_id: 11 })
+  });
+  const servers = await response.json();
+  return servers;
+};
+```
+
+---
+
+## 8. 重要说明
+
+- **向后兼容**: 不提供uid时,接口行为与之前完全一致
+- **性能考虑**: 提供uid时会查询所有服务器的角色信息,可能影响响应时间
+- **数据一致性**: 角色信息来自MongoDB,与游戏服务器数据保持同步
+- **错误容错**: 单个服务器查询失败不会影响整体结果

+ 299 - 72
webServer/src/channels/handlers/MiniappChannelHandler.ts

@@ -2,8 +2,11 @@ import {Context} from "koa";
 import * as crypto from 'crypto';
 import {ChannelHandler, LoginResult, PaymentResult,} from "../interfaces/ChannelHandler";
 import {ChannelConfig} from "../../config/channelConfig";
+import {ChannelConfigManager} from "../../utils/ChannelConfigManager";
 import {formatDate, getServerList} from "../../utils/common";
 import {Account} from "../../config/thirdParams";
+import {PaymentHelper} from "../../utils/PaymentHelper";
+
 import Msg from "../../utils/msg";
 import axios from "axios";
 
@@ -121,7 +124,7 @@ export class MiniappChannelHandler implements ChannelHandler {
     private async handleIOSLogin(data: any, config: ChannelConfig): Promise<LoginResult> {
         try {
             const { token } = data;
-            const gameid = config.paymentConfig.gameId;
+            const gameid = '1550';
             // 验证必要参数
             if (!token) {
                 logger.error("iOS登录鉴权失败 - 缺少token参数");
@@ -132,7 +135,7 @@ export class MiniappChannelHandler implements ChannelHandler {
                 };
             }
 
-            logger.info("iOS登录鉴权请求参数:", { token });
+            logger.info("iOS登录鉴权请求参数:", { token,gameid });
 
             // 调用第三方鉴权API
             const apiUrl = `https://api.11h5.com/login?cmd=checkUserToken&userToken=${encodeURIComponent(token)}&gameid=${gameid}`;
@@ -207,18 +210,64 @@ export class MiniappChannelHandler implements ChannelHandler {
     }
 
     /**
-     * 小程序支付回调处理
+     * 统一支付回调处理
      * @param ctx Koa上下文
      * @param config 渠道配置
      */
     async handlePayment(ctx: Context, config: ChannelConfig): Promise<PaymentResult> {
         try {
-            // 获取请求参数
+            // 获取请求参数 - 支持GET和POST请求
             const params = ctx.request.query as any;
-            logger.info("小程序支付回调参数:", {url: ctx.href, params: params});
 
+            const platform = params.platform || 'miniapp'; // 平台参数:ios、Android、miniapp、h5,默认为miniapp
+            
+            logger.info("统一支付回调请求", { platform, url: ctx.href, params: params });
+
+            // 根据平台选择不同的支付处理方式和配置
+            if (platform === 'ios') {
+                // iOS使用渠道12的配置
+                const iosConfig = ChannelConfigManager.getConfig(12);
+                if (!iosConfig) {
+                    logger.error("未找到iOS渠道配置");
+                    return {
+                        code: -1,
+                        msg: "iOS渠道配置不存在",
+                        data: null
+                    };
+                }
+                return await this.handleIOSPayment(params, iosConfig);
+            } else {
+                // 默认使用小程序支付方式和渠道11的配置
+                const miniappConfig = ChannelConfigManager.getConfig(11);
+                if (!miniappConfig) {
+                    logger.error("未找到小程序渠道配置");
+                    return {
+                        code: -1,
+                        msg: "小程序渠道配置不存在",
+                        data: null
+                    };
+                }
+                return await this.handleMiniappPayment(params, miniappConfig);
+            }
+        } catch (error) {
+            logger.error('统一支付处理错误:', error);
+            return {
+                code: -1,
+                msg: error.message || "支付处理失败",
+                data: null
+            };
+        }
+    }
+
+    /**
+     * iOS支付回调处理
+     * @param params 请求参数
+     * @param config 渠道配置
+     */
+    private async handleIOSPayment(params: any, config: ChannelConfig): Promise<PaymentResult> {
+        try {
             // 验证必要参数
-            const requiredParams = ['uid', 'rmb', 'reqid', 'trans_id', 'product_id', 'notify_id', 'sign'];
+            const requiredParams = ['openid', 'rmb', 'reqid', 'trans_id', 'product_id', 'notify_id', 'sign','txid'];
             for (const param of requiredParams) {
                 if (!params[param]) {
                     logger.error(`缺少必要参数: ${param}`);
@@ -230,16 +279,12 @@ export class MiniappChannelHandler implements ChannelHandler {
                 }
             }
 
-            // 验证签名
-            const parameters = Object.keys(params)
-                .filter(key => key !== 'sign')
-                .map(key => `${key}=${params[key]}`);
-
-            const expectedSignature = this.generateSignature(parameters, config.paymentConfig.signKey!);
-            if (params.sign !== expectedSignature) {
-                logger.error("小程序支付签名验证失败", {
+            // 验证签名(使用iOS的签名算法)
+            const isValidSignature = this.verifyPaymentSignature(params, config);
+            if (!isValidSignature) {
+                logger.error("iOS支付签名验证失败", {
                     received: params.sign,
-                    expected: expectedSignature
+                    params: params
                 });
                 return {
                     code: -1,
@@ -248,42 +293,88 @@ export class MiniappChannelHandler implements ChannelHandler {
                 };
             }
 
-            // 获取订单信息
-            const orderId = params.reqid;
-            const orderInfo = (await Order.getOrder(orderId))[0];
-
-            if (!orderInfo) {
-                logger.error(`订单${orderId}不存在`);
+            // 订单校验
+            const orderCheckResult = await this.verifyOrderInternal(params.notify_id, config.paymentConfig.gameId, config);
+            if (!orderCheckResult.success) {
+                logger.error(`订单${params.reqid}校验失败: ${orderCheckResult.message}`);
                 return {
                     code: -1,
-                    msg: "订单不存在",
+                    msg: `订单校验失败: ${orderCheckResult.message}`,
                     data: null
                 };
             }
-
-            if (orderInfo.status == 2) {
-                logger.info(`订单${orderId}已经重复发货`);
+            const orderId = params.txid;//订单号
+            const out_trade_no = params.trans_id;//平台订单号
+            const validation = await PaymentHelper.validateOrder(orderId);
+            if (!validation.valid) {
                 return {
-                    code: 0,
-                    msg: "订单已发货",
-                    data: null
+                    code: validation.message?.includes("重复发货") ? 1 : 0,
+                    msg: validation.message || "订单验证失败"
                 };
             }
+            const orderInfo = validation.orderInfo;
+    
+            logger.info("IOS订单校验成功,开始处理支付发货逻辑");
+            const result = await PaymentHelper.deliverOrder(
+                orderInfo,
+                "127.0.0.1", // 使用默认IP地址,因为iOS支付没有客户端IP
+                validation.url,
+                out_trade_no
+            );
+            // 处理支付成功逻辑
+            logger.info(`iOS支付成功 - 用户: ${params.openid}, 金额: ${params.rmb}, 订单: ${out_trade_no}, 发货结果:`, result);
+            return result;
+
+        } catch (error) {
+            logger.error("iOS支付处理出错:", error);
+            return {
+                code: -1,
+                msg: "iOS支付处理失败",
+                data: null
+            };
+        }
+    }
+
+    /**
+     * 小程序支付回调处理
+     * @param params 请求参数
+     * @param config 渠道配置
+     */
+    private async handleMiniappPayment(params: any, config: ChannelConfig): Promise<PaymentResult> {
+        try {
+
+            // 验证必要参数
+            const requiredParams = ['uid', 'rmb', 'reqid', 'trans_id', 'product_id', 'notify_id', 'sign'];
+            for (const param of requiredParams) {
+                if (!params[param]) {
+                    logger.error(`缺少必要参数: ${param}`);
+                    return {
+                        code: -1,
+                        msg: `缺少必要参数: ${param}`,
+                        data: null
+                    };
+                }
+            }
 
-            // 验证金额
-            const amount = parseFloat(params.rmb);
-            if (orderInfo.amount != amount) {
-                logger.error("订单金额不一致", {
-                    "orderInfo.amount": orderInfo.amount,
-                    "params.amount": amount,
+            // 验证签名 - 排除platform和channel_id参数
+            const parameters = Object.keys(params)
+                .filter(key => key !== 'sign')
+                .map(key => `${key}=${params[key]}`);
+
+            const expectedSignature = this.generateSignature(parameters, config.paymentConfig.signKey!);
+            if (params.sign !== expectedSignature) {
+                logger.error("小程序支付签名验证失败", {
+                    received: params.sign,
+                    expected: expectedSignature
                 });
                 return {
                     code: -1,
-                    msg: "订单金额不一致",
+                    msg: "签名验证失败",
                     data: null
                 };
             }
-
+            const orderId = params.txid;//订单号
+            const out_trade_no = params.trans_id;//平台订单号
             // 订单校验 - 验证支付是否成功
             const orderCheckResult = await this.wxOrderCheck(params.notify_id, config);
             if (!orderCheckResult.success) {
@@ -295,42 +386,25 @@ export class MiniappChannelHandler implements ChannelHandler {
                 };
             }
 
-            // 获取服务器URL
-            const url = await getServerList(orderInfo.server_id, orderInfo.channel_id);
-            if (!url) {
-                logger.error(`区服id错误: serverId ${orderInfo.server_id}`);
+            const validation = await PaymentHelper.validateOrder(orderId);
+            if (!validation.valid) {
                 return {
-                    code: -1,
-                    msg: "区服id错误",
-                    data: null
+                    code: validation.message?.includes("重复发货") ? 1 : 0,
+                    msg: validation.message || "订单验证失败"
                 };
             }
-
+            const orderInfo = validation.orderInfo;
             // 发货处理
             logger.info(`小程序支付订单${orderId}通知游戏发货开始`);
-            const result = await this.deliverOrder(
+            const result = await PaymentHelper.deliverOrder(
                 orderInfo,
-                ctx.request.ip,
-                url,
-                params.trans_id
+                "127.0.0.1", // 使用默认IP地址,因为小程序支付没有客户端IP
+                validation.url,
+                out_trade_no
             );
-
-            if ((result as any).code === 1) {
-                logger.info(`小程序支付订单${orderId}发货成功`);
-                return {
-                    code: 0,
-                    msg: "发货成功",
-                    data: null
-                };
-            } else {
-                logger.error(`小程序支付订单${orderId}发货失败: ${(result as any).msg}`);
-                return {
-                    code: -1,
-                    msg: `发货失败: ${(result as any).msg}`,
-                    data: null
-                };
-            }
-
+            // 处理支付成功逻辑
+            logger.info(`小程序支付成功 - 用户: ${params.uid}, 金额: ${params.rmb}, 订单: ${out_trade_no}, 发货结果:`, result);
+            return result;
         } catch (error) {
             logger.error("小程序支付处理出错:", error);
             return {
@@ -846,16 +920,38 @@ export class MiniappChannelHandler implements ChannelHandler {
      * @returns 签名结果
      */
     private generateSignature(parameters: string[], privateKey: string): string {
-        // 将参数数组转换为对象
-        const params: any = {};
-        parameters.forEach(param => {
+        // 按照签名算法3处理
+        // 1. 过滤掉空值参数
+        const filteredParams = parameters.filter(param => {
             const [key, value] = param.split('=');
-            if (key && value) {
-                params[key] = value;
-            }
+            return key && value && value.trim() !== '';
         });
 
-        return this.generateCommonSignature(params, privateKey, ['sign'], true);
+        // 2. 按字母顺序排序
+        filteredParams.sort();
+
+        // 3. 对参数值进行URL编码,然后用&连接
+        const encodedParams = filteredParams.map(param => {
+            const [key, value] = param.split('=');
+            return `${key}=${encodeURIComponent(value)}`;
+        });
+        const queryString = encodedParams.join('&');
+
+        // 4. 添加私钥
+        const stringToSign = `${queryString}&key=${privateKey}`;
+
+        // 5. MD5哈希并转大写
+        const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
+
+        logger.info("签名算法3生成详情:", {
+            filteredParams,
+            encodedParams,
+            queryString,
+            stringToSign,
+            signature
+        });
+
+        return signature;
     }
 
     /**
@@ -894,4 +990,135 @@ export class MiniappChannelHandler implements ChannelHandler {
             return false;
         }
     }
+
+    /**
+     * 验证iOS支付签名(签名算法3)
+     * @param params 请求参数
+     * @param config 渠道配置
+     * @returns 验证结果
+     */
+    private verifyPaymentSignature(params: any, config: ChannelConfig): boolean {
+        try {
+            // 获取签名密钥
+            const signKey = config.paymentConfig.signKey;
+            if (!signKey) {
+                logger.error("iOS支付签名密钥未配置");
+                return false;
+            }
+
+            // 过滤掉sign参数,按参数名排序
+            const sortedParams = Object.keys(params)
+                .filter(key => key !== 'sign')
+                .sort()
+                .map(key => `${key}=${params[key]}`);
+
+            // 构建签名字符串
+            const signString = sortedParams.join('&') + `&key=${signKey}`;
+
+            // 计算MD5签名
+            const expectedSignature = CryptoJS.MD5(signString).toString().toUpperCase();
+
+            logger.info("iOS支付签名验证", {
+                signString,
+                receivedSignature: params.sign,
+                expectedSignature,
+                isValid: params.sign === expectedSignature
+            });
+
+            return params.sign === expectedSignature;
+        } catch (error) {
+            logger.error("iOS支付签名验证出错:", error);
+            return false;
+        }
+    }
+
+    /**
+     * 内部订单校验方法
+     * @param notifyId 平台通知ID
+     * @param gameId 游戏ID
+     * @param config 渠道配置
+     * @returns 校验结果
+     */
+    private async verifyOrderInternal(notifyId: string, gameId: string, config: ChannelConfig): Promise<{ success: boolean; message: string }> {
+        try {
+            // 构建验证参数
+            const parameters = [
+                `gameid=${gameId}`,
+                `notify_id=${notifyId}`
+            ];
+
+            // 生成签名
+            const signature = this.generatePaymentSignature(parameters, config.paymentConfig.signKey!);
+            const apiUrl = `https://login.11h5.com/pay/paygate/verify.php?${parameters.join('&')}&sign=${signature}`;
+
+            logger.info("iOS订单校验请求", { apiUrl });
+
+            // 调用第三方API验证订单
+            const response = await axios.get(apiUrl, {
+                timeout: 10000,
+                headers: {
+                    'User-Agent': 'Mozilla/5.0 (compatible; GameServer/1.0)'
+                }
+            });
+
+            logger.info("iOS订单校验响应", { 
+                status: response.status, 
+                data: response.data 
+            });
+
+            if (response.status === 200) {
+                const result = response.data;
+                // 处理纯文本"SUCCESS"响应
+                if (result === "SUCCESS") {
+                    return { success: true, message: "订单校验成功" };
+                } 
+                // 处理JSON格式响应
+                else if (result && typeof result === 'object' && result.code === 0) {
+                    return { success: true, message: "订单校验成功" };
+                } else {
+                    return { 
+                        success: false, 
+                        message: result?.msg || "订单校验失败" 
+                    };
+                }
+            } else {
+                return { 
+                    success: false, 
+                    message: `HTTP错误: ${response.status}` 
+                };
+            }
+        } catch (error) {
+            logger.error("iOS订单校验出错:", error);
+            return { 
+                success: false, 
+                message: error.message || "订单校验异常" 
+            };
+        }
+    }
+
+    /**
+     * 生成iOS支付签名(签名算法3)
+     * @param parameters 参数数组
+     * @param signKey 签名密钥
+     * @returns 签名字符串
+     */
+    private generatePaymentSignature(parameters: string[], signKey: string): string {
+        try {
+            // 构建签名字符串
+            const signString = parameters.join('&') + `&key=${signKey}`;
+            
+            // 计算MD5签名并转为大写
+            const signature = CryptoJS.MD5(signString).toString().toUpperCase();
+            
+            logger.info("生成iOS支付签名", {
+                signString,
+                signature
+            });
+            
+            return signature;
+        } catch (error) {
+            logger.error("生成iOS支付签名出错:", error);
+            return "";
+        }
+    }
 }

+ 22 - 15
webServer/src/channels/handlers/SYIOSChannelHandler.ts

@@ -2,6 +2,7 @@ import { Context } from "koa";
 import { ChannelHandler, LoginResult, PaymentResult } from "../interfaces/ChannelHandler";
 import { ChannelConfig } from "../../config/channelConfig";
 import axios from "axios";
+import {PaymentHelper} from "../../utils/PaymentHelper";
 
 const logger = require("../../utils/log");
 
@@ -43,7 +44,7 @@ export class SYIOSChannelHandler implements ChannelHandler {
      */
     async handleLogin(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
         try {
-            const { userToken, gameid } = ctx.request.body || ctx.request.query;
+            const { userToken } = ctx.request.body || ctx.request.query;
             
             // 验证必要参数
             if (!userToken) {
@@ -54,16 +55,7 @@ export class SYIOSChannelHandler implements ChannelHandler {
                     data: null
                 };
             }
-
-            if (!gameid) {
-                logger.error("iOS登录鉴权失败 - 缺少gameid参数");
-                return {
-                    code: -1,
-                    msg: "缺少必要参数: gameid",
-                    data: null
-                };
-            }
-
+            const gameid = config.paymentConfig?.gameId || '1550';
             logger.info("iOS登录鉴权请求参数:", { userToken, gameid });
 
             // 调用第三方鉴权API
@@ -149,7 +141,7 @@ export class SYIOSChannelHandler implements ChannelHandler {
             logger.info("iOS支付回调参数:", data);
 
             // 验证必要参数(根据API文档)
-            const requiredParams = ['openid', 'rmb', 'reqid', 'trans_id', 'product_id', 'notify_id', 'sign'];
+            const requiredParams = ['openid', 'rmb', 'reqid', 'trans_id', 'product_id', 'notify_id', 'sign','txid'];
             for (const param of requiredParams) {
                 if (!data[param]) {
                     logger.error(`iOS支付回调缺少必要参数: ${param}`);
@@ -171,7 +163,7 @@ export class SYIOSChannelHandler implements ChannelHandler {
 
             // 提取notify_id进行订单校验
             const notify_id = data.notify_id;
-            const gameid = data.product_id || config.paymentConfig?.gameId || '1540';
+            const gameid =  config.paymentConfig?.gameId || '1550';
             
             logger.info(`开始订单校验 - notify_id: ${notify_id}, gameid: ${gameid}`);
 
@@ -185,11 +177,26 @@ export class SYIOSChannelHandler implements ChannelHandler {
                     msg: `订单校验失败: ${verifyResult.message}`
                 };
             }
+            const orderId = data.txid;//订单号
+            const out_trade_no = data.trans_id;//平台订单号
+            const validation = await PaymentHelper.validateOrder(orderId);
+            if (!validation.valid) {
+                return {
+                    code: validation.message?.includes("重复发货") ? 1 : 0,
+                    msg: validation.message || "订单验证失败"
+                };
+            }
+            const orderInfo = validation.orderInfo;
 
             logger.info("订单校验成功,开始处理支付发货逻辑");
-
+            const result = await PaymentHelper.deliverOrder(
+                orderInfo,
+                ctx.request.ip,
+                validation.url,
+                out_trade_no
+            );
             // 处理支付成功逻辑
-            logger.info(`iOS支付成功 - 用户: ${data.openid}, 金额: ${data.rmb}, 订单: ${data.reqid}`);
+            logger.info(`iOS支付成功 - 用户: ${data.openid}, 金额: ${data.rmb}, 订单: ${out_trade_no}, 发货结果:`, result);
             
             // 返回SUCCESS表示处理成功(根据API文档要求)
             ctx.body = "SUCCESS";

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

@@ -211,7 +211,7 @@ export const channelConfigs: Record<number, ChannelConfig> = {
     },
     12: {
         // ios
-        channelId: 12,
+        channelId: 11,
         name: "ios",
         platform: "sy_ios",
         paymentConfig: {

+ 11 - 3
webServer/src/config/dbConfig.ts

@@ -1,12 +1,20 @@
 
 export const mysqlConfig = {
-    user: "admin", //账号
-    password: "aEsf$sfd@#411", //密码
+    user: "root", //账号
+    password: "wch123.com", //密码
     database: "sdk", //数据库
-    host: "118.145.139.78", //服务器地址
+    host: "127.0.0.1", //服务器地址
     port: 3306, //数据库端口
 };
 
+// export const mysqlConfig = {
+//   user: "admin", //账号
+//   password: "aEsf$sfd@#411", //密码
+//   database: "sdk", //数据库
+//   host: "118.145.139.78", //服务器地址
+//   port: 3306, //数据库端口
+// };
+
 export const redisConfig = {
     host: 'localhost',
     port: 6379,

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

@@ -94,7 +94,7 @@ export const QIAI_QUICK_H5_PRODUCT_CODE = "80939198711722706880933346971719";
 //小程序
 
 export const MINI_APP_DOMAIN = "wefunol.com";
-export const MINI_APP_PRIVATE_KEY = "yduLY3ovg4NwTHMMemGg1vO6VHuYBcYD";
+export const MINI_APP_PRIVATE_KEY = "lxq05Tka0pXR8IxK5Imz61qF1XZVwhEj";
 export const MINI_APP_GAME_ID = "1540";
 export const MINI_APP_API_KEY = "your_api_key_here";
 export const MINI_APP_STAT_API_KEY = "your_stat_api_key_here";

+ 102 - 11
webServer/src/controller/ApiController.ts

@@ -18,6 +18,7 @@ import {
 import { PaymentHelper } from "../utils/PaymentHelper";
 import { SignatureVerifier } from "../utils/SignatureVerifier";
 import { ChannelConfigManager } from "../utils/ChannelConfigManager";
+import {getRoleInfoByUidAndServerId} from "../mongo/mongodb";
 
 // 导入依赖
 const CryptoJS = require("crypto-js");
@@ -484,7 +485,8 @@ class ApiController {
   async callPay(ctx) {
     const data = ctx.request.body;
     let channelId = 1; // 默认渠道ID
-
+    //打印回调参数
+    logger.info("callPay支付回调参数:", { url: ctx.href, params: data, query: ctx.query });
 
     // 从请求中获取渠道ID
     if (ctx.query.channel_id) {
@@ -530,7 +532,6 @@ class ApiController {
 
     // 调用渠道处理器的支付方法
     const result = await handler.handlePayment(ctx, channelConfig);
-
     // 根据不同渠道的返回格式要求处理结果
     switch (channelConfig.channelId) {
       case 6:
@@ -548,6 +549,9 @@ class ApiController {
       case 11:
         ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
         break;
+      case 12:
+        ctx.body = result.code === 1 ? "SUCCESS" : "Fail";
+        break;
       default:
         ctx.body = result;
     }
@@ -601,11 +605,46 @@ class ApiController {
   }
   async getAllServerList(ctx) {
     let tag = ctx.query.channel_id || 1;
+    let uid = ctx.query.uid || ctx.request.body?.uid; // uid是可选参数,支持GET和POST请求
     let ip = getClientIp(ctx);
-    logger.info("getAllServerList 区服接口", { tag: tag, ip: ip });
+    logger.info("getAllServerList 区服接口", { tag: tag, uid: uid, ip: ip });
+    
     const servers = await Server.getAllServerList(tag, ip);
-    ctx.body = servers;
-    // ctx.body = ApiController.success("获取服务器列表成功", 1, false, servers);
+    
+    // 如果提供了玩家ID,则查询每个服务器的角色信息;否则只返回基本服务器信息
+    if (uid) {
+      const { getRoleInfoByUidAndServerId } = require("../mongo/mongodb");
+      
+      // 为每个服务器添加角色信息
+      const serversWithRoleInfo = await Promise.all(
+        servers.map(async (server) => {
+          try {
+            const newUniqueTag = `${server.tag}|${server.sid}|${uid}`;
+            const roleInfo = await getRoleInfoByUidAndServerId(newUniqueTag, server.db_name);
+            return {
+              ...server,
+              roleName: roleInfo?.roleName || null,
+              roleLevel: roleInfo?.roleLevel || null,
+              head: roleInfo?.head || null,
+              hasRole: !!roleInfo
+            };
+          } catch (error) {
+            logger.error(`查询服务器${server.id}角色信息失败:`, error);
+            return {
+              ...server,
+              roleName: null,
+              roleLevel: null,
+              head: null,
+              hasRole: false
+            };
+          }
+        })
+      );
+      
+      ctx.body = serversWithRoleInfo;
+    } else {
+      ctx.body = servers;
+    }
   }
 
   async enterServer(ctx) {
@@ -645,13 +684,14 @@ class ApiController {
 
   async getLastServerList(ctx) {
     let { uid, channel_id } = ctx.request.body;
+    // uid是可选参数,如果不提供则只返回基本服务器信息
 
     let tag = channel_id || 1;
     let data = [];
     let isNewAccount = 1;
-    let enterServerList = await Server.getEnterServerListByUid(uid, channel_id);
+    let enterServerList = [];
     let ip = getClientIp(ctx);
-    logger.info("getLastServerList 区服接口", { ip: ip });
+    logger.info("getLastServerList 区服接口", { uid: uid, ip: ip });
 
     // 获取渠道配置
     const channelConfig = ChannelConfigManager.getConfig(tag);
@@ -661,11 +701,29 @@ class ApiController {
       return;
     }
 
+    // 如果提供了uid,查询用户进入过的服务器列表
+    if (uid) {
+      enterServerList = await Server.getEnterServerListByUid(uid, channel_id);
+    }
+
     if (enterServerList.length > 0) {
       isNewAccount = 0;
       const servers = await Server.getAllServerList(tag, ip);
-      enterServerList.forEach(function (element) {
-        if (element.server_id != "999"){
+      const { getRoleInfoByUidAndServerId } = require("../mongo/mongodb");
+      
+      // 为每个进入过的服务器添加角色信息
+      for (const element of enterServerList) {
+          let roleInfo = null;
+          // 只有在提供了uid时才查询角色信息
+          if (uid) {
+            try {
+              const newUniqueTag = `${tag}|${element.server_id}|${uid}`;
+               roleInfo = await getRoleInfoByUidAndServerId(newUniqueTag, element.db_name);
+            } catch (error) {
+              logger.error(`查询服务器${element.server_id}角色信息失败:`, error);
+            }
+          }
+          
           data.push({
             channel: channelConfig.name, //渠道固定
             minSid: 1, //最小服务器
@@ -680,20 +738,38 @@ class ApiController {
                 ? element.wss
                 : `ws://${element.ip}:${element.port}`,
             status: element.status || 0,
+            // 角色信息(只有在提供了uid时才包含)
+            roleName: roleInfo?.roleName || null,
+            roleLevel: roleInfo?.roleLevel || null,
+            head: roleInfo?.head || null,
+            hasRole: !!roleInfo
           });
-        }
-      });
+
+      }
       logger.info("getLastServerList 区服接口 enterServerList", {
         enterServerList: enterServerList,
       });
     } else {
       const servers = await Server.getAllServerList(tag, ip);
+      let roleInfo = null;
 
       if (servers.length > 0) {
         const serverInfo = servers[servers.length - 1];
         logger.info("getLastServerList 区服接口 serverInfo", {
           serverInfo: serverInfo,
         });
+        
+        // 只有在提供了uid时才查询最后一个服务器的角色信息
+        if (uid) {
+          const { getRoleInfoByUidAndServerId } = require("../mongo/mongodb");
+          try {
+            const newUniqueTag = `${serverInfo.tag}|${serverInfo.sid}|${uid}`;
+             roleInfo = await getRoleInfoByUidAndServerId(newUniqueTag, serverInfo.db_name);
+          } catch (error) {
+            logger.error(`查询服务器${serverInfo.id}角色信息失败:`, error);
+          }
+        }
+        
         data.push({
           channel: channelConfig.name, //渠道固定
           minSid: 1, //最小服务器
@@ -708,6 +784,11 @@ class ApiController {
             ? serverInfo.server
             : `ws://${serverInfo.ip}:${serverInfo.port}/`,
           status: serverInfo.status || 0,
+          // 角色信息
+          roleName: roleInfo?.roleName || null,
+          roleLevel: roleInfo?.roleLevel || null,
+          head: roleInfo?.head || null,
+          hasRole: !!roleInfo
         });
       } else {
         data.push({
@@ -722,6 +803,11 @@ class ApiController {
           tips: "",
           server: "",
           status: 0,
+          // 角色信息(新用户,无角色)
+          roleName: null,
+          roleLevel: null,
+          head: null,
+          hasRole: false
         });
       }
     }
@@ -961,6 +1047,11 @@ class ApiController {
         "GFYX333",
         "GFYX888",
         "GFYX1818",
+        "GQ888",
+        "ZQ888",
+        "gfyx8888",
+        "gfyx6666",
+        "gfyx7777"
       ];
       let param: string = "";
 

+ 5 - 1
webServer/src/model/ServerModel.ts

@@ -48,6 +48,8 @@ class ServerModel {
         tips: row.tips,
         ip: row.ip,
         port: row.port,
+        db_name: row.db_name,
+        tag:row.tag,
       };
     });
     return resultArray;
@@ -62,7 +64,9 @@ class ServerModel {
 
   async getEnterServerListByUid(uid: any, tag: any) {
     return await query(
-      `SELECT ges.id, ges.uid, ges.server_id, ges.create_time as last_login_time , gs.ip, gs.port,gs.status,gs.name,gs.tips,gs.wss FROM game_enter_server as ges left join game_server as gs on ges.server_id = gs.sid and ges.tag = gs.tag  WHERE ges.uid = ? and ges.tag = ? order by ges.create_time desc `,
+      `SELECT ges.id, ges.uid, ges.server_id, ges.create_time as last_login_time , gs.ip, gs.port,gs.status,gs.name,
+           gs.tips,gs.wss,gs.db_name,gs.is_show FROM game_enter_server as ges left join game_server as gs on ges.server_id = gs.sid and
+            ges.tag = gs.tag  WHERE ges.uid = ? and ges.tag = ? AND gs.is_show=1 order by ges.create_time desc `,
       [uid, tag]
     );
   }

+ 29 - 1
webServer/src/mongo/mongodb.ts

@@ -82,4 +82,32 @@ async function getGuildInfoById(dbName: string, uuid: string) {
     }
 }
 
-export { getDb, getCollection, closeConnection, getRoleInfoById, connectToMongo, getGuildInfoById };
+//根据用户ID和服务器ID查询角色信息
+async function getRoleInfoByUidAndServerId(newUniqueTag: string, dbName: string) {
+    try {
+        if (!client) {
+            await connectToMongo();
+        }
+        const database = client!.db(dbName);
+        const collection = database.collection('char');
+        
+        // 查询该用户在该服务器上的角色信息
+        const roleInfo = await collection.findOne({newUniqueTag: newUniqueTag});
+        
+        if (roleInfo) {
+            return {
+                roleName: roleInfo.name || "未知角色",
+                roleLevel: roleInfo.lv || 1,
+                roleId: roleInfo.roleId || "",
+                head: roleInfo.head || "",
+            };
+        }
+        
+        return null;
+    } catch (error) {
+        console.error('查询角色信息失败:', error);
+        return null;
+    }
+}
+
+export { getDb, getCollection, closeConnection, getRoleInfoById, connectToMongo, getGuildInfoById, getRoleInfoByUidAndServerId };

+ 21 - 6
webServer/src/utils/msg.ts

@@ -163,6 +163,12 @@ export default class Msg {
   }
 
   writeString(str) {
+    // 参数验证:确保 str 不为 undefined 或 null
+    if (str === undefined || str === null) {
+      console.error("writeString 接收到 undefined 或 null 参数:", str);
+      str = ""; // 使用空字符串作为默认值
+    }
+    
     var oldPos = this.sendBufLen;
     this.writeShort(0);
     var totalByte = 0;
@@ -223,14 +229,23 @@ export default class Msg {
     logger.info("发送发货消息开始", {params:params, readyState: this.websocket.readyState, isConnect: this.isConnect });
     // console.log("发送登录消息", this.websocket.readyState, this.isConnect);
     if (this.isConnect != true || this.websocket.readyState != 1) return false;
+    
+    // 参数验证和默认值处理
+    const safeAccount = account || "";
+    const safeAuthkey = authkey || "";
+    const safeLang = lang || "cn";
+    const safeRegion = region || "CN";
+    const safeIp = ip || "127.0.0.1";
+    const safeParams = params || "{}";
+    
     this.sendBufLen = 4;
-    this.writeString(account);
+    this.writeString(safeAccount);
     this.writeInt(timestamp);
-    this.writeString(authkey);
-    this.writeString(lang);
-    this.writeString(region);
-    this.writeString(ip);
-    this.writeString(params);
+    this.writeString(safeAuthkey);
+    this.writeString(safeLang);
+    this.writeString(safeRegion);
+    this.writeString(safeIp);
+    this.writeString(safeParams);
     this.writeInt(server_id);
     this.sendBufDV.setInt16(2, 62);
     this.sendBufDV.setInt16(0, this.sendBufLen - 4);