initx
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.env
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
# Используем официальный образ Node.js
|
||||
FROM node:20-alpine
|
||||
|
||||
# Устанавливаем pnpm (установится последняя стабильная версия)
|
||||
RUN npm install -g pnpm
|
||||
|
||||
# Устанавливаем рабочую директорию внутри контейнера
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем package.json и pnpm-lock.yaml для установки зависимостей
|
||||
COPY package.json pnpm-lock.yaml ./
|
||||
|
||||
# Устанавливаем зависимости
|
||||
RUN pnpm install
|
||||
|
||||
# Копируем оставшиеся файлы в контейнер
|
||||
COPY . .
|
||||
|
||||
# Указываем команду для запуска
|
||||
CMD ["pnpm", "run", "distribution"]
|
||||
|
||||
# Открываем порт 4500
|
||||
EXPOSE 4500
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
version: '3'
|
||||
services:
|
||||
distributor:
|
||||
container_name: distributor
|
||||
build: .
|
||||
ports:
|
||||
- "4500:4500"
|
||||
volumes:
|
||||
- .:/app
|
||||
command: pnpm run distribution
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"watch": ["src"],
|
||||
"ext": "ts",
|
||||
"exec": "esno src/index.ts"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "distributor",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "nodemon",
|
||||
"distribution": "esno src/index.ts",
|
||||
"refresh": "esno src/refresh.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.7.7",
|
||||
"body-parser": "^1.20.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"esno": "^4.8.0",
|
||||
"express": "^4.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/body-parser": "^1.19.5",
|
||||
"@types/express": "^5.0.0",
|
||||
"nodemon": "^3.1.7"
|
||||
}
|
||||
}
|
||||
Generated
+1222
File diff suppressed because it is too large
Load Diff
+350
@@ -0,0 +1,350 @@
|
||||
import type { Response, Request } from "express";
|
||||
import type { IContribution, IParticipant, IState, IWithdraw } from "./types";
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
// Инициализация dotenv
|
||||
dotenv.config();
|
||||
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const axios = require('axios');
|
||||
|
||||
const app = express();
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// NocoDB API базовый URL и API ключ
|
||||
const NOCODB_BASE_URL = process.env.NOCODB_BASE_URL
|
||||
|
||||
|
||||
const NOCODB_API_KEY = process.env.NOCODB_API_KEY; // Замените на ваш NocoDB API ключ
|
||||
|
||||
const headers = {
|
||||
'xc-token': NOCODB_API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
// IDs таблиц в NocoDB
|
||||
const GLOBAL_STATE_TABLE_ID = process.env.GLOBAL_STATE_TABLE_ID; // Замените на ID вашей таблицы глобального состояния
|
||||
const PARTICIPANTS_TABLE_ID = process.env.PARTICIPANTS_TABLE_ID; // Замените на ID вашей таблицы участников
|
||||
const WITHDRAW_TABLE_ID = process.env.WITHDRAW_TABLE_ID; // Замените на ID вашей таблицы участников
|
||||
|
||||
// Эндпоинт для получения вебхуков из NocoDB
|
||||
app.post('/withdraw', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
// Проверка на наличие и соответствие заголовка Authorization
|
||||
if (!authHeader || authHeader !== `Bearer ${process.env.SECRET}`) {
|
||||
console.log('on auth error', authHeader)
|
||||
return res.status(403).json({ error: 'Forbidden: Invalid or missing Authorization header' });
|
||||
}
|
||||
|
||||
const [data] = req.body?.data?.rows;
|
||||
console.log('Получен вебхук вывода:', data);
|
||||
|
||||
// Проверяем, является ли взнос интеллектуальным
|
||||
await processWithdraw(data);
|
||||
|
||||
res.status(200).send('OK');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при обработке вебхука:', error);
|
||||
res.status(500).send('Ошибка');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Эндпоинт для получения вебхуков из NocoDB
|
||||
app.post('/contribution', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const [data] = req.body?.data?.rows;
|
||||
console.log('Получен вебхук взноса:', data);
|
||||
console.log("req.headers", req.headers)
|
||||
const authHeader = req.headers.authorization;
|
||||
|
||||
// Проверка на наличие и соответствие заголовка Authorization
|
||||
if (!authHeader || authHeader !== `Authorization ${process.env.SECRET}`) {
|
||||
console.log('on auth error')
|
||||
return res.status(403).json({ error: 'Forbidden: Invalid or missing Authorization header' });
|
||||
}
|
||||
|
||||
// Проверяем, является ли взнос интеллектуальным
|
||||
if (data.type === 'интеллектуальный') {
|
||||
await processIntellectual(data);
|
||||
} else if (data.type === 'имущественный') {
|
||||
await processPropertual(data);
|
||||
} else {
|
||||
console.error('Неизвестный тип взноса')
|
||||
}
|
||||
|
||||
res.status(200).send('OK');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при обработке вебхука:', error);
|
||||
res.status(500).send('Ошибка');
|
||||
}
|
||||
});
|
||||
|
||||
async function processWithdraw(newWithdraw: IWithdraw) {
|
||||
console.log('newWithdraw', newWithdraw)
|
||||
const participant = await getParticipant(newWithdraw.participant.Id)
|
||||
console.log("old participant: ", participant)
|
||||
|
||||
const balance = participant.intellectual_contributions - participant.withdrawed
|
||||
|
||||
if (balance < newWithdraw.amount){
|
||||
console.log('not available balance')
|
||||
return
|
||||
}
|
||||
|
||||
const remain = balance - newWithdraw.amount
|
||||
|
||||
participant.share_balance -= newWithdraw.amount
|
||||
participant.withdrawed += newWithdraw.amount
|
||||
console.log("new participant: ", participant)
|
||||
await updateParticipant(participant)
|
||||
|
||||
let globalState = await getGlobalState();
|
||||
console.log("old globalState: ", globalState);
|
||||
globalState.total_shares -= newWithdraw.amount;
|
||||
globalState.total_withdrawed += newWithdraw.amount;
|
||||
console.log("new globalState: ", globalState);
|
||||
await updateGlobalState(globalState);
|
||||
|
||||
newWithdraw.processed = true
|
||||
await updateWithdraw(newWithdraw)
|
||||
|
||||
}
|
||||
|
||||
async function processPropertual(newContribution: IContribution) {
|
||||
try {
|
||||
console.log('on processPropertual');
|
||||
const contributionAmount = newContribution.amount;
|
||||
|
||||
// 1. Получаем глобальное состояние
|
||||
let globalState = await getGlobalState();
|
||||
console.log("globalState: ", globalState);
|
||||
|
||||
//2. Добавляем взнос в глобальное состояние
|
||||
globalState.total_shares += contributionAmount;
|
||||
globalState.total_contributions += contributionAmount;
|
||||
globalState.total_property_contributions += contributionAmount;
|
||||
await updateGlobalState(globalState);
|
||||
|
||||
console.log("new total_shares: ", globalState.total_shares)
|
||||
|
||||
//3. Получаем всех участников
|
||||
let participants = await getParticipants();
|
||||
console.log("participants:", participants);
|
||||
|
||||
|
||||
//4. Обновляем share_balance участника, сделавшего новый имущественный взнос
|
||||
const participantId = newContribution.participant.Id;
|
||||
const participant = participants.find((p: {Id: number, name: string}) => p.Id === participantId);
|
||||
|
||||
if (!participant) {
|
||||
throw new Error(`Участник с ID ${participantId} не найден`);
|
||||
}
|
||||
|
||||
//4.1. Увеличиваем его share_balance на сумму взноса
|
||||
participant.share_balance += contributionAmount;
|
||||
participant.property_contributions += contributionAmount;
|
||||
participant.total_contributions += contributionAmount;
|
||||
|
||||
await updateParticipant(participant)
|
||||
|
||||
} catch(e) {
|
||||
console.error('Ошибка при обновлении вознаграждений:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Основная функция перерасчёта вознаграждений
|
||||
async function processIntellectual(newContribution: IContribution) {
|
||||
try {
|
||||
console.log('on processIntellectual');
|
||||
|
||||
// 1. Получаем глобальное состояние
|
||||
let globalState = await getGlobalState();
|
||||
console.log("globalState: ", globalState);
|
||||
|
||||
// 2. Получаем всех участников
|
||||
let participants = await getParticipants();
|
||||
console.log("participants:", participants);
|
||||
|
||||
// 3. Рассчитываем сумму распределения (168% от суммы взноса)
|
||||
const contributionAmount = newContribution.amount;
|
||||
const distributionAmount = contributionAmount * 1.618;
|
||||
console.log("distributionAmount: ", distributionAmount);
|
||||
|
||||
// 4. Обновляем накопленные вознаграждения всех участников
|
||||
let cumulativeRewardPerShare = globalState.cumulative_reward_per_share || 0;
|
||||
let totalShares = globalState.total_shares || 0;
|
||||
console.log('totalShares: ', totalShares)
|
||||
|
||||
if (totalShares > 0) {
|
||||
// 4.1. Вычисляем delta для текущего распределения
|
||||
const delta = distributionAmount / totalShares;
|
||||
console.log("delta: ", delta);
|
||||
|
||||
// 4.2. Обновляем cumulative_reward_per_share для будущих распределений
|
||||
cumulativeRewardPerShare += delta;
|
||||
|
||||
// 4.3. Обновляем pending_rewards и reward_per_share_last для всех участников
|
||||
await updateParticipantsRewards(participants, cumulativeRewardPerShare, globalState.cumulative_reward_per_share);
|
||||
|
||||
// 4.4. Обновляем total_rewards_distributed
|
||||
globalState.total_rewards_distributed = globalState.total_rewards_distributed || 0 + distributionAmount;
|
||||
|
||||
globalState.total_shares += contributionAmount + distributionAmount;
|
||||
|
||||
} else {
|
||||
// Если totalShares равно 0, то не делаем распределение
|
||||
globalState.total_shares += contributionAmount;
|
||||
console.log("totalShares равно 0, распределение не производится");
|
||||
}
|
||||
|
||||
// 5. Обновляем share_balance участника, сделавшего новый взнос
|
||||
const participantId = newContribution.participant.Id;
|
||||
//получаем participants повторно, т.к. мы могли им уже обновить балансы распределения
|
||||
let participants2 = await getParticipants();
|
||||
console.log("participants:", participants2);
|
||||
|
||||
|
||||
const participant = participants2.find((p: {Id: number, name: string}) => p.Id === participantId);
|
||||
|
||||
if (!participant) {
|
||||
throw new Error(`Участник с ID ${participantId} не найден`);
|
||||
}
|
||||
|
||||
// 5.1. Увеличиваем его share_balance на сумму взноса
|
||||
participant.share_balance += contributionAmount;
|
||||
participant.intellectual_contributions += contributionAmount;
|
||||
participant.total_contributions += contributionAmount;
|
||||
|
||||
// 6. Сохраняем обновления
|
||||
await updateParticipant(participant);
|
||||
|
||||
globalState.cumulative_reward_per_share = cumulativeRewardPerShare;
|
||||
globalState.total_contributions += contributionAmount;
|
||||
globalState.total_intellectual_contributions += contributionAmount;
|
||||
await updateGlobalState(globalState);
|
||||
|
||||
// 7. Записываем изменения в лог
|
||||
// await logChanges(newContribution);
|
||||
|
||||
console.log('Перерасчет завершен успешно');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Ошибка при перерасчете вознаграждений:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Вспомогательная функция для получения глобального состояния
|
||||
async function getGlobalState(): Promise<IState> {
|
||||
const response = await axios.get(`${NOCODB_BASE_URL}/api/v2/tables/${GLOBAL_STATE_TABLE_ID}/records`, { headers });
|
||||
let [globalState] = response.data.list;
|
||||
return globalState;
|
||||
}
|
||||
|
||||
// Вспомогательная функция для получения списка участников
|
||||
async function getParticipants(): Promise<IParticipant[]> {
|
||||
const response = await axios.get(`${NOCODB_BASE_URL}/api/v2/tables/${PARTICIPANTS_TABLE_ID}/records`, { headers });
|
||||
return response.data.list;
|
||||
}
|
||||
|
||||
// Вспомогательная функция для получения списка участников
|
||||
async function getParticipant(id: number): Promise<IParticipant> {
|
||||
const response = await axios.get(`${NOCODB_BASE_URL}/api/v2/tables/${PARTICIPANTS_TABLE_ID}/records/${id}`, { headers });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Вспомогательная функция для обновления вознаграждений участников
|
||||
async function updateParticipantsRewards(participants: IParticipant[], newCumulativeRewardPerShare: number, oldCumulativeRewardPerShare: number) {
|
||||
for (const participant of participants) {
|
||||
const shareBalance = participant.share_balance || 0;
|
||||
|
||||
const rewardPerShareLast = participant.reward_per_share_last || 0;
|
||||
const pendingRewards = participant.pending_rewards || 0;
|
||||
|
||||
// Вычисляем накопленные вознаграждения
|
||||
const participantDelta = newCumulativeRewardPerShare - rewardPerShareLast;
|
||||
const rewardAmount = shareBalance * participantDelta;
|
||||
console.log('\n')
|
||||
console.log('participant Id:', participant.Id);
|
||||
console.log('participantDelta: ', participantDelta)
|
||||
|
||||
console.log("old shareBalance: ", shareBalance)
|
||||
console.log("new shareBalance: ", shareBalance + rewardAmount)
|
||||
console.log("pendingRewards: ", pendingRewards)
|
||||
|
||||
console.log('rewardAmount: ', rewardAmount)
|
||||
|
||||
console.log('new_pending_rewards:', pendingRewards + rewardAmount);
|
||||
console.log('old_cumulativeRewardPerShare:', oldCumulativeRewardPerShare);
|
||||
console.log('new_cumulativeRewardPerShare:', newCumulativeRewardPerShare);
|
||||
console.log('\n')
|
||||
|
||||
// Обновляем pending_rewards и reward_per_share_last участника
|
||||
await axios.patch(`${NOCODB_BASE_URL}/api/v2/tables/${PARTICIPANTS_TABLE_ID}/records`,
|
||||
[{
|
||||
Id: participant.Id,
|
||||
pending_rewards: pendingRewards + rewardAmount,
|
||||
reward_per_share_last: newCumulativeRewardPerShare,
|
||||
share_balance: shareBalance + rewardAmount,
|
||||
}]
|
||||
, { headers });
|
||||
}
|
||||
}
|
||||
|
||||
// Вспомогательная функция для обновления данных участника
|
||||
async function updateParticipant(participant: IParticipant) {
|
||||
// Убираем поля CreatedAt и UpdatedAt с помощью деструктуризации
|
||||
const { CreatedAt, UpdatedAt, ...rest } = participant;
|
||||
|
||||
// Отправляем PATCH запрос с оставшимися данными
|
||||
await axios.patch(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${PARTICIPANTS_TABLE_ID}/records`,
|
||||
[{ ...rest }], // Передаем только оставшиеся поля
|
||||
{ headers }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Вспомогательная функция для обновления данных участника
|
||||
async function updateWithdraw(withdraw: IWithdraw) {
|
||||
// Убираем поля CreatedAt и UpdatedAt с помощью деструктуризации
|
||||
const { CreatedAt, UpdatedAt, ...rest } = withdraw;
|
||||
|
||||
// Отправляем PATCH запрос с оставшимися данными
|
||||
await axios.patch(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${WITHDRAW_TABLE_ID}/records`,
|
||||
[{ ...rest }], // Передаем только оставшиеся поля
|
||||
{ headers }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Вспомогательная функция для обновления глобального состояния
|
||||
async function updateGlobalState(globalState: IState) {
|
||||
const { CreatedAt, UpdatedAt, ...rest } = globalState;
|
||||
|
||||
await axios.patch(`${NOCODB_BASE_URL}/api/v2/tables/${GLOBAL_STATE_TABLE_ID}/records`,
|
||||
[{
|
||||
...rest
|
||||
}]
|
||||
, { headers });
|
||||
}
|
||||
|
||||
// Вспомогательная функция для записи изменений в лог
|
||||
// async function logChanges(newContribution: IContribution) {
|
||||
// await axios.post(`${NOCODB_BASE_URL}/api/v2/tables/${LOG_TABLE_ID}/records`,
|
||||
// [{
|
||||
// description: `Перерасчет вознаграждений после интеллектуального взноса на сумму ${newContribution.amount} от участника с ID ${newContribution.participant.Id}`
|
||||
// }]
|
||||
// , { headers });
|
||||
// }
|
||||
|
||||
|
||||
|
||||
app.listen(4500, () => {
|
||||
console.log('Сервер запущен на порту 4500');
|
||||
});
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
// refresh.ts
|
||||
|
||||
import axios from 'axios';
|
||||
import type { IState } from './types';
|
||||
|
||||
// NocoDB API базовый URL и API ключ
|
||||
const NOCODB_BASE_URL = 'https://db.coopenomics.world'; // Обновите при необходимости
|
||||
const NOCODB_API_KEY = 'iZQVzIpCPBU3Y2auYQGskrueBPtj3nkiUWgtjnvY'; // Замените на ваш NocoDB API ключ
|
||||
|
||||
const headers = {
|
||||
'xc-token': NOCODB_API_KEY,
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
// IDs таблиц в NocoDB
|
||||
const GLOBAL_STATE_TABLE_ID = 'mgz4zwjl6qwiw1u'; // Замените на ID вашей таблицы глобального состояния
|
||||
const PARTICIPANTS_TABLE_ID = 'mfigch0rlgz4cqa'; // Замените на ID вашей таблицы участников
|
||||
const CONTRIBUTIONS_TABLE_ID = 'mmcm3kr9ddwrtz5'; // Замените на ID вашей таблицы взносов
|
||||
const WITHDRAW_TABLE_ID = 'm1knfvrdoxeu5nt'
|
||||
|
||||
// Интерфейсы для структур данных
|
||||
interface GlobalState {
|
||||
Id: number;
|
||||
cumulative_reward_per_share: number;
|
||||
total_rewards_distributed: number;
|
||||
total_contributions: number;
|
||||
total_intellectual_contributions: number;
|
||||
total_property_contributions: number;
|
||||
}
|
||||
|
||||
interface Participant {
|
||||
Id: number;
|
||||
name: string;
|
||||
intellectual_contributions: number;
|
||||
property_contributions: number;
|
||||
total_contributions: number;
|
||||
pending_rewards: number;
|
||||
total_rewards_received: number;
|
||||
reward_per_share_last: number;
|
||||
total_assets: number;
|
||||
}
|
||||
|
||||
// Функция для обнуления глобального состояния
|
||||
async function resetGlobalState() {
|
||||
try {
|
||||
// Получаем глобальное состояние
|
||||
const response = await axios.get(`${NOCODB_BASE_URL}/api/v2/tables/${GLOBAL_STATE_TABLE_ID}/records`, { headers });
|
||||
const [globalStateData] = response.data.list;
|
||||
|
||||
if (!globalStateData) {
|
||||
console.log('Глобальное состояние не найдено, пропускаем сброс.');
|
||||
return;
|
||||
}
|
||||
|
||||
const globalState: IState = {
|
||||
Id: globalStateData.Id,
|
||||
cumulative_reward_per_share: 0,
|
||||
total_rewards_distributed: 0,
|
||||
total_contributions: 0,
|
||||
total_intellectual_contributions: 0,
|
||||
total_property_contributions: 0,
|
||||
total_shares: 0,
|
||||
total_withdrawed: 0
|
||||
};
|
||||
|
||||
// Обнуляем поля глобального состояния
|
||||
await axios.patch(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${GLOBAL_STATE_TABLE_ID}/records`,
|
||||
[
|
||||
{
|
||||
Id: globalState.Id,
|
||||
cumulative_reward_per_share: globalState.cumulative_reward_per_share,
|
||||
total_rewards_distributed: globalState.total_rewards_distributed,
|
||||
total_contributions: globalState.total_contributions,
|
||||
total_intellectual_contributions: globalState.total_intellectual_contributions,
|
||||
total_property_contributions: globalState.total_property_contributions,
|
||||
total_shares: globalState.total_shares,
|
||||
},
|
||||
],
|
||||
{ headers }
|
||||
);
|
||||
|
||||
console.log('Глобальное состояние успешно обнулено.');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при обнулении глобального состояния:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для удаления всех записей взносов
|
||||
async function deleteAllContributions() {
|
||||
try {
|
||||
// Получаем все записи взносов
|
||||
const response = await axios.get(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${CONTRIBUTIONS_TABLE_ID}/records`,
|
||||
{ headers }
|
||||
);
|
||||
const contributions = response.data.list;
|
||||
|
||||
if (contributions.length === 0) {
|
||||
console.log('Нет записей взносов для удаления.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Собираем массив Id для удаления
|
||||
const idsToDelete = contributions.map((contribution: any) => ({ Id: contribution.Id }));
|
||||
|
||||
// Удаляем записи
|
||||
await axios.delete(`${NOCODB_BASE_URL}/api/v2/tables/${CONTRIBUTIONS_TABLE_ID}/records`, {
|
||||
headers,
|
||||
data: idsToDelete,
|
||||
});
|
||||
|
||||
console.log('Все записи взносов успешно удалены.');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при удалении записей взносов:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Функция для удаления всех записей взносов
|
||||
async function deleteAllWithdraws() {
|
||||
try {
|
||||
// Получаем все записи взносов
|
||||
const response = await axios.get(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${WITHDRAW_TABLE_ID}/records`,
|
||||
{ headers }
|
||||
);
|
||||
const withdraws = response.data.list;
|
||||
|
||||
if (withdraws.length === 0) {
|
||||
console.log('Нет записей взносов для удаления.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Собираем массив Id для удаления
|
||||
const idsToDelete = withdraws.map((contribution: any) => ({ Id: contribution.Id }));
|
||||
|
||||
// Удаляем записи
|
||||
await axios.delete(`${NOCODB_BASE_URL}/api/v2/tables/${WITHDRAW_TABLE_ID}/records`, {
|
||||
headers,
|
||||
data: idsToDelete,
|
||||
});
|
||||
|
||||
console.log('Все записи взносов успешно удалены.');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при удалении записей взносов:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Функция для обнуления балансов всех пайщиков
|
||||
async function resetParticipants() {
|
||||
try {
|
||||
// Получаем всех участников
|
||||
const response = await axios.get(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${PARTICIPANTS_TABLE_ID}/records`,
|
||||
{ headers }
|
||||
);
|
||||
const participantsData = response.data.list;
|
||||
|
||||
if (participantsData.length === 0) {
|
||||
console.log('Нет участников для сброса балансов.');
|
||||
return;
|
||||
}
|
||||
|
||||
const participants: Participant[] = participantsData.map((participantData: any) => ({
|
||||
Id: participantData.Id,
|
||||
name: participantData.name,
|
||||
intellectual_contributions: 0,
|
||||
property_contributions: 0,
|
||||
total_contributions: 0,
|
||||
pending_rewards: 0,
|
||||
total_rewards_received: 0,
|
||||
reward_per_share_last: 0,
|
||||
share_balance: 0,
|
||||
withdrawed: 0,
|
||||
}));
|
||||
|
||||
// Обновляем каждого участника
|
||||
for (const participant of participants) {
|
||||
await axios.patch(
|
||||
`${NOCODB_BASE_URL}/api/v2/tables/${PARTICIPANTS_TABLE_ID}/records`,
|
||||
[
|
||||
{
|
||||
Id: participant.Id,
|
||||
intellectual_contributions: participant.intellectual_contributions,
|
||||
property_contributions: participant.property_contributions,
|
||||
total_contributions: participant.total_contributions,
|
||||
pending_rewards: participant.pending_rewards,
|
||||
total_rewards_received: participant.total_rewards_received,
|
||||
reward_per_share_last: participant.reward_per_share_last,
|
||||
share_balance: 0,
|
||||
withdrawed: 0,
|
||||
},
|
||||
],
|
||||
{ headers }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Балансы всех пайщиков успешно обнулены.');
|
||||
} catch (error) {
|
||||
console.error('Ошибка при обнулении балансов пайщиков:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Главная функция для выполнения всех операций
|
||||
async function refreshAll() {
|
||||
await resetGlobalState();
|
||||
await deleteAllContributions();
|
||||
await deleteAllWithdraws();
|
||||
await resetParticipants();
|
||||
}
|
||||
|
||||
// Запуск главной функции
|
||||
refreshAll();
|
||||
@@ -0,0 +1,49 @@
|
||||
export interface IParticipant {
|
||||
Id: number
|
||||
name: string
|
||||
reward_per_share_last: number
|
||||
pending_rewards: number
|
||||
intellectual_contributions: number
|
||||
property_contributions: number
|
||||
total_contributions: number
|
||||
share_balance: number
|
||||
withdrawed: number
|
||||
CreatedAt?: Date
|
||||
UpdatedAt?: Date
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
Id: number
|
||||
cumulative_reward_per_share: number
|
||||
total_rewards_distributed: number
|
||||
total_contributions: number
|
||||
total_intellectual_contributions: number
|
||||
total_property_contributions: number
|
||||
total_shares: number
|
||||
total_withdrawed: number
|
||||
CreatedAt?: Date
|
||||
UpdatedAt?: Date
|
||||
}
|
||||
|
||||
export interface IContribution {
|
||||
Id: number
|
||||
type: 'интеллектуальный' | 'имущественный'
|
||||
amount: number
|
||||
participant: {
|
||||
Id: number
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWithdraw {
|
||||
amount: number
|
||||
participant: {
|
||||
Id: number
|
||||
name: string
|
||||
}
|
||||
transfer_data: string
|
||||
approved: boolean
|
||||
processed: boolean
|
||||
CreatedAt?: Date
|
||||
UpdatedAt?: Date
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src/**/*", "index.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user