flowerpig 8 месяцев назад
Родитель
Сommit
f31f05d859

+ 742 - 662
webServer/src/channels/handlers/MiniappChannelHandler.ts

@@ -1,12 +1,9 @@
-import { Context } from "koa";
-import {
-  ChannelHandler,
-  LoginResult,
-  PaymentResult,
-} from "../interfaces/ChannelHandler";
-import { ChannelConfig } from "../../config/channelConfig";
-import { getServerList, formatDate } from "../../utils/common";
-import { Account } from "../../config/thirdParams";
+import {Context} from "koa";
+import * as crypto from 'crypto';
+import {ChannelHandler, LoginResult, PaymentResult,} from "../interfaces/ChannelHandler";
+import {ChannelConfig} from "../../config/channelConfig";
+import {formatDate, getServerList} from "../../utils/common";
+import {Account} from "../../config/thirdParams";
 import Msg from "../../utils/msg";
 import axios from "axios";
 
@@ -15,682 +12,765 @@ const CryptoJS = require("crypto-js");
 const logger = require("../../utils/log");
 
 export class MiniappChannelHandler implements ChannelHandler {
-  /**
-   * 获取HTTPS代理配置
-   * @returns HTTPS代理配置
-   */
-  private getHttpsAgent() {
-    const https = require('https');
-    return new https.Agent({
-      rejectUnauthorized: false,
-      secureProtocol: 'TLSv1_2_method',
-      timeout: 10000
-    });
-  }
-
-  /**
-   * 获取通用请求头
-   * @returns 请求头配置
-   */
-  private getCommonHeaders() {
-    return {
-      'User-Agent': 'Mozilla/5.0 (compatible; WebServer/1.0)',
-      'Accept': 'application/json',
-      'Content-Type': 'application/json'
-    };
-  }
-
-  /**
-   * 小程序登录鉴权
-   * @param ctx Koa上下文
-   * @param config 渠道配置
-   */
-    async handleLogin(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
-    try {
-      const data = ctx.request.body as any;
-      const token = data.token;
-      
-      if (!token) {
+    /**
+     * 获取HTTPS代理配置
+     * @returns HTTPS代理配置
+     */
+    private getHttpsAgent() {
+        const https = require('https');
+        return new https.Agent({
+            rejectUnauthorized: false,
+            secureProtocol: 'TLSv1_2_method',
+            timeout: 10000
+        });
+    }
+
+    /**
+     * 获取通用请求头
+     * @returns 请求头配置
+     */
+    private getCommonHeaders() {
         return {
-          code: -1,
-          msg: "缺少token参数",
-          data: null
+            'User-Agent': 'Mozilla/5.0 (compatible; WebServer/1.0)',
+            'Accept': 'application/json',
+            'Content-Type': 'application/json'
         };
-      }
-
-      const loginUrl = `https://wxlogin.${config.paymentConfig.apiUrl}/wxlogin?cmd=checkUserToken&token=${token}`;
-      logger.info("小程序登录请求", { token, loginUrl });
-      
-      const wxLogin = await axios.get(loginUrl, {
-        httpsAgent: this.getHttpsAgent(),
-        headers: this.getCommonHeaders()
-      });
-      logger.info("小程序登录响应", { data: wxLogin.data });
-
-      return {
-        code: 0,
-        msg: "success",
-        data: wxLogin.data,
-      };
-    } catch (error) {
-      logger.error('wxLogin error:', error);
-      return {
-        code: -1,
-        msg: error.message || "登录验证失败",
-        data: null,
-      };
     }
-  }
-
-  /**
-   * 小程序支付回调处理
-   * @param ctx Koa上下文
-   * @param config 渠道配置
-   */
-  async handlePayment(ctx: Context, config: ChannelConfig): Promise<PaymentResult> {
-    try {
-      // 获取请求参数
-      const params = ctx.request.query as any;
-      logger.info("小程序支付回调参数:", { url: ctx.href, params: params });
-
-      // 验证必要参数
-      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
-          };
+
+    /**
+     * 小程序登录鉴权
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async handleLogin(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        try {
+            const data = ctx.request.body as any;
+            const token = data.token;
+
+            if (!token) {
+                return {
+                    code: -1,
+                    msg: "缺少token参数",
+                    data: null
+                };
+            }
+
+            const loginUrl = `https://wxlogin.${config.paymentConfig.apiUrl}/wxlogin?cmd=checkUserToken&token=${token}`;
+            logger.info("小程序登录请求", {token, loginUrl});
+
+            const wxLogin = await axios.get(loginUrl, {
+                httpsAgent: this.getHttpsAgent(),
+                headers: this.getCommonHeaders()
+            });
+            logger.info("小程序登录响应", {data: wxLogin.data});
+            if (!wxLogin.data || Object.keys(wxLogin.data).length === 0) {
+                return {
+                    code: -1,
+                    msg: "登录验证失败",
+                    data: null,
+                };
+            }
+            return {
+                code: 0,
+                msg: "success",
+                data: wxLogin.data,
+            };
+        } catch (error) {
+            logger.error('wxLogin error:', error);
+            return {
+                code: -1,
+                msg: error.message || "登录验证失败",
+                data: null,
+            };
         }
-      }
-
-      // 验证签名
-      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: "签名验证失败",
-          data: null
-        };
-      }
+    }
 
-      // 获取订单信息
-      const orderId = params.reqid;
-      const orderInfo = (await Order.getOrder(orderId))[0];
+    /**
+     * 小程序支付回调处理
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async handlePayment(ctx: Context, config: ChannelConfig): Promise<PaymentResult> {
+        try {
+            // 获取请求参数
+            const params = ctx.request.query as any;
+            logger.info("小程序支付回调参数:", {url: ctx.href, params: params});
+
+            // 验证必要参数
+            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 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: "签名验证失败",
+                    data: null
+                };
+            }
+
+            // 获取订单信息
+            const orderId = params.reqid;
+            const orderInfo = (await Order.getOrder(orderId))[0];
+
+            if (!orderInfo) {
+                logger.error(`订单${orderId}不存在`);
+                return {
+                    code: -1,
+                    msg: "订单不存在",
+                    data: null
+                };
+            }
+
+            if (orderInfo.status == 2) {
+                logger.info(`订单${orderId}已经重复发货`);
+                return {
+                    code: 0,
+                    msg: "订单已发货",
+                    data: null
+                };
+            }
+
+            // 验证金额
+            const amount = parseFloat(params.rmb);
+            if (orderInfo.amount != amount) {
+                logger.error("订单金额不一致", {
+                    "orderInfo.amount": orderInfo.amount,
+                    "params.amount": amount,
+                });
+                return {
+                    code: -1,
+                    msg: "订单金额不一致",
+                    data: null
+                };
+            }
+
+            // 订单校验 - 验证支付是否成功
+            const orderCheckResult = await this.wxOrderCheck(params.notify_id, config);
+            if (!orderCheckResult.success) {
+                logger.error(`订单${orderId}校验失败: ${orderCheckResult.message}`);
+                return {
+                    code: -1,
+                    msg: `订单校验失败: ${orderCheckResult.message}`,
+                    data: null
+                };
+            }
+
+            // 获取服务器URL
+            const url = await getServerList(orderInfo.server_id, orderInfo.channel_id);
+            if (!url) {
+                logger.error(`区服id错误: serverId ${orderInfo.server_id}`);
+                return {
+                    code: -1,
+                    msg: "区服id错误",
+                    data: null
+                };
+            }
+
+            // 发货处理
+            logger.info(`小程序支付订单${orderId}通知游戏发货开始`);
+            const result = await this.deliverOrder(
+                orderInfo,
+                ctx.request.ip,
+                url,
+                params.trans_id
+            );
+
+            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
+                };
+            }
 
-      if (!orderInfo) {
-        logger.error(`订单${orderId}不存在`);
-        return {
-          code: -1,
-          msg: "订单不存在",
-          data: null
-        };
-      }
+        } catch (error) {
+            logger.error("小程序支付处理出错:", error);
+            return {
+                code: -1,
+                msg: "支付处理失败",
+                data: null
+            };
+        }
+    }
 
-      if (orderInfo.status == 2) {
-        logger.info(`订单${orderId}已经重复发货`);
-        return {
-          code: 0,
-          msg: "订单已发货",
-          data: null
-        };
-      }
-
-      // 验证金额
-      const amount = parseFloat(params.rmb);
-      if (orderInfo.amount != amount) {
-        logger.error("订单金额不一致", {
-          "orderInfo.amount": orderInfo.amount,
-          "params.amount": amount,
-        });
-        return {
-          code: -1,
-          msg: "订单金额不一致",
-          data: null
-        };
-      }
+    /**
+     * 订单校验 - 验证支付是否成功
+     * @param notifyId 平台通知ID
+     * @param config 渠道配置
+     * @returns 校验结果
+     */
+    async wxOrderCheck(notifyId: string, config: ChannelConfig): Promise<{ success: boolean; message: string }> {
+        try {
+            // 构建验证参数
+            const parameters = [
+                `gameid=${config.paymentConfig.gameId}`,
+                `notify_id=${notifyId}`
+            ];
+
+            // 生成签名
+            const signature = this.generateSignature(parameters, config.paymentConfig.signKey!);
+
+            // 构建验证URL
+            const verifyUrl = `https://login.${config.paymentConfig.apiUrl}/pay/paygate/verify.php?gameid=${config.paymentConfig.gameId}&notify_id=${notifyId}&sign=${signature}`;
+
+            logger.info("订单校验请求", {notifyId, verifyUrl});
+
+            // 调用平台验证接口
+            const response = await axios.get(verifyUrl, {
+                httpsAgent: this.getHttpsAgent(),
+                headers: {
+                    ...this.getCommonHeaders(),
+                    'Accept': 'text/plain'
+                }
+            });
+
+            logger.info("订单校验响应", {notifyId, response: response.data});
+
+            // 检查返回结果
+            if (response.data === "SUCCESS") {
+                return {
+                    success: true,
+                    message: "订单支付成功"
+                };
+            } else {
+                return {
+                    success: false,
+                    message: "订单支付失败或无效"
+                };
+            }
 
-      // 订单校验 - 验证支付是否成功
-      const orderCheckResult = await this.wxOrderCheck(params.notify_id, config);
-      if (!orderCheckResult.success) {
-        logger.error(`订单${orderId}校验失败: ${orderCheckResult.message}`);
-        return {
-          code: -1,
-          msg: `订单校验失败: ${orderCheckResult.message}`,
-          data: null
-        };
-      }
+        } catch (error) {
+            logger.error("订单校验出错:", {notifyId, error: error.message});
+            return {
+                success: false,
+                message: "订单校验失败"
+            };
+        }
+    }
 
-      // 获取服务器URL
-      const url = await getServerList(orderInfo.server_id, orderInfo.channel_id);
-      if (!url) {
-        logger.error(`区服id错误: serverId ${orderInfo.server_id}`);
-        return {
-          code: -1,
-          msg: "区服id错误",
-          data: null
-        };
-      }
-
-      // 发货处理
-      logger.info(`小程序支付订单${orderId}通知游戏发货开始`);
-      const result = await this.deliverOrder(
-        orderInfo,
-        ctx.request.ip,
-        url,
-        params.trans_id
-      );
-
-      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
-        };
-      }
-
-    } catch (error) {
-      logger.error("小程序支付处理出错:", error);
-      return {
-        code: -1,
-        msg: "支付处理失败",
-        data: null
-      };
+    /**
+     * 内容安全审核接口
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async contentSecurityCheck(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        try {
+            const data = ctx.request.body as any;
+            logger.info("内容安全审核请求参数:", data);
+
+            // 验证必要参数
+            const requiredParams = ['uid', 'scene', 'content'];
+            for (const param of requiredParams) {
+                if (!data[param]) {
+                    logger.error(`缺少必要参数: ${param}`);
+                    return {
+                        code: -1,
+                        msg: `缺少必要参数: ${param}`,
+                        data: null
+                    };
+                }
+            }
+            data['timestamp'] = Math.floor(Date.now() / 1000); // 添加时间戳参数
+            data['gameid'] = config.paymentConfig.gameId; // 添加游戏ID参数
+            data['nonce'] = Math.floor(Math.random() * 1000000); // 添加随机数参数
+            //生成签名
+            data['signature'] = this.generateContentSecuritySignature(data, config);
+            if (!data['signature']) {
+                return {
+                    code: -1,
+                    msg: "签名生成失败",
+                    data: null
+                };
+            }
+            // 构建请求参数
+            const requestData = {
+                uid: data.uid,
+                gameid: data.gameid,
+                signature: data.signature,
+                timestamp: data.timestamp,
+                nonce: data.nonce,
+                scene: data.scene,
+                content: data.content,
+                nickname: data.nickname || '',
+                title: data.title || '',
+                usersign: data.usersign || ''
+            };
+
+            // 调用内容安全API
+            const apiUrl = `https://api.${config.paymentConfig.apiUrl}/mpcommon/?cmd=wxaMsgSecCheck`;
+            logger.info("调用内容安全API:", {url: apiUrl, data: requestData});
+
+            const response = await axios.post(apiUrl, requestData, {
+                httpsAgent: this.getHttpsAgent(),
+                headers: this.getCommonHeaders()
+            });
+
+            logger.info("内容安全API响应:", response.data);
+
+            // 处理响应结果
+            if (response.data && response.data.error === 0) {
+                const result = response.data.result;
+                return {
+                    code: 0,
+                    msg: "success",
+                    data: {
+                        suggest: result.suggest, // pass/review/reject
+                        label: result.label,
+                        trace_id: response.data.trace_id,
+                        detail: response.data.detail
+                    }
+                };
+            } else {
+                return {
+                    code: -1,
+                    msg: response.data?.errmsg || "内容安全检测失败",
+                    data: null
+                };
+            }
+
+        } catch (error) {
+            logger.error("内容安全审核出错:", error);
+            return {
+                code: -1,
+                msg: "内容安全审核失败",
+                data: null
+            };
+        }
     }
-  }
-
-  /**
-   * 订单校验 - 验证支付是否成功
-   * @param notifyId 平台通知ID
-   * @param config 渠道配置
-   * @returns 校验结果
-   */
-  async wxOrderCheck(notifyId: string, config: ChannelConfig): Promise<{ success: boolean; message: string }> {
-    try {
-      // 构建验证参数
-      const parameters = [
-        `gameid=${config.paymentConfig.gameId}`,
-        `notify_id=${notifyId}`
-      ];
-
-      // 生成签名
-      const signature = this.generateSignature(parameters, config.paymentConfig.signKey!);
-      
-      // 构建验证URL
-      const verifyUrl = `https://login.${config.paymentConfig.apiUrl}/pay/paygate/verify.php?gameid=${config.paymentConfig.gameId}&notify_id=${notifyId}&sign=${signature}`;
-      
-      logger.info("订单校验请求", { notifyId, verifyUrl });
-
-      // 调用平台验证接口
-      const response = await axios.get(verifyUrl, {
-        httpsAgent: this.getHttpsAgent(),
-        headers: {
-          ...this.getCommonHeaders(),
-          'Accept': 'text/plain'
+
+    /**
+     * 角色名称修改上报
+     * @param ctx Koa上下文
+     * @param config 渠道配置
+     */
+    async editUserRoleInfo(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
+        try {
+            const data = ctx.request.body as any;
+            logger.info("角色名称修改上报请求参数:", data);
+            // 验证必要参数
+            const requiredParams = ['openId', 'serverid', 'playerName'];
+            for (const param of requiredParams) {
+                if (!data[param]) {
+                    logger.error(`缺少必要参数: ${param}`);
+                    return {
+                        code: -1,
+                        msg: `缺少必要参数: ${param}`,
+                        data: null
+                    };
+                }
+            }
+            data['time'] = Math.floor(Date.now() / 1000); // 添加时间戳参数
+            data['gameid'] = config.paymentConfig.gameId; // 添加游戏ID参数
+            
+            // 处理playerName的各种编码格式
+            let decodedPlayerName = data.playerName;
+            try {
+                // 检查是否是URL编码格式
+                if (data.playerName.includes('%')) {
+                    decodedPlayerName = decodeURIComponent(data.playerName);
+                    console.log("playerName URL decoded from:", data.playerName);
+                    console.log("playerName URL decoded to:", decodedPlayerName);
+                }
+                // 检查是否是Unicode转义序列格式
+                else if (data.playerName.includes('\\u')) {
+                    decodedPlayerName = JSON.parse('"' + data.playerName + '"');
+                    console.log("playerName Unicode decoded from:", data.playerName);
+                    console.log("playerName Unicode decoded to:", decodedPlayerName);
+                } else {
+                    console.log("playerName is not encoded:", data.playerName);
+                }
+            } catch (error) {
+                console.log("playerName decode error, using original:", error.message);
+                decodedPlayerName = data.playerName;
+            }
+            
+            // 更新data中的playerName为解码后的值
+            data.playerName = decodedPlayerName;
+            console.log("playerName for signature:", data.playerName);
+            
+            //生成签名
+            data['sign'] = this.generateRoleInfoSignature(data, config);
+            
+            // 构建请求参数 - 对playerName进行URL编码
+            const encodedPlayerName = encodeURIComponent(data.playerName);
+            console.log("playerName for request:", encodedPlayerName);
+            
+            // 手动构建URL查询字符串,避免对已编码参数进行二次编码
+            const queryString = [
+                `openId=${encodeURIComponent(data.openId)}`,
+                `time=${encodeURIComponent(data.time)}`,
+                `gameid=${encodeURIComponent(data.gameid)}`,
+                `serverid=${encodeURIComponent(data.serverid)}`,
+                `playerName=${encodedPlayerName}`, // 已经编码过的playerName,不再编码
+                `sign=${encodeURIComponent(data.sign)}`
+            ].join('&');
+            
+            // 调用角色信息修改API
+            const apiUrl = `https://platform.${config.paymentConfig.apiUrl}/stat/api/?cmd=editUserRoleInfo&${queryString}`;
+            logger.info("调用角色信息修改API:", {url: apiUrl});
+
+            const response = await axios.get(apiUrl, {
+                timeout: 10000,
+                headers: {
+                    'User-Agent': 'Mozilla/5.0 (compatible; WebServer/1.0)',
+                    'Accept': 'application/json'
+                },
+                httpsAgent: this.getHttpsAgent()
+            });
+            logger.info("角色信息修改API响应:", response.data);
+
+            // 处理响应结果
+            if (response.data && response.data.error === 0) {
+                return {
+                    code: 0,
+                    msg: "success",
+                    data: null
+                };
+            } else {
+                return {
+                    code: -1,
+                    msg: response.data?.errmsg || "角色信息修改失败",
+                    data: null
+                };
+            }
+
+        } catch (error) {
+            logger.error("角色名称修改上报出错:", error);
+            return {
+                code: -1,
+                msg: "角色信息修改失败",
+                data: null
+            };
         }
-      });
-      
-      logger.info("订单校验响应", { notifyId, response: response.data });
+    }
 
-      // 检查返回结果
-      if (response.data === "SUCCESS") {
-        return {
-          success: true,
-          message: "订单支付成功"
-        };
-      } else {
-        return {
-          success: false,
-          message: "订单支付失败或无效"
-        };
-      }
-
-    } catch (error) {
-      logger.error("订单校验出错:", { notifyId, error: error.message });
-      return {
-        success: false,
-        message: "订单校验失败"
-      };
+    /**
+     * 签名算法2验证
+     * @param data 请求数据
+     * @param config 渠道配置
+     * @returns 验证结果
+     */
+    private verifySignatureAlgorithm2(data: any, config: ChannelConfig): boolean {
+        try {
+            // 固定密钥
+            const secret = 'qka8qKcvRcGN1u0bLA8O';
+            
+            // 准备签名参数
+            const signParams: any = {
+                gameId: data.gameid || config.paymentConfig.gameId,
+                openId: data.openId,
+                playerName: encodeURIComponent(data.playerName), // 中文URL编码
+                secret: secret,
+                serverId: data.serverid,
+                time: data.time
+            };
+            
+            // 按字典排序参数名
+            const sortedKeys = Object.keys(signParams).sort();
+            
+            // 构建签名字符串(去掉[])
+            const signString = sortedKeys.map(key => {
+                return `${key}=${signParams[key]}`;
+            }).join('');
+            
+            console.log("验证签名参数:", signParams);
+            console.log("验证签名字符串:", signString);
+            
+            // 计算MD5签名
+            const expectedSignature = CryptoJS.MD5(signString).toString();
+            const actualSignature = data.sign;
+            
+            console.log("期望签名:", expectedSignature);
+            console.log("实际签名:", actualSignature);
+            
+            return expectedSignature === actualSignature;
+        } catch (error) {
+            logger.error("签名算法2验证出错:", error);
+            return false;
+        }
     }
-  }
-
-  /**
-   * 内容安全审核接口
-   * @param ctx Koa上下文
-   * @param config 渠道配置
-   */
-  async contentSecurityCheck(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
-    try {
-      const data = ctx.request.body as any;
-      logger.info("内容安全审核请求参数:", data);
-
-      // 验证必要参数
-      const requiredParams = ['uid', 'gameid', 'signature', 'timestamp', 'nonce', 'scene', 'content'];
-      for (const param of requiredParams) {
-        if (!data[param]) {
-          logger.error(`缺少必要参数: ${param}`);
-          return {
-            code: -1,
-            msg: `缺少必要参数: ${param}`,
-            data: null
-          };
+
+    /**
+     * 生成角色信息修改签名(签名算法2)
+     * @param params 签名参数
+     * @param config 渠道配置
+     * @returns 签名结果
+     */
+    generateRoleInfoSignature(params: any, config: ChannelConfig): string | null {
+        try {
+            // 固定密钥
+            const secret = 'qka8qKcvRcGN1u0bLA8O';
+            
+            // 准备签名参数
+            const signParams: any = {
+                gameid: params.gameid || config.paymentConfig.gameId,
+                openId: params.openId,
+                playerName: encodeURIComponent(params.playerName), // 中文URL编码
+                secret: secret,
+                serverid: params.serverid,
+                time: params.time
+            };
+            
+            // 按字典排序参数名
+            const sortedKeys = Object.keys(signParams).sort();
+            
+            // 构建签名字符串(去掉[])
+            const signString = sortedKeys.map(key => {
+                return `${key}=${signParams[key]}`;
+            }).join('');
+            
+            console.log("角色信息签名参数:", signParams);
+            console.log("角色信息签名字符串:", signString);
+            
+            // 计算MD5签名
+            const signature = CryptoJS.MD5(signString).toString();
+            
+            console.log("角色信息签名结果:", signature);
+            
+            return signature;
+        } catch (error) {
+            logger.error("生成角色信息修改签名出错:", error);
+            return null;
         }
-      }
+    }
 
-      // 验证签名算法1
-      const isValidSignature = this.verifySignatureAlgorithm1(data, config);
-      if (!isValidSignature) {
-        logger.error("内容安全审核签名验证失败");
-        return {
-          code: -1,
-          msg: "签名验证失败",
-          data: null
-        };
-      }
-
-      // 构建请求参数
-      const requestData = {
-        uid: data.uid,
-        gameid: data.gameid,
-        signature: data.signature,
-        timestamp: data.timestamp,
-        nonce: data.nonce,
-        scene: data.scene,
-        content: data.content,
-        nickname: data.nickname || '',
-        title: data.title || '',
-        usersign: data.usersign || ''
-      };
-
-      // 调用内容安全API
-      const apiUrl = `https://api.${config.paymentConfig.apiUrl}/mpcommon/?cmd=wxaMsgSecCheck`;
-      logger.info("调用内容安全API:", { url: apiUrl, data: requestData });
-
-      const response = await axios.post(apiUrl, requestData, {
-        httpsAgent: this.getHttpsAgent(),
-        headers: this.getCommonHeaders()
-      });
-
-      logger.info("内容安全API响应:", response.data);
-
-      // 处理响应结果
-      if (response.data && response.data.error === 0) {
-        const result = response.data.result;
-        return {
-          code: 0,
-          msg: "success",
-          data: {
-            suggest: result.suggest, // pass/review/reject
-            label: result.label,
-            trace_id: response.data.trace_id,
-            detail: response.data.detail
-          }
-        };
-      } else {
-        return {
-          code: -1,
-          msg: response.data?.errmsg || "内容安全检测失败",
-          data: null
-        };
-      }
-
-    } catch (error) {
-      logger.error("内容安全审核出错:", error);
-      return {
-        code: -1,
-        msg: "内容安全审核失败",
-        data: null
-      };
+    /**
+     * 签名算法1验证
+     * @param data 请求数据
+     * @param config 渠道配置
+     * @returns 验证结果
+     */
+    private verifySignatureAlgorithm1(data: any, config: ChannelConfig): boolean {
+        return this.verifyCommonSignature(
+            data,
+            config.loginConfig?.apiKey || '',
+            'signature',
+            ['signature'],
+            false // 内容安全签名不需要URL编码
+        );
     }
-  }
-
-  /**
-   * 角色名称修改上报
-   * @param ctx Koa上下文
-   * @param config 渠道配置
-   */
-  async editUserRoleInfo(ctx: Context, config: ChannelConfig): Promise<LoginResult> {
-    try {
-        const data = ctx.request.body as any;
-      logger.info("角色名称修改上报请求参数:", data);
-
-      // 验证必要参数
-      const requiredParams = ['openId', 'time', 'gameid', 'serverid', 'playerName', 'sign'];
-      for (const param of requiredParams) {
-        if (!data[param]) {
-          logger.error(`缺少必要参数: ${param}`);
-          return {
-            code: -1,
-            msg: `缺少必要参数: ${param}`,
-            data: null
-          };
+
+    /**
+     * 生成内容安全签名(供客户端使用)
+     * @param params 签名参数
+     * @param config 渠道配置
+     * @returns 签名结果
+     */
+    generateContentSecuritySignature(params: any, config: ChannelConfig): string | null {
+        try {
+            let arr = [config.loginConfig.apiKey, params.timestamp, params.nonce];
+            arr.sort();
+            let str = arr.join('');
+            return crypto.createHash('sha1').update(str).digest('hex');
+        } catch (error) {
+            logger.error("生成内容安全签名出错:", error);
+            return null;
         }
-      }
+    }
 
-      // 验证签名算法2
-      const isValidSignature = this.verifySignatureAlgorithm2(data, config);
-      if (!isValidSignature) {
-        logger.error("角色名称修改上报签名验证失败");
-        return {
-          code: -1,
-          msg: "签名验证失败",
-          data: null
-        };
-      }
-
-      // 构建请求参数
-      const requestData = {
-        openId: data.openId,
-        time: data.time,
-        gameid: data.gameid,
-        serverid: data.serverid,
-        playerName: encodeURIComponent(data.playerName), // 中文URL编码
-        sign: data.sign
-      };
-
-      // 调用角色信息修改API
-      const apiUrl = `https://platform.${config.paymentConfig.apiUrl}/stat/api/?cmd=editUserRoleInfo`;
-      logger.info("调用角色信息修改API:", { url: apiUrl, data: requestData });
-
-      const response = await axios.get(apiUrl, { 
-        params: requestData,
-        timeout: 10000,
-        headers: {
-          'User-Agent': 'Mozilla/5.0 (compatible; WebServer/1.0)',
-          'Accept': 'application/json'
-        },
-        httpsAgent: this.getHttpsAgent()
-      });
-      logger.info("角色信息修改API响应:", response.data);
-
-      // 处理响应结果
-      if (response.data && response.data.error === 0) {
-        return {
-          code: 0,
-          msg: "success",
-          data: null
-        };
-      } else {
-        return {
-          code: -1,
-          msg: response.data?.errmsg || "角色信息修改失败",
-          data: null
-        };
-      }
-
-    } catch (error) {
-      logger.error("角色名称修改上报出错:", error);
-      return {
-        code: -1,
-        msg: "角色信息修改失败",
-        data: null
-      };
+    /**
+     * 发货处理方法
+     * @param orderInfo 订单信息
+     * @param ip 客户端IP
+     * @param url 游戏服务器URL
+     * @param out_trade_no 外部交易号
+     * @returns 发货结果
+     */
+    private async deliverOrder(orderInfo: any, ip: string, url: string, out_trade_no: string): Promise<{
+        code: number;
+        msg: string
+    }> {
+        return new Promise((resolve) => {
+            const sendMsg = new Msg();
+            logger.info("通知游戏服务器url", {url: url});
+
+            sendMsg.connect(url, Account);
+            setTimeout(async () => {
+                try {
+                    // 构建消息参数
+                    const params = JSON.stringify({
+                        account: orderInfo.uid,
+                        channel_id: orderInfo.channel_id,
+                        order: orderInfo.order_id,
+                        id: orderInfo.product_id,
+                        cnt: 100,
+                        money: orderInfo.amount,
+                    });
+
+                    logger.info("通知游戏服务器参数", {data: params});
+                    logger.info("通知游戏服务器orderInfo", {orderInfo: orderInfo});
+                    let send_res = sendMsg.CG_ASK_LOGIN(
+                        Account,
+                        0,
+                        "",
+                        "cn",
+                        "CN",
+                        ip,
+                        params,
+                        orderInfo.server_id
+                    );
+
+                    if (!send_res) {
+                        resolve({code: 0, msg: "通知服务器失败"});
+                        return;
+                    }
+
+                    // 更新订单状态
+                    const update_time = formatDate(new Date());
+                    const res = await Order.updateOrderStats(
+                        orderInfo.order_id,
+                        2,
+                        out_trade_no,
+                        update_time,
+                        orderInfo.uid
+                    );
+
+                    if (res.affectedRows <= 0) {
+                        logger.info(`订单${orderInfo.order_id} 发货失败`);
+                        resolve({code: 0, msg: "发货失败"});
+                        return;
+                    }
+
+                    resolve({code: 1, msg: "发货成功"});
+                } catch (error) {
+                    logger.error("发货过程出错:", error);
+                    resolve({code: 0, msg: "发货失败"});
+                }
+            }, 1500);
+        });
     }
-  }
-
-  /**
-   * 签名算法2验证
-   * @param data 请求数据
-   * @param config 渠道配置
-   * @returns 验证结果
-   */
-  private verifySignatureAlgorithm2(data: any, config: ChannelConfig): boolean {
-    return this.verifyCommonSignature(
-      data, 
-      config.loginConfig?.statApiKey || '', 
-      'sign',
-      ['sign'],
-      false // 角色信息签名不需要URL编码
-    );
-  }
-
-  /**
-   * 生成角色信息修改签名(供客户端使用)
-   * @param params 签名参数
-   * @param config 渠道配置
-   * @returns 签名结果
-   */
-  generateRoleInfoSignature(params: any, config: ChannelConfig): string | null {
-    try {
-      return this.generateCommonSignature(
-        params, 
-        config.loginConfig?.statApiKey || '', 
-        ['sign'],
-        false // 角色信息签名不需要URL编码
-      );
-    } catch (error) {
-      logger.error("生成角色信息修改签名出错:", error);
-      return null;
+
+    /**
+     * 通用签名生成函数
+     * @param params 参数对象
+     * @param privateKey 私钥
+     * @param excludeKeys 需要排除的键(如sign)
+     * @param needUrlEncode 是否需要URL编码
+     * @returns 签名结果
+     */
+    public generateCommonSignature(
+        params: any,
+        privateKey: string,
+        excludeKeys: string[] = ['sign'],
+        needUrlEncode: boolean = true
+    ): string {
+        try {
+            // 1. 过滤掉空值和排除的键
+            const filteredParams: any = {};
+            Object.keys(params).forEach(key => {
+                if (!excludeKeys.includes(key) && params[key] !== null && params[key] !== undefined && params[key] !== '') {
+                    filteredParams[key] = params[key].toString();
+                }
+            });
+
+            // 2. 按字母顺序排序并构建查询字符串
+            const sortedKeys = Object.keys(filteredParams).sort();
+            const queryString = sortedKeys.map(key => {
+                const value = filteredParams[key];
+                const encodedValue = needUrlEncode ? encodeURIComponent(value) : value;
+                return `${key}=${encodedValue}`;
+            }).join('&');
+
+            // 3. 拼接私钥
+            const stringToSign = `${queryString}&key=${privateKey}`;
+            console.log("String to Sign:", stringToSign);
+            // 4. 计算MD5并转大写
+            const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
+
+            logger.info("通用签名生成详情:", {
+                filteredParams,
+                queryString,
+                stringToSign,
+                signature
+            });
+
+            return signature;
+        } catch (error) {
+            logger.error("通用签名生成出错:", error);
+            return '';
+        }
     }
-  }
-
-  /**
-   * 签名算法1验证
-   * @param data 请求数据
-   * @param config 渠道配置
-   * @returns 验证结果
-   */
-  private verifySignatureAlgorithm1(data: any, config: ChannelConfig): boolean {
-    return this.verifyCommonSignature(
-      data, 
-      config.loginConfig?.apiKey || '', 
-      'signature',
-      ['signature'],
-      false // 内容安全签名不需要URL编码
-    );
-  }
-
-  /**
-   * 生成内容安全签名(供客户端使用)
-   * @param params 签名参数
-   * @param config 渠道配置
-   * @returns 签名结果
-   */
-  generateContentSecuritySignature(params: any, config: ChannelConfig): string | null {
-    try {
-      return this.generateCommonSignature(
-        params, 
-        config.loginConfig?.apiKey || '', 
-        ['signature'],
-        false // 内容安全签名不需要URL编码
-      );
-    } catch (error) {
-      logger.error("生成内容安全签名出错:", error);
-      return null;
+
+    /**
+     * 小程序签名函数(签名算法3)- 兼容旧接口
+     * @param parameters 参数数组
+     * @param privateKey 私钥
+     * @returns 签名结果
+     */
+    private generateSignature(parameters: string[], privateKey: string): string {
+        // 将参数数组转换为对象
+        const params: any = {};
+        parameters.forEach(param => {
+            const [key, value] = param.split('=');
+            if (key && value) {
+                params[key] = value;
+            }
+        });
+
+        return this.generateCommonSignature(params, privateKey, ['sign'], true);
     }
-  }
-
-  /**
-   * 发货处理方法
-   * @param orderInfo 订单信息
-   * @param ip 客户端IP
-   * @param url 游戏服务器URL
-   * @param out_trade_no 外部交易号
-   * @returns 发货结果
-   */
-  private async deliverOrder(orderInfo: any, ip: string, url: string, out_trade_no: string): Promise<{ code: number; msg: string }> {
-    return new Promise((resolve) => {
-      const sendMsg = new Msg();
-      logger.info("通知游戏服务器url", { url: url });
-
-      sendMsg.connect(url, Account);
-      setTimeout(async () => {
+
+    /**
+     * 通用签名验证函数
+     * @param data 请求数据
+     * @param privateKey 私钥
+     * @param signKey 签名字段名
+     * @param excludeKeys 需要排除的键
+     * @param needUrlEncode 是否需要URL编码
+     * @returns 验证结果
+     */
+    private verifyCommonSignature(
+        data: any,
+        privateKey: string,
+        signKey: string = 'sign',
+        excludeKeys: string[] = ['sign'],
+        needUrlEncode: boolean = true
+    ): boolean {
         try {
-          // 构建消息参数
-          const params = JSON.stringify({
-            account: orderInfo.uid,
-            channel_id: orderInfo.channel_id,
-            order: orderInfo.order_id,
-            id: orderInfo.product_id,
-            cnt: 100,
-            money: orderInfo.amount,
-          });
-
-          logger.info("通知游戏服务器参数", { data: params });
-          logger.info("通知游戏服务器orderInfo", { orderInfo: orderInfo });
-          let send_res = sendMsg.CG_ASK_LOGIN(
-            Account,
-            0,
-            "",
-            "cn",
-            "CN",
-            ip,
-            params,
-            orderInfo.server_id
-          );
-
-          if (!send_res) {
-            resolve({ code: 0, msg: "通知服务器失败" });
-            return;
-          }
-
-          // 更新订单状态
-          const update_time = formatDate(new Date());
-          const res = await Order.updateOrderStats(
-            orderInfo.order_id,
-            2,
-            out_trade_no,
-            update_time,
-            orderInfo.uid
-          );
-
-          if (res.affectedRows <= 0) {
-            logger.info(`订单${orderInfo.order_id} 发货失败`);
-            resolve({ code: 0, msg: "发货失败" });
-            return;
-          }
-
-          resolve({ code: 1, msg: "发货成功" });
+            const receivedSignature = data[signKey];
+            if (!receivedSignature) {
+                return false;
+            }
+
+            const expectedSignature = this.generateCommonSignature(data, privateKey, excludeKeys, needUrlEncode);
+
+            logger.info("通用签名验证详情:", {
+                receivedSignature,
+                expectedSignature,
+                isValid: receivedSignature === expectedSignature
+            });
+
+            return receivedSignature === expectedSignature;
         } catch (error) {
-          logger.error("发货过程出错:", error);
-          resolve({ code: 0, msg: "发货失败" });
+            logger.error("通用签名验证出错:", error);
+            return false;
         }
-      }, 1500);
-    });
-  }
-
-  /**
-   * 通用签名生成函数
-   * @param params 参数对象
-   * @param privateKey 私钥
-   * @param excludeKeys 需要排除的键(如sign)
-   * @param needUrlEncode 是否需要URL编码
-   * @returns 签名结果
-   */
-  public generateCommonSignature(
-    params: any, 
-    privateKey: string, 
-    excludeKeys: string[] = ['sign'], 
-    needUrlEncode: boolean = true
-  ): string {
-    try {
-      // 1. 过滤掉空值和排除的键
-      const filteredParams: any = {};
-      Object.keys(params).forEach(key => {
-        if (!excludeKeys.includes(key) && params[key] !== null && params[key] !== undefined && params[key] !== '') {
-          filteredParams[key] = params[key].toString();
-        }
-      });
-
-      // 2. 按字母顺序排序并构建查询字符串
-      const sortedKeys = Object.keys(filteredParams).sort();
-      const queryString = sortedKeys.map(key => {
-        const value = filteredParams[key];
-        const encodedValue = needUrlEncode ? encodeURIComponent(value) : value;
-        return `${key}=${encodedValue}`;
-      }).join('&');
-
-      // 3. 拼接私钥
-      const stringToSign = `${queryString}&key=${privateKey}`;
-
-      // 4. 计算MD5并转大写
-      const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
-
-      logger.info("通用签名生成详情:", {
-        filteredParams,
-        queryString,
-        stringToSign,
-        signature
-      });
-
-      return signature;
-    } catch (error) {
-      logger.error("通用签名生成出错:", error);
-      return '';
-    }
-  }
-
-  /**
-   * 小程序签名函数(签名算法3)- 兼容旧接口
-   * @param parameters 参数数组
-   * @param privateKey 私钥
-   * @returns 签名结果
-   */
-  private generateSignature(parameters: string[], privateKey: string): string {
-    // 将参数数组转换为对象
-    const params: any = {};
-    parameters.forEach(param => {
-      const [key, value] = param.split('=');
-      if (key && value) {
-        params[key] = value;
-      }
-    });
-
-    return this.generateCommonSignature(params, privateKey, ['sign'], true);
-  }
-
-  /**
-   * 通用签名验证函数
-   * @param data 请求数据
-   * @param privateKey 私钥
-   * @param signKey 签名字段名
-   * @param excludeKeys 需要排除的键
-   * @param needUrlEncode 是否需要URL编码
-   * @returns 验证结果
-   */
-  private verifyCommonSignature(
-    data: any, 
-    privateKey: string, 
-    signKey: string = 'sign',
-    excludeKeys: string[] = ['sign'],
-    needUrlEncode: boolean = true
-  ): boolean {
-    try {
-      const receivedSignature = data[signKey];
-      if (!receivedSignature) {
-        return false;
-      }
-
-      const expectedSignature = this.generateCommonSignature(data, privateKey, excludeKeys, needUrlEncode);
-      
-      logger.info("通用签名验证详情:", {
-        receivedSignature,
-        expectedSignature,
-        isValid: receivedSignature === expectedSignature
-      });
-
-      return receivedSignature === expectedSignature;
-    } catch (error) {
-      logger.error("通用签名验证出错:", error);
-      return false;
     }
-  }
 }

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

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

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

@@ -1,6 +1,6 @@
 export const serverConfig = {
   // 默认端口列表
-  ports: [3500],
+  ports: [3000, 3001, 3002],
   
   // 其他服务器相关配置
   maxConnections: 1000,

+ 17 - 16
webServer/src/controller/ApiController.ts

@@ -650,24 +650,25 @@ class ApiController {
 
     if (enterServerList.length > 0) {
       isNewAccount = 0;
-      // const servers = (await Server.getServerList(tag, 1))
       const servers = await Server.getAllServerList(tag, ip);
       enterServerList.forEach(function (element) {
-        data.push({
-          channel: channelConfig.name, //渠道固定
-          minSid: 1, //最小服务器
-          maxSid: servers.length, //最大服务器 这里会控制 服务器列表显示的数量
-          isNewAccount: isNewAccount, //1为新号 会弹出用户协议
-          //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区)
-          sid: element.server_id || 1,
-          id: element.server_id || 1,
-          name: element.name || "1区",
-          tips: element.tips || "",
-          server: element.wss
-            ? element.wss
-            : `ws://${element.ip}:${element.port}`,
-          status: element.status || 0,
-        });
+        if (element.server_id != "999"){
+          data.push({
+            channel: channelConfig.name, //渠道固定
+            minSid: 1, //最小服务器
+            maxSid: servers.length, //最大服务器 这里会控制 服务器列表显示的数量
+            isNewAccount: isNewAccount, //1为新号 会弹出用户协议
+            //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区)
+            sid: element.server_id || 1,
+            id: element.server_id || 1,
+            name: element.name || "1区",
+            tips: element.tips || "",
+            server: element.wss
+                ? element.wss
+                : `ws://${element.ip}:${element.port}`,
+            status: element.status || 0,
+          });
+        }
       });
       logger.info("getLastServerList 区服接口 enterServerList", {
         enterServerList: enterServerList,

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

@@ -19,7 +19,7 @@ class ServerModel {
     return await query(`SELECT *  FROM game_enter_server WHERE  id = ? `, [id]);
   }
   async getAllServerList(tag: any = 1, ip: string) {
-    const rows = await query(`SELECT * FROM game_server WHERE tag = ? and is_show = 1`, [tag]);
+    const rows = await query(`SELECT * FROM game_server WHERE tag = ? and is_show = 1` , [tag]);
     const resultArray = (rows as any[]).map((row) => {
       let status = row.status;
       if ((status == 0 || status == 3) && row.white_list) {