const axios = require('axios'); const Logs = require('../libs/logs'); const Cache = require('../libs/cache'); const Setting = require('./Setting'); const { eventSolutions } = require('../triangle/eventSolutions'); const { calcTotalProfit, calcTotalProfitWithFixedFirst } = require('../triangle/totalProfitCalc'); const { getSetting, updateSetting } = require('../triangle/settings'); const fs = require('fs'); const path = require('path'); const GamesCacheFile = path.join(__dirname, '../data/games.cache'); const DevGameTastFile = path.join(__dirname, '../data/gameTast.json'); const childOptions = process.env.NODE_ENV == 'development' ? { execArgv: ['--inspect=9228'], stdio: ['pipe', 'pipe', 'pipe', 'ipc'] } : {}; const { fork } = require('child_process'); const events_child = fork('./triangle/eventsMatch.js', [], childOptions); const PS_IOR_KEYS = [ ['0', 'ior_mh', 'ior_mn', 'ior_mc'], ['-1', 'ior_rh_15', 'ior_wmh_1', 'ior_rac_05'], ['-2', 'ior_rh_25', 'ior_wmh_2', 'ior_rac_15'], ['+1', 'ior_rah_05', 'ior_wmc_1', 'ior_rc_15'], ['+2', 'ior_rah_15', 'ior_wmc_2', 'ior_rc_25'], // ['0-1', 'ior_ot_0', 'ior_os_0-1', 'ior_ot_1'], // ['2-3', 'ior_ot_2', 'ior_os_2-3', 'ior_ot_3'], ['jqs', 'ior_ot_1', 'ior_ot_2', 'ior_ot_3', 'ior_ot_4', 'ior_ot_5', 'ior_ot_6', 'ior_ot_7'], ]; // 测试环境 // const BASE_API_URL = 'https://dev.api.czxd8.com/api/p'; const IS_DEV = process.env.NODE_ENV == 'development'; const BASE_API_URL = IS_DEV ? 'https://api.qboss.vip/api' : 'http://172.17.222.37/api'; const GAMES = { Leagues: {}, Baselist: {}, Relations: {}, Solutions: {}, UpdateTimestamp: {}, }; const Request = { callbacks: {}, count: 0, } /** * 精确浮点数字 * @param {number} number * @param {number} x * @returns {number} */ const fixFloat = (number, x=2) => { return parseFloat(number.toFixed(x)); } /** * 获取市场类型 */ const getMarketType = (mk) => { if (mk == 0) { return 'early'; } else if (mk == 2) { return 'rollball'; } return 'today'; } /** * 同步联赛列表 */ const syncLeaguesList = ({ mk, leagues }) => { if (IS_DEV) { return Logs.out('syncLeaguesList', { mk, leagues }); } axios.post(`${BASE_API_URL}/p/syncLeague`, { mk, leagues }, { proxy: false }) .then(res => { // Logs.out('syncLeaguesList', res.data); }) .catch(err => { Logs.out('syncLeaguesList', err.message); }); } /** * 更新联赛列表 */ const updateLeaguesList = ({ mk, leagues }) => { const leaguesMap = GAMES.Leagues; const nowTime = Date.now(); const expireTime = nowTime - 1000 * 60 * 5; if (!leaguesMap[mk]) { leaguesMap[mk] = { timestamp: 0, leagues: [], }; } if (leaguesMap[mk].timestamp < expireTime || JSON.stringify(leaguesMap[mk].leagues) != JSON.stringify(leagues)) { leaguesMap[mk].leagues = leagues; leaguesMap[mk].timestamp = nowTime; syncLeaguesList({ mk, leagues }); return leagues.length; } return 0; } /** * 获取筛选过的联赛 */ const getFilteredLeagues = async (mk) => { return axios.get(`${BASE_API_URL}/p/getLeagueTast?mk=${mk ?? ''}`, { proxy: false }) .then(res => { if (res.data.code == 0) { return res.data.data; } return Promise.reject(new Error(res.data.message)); }); } /** * 同步比赛列表到服务器 */ const syncGamesList = ({ platform, mk, games }) => { if (IS_DEV) { return Logs.out('syncGamesList', { platform, mk, games }); } axios.post(`${BASE_API_URL}/p/syncGames`, { platform, mk, games }) .then(res => { // Logs.out('syncGamesList', { platform, mk, count: games.length }, res.data); }) .catch(err => { Logs.out('syncGamesList', { platform, mk }, err.message); }); } /** * 同步基准比赛列表 */ const syncBaseList = ({ marketType, games }) => { const baseList = GAMES.Baselist; // 直接创建新列表 if (!baseList[marketType]) { baseList[marketType] = games; return; } const newMap = new Map(games.map(item => [item.eventId, item])); // 删除不存在的项 for (let i = baseList[marketType].length - 1; i >= 0; i--) { if (!newMap.has(baseList[marketType][i].eventId)) { baseList[marketType].splice(i, 1); } } // 添加或更新 const oldIds = new Set(baseList[marketType].map(item => item.eventId)); games.forEach(game => { if (!oldIds.has(game.eventId)) { // 添加新项 baseList[marketType].push(game); } }); } /** * 清理基准比赛列表 */ const cleanupBaseList = () => { const baseList = GAMES.Baselist; const nowTime = Date.now(); const expireTime = nowTime - 1000*60*60*3; Object.keys(baseList).forEach(marketType => { baseList[marketType] = baseList[marketType].filter(item => item.timestamp < expireTime); }); } /** * 更新比赛列表 */ const updateGamesList = (({ platform, mk, games } = {}) => { return new Promise((resolve, reject) => { if (!platform || !games) { return reject(new Error('PLATFORM_GAMES_INVALID')); } syncGamesList({ platform, mk, games }); resolve(); }); }); /** * 提交盘口数据 */ const submitOdds = ({ platform, mk, games }) => { if (IS_DEV) { return Logs.out('syncOdds', { platform, mk, games }); } axios.post(`${BASE_API_URL}/p/syncOdds`, { platform, mk, games}, { proxy: false }) .then(res => { // Logs.out('syncOdds', { platform, mk, count: games.length }, res.data); }) .catch(err => { Logs.out('syncOdds', { platform, mk }, err.message); }); } /** * 同步基准盘口 */ const syncBaseEvents = ({ mk, games, outrights }) => { const { expireTimeEvents, expireTimeSpecial } = getSetting(); const marketType = getMarketType(mk); const baseList = GAMES.Baselist; if (!baseList[marketType]) { return; } const baseMap = new Map(baseList[marketType].map(item => [item.eventId, item])); games?.forEach(game => { const { eventId, originId, stage, retime, score, wm, evtime, events } = game; const baseGame = baseMap.get(eventId); if (baseGame) { baseGame.originId = originId; baseGame.stage = stage; baseGame.retime = retime; baseGame.score = score; baseGame.wm = wm; baseGame.evtime = evtime; baseGame.events = events; } }); outrights?.forEach(outright => { const { parentId, sptime, special } = outright; const baseGame = baseMap.get(parentId); if (baseGame) { baseGame.sptime = sptime; baseGame.special = special; } else { const originBaseMap = new Map(baseList[marketType].map(item => [item.originId, item])); const originBaseGame = originBaseMap.get(parentId); if (originBaseGame) { originBaseGame.sptime = sptime; originBaseGame.special = special; } } }); if (games?.length) { const gamesList = baseList[marketType]?.map(game => { const { evtime, events, sptime, special, ...gameInfo } = game; const expireTimeEv = Date.now() - expireTimeEvents; const expireTimeSP = Date.now() - expireTimeSpecial; let odds = {}; if (evtime > expireTimeEv) { odds = { ...odds, ...events }; } if (sptime > expireTimeSP) { odds = { ...odds, ...special }; } const matches = PS_IOR_KEYS.map(([label, ...keys]) => { let match = keys.map(key => { return { key, value: odds[key]?.v ?? 0, origin: odds[key]?.r } }); if (label == 'jqs') { match = match.filter(item => item.value !== 0); } return { label, match }; }).filter(item => { if (item.label == 'jqs') { return item.match.length; } else { return item.match.every(entry => entry.value !== 0); } }); game.matches = matches; // matches 也记录下来 let uptime = evtime ?? 0; return { ...gameInfo, matches, uptime }; }); if (gamesList.filter(item => item.uptime > 0).length) { // if (mk == 2) { // Logs.out('syncBaseEvents', JSON.stringify({ mk, gamesList }, null, 2)); // } submitOdds({ platform: 'ps', mk, games: gamesList }); } const relatedGames = Object.values(GAMES.Relations).map(item => item.rel?.['ps'] ?? {}); if (!relatedGames.length) { return 0; } let update = 0; const relatedMap = new Map(relatedGames.map(item => [item.eventId, item])); gamesList?.forEach(game => { const { eventId, matches, uptime, stage, retime, score, wm } = game; const relatedGame = relatedMap.get(eventId); if (relatedGame) { const events = {}; matches.forEach(({ label, match }) => { match.forEach(({ key, value, origin }) => { events[key] = { v: value, r: origin }; }); }); relatedGame.evtime = uptime; relatedGame.events = events; relatedGame.stage = stage; relatedGame.retime = retime; relatedGame.score = score; relatedGame.wm = wm; update ++; } }); return update; } } const updateGamesEvents = ({ platform, mk, games, outrights, timestamp, tp }) => { return new Promise((resolve, reject) => { if (!platform || (!games && !outrights)) { return reject(new Error('PLATFORM_GAMES_INVALID')); } const { UpdateTimestamp } = GAMES; if (!UpdateTimestamp[tp] || UpdateTimestamp[tp] < timestamp) { // Logs.out('updateGamesEvents', { tp, timestamp }); UpdateTimestamp[tp] = timestamp; } else { return resolve({ update: 0 }); } if (platform == 'ps') { const update = syncBaseEvents({ mk, games, outrights }); return resolve({ update }); } const relatedGames = Object.values(GAMES.Relations).map(item => item.rel?.[platform] ?? {}); if (!relatedGames.length) { return resolve({ update: 0 }); } const updateCount = { update: 0 }; const relatedMap = new Map(relatedGames.map(item => [item.eventId, item])); games?.forEach(game => { const { eventId, evtime, events, stage, retime, score, wm } = game; const relatedGame = relatedMap.get(eventId); if (relatedGame) { relatedGame.evtime = evtime; relatedGame.events = events; relatedGame.stage = stage; relatedGame.retime = retime; relatedGame.score = score; relatedGame.wm = wm; updateCount.update ++; } }); outrights?.forEach(outright => { const { parentId, sptime, special } = outright; const relatedGame = relatedMap.get(parentId); if (relatedGame) { relatedGame.sptime = sptime; relatedGame.special = special; updateCount.update ++; } }); resolve(updateCount); }); } /** * 获取比赛盘口 */ const getGamesEvents = ({ platform, relIds = [] } = {}) => { if (!relIds.length) { return null; } const idSet = new Set(relIds); const relations = { ...GAMES.Relations }; Object.keys(relations).forEach(id => { if (idSet.size && !idSet.has(+id)) { delete relations[id]; } }); if (platform) { return Object.values(relations).map(rel => rel[platform] ?? {}); } const gamesEvents = {}; Object.values(relations).forEach(({ rel }) => { Object.keys(rel).forEach(platform => { const game = rel[platform] ?? {}; const { eventId, events, special } = game; if (!gamesEvents[platform]) { gamesEvents[platform] = {}; } gamesEvents[platform][eventId] = { ...events, ...special }; }); }); return gamesEvents; } /** * 获取关联比赛 */ const getDevGameTast = () => { return new Promise((resolve) => { const data = Cache.getData(DevGameTastFile, true); resolve({data}); }); } const fetchGamesRelation = async (mk='') => { const getGameTast = Promise.all([ getDevGameTast(), axios.get(`${BASE_API_URL}/p/getGameTast?mk=${mk}`, { proxy: false }) ]); return getGameTast.then(([res1, res2]) => { const resData = res1.data ?? res2.data; if (resData.code == 0) { const nowTime = Date.now(); const gamesRelation = resData.data?.filter(item => { const timestamp = new Date(item.timestamp).getTime(); if (nowTime > timestamp) { item.mk = 2; } item.timestamp = timestamp; const expireTime = timestamp + 1000*60*60*3; return expireTime > nowTime; }).map(item => { const { id, mk, league_name, event_id: ps_event_id, league_id: ps_league_id, team_home_name: ps_team_home_name, team_away_name: ps_team_away_name, ob_event_id, ob_league_id, ob_team_home_name, ob_team_away_name, hg_event_id, hg_league_id, hg_team_home_name, hg_team_away_name, timestamp, } = item; const rel = { ps: { eventId: +ps_event_id, leagueId: +ps_league_id, leagueName: league_name, teamHomeName: ps_team_home_name, teamAwayName: ps_team_away_name, timestamp }, ob: ob_event_id ? { eventId: +ob_event_id, leagueId: +ob_league_id, leagueName: league_name, teamHomeName: ob_team_home_name, teamAwayName: ob_team_away_name, timestamp } : null, hg: hg_event_id ? { eventId: +hg_event_id, leagueId: +hg_league_id, leagueName: league_name, teamHomeName: hg_team_home_name, teamAwayName: hg_team_away_name, timestamp } : null }; return { id: ps_event_id, mk, rel, timestamp }; }) ?? []; return gamesRelation; } return Promise.reject(new Error(resData.message)); }); } const getGamesRelation = ({ mk, ids, listEvents } = {}) => { let idsSet = null; if (ids?.length) { idsSet = new Set(ids); } const relations = Object.values(GAMES.Relations).filter(item => { if (idsSet && !idsSet.has(item.id)) { return false; } if (typeof(mk) === 'undefined' || mk === '') { return true; } return item.mk == mk; }).sort((a, b) => a.timestamp - b.timestamp); if (listEvents) { return relations; } const gamesRelation = relations.map(item => { const { rel, ...relationInfo } = item; const tempRel = { ...rel }; Object.keys(tempRel).forEach(platform => { const { events, evtime, sptime, special, ...gameInfo } = tempRel[platform]; tempRel[platform] = gameInfo; }); return { ...relationInfo, rel: tempRel }; }); return gamesRelation; } /** * 定时更新关联比赛列表 */ const updateGamesRelation = () => { fetchGamesRelation() .then(gamesRelation => { const baseList = {}; gamesRelation.map(item => { const baseGame = item.rel?.['ps'] ?? {}; return { ...baseGame, mk: item.mk }; }).forEach(item => { const marketType = getMarketType(item.mk); if (!baseList[marketType]) { baseList[marketType] = []; } baseList[marketType].push(item); }); Object.keys(baseList).forEach(marketType => { syncBaseList({ marketType, games: baseList[marketType] }); }); const updateCount = { add: 0, update: 0, delete: 0 }; gamesRelation.forEach(item => { const { id, mk } = item; const oldItem = GAMES.Relations[id]; if (!oldItem) { GAMES.Relations[id] = item; updateCount.add ++; } else if (oldItem.mk != mk) { GAMES.Relations[id] = item; updateCount.update ++; } }); const relations = new Set(gamesRelation.map(item => +item.id)); Object.keys(GAMES.Relations).forEach(id => { if (!relations.has(+id)) { delete GAMES.Relations[id]; updateCount.delete ++; } }); if (updateCount.add || updateCount.update || updateCount.delete) { Logs.out('updateGamesRelation', updateCount); } else { Logs.outDev('updateGamesRelation', updateCount); } }) .catch(err => { Logs.out('updateGamesRelation', err.message); }) .finally(() => { setTimeout(updateGamesRelation, 60000); }); } updateGamesRelation(); const gamesRelationCleanup = () => { const relations = Object.values(GAMES.Relations); const expireTime = Date.now() - 1000*60; relations.forEach(item => { const { rel } = item; Object.keys(rel).forEach(platform => { const { evtime, sptime } = rel[platform]; if (evtime && evtime < expireTime) { delete rel[platform].events; delete rel[platform].evtime; } if (sptime && sptime < expireTime) { delete rel[platform].special; delete rel[platform].sptime; } }); }); } /** * 同步比赛结果 */ const syncGamesResult = async (result) => { if (IS_DEV) { return Logs.out('updateGamesResult', result); } axios.post(`${BASE_API_URL}/p/syncMatchResult`, result, { proxy: false }) .then(res => { // Logs.out('syncMatchResult', res.data); }) .catch(err => { Logs.out('syncMatchResult', err.message); }); } /** * 更新比赛结果 */ const updateGamesResult = (result) => { syncGamesResult(result); return Promise.resolve(); } /** * 同步中单方案 */ const syncSolutions = (solutions) => { if (IS_DEV) { return Logs.out('syncSolutions', solutions); } axios.post(`${BASE_API_URL}/p/syncDsOpportunity`, solutions, { proxy: false }) .then(res => { // Logs.out('syncSolutions', res.data); }) .catch(err => { Logs.out('syncSolutions', err.message); }); } /** * 更新中单方案 */ const getCprKey = (cpr) => { const { k, p, v } = cpr; return `${k}_${p}_${v}`; } const compareCpr = (cpr1, cpr2) => { const key1 = getCprKey(cpr1); const key2 = getCprKey(cpr2); return key1 === key2; } const updateSolutions = (solutions, eventsLogsMap) => { if (solutions?.length) { const solutionsHistory = GAMES.Solutions; const updateIds = { add: [], update: [], retain: [], remove: [] } solutions.forEach(item => { const { sid, cpr, sol: { win_average } } = item; if (!solutionsHistory[sid]) { solutionsHistory[sid] = item; updateIds.add.push(sid); return; } const historySolution = solutionsHistory[sid]; if (historySolution.sol.win_average !== win_average || !compareCpr(historySolution.cpr, cpr)) { solutionsHistory[sid] = item; updateIds.update.push(sid); return; } const { timestamp } = item; historySolution.timestamp = timestamp; updateIds.retain.push(sid); }); const solutionsMap = new Map(solutions.map(item => [item.sid, item])); Object.keys(solutionsHistory).forEach(sid => { if (!solutionsMap.has(sid)) { delete solutionsHistory[sid]; updateIds.remove.push(sid); } }); const solutionUpdate = {}; Object.keys(updateIds).forEach(key => { if (key == 'retain' || key == 'remove') { solutionUpdate[key] = updateIds[key]; } else { solutionUpdate[key] = updateIds[key].map(sid => solutionsHistory[sid]); } }); syncSolutions(solutionUpdate); if (updateIds.add.length / solutions.length > 0.25 || updateIds.remove.length / solutions.length > 0.25 ) { const { expireEvents, removeEvents } = eventsLogsMap; const expireEvemtsMap = {}; expireEvents.forEach(item => { const { mk, platform, info, evExpire, spExpire, evtime, sptime } = item; if (!expireEvemtsMap[mk]) { expireEvemtsMap[mk] = {}; } if (!expireEvemtsMap[mk][platform]) { expireEvemtsMap[mk][platform] = {}; } if (!expireEvemtsMap[mk][platform].list) { expireEvemtsMap[mk][platform].list = []; } if (!expireEvemtsMap[mk][platform].evtime) { expireEvemtsMap[mk][platform].evtime = evtime; } if (!expireEvemtsMap[mk][platform].sptime) { expireEvemtsMap[mk][platform].sptime = sptime; } expireEvemtsMap[mk][platform].list.push({ info, evExpire, spExpire, evtime, sptime }); }); Object.keys(expireEvemtsMap).forEach(mk => { Object.keys(expireEvemtsMap[mk]).forEach(platform => { Logs.out('invalid events, mk %d, platform %s, expire %d, evtime %d, sptime %d', mk, platform, expireEvemtsMap[mk][platform].list.length, expireEvemtsMap[mk][platform].evtime, expireEvemtsMap[mk][platform].sptime, ) }); }); Logs.out('solutions add %d, update %d, retain %d, remove %d', updateIds.add.length, updateIds.update.length, updateIds.retain.length, updateIds.remove.length); } else { Logs.outDev('solutions update complete'); } } } /** * 获取中单方案 */ const getSolutions = async ({ win_min, no_events }) => { // Logs.out('getSolutions', win_min); const { minShowAmount } = getSetting(); const solutionsList = Object.values(GAMES.Solutions); const gamesRelation = getGamesRelation(); const relationsMap = new Map(gamesRelation.map(item => [item.id, item])); const solutions = solutionsList.sort((a, b) => b.sol.win_average - a.sol.win_average) .filter(item => { const { sol: { win_average } } = item; return win_average >= (win_min ?? minShowAmount); }) .map(item => { const { info: { id } } = item; const { mk, rel } = relationsMap.get(id); return { ...item, info: { id, mk, ...rel } } }); const relIds = solutions.map(item => item.info.id); let gamesEvents = null; if (!no_events) { gamesEvents = getGamesEvents({ relIds }); } return { solutions, gamesEvents }; } /** * 获取单个中单方案 */ const getSolution = async (sid) => { if (!sid) { return Promise.reject(new Error('sid is required')); } const solution = GAMES.Solutions[sid]; if (!solution) { return Promise.reject(new Error('solution not found')); } return solution; } /** * 通过比赛 ID 获取中单方案 */ const getSolutionsByIds = async (ids) => { const baseList = Object.values(GAMES.Baselist).flat(); const baseMap = new Map(baseList.map(item => [item.eventId, item])); const result = {}; ids.forEach(id => { const baseGame = baseMap.get(id); result[id] = {}; result[id].matches = baseGame?.matches ?? []; result[id].sols = []; }); Object.values(GAMES.Solutions).forEach(item => { const { info: { id } } = item; if (result[id]) { result[id].sols.push(item); } }); return result; } /** * 清理中单方案 */ const solutionsCleanup = () => { const solutionsHistory = GAMES.Solutions; const updateIds = { remove: [] } Object.keys(solutionsHistory).forEach(sid => { const { timestamp } = solutionsHistory[sid]; const nowTime = Date.now(); if (nowTime - timestamp > 1000*60) { delete solutionsHistory[sid]; updateIds.remove.push(sid); return; } const solution = solutionsHistory[sid]; const eventTime = solution.info.timestamp; if (nowTime > eventTime) { delete solutionsHistory[sid]; updateIds.remove.push(sid); } }); if (updateIds.remove.length) { syncSolutions(updateIds); } } /** * 定时清理中单方案 * 定时清理盘口信息 */ setInterval(() => { cleanupBaseList(); solutionsCleanup(); gamesRelationCleanup(); }, 1000*30); /** * 获取综合利润 */ const getTotalProfit = async (sol1, sol2, inner_base, inner_rebate) => { const { innerDefaultAmount, innerRebateRatio } = getSetting(); inner_base = inner_base ? +inner_base : innerDefaultAmount; inner_rebate = inner_rebate ? +inner_rebate : fixFloat(innerRebateRatio / 100, 3); const profit = calcTotalProfit(sol1, sol2, inner_base, inner_rebate); return profit; } /** * 通过 sid 获取综合利润 */ const getTotalProfitWithSid = async (sid1, sid2, inner_base, inner_rebate) => { const preSolution = GAMES.Solutions[sid1]; const subSolution = GAMES.Solutions[sid2]; const sol1 = preSolution?.sol; const sol2 = subSolution?.sol; if (!sol1) { return Promise.reject(new Error('sid1 已失效')); } if (!sol2) { return Promise.reject(new Error('sid2 已失效')); } const profit = await getTotalProfit(sol1, sol2, inner_base, inner_rebate); return { profit, solutions: [preSolution, subSolution] }; } /** * 通过盘口信息获取综合利润 */ const getTotalProfitWithBetInfo = async (betInfo1, betInfo2, fixed=false, inner_base, inner_rebate) => { const { innerDefaultAmount, innerRebateRatio } = getSetting(); inner_base = inner_base ? +inner_base : innerDefaultAmount; inner_rebate = inner_rebate ? +inner_rebate : fixFloat(innerRebateRatio / 100, 3); if (fixed) { return calcTotalProfitWithFixedFirst(betInfo1, betInfo2, inner_base, inner_rebate); } const [sol1, sol2] = [betInfo1, betInfo2].map(betinfo => eventSolutions({...betinfo, inner_base, inner_rebate })); return getTotalProfit(sol1, sol2, inner_base, inner_rebate); } /** * 获取后台设置 */ // const getSetting = async () => { // return ; // } /** * 异常通知 */ const notifyException = (message) => { if (IS_DEV) { return Logs.out('notifyException', { message }); } const chat_id = -1003032820471; axios.get(`${BASE_API_URL}/telegram/jump`, { params: { chat_id, message } }, { proxy: false }) .then(() => { Logs.out('notifyException', '通知成功'); }) .catch(err => { Logs.out('notifyException', err.message); }); } /** * 从子进程获取数据 */ const getDataFromChild = (type, callback) => { const id = ++Request.count; Request.callbacks[id] = callback; events_child.send({ method: 'get', id, type }); } /** * 向子进程发送数据 */ const postDataToChild = (type, data) => { events_child.send({ method: 'post', type, data }); } /** * 处理子进程消息 */ events_child.on('message', async (message) => { const { callbacks } = Request; const { method, id, type, data } = message; if (method == 'get' && id) { let responseData = null; if (type == 'getGamesRelation') { responseData = getGamesRelation({ listEvents: true }); } else if (type == 'getSetting') { responseData = getSetting(); } // else if (type == 'getSolutionHistory') { // responseData = getSolutionHistory(); // } events_child.send({ type: 'response', id, data: responseData }); } else if (method == 'post') { if (type == 'updateSolutions') { updateSolutions(data.solutions, data.eventsLogsMap); } } else if (method == 'response' && id && callbacks[id]) { callbacks[id](data); delete callbacks[id]; } }); events_child.stderr?.on('data', data => { Logs.out('events_child stderr', data.toString()); }); const settingUpdate = (fields) => { updateSetting(fields); postDataToChild('updateSetting', fields); } const syncSetting = async () => { const setting = await Setting.get(); settingUpdate(setting.toObject()); } syncSetting(); Setting.onUpdate(settingUpdate); /** * 保存GAMES数据到缓存文件 */ const saveGamesToCache = () => { Cache.setData(GamesCacheFile, GAMES, err => { if (err) { Logs.out('Failed to save games cache:', err.message); } else { Logs.out('Games cache saved successfully'); } }); } /** * 从缓存文件加载GAMES数据 */ const loadGamesFromCache = () => { const gamesCacheData = Cache.getData(GamesCacheFile, true); Object.assign(GAMES, gamesCacheData); Logs.out('Games cache loaded successfully'); } // 在模块加载时尝试从缓存恢复数据 loadGamesFromCache(); // 监听进程退出事件,保存GAMES数据 process.on('exit', saveGamesToCache); process.on('SIGINT', () => { process.exit(0); }); process.on('SIGTERM', () => { process.exit(0); }); process.on('SIGUSR2', () => { process.exit(0); }); module.exports = { updateLeaguesList, getFilteredLeagues, updateGamesList, updateGamesEvents, getGamesRelation, updateGamesResult, getSolutions, getSolution, getSolutionsByIds, getTotalProfitWithSid, getTotalProfitWithBetInfo, notifyException, }