Kaynağa Gözat

增加策略确认

flyzto 2 hafta önce
ebeveyn
işleme
85b4b06b64

+ 13 - 11
server/libs/cache.js

@@ -2,18 +2,20 @@ import fs from 'fs';
 import path from 'path';
 
 export const getData = (file) => {
-  let data = null;
-
-  if (fs.existsSync(file)) {
-    const arrayBuffer = fs.readFileSync(file);
-
-    try {
-      data = JSON.parse(arrayBuffer.toString());
+  return new Promise((resolve, reject) => {
+    if (fs.existsSync(file)) {
+      const arrayBuffer = fs.readFileSync(file);
+      try {
+        resolve(JSON.parse(arrayBuffer.toString()));
+      }
+      catch (error) {
+        reject(error);
+      }
     }
-    catch (e) {}
-  }
-
-  return Promise.resolve(data);
+    else {
+      reject(new Error(`File ${file} does not exist`));
+    }
+  });
 }
 
 export const setData = (file, data, indent = 2) => {

+ 2 - 0
server/main.js

@@ -6,6 +6,7 @@ import Logs from './libs/logs.js';
 import gamesRoutes from './routes/games.js';
 import localesRoutes from './routes/locales.js';
 import platformsRoutes from './routes/platforms.js';
+import partnerGateRoutes from './routes/partnerGate.js';
 
 const app = express();
 const wsInstance = expressWs(app);
@@ -78,6 +79,7 @@ app.use((req, res, next) => {
 app.use('/api/games', gamesRoutes);
 app.use('/api/locales', localesRoutes);
 app.use('/api/platforms', platformsRoutes);
+app.use('/api/partner', partnerGateRoutes);
 
 // 启动服务
 const PORT = process.env.PORT || 9020;

+ 2 - 2
server/models/Markets.js

@@ -338,12 +338,12 @@ export const getSolutionByLatestIors = (iorsInfo, cross_type, retry=false) => {
     return { error: `IORS_NULL_VALUE_AT_INDEX_${nullIndex}_RETRY_${askIndex}`, data: iorsInfo };
   }
 
+  const baseIndex = iorsValues.reduce((minIdx, cur, idx) => cur.value < iorsValues[minIdx].value ? idx : minIdx, 0);
+
   if (iorsValues.length === 2) {
     iorsValues.push({ value: 1, maxStake: 0, minStake: 0 });
   }
 
-  const baseIndex = iorsValues.reduce((minIdx, cur, idx) => cur.value < iorsValues[minIdx].value ? idx : minIdx, 0);
-
   const betInfo = {
     cross_type,
     base_index: baseIndex,

+ 64 - 8
server/models/Partner.js

@@ -1,9 +1,38 @@
 import crypto from "crypto";
 import axios from "axios";
+import dotenv from 'dotenv';
 
-const API_BASE_URL = 'https://dev.api.ppai.win/api/p/gate';
-const APP_ID = 'ppai.partner.taoge';
-const APP_KEY = '7F9AE8D6D2B9D39B7D76AA1EC06A5534';
+import path from "path";
+import { fileURLToPath } from "url";
+
+import { getData } from "../libs/cache.js";
+import Logs from "../libs/logs.js";
+
+dotenv.config();
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+const PARTNER_DATA_FILE = path.join(__dirname, '../data/partner.json');
+
+/**
+ * Partner API environment variables
+ */
+const PARTNER_API_BASE = 'https://dev.api.ppai.win/api/p/gate';
+const { PARTNER_APP_ID, PARTNER_APP_KEY } = process.env;
+
+const PARTNER_DATA = {};
+
+/**
+ * 加载本地数据
+ */
+const loadLocalData = async () => {
+  const data = await getData(PARTNER_DATA_FILE);
+  Object.assign(PARTNER_DATA, data);
+}
+loadLocalData().catch(error => {
+  Logs.out('not load partner data', error.message);
+});
 
 /**
  * 生成SHA256
@@ -34,18 +63,26 @@ const getTimestamp = () => {
 }
 
 /**
- * 请求数据接口
+ * 时间窗口校验
+ */
+const isTimestampValid = (timestamp, window=300) => {
+  const now = getTimestamp();
+  return Math.abs(now - timestamp) <= window;
+}
+
+/**
+ * 请求Partner数据接口
  */
 export const requestData = async (action, data) => {
   const timestamp = getTimestamp();
   const nonce = generateNonce();
-  const sign = generateSignature(APP_KEY, action, timestamp, nonce);
-  const appid = APP_ID;
+  const sign = generateSignature(PARTNER_APP_KEY, action, timestamp, nonce);
+  const appid = PARTNER_APP_ID;
   const requestData = { action, appid, nonce, sign, timestamp };
   if (Object.keys(data).length > 0) {
     requestData.params = data;
   }
-  return axios.post(API_BASE_URL, requestData).then(res => res.data);
+  return axios.post(PARTNER_API_BASE, requestData).then(res => res.data);
 }
 
 export const updateSolutions = async (solutions) => {
@@ -55,4 +92,23 @@ export const updateSolutions = async (solutions) => {
   return requestData('opps.soccer', solutions);
 }
 
-export default { updateSolutions };
+/**
+ * 接收Partner数据
+ */
+export const receivePartnerData = async (data) => {
+  const { action, appid, nonce, sign, timestamp, params } = data;
+  if (!isTimestampValid(timestamp)) {
+    return Promise.reject(new Error('timestamp invalid'));
+  }
+  const partnerData = PARTNER_DATA[appid];
+  if (!partnerData) {
+    return Promise.reject(new Error('appid invalid'));
+  }
+  const newSign = generateSignature(partnerData.appkey, action, timestamp, nonce);
+  if (newSign !== sign) {
+    return Promise.reject(new Error('sign invalid'));
+  }
+  return Promise.resolve({ action, params });
+}
+
+export default { updateSolutions, receivePartnerData };

+ 46 - 0
server/models/PartnerGate.js

@@ -0,0 +1,46 @@
+import Logs from "../libs/logs.js";
+import { receivePartnerData } from "./Partner.js";
+import eventSolutions from '../triangle/eventSolutions.js';
+
+/**
+ * 精确浮点数字
+ * @param {number} number
+ * @param {number} x
+ * @returns {number}
+ */
+const fixFloat = (number, x=3) => {
+  return parseFloat(number.toFixed(x));
+}
+
+const getSolutionWithIors = (params) => {
+  Logs.outDev('getGameSolution', params);
+  const { iors, cross_type, base_stake } = params;
+  const base_index = iors.reduce((minIdx, cur, idx) => cur.v < iors[minIdx].v ? idx : minIdx, 0);
+  if (iors.length === 2) {
+    iors.push({ v: 1 });
+  }
+  const betInfo = {
+    cross_type,
+    base_index,
+    base_stake,
+    odds_side_a: fixFloat(iors[0].v - 1),
+    odds_side_b: fixFloat(iors[1].v - 1),
+    odds_side_c: fixFloat(iors[2].v - 1),
+  };
+  const sol = eventSolutions(betInfo, true);
+  return sol;
+}
+
+export const partnerGate = async (data) => {
+  return receivePartnerData(data).
+  then(({ action, params }) => {
+    switch (action) {
+      case 'iors.solution':
+        return getSolutionWithIors(params);
+      default:
+        return Promise.reject(new Error('invalid action'));
+    }
+  });
+}
+
+export default { partnerGate };

+ 17 - 0
server/routes/partnerGate.js

@@ -0,0 +1,17 @@
+import express from 'express';
+const router = express.Router();
+
+import PartnerGate from '../models/PartnerGate.js';
+
+router.post('/gate', (req, res) => {
+  const data = req.body;
+  PartnerGate.partnerGate(data)
+  .then(result => {
+    res.sendSuccess(result);
+  })
+  .catch(err => {
+    res.sendError(err);
+  });
+});
+
+export default router;