import Msg from "../utils/msg"; import { RefreshToken, PackageName, ProductId, IosUrl, Account, ClientSecret, ClientId, ORDER_SIGN_KEY, } from "../config/thirdParams"; import { generateOrderNumber, formatDate, getServerList, getClientIp, getServerAddr, } from "../utils/common"; import {PaymentHelper} from "../utils/PaymentHelper"; import {SignatureVerifier} from "../utils/SignatureVerifier"; import {ChannelConfigManager} from "../utils/ChannelConfigManager"; import {getRoleInfoByUidAndServerId} from "../mongo/mongodb"; // platform内存缓存,key: ip:device_no,TTL 5分钟 const platformCache = new Map(); const PLATFORM_CACHE_TTL = 60 * 1000; // 缓存1分钟 function setPlatformCache(ip: string, platform: string) { platformCache.set(ip, {value: platform, expireAt: Date.now() + PLATFORM_CACHE_TTL}); } function getPlatformCache(ip: string): string { const entry = platformCache.get(ip); if (!entry) return ''; if (Date.now() > entry.expireAt) { platformCache.delete(ip); return ''; } return entry.value; } // 导入依赖 const CryptoJS = require("crypto-js"); const Order = require("../model/OrderModel"); const Server = require("../model/ServerModel"); const System = require("../model/SystemModel"); const User = require("../model/UserModel"); const logger = require("../utils/log"); const axios = require("axios"); const {channelFactory} = require("../channels/factory/ChannelFactory"); const {MianyouChannelHandler} = require("../channels/handlers/MianyouChannelHandler"); /** * Google支付回调处理 */ const googleCallPay = async (ctx, config) => { let data = ctx.request.body; let orderId = data.orderId; let googleToken = data.purchaseToken; let out_trade_no = ""; logger.info("Google支付回调参数:", {url: ctx.href, params: data}); // 更新订单Token await Order.updateOrderToken(orderId, googleToken, "google"); // 获取Google访问令牌 const redisClient = ctx.redis.client; let access_token = await redisClient.get("access_token"); if (!access_token) { logger.info("请求Google API获取token"); const apiData = { grant_type: "refresh_token", client_id: ClientId, client_secret: ClientSecret, refresh_token: RefreshToken, }; try { const response = await axios.post( "https://accounts.google.com/o/oauth2/token", apiData, { headers: { "Content-Type": "application/x-www-form-urlencoded", }, } ); logger.info("Google token响应:", {data: response.data}); if (!response.data.access_token) { return PaymentHelper.DEFAULT_RESULT; } access_token = response.data.access_token; await redisClient.set("access_token", response.data.access_token); await redisClient.expire("access_token", 1800); } catch (error) { logger.error("获取Google token失败:", error); return PaymentHelper.DEFAULT_RESULT; } } if (!access_token) { return PaymentHelper.DEFAULT_RESULT; } // 验证订单 const validation = await PaymentHelper.validateOrder(orderId); if (!validation.valid) { return { code: validation.message?.includes("重复发货") ? 1 : 0, msg: validation.message, }; } const orderInfo = validation.orderInfo; // 验证Google订单 const productId = ProductId + orderInfo.product_id; let apiUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${PackageName}/purchases/products/${productId}/tokens/${googleToken}?access_token=${access_token}`; let maxRetries = 3; let currentRetry = 0; let isCheck = false; // 使用循环进行重试 while (currentRetry < maxRetries) { try { const googleRes = await axios.get(apiUrl, { headers: { "Content-Type": "application/x-www-form-urlencoded", }, }); logger.info("Google验证响应:", googleRes.data); if (googleRes.data.purchaseState == 0) { out_trade_no = googleRes.data.orderId; isCheck = true; break; } } catch (error) { logger.error("Google验证请求失败:", error); } currentRetry++; } if (!isCheck) { return PaymentHelper.DEFAULT_RESULT; } // 发货处理 const result = await PaymentHelper.deliverOrder( orderInfo, ctx.request.ip, validation.url, out_trade_no ); // 确认和消费Google订单 try { // 确认订单 let acknowledgeUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${PackageName}/purchases/products/${productId}/tokens/${googleToken}:acknowledge?access_token=${access_token}`; await axios.post( acknowledgeUrl, {developerPayload: ""}, {headers: {"Content-Type": "application/json"}} ); // 消费订单 let consumeUrl = `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${PackageName}/purchases/products/${productId}/tokens/${googleToken}:consume?access_token=${access_token}`; await axios.post( consumeUrl, {developerPayload: ""}, {headers: {"Content-Type": "application/json"}} ); logger.info("Google订单处理完成:", {orderId: orderId}); } catch (error) { logger.error("Google订单确认/消费失败:", error); // 即使确认/消费失败,我们仍然返回发货结果,因为发货可能已经成功 } return result; }; /** * Apple支付回调处理 */ const appleCallPay = async (ctx, config) => { let data = ctx.request.body; logger.info("Apple支付回调参数:", {url: ctx.href, params: data}); if (!data.purchaseToken || !data.orderId) { return PaymentHelper.DEFAULT_RESULT; } let receipt_data = data.purchaseToken.replace(/ /g, "+"); let orderId = data.orderId; let out_trade_no = orderId; // 更新订单Token await Order.updateOrderToken(orderId, receipt_data, "apple"); // 验证Apple收据 let maxRetries = 5; let currentRetry = 0; let isCheck = false; while (currentRetry < maxRetries) { try { const apiData = { "receipt-data": receipt_data, }; const response = await axios.post(IosUrl, apiData, { headers: { "Content-Type": "application/json", }, }); logger.info(`Apple验证响应状态:${response.data.status}`); if (response.data.status == 0) { isCheck = true; break; } } catch (error) { logger.error("Apple验证请求失败:", error); } currentRetry++; } if (!isCheck) { logger.info(`Apple票据验证失败!`); return {code: 0, msg: "票据验证失败!"}; } // 验证订单 const validation = await PaymentHelper.validateOrder(orderId); if (!validation.valid) { return { code: validation.message?.includes("重复发货") ? 1 : 0, msg: validation.message, }; } const orderInfo = validation.orderInfo; // 发货处理 logger.info(`订单${orderId}通知游戏发货开始`); const result = await PaymentHelper.deliverOrder( orderInfo, ctx.request.ip, validation.url, out_trade_no ); logger.info(`订单${orderId}通知游戏发货结束,结果:`, result); return result; }; function splitString(input: string, separator: string): string[] { return input.split(separator); } //验证账号 const checkUserToken = async (ctx) => { let ip = getClientIp(ctx); let { uid, channel_id, device_no, reg_device, device_type, device_model, device_version, system_version, platform = '' } = ctx.request.body; // 前端未传platform时,从内存缓存(thirdLogin写入)中查询补全 if (channel_id == 17 && !platform && ip) { platform = getPlatformCache(ip); logger.info("从缓存获取platform:", {platform, ip}); } const create_time = formatDate(new Date()); const accountInfo = (await User.checkAccountIsExist(uid, channel_id))[0]; let accountRes = null; let res = null; if (!accountInfo) { accountRes = await User.createAccount( uid, channel_id, ip, device_no, reg_device, create_time, platform ); if (accountRes.affectedRows <= 0) { return ApiController.fail("添加账户失败"); } } res = await User.logAccountLogin( uid, ip, device_type, device_no, device_model, device_version, system_version, create_time, channel_id, platform, ); if (res.affectedRows <= 0) { return ApiController.fail("添加日志失败"); } return ApiController.success("请求成功", 1, false, {ip: ip}); }; class ApiController { /** * 生成成功响应 * @param msg 成功消息 * @param code 成功码,默认为1(部分API使用0表示成功) * @param useMessage 是否使用message字段而不是msg字段,默认为false * @param data 响应数据 * @returns 标准化的成功响应对象 */ static success( msg: string = "请求成功", code: number = 1, useMessage: boolean = false, data: any = null ) { return { code: code, [useMessage ? "message" : "msg"]: msg, data: data, }; } /** * 生成失败响应 * @param msg 错误消息 * @param code 错误码,默认为0 * @param data 额外的错误数据 * @param useMessage 是否使用message字段而不是msg字段,默认为false * @returns 标准化的失败响应对象 */ static fail( msg: string = "请求失败", code: number = 0, data: any = null, useMessage: boolean = false ) { return { code: code, [useMessage ? "message" : "msg"]: msg, data: data, }; } async createOrder(ctx) { let { uid, level, amount, role_id, role_name, product_id, server_id, channel_id, platform = '', } = ctx.request.body; logger.info("create params:", {params: ctx.request.body}); if ( !product_id || !server_id || !role_name || !role_id || !amount || !uid || !channel_id ) { ctx.body = ApiController.fail("参数错误,创建订单失败!!", -1); return; } const data = ctx.request.body; if (!SignatureVerifier.verifyMD5Sign(data, ORDER_SIGN_KEY)) { logger.error("创建订单签名验证失败"); ctx.body = ApiController.fail("签名错误,创建订单失败!!", -1); return; } if (amount <= 0) { ctx.body = ApiController.fail("金额错误,创建订单失败!!", -1); return; } const orderId = generateOrderNumber(); // 生成一个长度为8的订单号 const create_time = formatDate(new Date()); const res = await Order.createOrder( orderId, uid, level, amount, role_id, role_name, product_id, server_id, channel_id, create_time, platform ); if (res.affectedRows > 0) { ctx.body = ApiController.success("创建订单成功", 0, false, orderId); } else { ctx.body = ApiController.fail("创建订单失败", -1, null); } logger.info("创建订单返回结果:", {params: ctx.body}); } async checkUserToken(ctx) { let {channel_id} = ctx.request.body; const channelConfig = ChannelConfigManager.getConfig(channel_id); if (!channelConfig) { logger.info("未找到渠道配置:", {channel_id}); ctx.body = { code: 0, msg: "未知渠道id", }; return; } var result = await checkUserToken(ctx); ctx.body = result; } async thirdLogin(ctx) { const data = ctx.request.body; let channelId = parseInt(data.channel_id) || 1; const platform = data.platform; // 平台参数:ios、Android、miniapp、h5 logger.info("thirdLogin请求参数:", {data, channelId, platform}); // 渠道11和渠道12数据打通,统一使用渠道11 if (channelId === 12) { channelId = 11; logger.info("渠道12映射到渠道11,平台参数:", platform); } // 获取渠道配置 let channelConfig = ChannelConfigManager.getConfig(channelId); if (data.is_new == 1) { if (channelId == 6) { channelConfig = ChannelConfigManager.getConfig(999); } if (channelId == 1) { channelConfig = ChannelConfigManager.getConfig(9); } } if (!channelConfig) { logger.info("未找到渠道配置:", {channelId}); ctx.body = ApiController.fail("未知渠道id"); return; } // 从工厂获取对应的渠道处理器 const handler = channelFactory.getHandler(channelId); if (!handler) { logger.info("未找到渠道处理器:", {channelId}); ctx.body = ApiController.fail("未知渠道id"); return; } logger.info("使用渠道处理器处理登录:", { channelId: channelId, channelName: channelConfig.name, platform: platform }); // 调用渠道处理器的登录方法 const result = await handler.handleLogin(ctx, channelConfig); // 在返回结果中添加平台信息 if (result && result.data) { result.data.platform = platform || 'unknown'; } // 登录成功且有platform时,缓存到内存供checkUserToken查询 if (result && result.code === 1 && platform) { const ip = getClientIp(ctx); const device_no = data.device_no || ''; if (ip) { setPlatformCache(ip, platform); logger.info("thirdLogin缓存platform:", {ip, platform}); } } // 根据不同渠道的返回格式要求处理结果 ctx.body = result; } 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) { channelId = parseInt(ctx.query.channel_id); } if (data.channel_id) { channelId = parseInt(data.channel_id); } logger.info("当前支付渠道id:", {channelId}); // 获取渠道配置 let channelConfig = ChannelConfigManager.getConfig(channelId); if (!channelConfig) { logger.info("未找到渠道配置:", {channelId}); ctx.body = ApiController.fail("未知渠道id"); return; } if (ctx.query.is_new && ctx.query.is_new == 1) { if (channelId == 6) { channelConfig = ChannelConfigManager.getConfig(999); } if (channelId == 1) { channelConfig = ChannelConfigManager.getConfig(9); } } const handler = channelFactory.getHandler(channelId); if (!handler) { logger.info("未找到渠道处理器:", {channelId}); ctx.body = ApiController.fail("未知渠道id"); return; } logger.info("使用渠道处理器处理支付:", { channelId: channelId, channelName: channelConfig.name, }); // 调用渠道处理器的支付方法 const result = await handler.handlePayment(ctx, channelConfig); // 根据不同渠道的返回格式要求处理结果 switch (channelConfig.channelId) { case 6: ctx.body = result.code === 1 ? 1 : result.code; break; case 2: case 3: case 5: case 8: ctx.body = result.code === 1 ? "SUCCESS" : "Fail"; break; case 7: ctx.body = result.code === 1 ? "success" : "fail"; break; case 11: ctx.body = result.code === 1 ? "SUCCESS" : "Fail"; break; case 12: ctx.body = result.code === 1 ? "SUCCESS" : "Fail"; break; case 13: //{"status":0,"msg":"status 非 0 时,传入失败信息"} ctx.body = {status: result.code === 1 ? 0 : -1, msg: result.msg}; break; case 17: // 华为渠道:成功返回 {"errCode": 0},失败返回 {"errCode": 1, "errMsg": "..."} if (result.code === 0 || result.code === 1) { // 发货成功 if (ctx.query.platform == "mtminiapp") { ctx.body = {"code": 0, "msg": "ok"} }else { ctx.body = {errCode: 0}; } } else { // 发货失败 ctx.body = {errCode: 1, errMsg: result.msg || "发放失败"}; } break; case 14: case 21: ctx.body = result.code === 1 ? "SUCCESS" : "Fail"; break; default: ctx.body = result; } } async getServerList(ctx) { let tag = ctx.query.channel_id || 1; let data = ctx.request.body; let ip = getClientIp(ctx); // 尝试从渠道ID获取配置 const channelConfig = ChannelConfigManager.getConfig(1); const signStr = data.timestamp + channelConfig.loginConfig?.signKey; let newSign = CryptoJS.MD5(signStr).toString(); if (data.sign != newSign) { console.log("signStr:", signStr); logger.info( `签名错误: 签名串 ${signStr} newSign ${newSign} sign ${data.sign}` ); ctx.body = ApiController.fail("签名错误"); return; } let sort = data.sort == 0 ? "asc" : "desc"; logger.info("区服接口", {ip: ip}); const servers = await Server.getServerList(tag, 0, sort); let serverList = []; if (servers.length > 0) { servers.forEach(function (element) { let status = element.status; if ((status == 0 || status == 3) && element.white_list) { const list = element.white_list.split(","); if (list.length > 0) { if (list.includes(ip)) { status = 1; } } } serverList.push({ serverId: element.id, serverName: element.name, }); }); } ctx.body = ApiController.success("成功", 1, false, serverList); } 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, uid: uid, ip: ip}); const servers = await Server.getAllServerList(tag, ip); // 如果提供了玩家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 }; } }) ); // tag=11 时,找到第一个有角色的服,隐藏它之前的服;都没角色则只显示最新的1个服 if (tag == 11 || tag === '11') { const firstRoleIndex = serversWithRoleInfo.findIndex(s => s.hasRole); if (firstRoleIndex > 0) { ctx.body = serversWithRoleInfo.slice(firstRoleIndex); } else if (firstRoleIndex === -1) { ctx.body = serversWithRoleInfo.slice(-1); } else { ctx.body = serversWithRoleInfo; } } else { ctx.body = serversWithRoleInfo; } } else { ctx.body = servers; } } async enterServer(ctx) { let {uid, server_id, channel_id} = ctx.request.body; let url = await getServerList(server_id, channel_id || 1); if (!url) { ctx.body = ApiController.fail(`区服id错误: serverId ${server_id}`, -1); return; } logger.info("create params:", {params: ctx.request.body}); if (!server_id || !uid) { ctx.body = ApiController.fail("参数错误!!", -1); return; } const create_time = formatDate(new Date()); const serverInfo = ( await Server.checkEnterServerByUid(uid, server_id, channel_id) )[0]; let res = null; if (serverInfo) { res = await Server.updateEnterServer(serverInfo.id, create_time); } else { res = await Server.enterServer(uid, server_id, create_time, channel_id); } if (res.affectedRows > 0) { ctx.body = ApiController.success("请求成功", 0, true, ""); } else { ctx.body = ApiController.fail("请求失败", -1, null, true); } } async getLastServerList(ctx) { let {uid, channel_id} = ctx.request.body; // uid是可选参数,如果不提供则只返回基本服务器信息 let tag = channel_id || 1; let data = []; let isNewAccount = 1; let enterServerList = []; let ip = getClientIp(ctx); logger.info("getLastServerList 区服接口", {uid: uid, ip: ip}); // 获取渠道配置 const channelConfig = ChannelConfigManager.getConfig(tag); if (!channelConfig) { logger.info("未找到渠道配置:", {tag}); ctx.body = ApiController.fail("未知渠道id"); return; } // 如果提供了uid,查询用户进入过的服务器列表 if (uid) { enterServerList = await Server.getEnterServerListByUid(uid, channel_id); } if (enterServerList.length > 0) { isNewAccount = 0; const servers = await Server.getAllServerList(tag, ip); 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); } } const sid = Number(element.server_id) || 1; const minSid = Math.floor((sid - 1) / 10) * 10 + 1; data.push({ channel: channelConfig.name, //渠道固定 minSid: minSid, //最小服务器 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, // 角色信息(只有在提供了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: Math.floor(((servers[0].sid || 1) - 1) / 10) * 10 + 1, //最小服务器 maxSid: servers.length, //最大服务器 这里会控制 服务器列表显示的数量 isNewAccount: isNewAccount, //1为新号 会弹出用户协议 //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区) sid: serverInfo.sid || 1, id: serverInfo.id || 1, name: serverInfo.name || "1区", tips: serverInfo.tips || "", server: serverInfo.server ? 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({ channel: "未知", //渠道固定 minSid: 1, //最小服务器 maxSid: 10, //最大服务器 这里会控制 服务器列表显示的数量 isNewAccount: isNewAccount, //1为新号 会弹出用户协议 //以下是最近登陆的服务器 (不可为空 如果没有参数可以填最后一个区) sid: 1, id: 1, name: "1区", tips: "", server: "", status: 0, // 角色信息(新用户,无角色) roleName: null, roleLevel: null, head: null, hasRole: false }); } } // 根据sid去重 const seenSids = new Set(); data = data.filter(item => { if (seenSids.has(item.sid)) return false; seenSids.add(item.sid); return true; }); // 所有组的minSid统一取最小值 if (data.length > 0) { const globalMinSid = Math.min(...data.map(item => item.minSid)); data.forEach(item => { item.minSid = globalMinSid; }); } // tag=11 时,找到第一个有角色的服,隐藏它之前的服;都没角色则只显示最新的1个服 if ((tag == 11 || tag === '11') && enterServerList.length > 0) { const firstRoleIndex = data.findIndex(s => s.hasRole); if (firstRoleIndex > 0) { data = data.slice(firstRoleIndex); } else if (firstRoleIndex === -1) { data = data.slice(-1); } } ctx.body = data; // ctx.body = ApiController.success("获取最近服务器列表成功", 1, false, data); } async getNotice(ctx) { let tag = ctx.query.channel_id || 1; let content = ""; let content_wh = ""; const system = await System.getSystemConfig(tag); if (system) { system.forEach((element) => { if (element.key == "content") { content = element.value; } if (element.key == "content_wh") { content_wh = element.value; } }); } let notice_data = [ { status: 1, content: content, content_wh: content_wh, }, ]; ctx.body = notice_data; // ctx.body = ApiController.success("获取公告成功", 1, false, notice_data); } // 维护服务器,踢掉所有玩家 async maintenance(ctx) { let data = { channel_id: parseInt(ctx.query.channel_id), serverId: parseInt(ctx.query.serverId) || "", }; logger.info("maintenance :", {channelId: data.channel_id}); let url = await getServerList(data.serverId, data.channel_id || 1); if (!url) { ctx.body = ApiController.fail(`区服id错误: serverId ${data.serverId}`, 1); return; } let param = JSON.stringify({ type: "kickAllUser", }); let sendMsg = new Msg(); sendMsg.connect(url, Account); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO("test", param); }, 1000); }); ctx.body = ApiController.success("success", 0, false, null); } async maintenanceAll(ctx) { let channelId = parseInt(ctx.query.channel_id); let isMaintenance = ctx.query.isMaintenance; let key = ctx.query.key; logger.info("maintenanceAll :", { channelId: channelId, isMaintenance: isMaintenance, }); if (key != "dsdadsadajkkjkxab") { ctx.body = ApiController.fail("无权使用", -1); return; } const servers = await Server.getAllServerList(channelId, ""); logger.info("maintenanceAll servers:", {servers: servers}); if (servers.length === 0) { ctx.body = ApiController.fail("不存在可操作区服", -1); return; } const serversId = servers.map((obj) => obj.id); if (isMaintenance == "true") { const res = await Server.updateServerStatus(serversId, 3); } servers.forEach(function (element) { let url = "ws://" + element.ip + ":" + element.port; if (!url) { ctx.body = ApiController.fail(`区服id错误: serverId ${element.id}`, 1); return; } let param = JSON.stringify({ type: "kickAllUser", }); // Msg.connect(url, Account); let sendMsg = new Msg(); sendMsg.connect(url, Account); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO("test", param); }, 1000); }); }); logger.info("maintenanceAll 更新的区服id:", {serversId: serversId}); ctx.body = ApiController.success("success", 0, false, null); } async sendMail(ctx) { let data = ctx.request.body; let url = await getServerList(data.serverId, data.channel_id || 1); if (!url) { ctx.body = ApiController.fail(`区服id错误: serverId ${data.serverId}`, 1); return; } let param = JSON.stringify({ type: "sendMail", mail: JSON.stringify({ uuid: data.uuid, title: data.title, content: data.content, items: JSON.parse(data.items), expire: data.expire, }), }); // Msg.connect(url, Account); let sendMsg = new Msg(); sendMsg.connect(url, Account); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO("test", param); }, 1000); }); ctx.body = ApiController.success("success", 0, false, null); } async sendAllMail(ctx) { let data = ctx.request.body; if (!data.server_list || data.server_list.length === 0) { ctx.body = ApiController.fail("区服不能为空", -1); return; } logger.info("sendAllMail 接口请求 data:", {data: data}); const server_list = data.server_list; const servers = await Server.getServerList(data.channel_id || 1); const filteredServer = servers.filter((item) => server_list.includes(item.id) ); filteredServer.forEach(function (element) { let url = "ws://" + element.ip + ":" + element.port; if (!url) { ctx.body = ApiController.fail( `区服id错误: serverId ${element.server_id}`, 1 ); return; } let param = JSON.stringify({ type: "sendAllMail", mail: JSON.stringify({ title: data.title, content: data.content, items: JSON.parse(data.items), expire: data.expire, }), }); let sendMsg = new Msg(); sendMsg.connect(url, Account); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO("test", param); }, 1000); }); }); ctx.body = ApiController.success("success", 0); } async useCDKV2(ctx) { let body = ctx.request.body; logger.info("useCDKV2接口请求参数:", {data: body}); let data = { code: body.code, userId: body.account, channelId: body.channel_id || 0, }; if (body.uuid) { data.userId = body.uuid; } let serverUrl = await getServerAddr(body.serverId, body.channel_id); let err = ""; if (!serverUrl) { ctx.body = ApiController.fail(`区服id错误: serverId ${body.serverId}`, 1); return; } let port = 8004; let host = "127.0.0.1"; let url = "http://" + host + ":" + port + "/api/giftCode/exchange"; logger.info("接口请求后台 data:", {data: data, url: url}); try { const specialCodes = [ "gfyx555", "gfyx666", "gfyx777", "gfyx1000", "gfyx2000", "gfyx3000", "gfyxrqlb", "GFYX555", "GFYX666", "GFYX777", "GFYX1000", "GFYX2000", "GFYX3000", "GFYXrqlb", "GFYX000", "GFYX222", "GFYX333", "GFYX888", "GFYX1818", "GQ888", "ZQ888", "gfyx8888", "gfyx6666", "gfyx7777", "gfyxxjbc", "LIDONGLB", "XIAOXULB", "GANENLB", "WANSHLB", "HERO666", "HERO777", "HERO888", "gfyx9999", "aiqi6666", "aiqi7777", "aiqi8888", "aiqi9999", "gf555", "gf666", "gf777", "gf100", "gf200", "gf300", "YDKL888", "SDKL888", "CHUXILB", "CHUNJIELB", "YUANXIAOLB", "VIP555", "VIP666", "VIP777", "VIP888", ]; let param: string = ""; const h5Codes = ["gfyx000", "gfyx222", "gfyx333", "gfyx888", "gfyx1818"]; if (h5Codes.includes(data.code) && body.channel_id != 5) { ctx.body = ApiController.fail("该激活码无法在h5渠道使用", 1); } if (specialCodes.includes(data.code) || h5Codes.includes(data.code)) { param = JSON.stringify({ code: data.code, type: "UseFixCDK", channel_id: body.channel_id, }); } else { const response = await axios.post(url, data, { headers: { "Content-Type": "application/json", }, timeout: 10000, }); logger.info("接口请求后台 接口返回 response:", {data: response.data}); if (response.data.code != 200) { ctx.body = ApiController.fail(response.data.msg, 1); return; } const result = splitString(response.data.data, ";"); // 重构itemList let itemList = Array(); for (let i = 0; i < result.length; i++) { let elem = splitString(result[i], ":"); let output0 = parseInt(elem[0], 10); let output1 = parseInt(elem[1], 10); itemList.push([output0, output1]); } param = JSON.stringify({ type: "UseCDKV2", itemList: itemList, channel_id: body.channel_id, }); } let sendMsg = new Msg(); sendMsg.connect(serverUrl, Account); logger.info("api cdk 发送服务器 param:", {param: param}); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO(body.account, param, body.serverId); }, 1000); }); } catch (error) { console.log(error); } ctx.body = ApiController.success("success", 0); } async banUser(ctx) { let data = ctx.request.body; logger.info("banUser 接口请求 data:", {data: data}); if (!data.server_tag) { ctx.body = ApiController.fail("区服id不能为空", -1, ""); return; } let banInfo = {}; switch (parseInt(data.ban_type)) { case 1: banInfo = { roleBanInfo: { channelTag: data.channel_id, serverTag: data.server_tag, roleTag: data.role_tag, banTime: data.ban_time, }, type: "setBan", }; break; case 2: banInfo = { accountBanInfo: { channelTag: data.channel_id, accountTag: data.account_tag, banTime: data.ban_time, }, type: "setBan", }; break; case 3: banInfo = { appBanInfo: { appId: data.device_id, banTime: data.ban_time, }, type: "setBan", }; break; default: } logger.info("banUser 发送服务器 banInfo:", banInfo); let url = await getServerList(data.server_tag, data.channel_id); if (!url) { ctx.body = ApiController.fail( `区服id错误: serverId ${data.server_tag}`, 1 ); return; } let param = JSON.stringify(banInfo); logger.info("banUser 发送服务器 json字符串: " + param); let sendMsg = new Msg(); sendMsg.connect(url, Account); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO("test", param, data.server_tag); // sendMsg.CG_SET_BAN(param); }, 1000); }); ctx.body = ApiController.success("success", 0); } async uicFilter4399(ctx) { let body = ctx.request.body; let channelConfig = ChannelConfigManager.getConfig(6); if (body.is_new == 1) { channelConfig = ChannelConfigManager.getConfig(999); } logger.info("uicFilter4399 body:", {data: body}); let toCheck = body.toCheck; let signStr = channelConfig.loginConfig?.signKey + toCheck; logger.info("uicFilter4399 signStr:", {signStr: signStr}); let apiParams = { toCheck: toCheck, app: channelConfig.loginConfig.appId, byPinyin: true, isBatch: false, sig: CryptoJS.MD5(signStr).toString(), }; logger.info("uicFilter4399 apiParams:", {data: apiParams}); const queryStr = Object.entries(apiParams) .map( ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}` ) .join("&"); let url = "https://wo.webgame138.com/test/matchService.do" + "?" + queryStr; // let url = "https://wo.webgame138.com/test/matchService.do"; logger.info("uicFilter4399 url:", {url: url}); try { // let response = await axios.get(url, { // timeout: 10000, // 设置10秒超时 // }); const response = await axios.post(url, apiParams, { headers: { "Content-Type": "application/json", }, }); logger.info("uicFilter4399 接口返回 response:", {data: response.data}); ctx.body = ApiController.success("success", 0, false, response.data); } catch (error) { console.log(error); ctx.body = ApiController.fail(error.message || "请求失败", 1); } } async roleList4399(ctx) { let body = ctx.request.body; logger.info("RoleList4399 请求参数:", {body: body}); let data = { userId: body.userId, server: body.server, is_new: ctx.query.is_new || 0 }; let port = 8004; let host = "127.0.0.1"; let url = "http://" + host + ":" + port + "/api/roleList/checkBy4399"; logger.info("RoleList4399 url:", {url: url}); try { const response = await axios.post(url, data, { headers: { "Content-Type": "application/json", }, }); logger.info("RoleList4399 返回 response:", {data: response.data}); ctx.body = response.data; } catch (error) { console.log(error); } } async cdkBy4399(ctx) { let body = ctx.request.body; logger.info("cdkBy4399:", {data: body}); let server = body.server || 1; let channel = 5; let data = { code: body.giftId, userId: body.userId, channelId: channel, }; if (body.uuid) { data.userId = body.uuid; } let serverUrl = await getServerList(server, channel); let err = ""; if (!serverUrl) { ctx.body = ApiController.fail(`区服id错误: serverId ${body.serverId}`, 1); return; } let port = 8004; let host = "127.0.0.1"; let url = "http://" + host + ":" + port + "/api/giftCode/exchange"; logger.info("cdkBy4399接口请求后台 data:", {data: data, url: url}); try { const response = await axios.post(url, data, { headers: { "Content-Type": "application/json", }, timeout: 10000, }); logger.info("cdkBy4399接口请求后台 接口返回 response:", { data: response.data, }); if (response.data.code != 200) { ctx.body = ApiController.fail(response.data.msg, 10004); return; } const result = splitString(response.data.data, ";"); // 重构itemList let itemList = Array(); for (let i = 0; i < result.length; i++) { let elem = splitString(result[i], ":"); let output0 = parseInt(elem[0], 10); let output1 = parseInt(elem[1], 10); itemList.push([output0, output1]); } let param = JSON.stringify({ type: "UseCDKV2", itemList: itemList, channel_id: channel, }); let sendMsg = new Msg(); sendMsg.connect(serverUrl, Account); logger.info("cdkBy4399 cdk 发送服务器 param:", {param: param}); new Promise((resolve) => { setTimeout(async () => { sendMsg.CG_TEST_PROTO("test", param, server); }, 1000); }); } catch (error) { console.log(error); } ctx.body = ApiController.success("success", 10000); } async roleListYfy(ctx) { let gameCode = ctx.query.gameCode; let timestamp = ctx.query.timestamp; let uid = ctx.query.uid; let sign = ctx.query.sign; let AppSecret = "H1EqhbpA80jt0Jw6Q3T2"; logger.info("roleListYfy 请求参数:", { uid: uid, gameCode: gameCode, timestamp: timestamp, }); const signStr = `gameCode-${gameCode}×tamp=${timestamp}&uid=${uid}${AppSecret}`; const newSign = CryptoJS.MD5(signStr).toString(); if (newSign != sign) { logger.info("roleListYfy 签名错误:", { signStr: signStr, sign: sign, newSign: newSign, }); ctx.body = ApiController.fail("签名错误!!", -1); } let port = 8004; let host = "127.0.0.1"; let url = "http://" + host + ":" + port + "/api/roleList/checkByYfy"; logger.info("roleListYfy url:", {url: url}); try { const response = await axios.post( url, { userId: uid, }, { headers: { "Content-Type": "application/json", }, } ); logger.info("roleListYfy 返回 response:", {data: response.data}); ctx.body = response.data; } catch (error) { console.log(error); } } async getQuickSign(ctx) { let data = ctx.request.body; logger.info("getQuickSign 请求参数:", {body: data}); if (!SignatureVerifier.verifyMD5Sign(data, ORDER_SIGN_KEY)) { logger.error("签名验证失败"); ctx.body = ApiController.fail("签名错误!!", -1); return; } let key = "H1EqhbpA80jt0Jw6Q3T2"; let signStr = "uid=" + data.uid + "&username=" + data.username + "&serverId=" + data.serverId + "&serverName=" + data.serverName + "&userRoleId=" + data.userRoleId + "&userRoleName=" + data.userRoleName + "&userRoleLevel=" + data.userRoleLevel + "&vipLevel=" + data.vipLevel + "&gameRolePower=" + data.gameRolePower + "&key=" + key; logger.info("getQuickSign signStr:", {signStr: signStr}); data.sign = CryptoJS.MD5(signStr).toString(); ctx.body = ApiController.success("success", 0, false, data); } /** * mianyou开服同步处理 * @param ctx Koa上下文 */ async mianyouSyncServer(ctx) { try { const data = ctx.request.body; logger.info("mianyou开服同步请求参数:", {data}); // 验证必要参数 const {serverId, gameServerId, serverName, startTime, channelId} = data; if (!serverId || !gameServerId || !serverName || !startTime) { logger.warn("mianyou开服同步失败: 缺少必要参数", {data}); ctx.body = ApiController.fail("缺少必要参数: serverId, gameServerId, serverName, startTime"); return; } // 获取渠道配置 const channelConfig = ChannelConfigManager.getConfig(channelId || 1); if (!channelConfig) { logger.error("mianyou开服同步失败: 未找到渠道配置", {channelId}); ctx.body = ApiController.fail("未找到渠道配置"); return; } // 创建MianyouChannelHandler实例 const mianyouHandler = new MianyouChannelHandler(); // 调用开服同步方法 const result = await mianyouHandler.openServer( parseInt(serverId), gameServerId, serverName, startTime, channelConfig ); logger.info("mianyou开服同步结果:", {result}); // 根据返回结果设置响应 if (result.status === 0) { ctx.body = ApiController.success("开服同步成功", 0, false, result); } else { ctx.body = ApiController.fail(result.msg || "开服同步失败", result.status); } } catch (error) { logger.error("mianyou开服同步异常:", error); ctx.body = ApiController.fail("服务器内部错误", -1); } } } module.exports = new ApiController();