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

+ 120 - 148
webServer/src/channels/handlers/MiniappChannelHandler.ts

@@ -454,40 +454,13 @@ export class MiniappChannelHandler implements ChannelHandler {
    * @returns 验证结果
    */
   private verifySignatureAlgorithm2(data: any, config: ChannelConfig): boolean {
-    try {
-      const { openId, time, gameid, serverid, playerName, sign } = data;
-      
-      // 构建待签名字符串 - 按字母顺序排序
-      const params: any = {
-        openId: openId.toString(),
-        time: time.toString(),
-        gameid: gameid.toString(),
-        serverid: serverid.toString(),
-        playerName: playerName.toString()
-      };
-
-      // 按字母顺序排序并拼接
-      const sortedKeys = Object.keys(params).sort();
-      const queryString = sortedKeys.map(key => `${key}=${params[key]}`).join('&');
-      
-      // 添加统计API密钥
-      const stringToSign = `${queryString}&key=${config.loginConfig?.statApiKey}`;
-      
-      // 计算MD5签名
-      const expectedSignature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
-      
-      logger.info("签名算法2验证详情:", {
-        queryString,
-        stringToSign,
-        expectedSignature,
-        receivedSignature: sign
-      });
-
-      return sign === expectedSignature;
-    } catch (error) {
-      logger.error("签名算法2验证出错:", error);
-      return false;
-    }
+    return this.verifyCommonSignature(
+      data, 
+      config.loginConfig?.statApiKey || '', 
+      'sign',
+      ['sign'],
+      false // 角色信息签名不需要URL编码
+    );
   }
 
   /**
@@ -498,28 +471,12 @@ export class MiniappChannelHandler implements ChannelHandler {
    */
   generateRoleInfoSignature(params: any, config: ChannelConfig): string | null {
     try {
-      const { openId, time, gameid, serverid, playerName } = params;
-      
-      // 构建待签名字符串 - 按字母顺序排序
-      const signParams: any = {
-        openId: openId.toString(),
-        time: time.toString(),
-        gameid: gameid.toString(),
-        serverid: serverid.toString(),
-        playerName: playerName.toString()
-      };
-
-      // 按字母顺序排序并拼接
-      const sortedKeys = Object.keys(signParams).sort();
-      const queryString = sortedKeys.map(key => `${key}=${signParams[key]}`).join('&');
-      
-      // 添加统计API密钥
-      const stringToSign = `${queryString}&key=${config.loginConfig?.statApiKey}`;
-      
-      // 计算MD5签名
-      const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
-      
-      return signature;
+      return this.generateCommonSignature(
+        params, 
+        config.loginConfig?.statApiKey || '', 
+        ['sign'],
+        false // 角色信息签名不需要URL编码
+      );
     } catch (error) {
       logger.error("生成角色信息修改签名出错:", error);
       return null;
@@ -533,46 +490,13 @@ export class MiniappChannelHandler implements ChannelHandler {
    * @returns 验证结果
    */
   private verifySignatureAlgorithm1(data: any, config: ChannelConfig): boolean {
-    try {
-      const { uid, gameid, timestamp, nonce, scene, content, nickname, title, usersign, signature } = data;
-      
-      // 构建待签名字符串 - 按字母顺序排序
-      const params: any = {
-        uid: uid.toString(),
-        gameid: gameid.toString(),
-        timestamp: timestamp.toString(),
-        nonce: nonce.toString(),
-        scene: scene.toString(),
-        content: content
-      };
-
-      // 添加可选参数(如果存在)
-      if (nickname) params.nickname = nickname;
-      if (title) params.title = title;
-      if (usersign) params.usersign = usersign;
-
-      // 按字母顺序排序并拼接
-      const sortedKeys = Object.keys(params).sort();
-      const queryString = sortedKeys.map(key => `${key}=${params[key]}`).join('&');
-      
-      // 添加API密钥
-      const stringToSign = `${queryString}&key=${config.loginConfig?.apiKey}`;
-      
-      // 计算MD5签名
-      const expectedSignature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
-      
-      logger.info("签名验证详情:", {
-        queryString,
-        stringToSign,
-        expectedSignature,
-        receivedSignature: signature
-      });
-
-      return signature === expectedSignature;
-    } catch (error) {
-      logger.error("签名算法1验证出错:", error);
-      return false;
-    }
+    return this.verifyCommonSignature(
+      data, 
+      config.loginConfig?.apiKey || '', 
+      'signature',
+      ['signature'],
+      false // 内容安全签名不需要URL编码
+    );
   }
 
   /**
@@ -583,34 +507,12 @@ export class MiniappChannelHandler implements ChannelHandler {
    */
   generateContentSecuritySignature(params: any, config: ChannelConfig): string | null {
     try {
-      const { uid, gameid, timestamp, nonce, scene, content, nickname, title, usersign } = params;
-      
-      // 构建待签名字符串 - 按字母顺序排序
-      const signParams: any = {
-        uid: uid.toString(),
-        gameid: gameid.toString(),
-        timestamp: timestamp.toString(),
-        nonce: nonce.toString(),
-        scene: scene.toString(),
-        content: content
-      };
-
-      // 添加可选参数(如果存在)
-      if (nickname) signParams.nickname = nickname;
-      if (title) signParams.title = title;
-      if (usersign) signParams.usersign = usersign;
-
-      // 按字母顺序排序并拼接
-      const sortedKeys = Object.keys(signParams).sort();
-      const queryString = sortedKeys.map(key => `${key}=${signParams[key]}`).join('&');
-      
-      // 添加API密钥
-      const stringToSign = `${queryString}&key=${config.loginConfig?.apiKey}`;
-      
-      // 计算MD5签名
-      const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
-      
-      return signature;
+      return this.generateCommonSignature(
+        params, 
+        config.loginConfig?.apiKey || '', 
+        ['signature'],
+        false // 内容安全签名不需要URL编码
+      );
     } catch (error) {
       logger.error("生成内容安全签名出错:", error);
       return null;
@@ -687,39 +589,109 @@ export class MiniappChannelHandler implements ChannelHandler {
   }
 
   /**
-   * 小程序签名函数(签名算法3)
+   * 通用签名生成函数
+   * @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 {
-    // 1. 过滤掉空值和sign参数
-    const filteredParams = parameters.filter(param => {
-      const [key, value] = param.split('=');
-      return key !== 'sign' && value && value.trim() !== '';
-    });
-
-    // 2. 对参数进行URL编码
-    const encodedParams = filteredParams.map(param => {
+    // 将参数数组转换为对象
+    const params: any = {};
+    parameters.forEach(param => {
       const [key, value] = param.split('=');
-      const encodedValue = encodeURIComponent(value);
-      return `${key}=${encodedValue}`;
-    });
-
-    // 3. 按字母顺序排序
-    encodedParams.sort((a, b) => {
-      return a.localeCompare(b);
+      if (key && value) {
+        params[key] = value;
+      }
     });
 
-    // 4. 用&连接所有参数
-    const queryString = encodedParams.join('&');
+    return this.generateCommonSignature(params, privateKey, ['sign'], true);
+  }
 
-    // 5. 拼接私钥
-    const stringToSign = `${queryString}&key=${privateKey}`;
+  /**
+   * 通用签名验证函数
+   * @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;
+      }
 
-    // 6. 计算MD5并转大写
-    const signature = CryptoJS.MD5(stringToSign).toString().toUpperCase();
+      const expectedSignature = this.generateCommonSignature(data, privateKey, excludeKeys, needUrlEncode);
+      
+      logger.info("通用签名验证详情:", {
+        receivedSignature,
+        expectedSignature,
+        isValid: receivedSignature === expectedSignature
+      });
 
-    return signature;
-      }
+      return receivedSignature === expectedSignature;
+    } catch (error) {
+      logger.error("通用签名验证出错:", error);
+      return false;
+    }
+  }
 }

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

@@ -3,6 +3,7 @@ import { getServerList, formatDate } from "../utils/common";
 import { Account } from "../config/thirdParams";
 import Msg from "../utils/msg";
 import axios from "axios";
+import { getRoleInfoById } from "../mongo/mongodb";
 
 const Order = require("../model/OrderModel");
 const CryptoJS = require("crypto-js");
@@ -107,6 +108,314 @@ class MiniappController {
       };
     }
   }
+
+  /**
+   * 禁言接口
+   * @param ctx Koa上下文
+   */
+  async forbidChat(ctx: Context) {
+    try {
+      const data = ctx.request.body;
+      logger.info("禁言接口请求参数:", data);
+
+      // 验证必要参数
+      const requiredParams = ['gameid', 'openid', 'accountid', 'roleid', 'forbidDays', 'reason', 'time', 'sign'];
+      for (const param of requiredParams) {
+        if (!data[param]) {
+          logger.error(`缺少必要参数: ${param}`);
+          ctx.body = {
+            error: -1,
+            errmsg: `缺少必要参数: ${param}`
+          };
+          return;
+        }
+      }
+
+      const handler = MiniappController.getChannelHandler();
+      const expectedSignature = (handler as any).generateCommonSignature(
+        data, 
+        MINI_APP_STAT_API_KEY, 
+        ['sign'],
+        false // 禁言接口签名不需要URL编码
+      );
+      // 验证签名
+      if (expectedSignature !== data.sign) {
+        logger.error("禁言接口签名验证失败");
+        ctx.body = {
+          error: -1,
+          errmsg: "签名验证失败"
+        };
+        return;
+      }
+
+      // 验证禁言天数
+      const forbidDays = parseInt(data.forbidDays);
+      if (forbidDays < -1 || forbidDays === 0 || forbidDays > 30) {
+        logger.error("禁言天数参数错误:", forbidDays);
+        ctx.body = {
+          error: -1,
+          errmsg: "禁言天数参数错误,应为1-30天或-1(永久)"
+        };
+        return;
+      }
+
+      // 调用游戏服禁言接口
+      const gameResult = await MiniappController.callGameForbidChat(data);
+      
+      if (gameResult) {
+        ctx.body = {
+          error: 0,
+          errmsg: ""
+        };
+      } else {
+        ctx.body = {
+          error: -1,
+          errmsg: "禁言失败"
+        };
+      }
+
+    } catch (error) {
+      logger.error("禁言接口出错:", error);
+      ctx.body = {
+        error: -1,
+        errmsg: "禁言失败"
+      };
+    }
+  }
+
+
+  /**
+   * 解除禁言接口
+   * @param ctx Koa上下文
+   */
+  async freeChat(ctx: Context) {
+    try {
+      const data = ctx.request.body;
+      logger.info("解除禁言接口请求参数:", data);
+
+      // 验证必要参数
+      const requiredParams = ['gameid', 'openid', 'accountid', 'roleid', 'time', 'sign'];
+      for (const param of requiredParams) {
+        if (!data[param]) {
+          logger.error(`缺少必要参数: ${param}`);
+          ctx.body = {
+            error: -1,
+            errmsg: `缺少必要参数: ${param}`
+          };
+          return;
+        }
+      }
+
+      const handler = MiniappController.getChannelHandler();
+      const expectedSignature = (handler as any).generateCommonSignature(
+        data, 
+        MINI_APP_STAT_API_KEY, 
+        ['sign'],
+        false // 解除禁言接口签名不需要URL编码
+      );
+      
+      // 验证签名
+      if (expectedSignature !== data.sign) {
+        logger.error("解除禁言接口签名验证失败");
+        ctx.body = {
+          error: -1,
+          errmsg: "签名验证失败"
+        };
+        return;
+      }
+
+      // 调用游戏服解除禁言接口
+      const gameResult = await MiniappController.callGameFreeChat(data);
+      
+      if (gameResult) {
+        ctx.body = {
+          error: 0,
+          errmsg: ""
+        };
+      } else {
+        ctx.body = {
+          error: -1,
+          errmsg: "解除禁言失败"
+        };
+      }
+
+    } catch (error) {
+      logger.error("解除禁言接口出错:", error);
+      ctx.body = {
+        error: -1,
+        errmsg: "解除禁言失败"
+      };
+    }
+  }
+
+  /**
+   * 角色查询接口
+   * @param ctx Koa上下文
+   */
+  async roleQuery(ctx: Context) {
+    try {
+      const data = ctx.request.body;
+      logger.info("角色查询接口请求参数:", data);
+
+      // 验证必要参数
+      const requiredParams = ['gameid', 'serverid', 'openid', 'accountid', 'roleid', 'time', 'sign'];
+      for (const param of requiredParams) {
+        if (!data[param]) {
+          logger.error(`缺少必要参数: ${param}`);
+          ctx.body = {
+            error: -1,
+            errmsg: `缺少必要参数: ${param}`
+          };
+          return;
+        }
+      }
+
+      const handler = MiniappController.getChannelHandler();
+      const expectedSignature = (handler as any).generateCommonSignature(
+        data, 
+        MINI_APP_STAT_API_KEY, 
+        ['sign'],
+        false // 角色查询接口签名不需要URL编码
+      );
+      
+      // 验证签名
+      if (expectedSignature !== data.sign) {
+        logger.error("角色查询接口签名验证失败");
+        ctx.body = {
+          error: -1,
+          errmsg: "签名验证失败"
+        };
+        return;
+      }
+
+      // 调用游戏服角色查询接口
+      const roleData = await MiniappController.callGameRoleQuery(data);
+      
+      if (roleData) {
+        ctx.body = {
+          error: 0,
+          errmsg: "",
+          role: roleData
+        };
+      } else {
+        ctx.body = {
+          error: -1,
+          errmsg: "角色查询失败"
+        };
+      }
+
+    } catch (error) {
+      logger.error("角色查询接口出错:", error);
+      ctx.body = {
+        error: -1,
+        errmsg: "角色查询失败"
+      };
+    }
+  }
+
+  /**
+   * 调用游戏服禁言接口
+   * @param data 禁言数据
+   * @returns 调用结果
+   */
+  private static callGameForbidChat(data: any): boolean {
+    try {
+      logger.info("调用游戏服禁言接口:", {
+        gameid: data.gameid,
+        openid: data.openid,
+        accountid: data.accountid,
+        roleid: data.roleid,
+        forbidDays: data.forbidDays,
+        reason: data.reason,
+        ext: data.ext
+      });
+      //调用游戏服禁言接口
+      return true
+
+    } catch (error) {
+      logger.error("调用游戏服禁言接口出错:", error);
+      return false
+    }
+  }
+
+  /**
+   * 调用游戏服解除禁言接口
+   * @param data 解除禁言数据
+   * @returns 调用结果
+   */
+  private static callGameFreeChat(data: any): boolean {
+    try {
+      
+      logger.info("调用游戏服解除禁言接口:", {
+        gameid: data.gameid,
+        openid: data.openid,
+        accountid: data.accountid,
+        roleid: data.roleid,
+        ext: data.ext
+      });
+      //调用游戏服接口
+
+      // 临时返回成功,实际需要调用游戏服
+      return true
+
+    } catch (error) {
+      logger.error("调用游戏服解除禁言接口出错:", error);
+      return false
+    }
+  }
+
+  /**
+   * 调用游戏服角色查询接口
+   * @param data 角色查询数据
+   * @returns 角色数据
+   */
+  private static async callGameRoleQuery(data: any): Promise<any> {
+    try {
+      
+     logger.info("调用游戏服角色查询接口:", {
+        gameid: data.gameid,
+        serverid: data.serverid,
+        openid: data.openid,
+        accountid: data.accountid,
+        roleid: data.roleid,
+        ext: data.ext
+      });
+       
+      // 根据服务器ID生成数据库名称
+      let dbName = "ckwy_fy_S"+(String(350001+parseInt(data.serverid)).padStart(3,'0'));
+      
+      // 从MongoDB查询角色信息
+      const roleInfo = await getRoleInfoById(dbName, data.roleid);
+      
+      if (roleInfo) {
+        // 如果找到角色信息,返回格式化的数据
+        return {
+          rolename: roleInfo.name || "未知角色",
+          rolelv: roleInfo.lv || 1,
+          rolepwr: roleInfo.rolepwr || 0, //战力
+          viplv: roleInfo.viplv || 0, //vip等级
+          alliance: roleInfo.alliance || "", //帮派
+          paymoney: roleInfo.topupAcount || 0 //累计充值金额
+        };
+      } else {
+        // 如果没有找到角色信息,返回默认数据
+        logger.warn(`未找到角色信息: ${data.roleid}`);
+        return {
+          rolename: "未知角色",
+          rolelv: 1,
+          rolepwr: 0,
+          viplv: 0,
+          alliance: "",
+          paymoney: 0
+        };
+      }
+
+    } catch (error) {
+      logger.error("调用游戏服角色查询接口出错:", error);
+      return null;
+    }
+  }
+  
 }
 
 module.exports = new MiniappController();

+ 57 - 0
webServer/src/mongo/mongodb.ts

@@ -0,0 +1,57 @@
+import {mongoConfig,} from '../config/dbConfig'
+
+import {Db, MongoClient} from 'mongodb';
+
+const url = `mongodb://${mongoConfig.url}:${mongoConfig.port}`;
+let db: Db | null = null;
+const client = new MongoClient(url);
+async function connectToMongo() {
+    if (db) {
+        return db;
+    }
+    try {
+        await client.connect();
+        db = client.db('sdk'); // 连接到数据库
+        console.log("成功连接到MongoDB");
+        return db;
+    } catch (error) {
+        console.error("连接到MongoDB失败:", error);
+        throw error;
+    }
+}
+
+// 获取某一个数据库连接
+async function getDb(): Promise<Db> {
+    if (!db) {
+        db = await connectToMongo();
+    }
+    return db;
+}
+
+// 查询某一个db的集合
+async function getCollection(collectionName: string) {
+    const database = await getDb();
+    return database.collection(collectionName);
+}
+//关闭连接
+async function closeConnection() {
+    if (client) {
+        await client.close();
+        db = null;
+        console.log("MongoDB连接已关闭");
+    }
+}
+//通过id和db查询某一个角色信息
+async function getRoleInfoById(dbName: string, roleId: string) {
+    try {
+        const database = client.db(dbName);
+        const collection = database.collection('char');
+        const roleInfo = await collection.findOne({roleId: roleId});
+        return roleInfo;
+    } catch (error) {
+        console.error('查询角色信息失败:', error);
+        return null;
+    }
+}
+
+export { getDb, getCollection, closeConnection, getRoleInfoById, connectToMongo };

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

@@ -9,4 +9,13 @@ miniApprouter.post("/contentSecurityCheck", MiniappController.contentSecurityChe
 // 角色名称修改上报
 miniApprouter.post("/editUserRoleInfo", MiniappController.editUserRoleInfo);
 
+// 禁言接口
+miniApprouter.post("/forbidChat", MiniappController.forbidChat);
+
+// 解除禁言接口
+miniApprouter.post("/freeChat", MiniappController.freeChat);
+
+// 角色查询接口
+miniApprouter.post("/roleQuery", MiniappController.roleQuery);
+
 export default miniApprouter;

+ 37 - 9
webServer/src/server.ts

@@ -2,21 +2,49 @@
 import { ServerManager } from './utils/serverManager';
 import { serverConfig } from './config/serverConfig';
 import Task from './utils/task';
+import { connectToMongo } from './mongo/mongodb';
 const router = require('./router/index');
 
-// 创建服务器管理器实例
-const serverManager = new ServerManager(router);
-
-// 启动所有配置的端口
-serverConfig.ports.forEach(port => {
-  const success = serverManager.createServer(port);
-  if (success) {
-    console.log(`启动成功,服务端口为:${port}`);
+// 初始化MongoDB连接
+async function initializeMongoDB() {
+  try {
+    console.log('开始初始化MongoDB');
+    await connectToMongo();
+    console.log('MongoDB初始化成功');
+  } catch (error) {
+    console.error('MongoDB初始化失败:', error);
+    process.exit(1); // 如果MongoDB连接失败,退出进程
   }
+}
+
+// 启动服务器
+async function startServer() {
+  // 先初始化MongoDB
+  await initializeMongoDB();
+  
+  // 创建服务器管理器实例
+  const serverManager = new ServerManager(router);
+
+  // 启动所有配置的端口
+  serverConfig.ports.forEach(port => {
+    const success = serverManager.createServer(port);
+    if (success) {
+      console.log(`启动成功,服务端口为:${port}`);
+    }
+  });
+
+  // 导出服务器管理器以便其他模块可以使用
+  return serverManager;
+}
+
+// 启动服务器
+startServer().catch(error => {
+  console.error('服务器启动失败:', error);
+  process.exit(1);
 });
 
 // 导出服务器管理器以便其他模块可以使用
-export default serverManager;
+export default startServer;
 
 // 注释掉的任务相关代码保留
 // const appTask = new Task();