feat(sync): 添加批量同步 API 端点
- 为 contribution-service、mining-service、trading-service 添加 AdminController - 提供 /admin/accounts/sync 端点用于批量获取账户数据 - 在 mining-admin-service 添加同步 mining/trading 账户的初始化端点 - 添加 sync-all 端点支持一键同步所有数据 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
033f94c0c2
commit
3fe6bdbbf0
|
|
@ -4,9 +4,10 @@ import { InfrastructureModule } from '../infrastructure/infrastructure.module';
|
|||
import { ContributionController } from './controllers/contribution.controller';
|
||||
import { SnapshotController } from './controllers/snapshot.controller';
|
||||
import { HealthController } from './controllers/health.controller';
|
||||
import { AdminController } from './controllers/admin.controller';
|
||||
|
||||
@Module({
|
||||
imports: [ApplicationModule, InfrastructureModule],
|
||||
controllers: [ContributionController, SnapshotController, HealthController],
|
||||
controllers: [ContributionController, SnapshotController, HealthController, AdminController],
|
||||
})
|
||||
export class ApiModule {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
|
||||
|
||||
@ApiTags('Admin')
|
||||
@Controller('admin')
|
||||
export class AdminController {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
@Get('accounts/sync')
|
||||
@ApiOperation({ summary: '获取所有贡献值账户用于同步' })
|
||||
async getAllAccountsForSync() {
|
||||
const accounts = await this.prisma.contributionAccount.findMany({
|
||||
select: {
|
||||
accountSequence: true,
|
||||
personalContribution: true,
|
||||
totalLevelPending: true,
|
||||
totalBonusPending: true,
|
||||
totalUnlocked: true,
|
||||
effectiveContribution: true,
|
||||
hasAdopted: true,
|
||||
directReferralAdoptedCount: true,
|
||||
unlockedLevelDepth: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
accounts: accounts.map((acc) => ({
|
||||
accountSequence: acc.accountSequence,
|
||||
personalContribution: acc.personalContribution.toString(),
|
||||
teamLevelContribution: acc.totalLevelPending.toString(),
|
||||
teamBonusContribution: acc.totalBonusPending.toString(),
|
||||
totalContribution: acc.effectiveContribution.toString(),
|
||||
effectiveContribution: acc.effectiveContribution.toString(),
|
||||
hasAdopted: acc.hasAdopted,
|
||||
directReferralAdoptedCount: acc.directReferralAdoptedCount,
|
||||
unlockedLevelDepth: acc.unlockedLevelDepth,
|
||||
createdAt: acc.createdAt,
|
||||
updatedAt: acc.updatedAt,
|
||||
})),
|
||||
total: accounts.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -44,4 +44,34 @@ export class InitializationController {
|
|||
async syncContributionAccounts(@Req() req: any) {
|
||||
return this.initService.syncAllContributionAccounts(req.admin.id);
|
||||
}
|
||||
|
||||
@Post('sync-mining-accounts')
|
||||
@ApiOperation({ summary: '同步所有挖矿账户(从mining-service初始同步)' })
|
||||
async syncMiningAccounts(@Req() req: any) {
|
||||
return this.initService.syncAllMiningAccounts(req.admin.id);
|
||||
}
|
||||
|
||||
@Post('sync-trading-accounts')
|
||||
@ApiOperation({ summary: '同步所有交易账户(从trading-service初始同步)' })
|
||||
async syncTradingAccounts(@Req() req: any) {
|
||||
return this.initService.syncAllTradingAccounts(req.admin.id);
|
||||
}
|
||||
|
||||
@Post('sync-all')
|
||||
@ApiOperation({ summary: '执行完整的数据同步(用户+算力+挖矿+交易)' })
|
||||
async syncAll(@Req() req: any) {
|
||||
const adminId = req.admin.id;
|
||||
const results = {
|
||||
users: await this.initService.syncAllUsers(adminId),
|
||||
contribution: await this.initService.syncAllContributionAccounts(adminId),
|
||||
mining: await this.initService.syncAllMiningAccounts(adminId),
|
||||
trading: await this.initService.syncAllTradingAccounts(adminId),
|
||||
};
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '全部同步完成',
|
||||
details: results,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,4 +199,100 @@ export class InitializationService {
|
|||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async syncAllMiningAccounts(adminId: string): Promise<{ success: boolean; message: string; syncedCount?: number }> {
|
||||
try {
|
||||
const miningServiceUrl = this.configService.get<string>('MINING_SERVICE_URL', 'http://localhost:3021');
|
||||
const response = await fetch(`${miningServiceUrl}/api/v1/admin/accounts/sync`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch accounts: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const { accounts } = await response.json();
|
||||
let syncedCount = 0;
|
||||
|
||||
for (const account of accounts) {
|
||||
try {
|
||||
await this.prisma.syncedMiningAccount.upsert({
|
||||
where: { accountSequence: account.accountSequence },
|
||||
create: {
|
||||
accountSequence: account.accountSequence,
|
||||
totalMined: account.totalMined || 0,
|
||||
availableBalance: account.availableBalance || 0,
|
||||
frozenBalance: account.frozenBalance || 0,
|
||||
totalContribution: account.totalContribution || 0,
|
||||
},
|
||||
update: {
|
||||
totalMined: account.totalMined,
|
||||
availableBalance: account.availableBalance,
|
||||
frozenBalance: account.frozenBalance,
|
||||
totalContribution: account.totalContribution,
|
||||
},
|
||||
});
|
||||
syncedCount++;
|
||||
} catch (err) {
|
||||
this.logger.warn(`Failed to sync mining account ${account.accountSequence}: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
await this.prisma.auditLog.create({
|
||||
data: { adminId, action: 'SYNC', resource: 'MINING_ACCOUNT', newValue: { syncedCount } },
|
||||
});
|
||||
|
||||
return { success: true, message: `Synced ${syncedCount} mining accounts`, syncedCount };
|
||||
} catch (error: any) {
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async syncAllTradingAccounts(adminId: string): Promise<{ success: boolean; message: string; syncedCount?: number }> {
|
||||
try {
|
||||
const tradingServiceUrl = this.configService.get<string>('TRADING_SERVICE_URL', 'http://localhost:3022');
|
||||
const response = await fetch(`${tradingServiceUrl}/api/v1/admin/accounts/sync`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch accounts: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const { accounts } = await response.json();
|
||||
let syncedCount = 0;
|
||||
|
||||
for (const account of accounts) {
|
||||
try {
|
||||
await this.prisma.syncedTradingAccount.upsert({
|
||||
where: { accountSequence: account.accountSequence },
|
||||
create: {
|
||||
accountSequence: account.accountSequence,
|
||||
shareBalance: account.shareBalance || 0,
|
||||
cashBalance: account.cashBalance || 0,
|
||||
frozenShares: account.frozenShares || 0,
|
||||
frozenCash: account.frozenCash || 0,
|
||||
totalBought: account.totalBought || 0,
|
||||
totalSold: account.totalSold || 0,
|
||||
},
|
||||
update: {
|
||||
shareBalance: account.shareBalance,
|
||||
cashBalance: account.cashBalance,
|
||||
frozenShares: account.frozenShares,
|
||||
frozenCash: account.frozenCash,
|
||||
totalBought: account.totalBought,
|
||||
totalSold: account.totalSold,
|
||||
},
|
||||
});
|
||||
syncedCount++;
|
||||
} catch (err) {
|
||||
this.logger.warn(`Failed to sync trading account ${account.accountSequence}: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
await this.prisma.auditLog.create({
|
||||
data: { adminId, action: 'SYNC', resource: 'TRADING_ACCOUNT', newValue: { syncedCount } },
|
||||
});
|
||||
|
||||
return { success: true, message: `Synced ${syncedCount} trading accounts`, syncedCount };
|
||||
} catch (error: any) {
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ import { InfrastructureModule } from '../infrastructure/infrastructure.module';
|
|||
import { MiningController } from './controllers/mining.controller';
|
||||
import { PriceController } from './controllers/price.controller';
|
||||
import { HealthController } from './controllers/health.controller';
|
||||
import { AdminController } from './controllers/admin.controller';
|
||||
|
||||
@Module({
|
||||
imports: [ApplicationModule, InfrastructureModule],
|
||||
controllers: [MiningController, PriceController, HealthController],
|
||||
controllers: [MiningController, PriceController, HealthController, AdminController],
|
||||
})
|
||||
export class ApiModule {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
|
||||
|
||||
@ApiTags('Admin')
|
||||
@Controller('admin')
|
||||
export class AdminController {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
@Get('accounts/sync')
|
||||
@ApiOperation({ summary: '获取所有挖矿账户用于同步' })
|
||||
async getAllAccountsForSync() {
|
||||
const accounts = await this.prisma.miningAccount.findMany({
|
||||
select: {
|
||||
accountSequence: true,
|
||||
totalMined: true,
|
||||
availableBalance: true,
|
||||
frozenBalance: true,
|
||||
totalContribution: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
accounts: accounts.map((acc) => ({
|
||||
accountSequence: acc.accountSequence,
|
||||
totalMined: acc.totalMined.toString(),
|
||||
availableBalance: acc.availableBalance.toString(),
|
||||
frozenBalance: acc.frozenBalance.toString(),
|
||||
totalContribution: acc.totalContribution.toString(),
|
||||
createdAt: acc.createdAt,
|
||||
updatedAt: acc.updatedAt,
|
||||
})),
|
||||
total: accounts.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,10 @@ import { InfrastructureModule } from '../infrastructure/infrastructure.module';
|
|||
import { TradingController } from './controllers/trading.controller';
|
||||
import { TransferController } from './controllers/transfer.controller';
|
||||
import { HealthController } from './controllers/health.controller';
|
||||
import { AdminController } from './controllers/admin.controller';
|
||||
|
||||
@Module({
|
||||
imports: [ApplicationModule, InfrastructureModule],
|
||||
controllers: [TradingController, TransferController, HealthController],
|
||||
controllers: [TradingController, TransferController, HealthController, AdminController],
|
||||
})
|
||||
export class ApiModule {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';
|
||||
|
||||
@ApiTags('Admin')
|
||||
@Controller('admin')
|
||||
export class AdminController {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
@Get('accounts/sync')
|
||||
@ApiOperation({ summary: '获取所有交易账户用于同步' })
|
||||
async getAllAccountsForSync() {
|
||||
const accounts = await this.prisma.tradingAccount.findMany({
|
||||
select: {
|
||||
accountSequence: true,
|
||||
shareBalance: true,
|
||||
cashBalance: true,
|
||||
frozenShares: true,
|
||||
frozenCash: true,
|
||||
totalBought: true,
|
||||
totalSold: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
accounts: accounts.map((acc) => ({
|
||||
accountSequence: acc.accountSequence,
|
||||
shareBalance: acc.shareBalance.toString(),
|
||||
cashBalance: acc.cashBalance.toString(),
|
||||
frozenShares: acc.frozenShares.toString(),
|
||||
frozenCash: acc.frozenCash.toString(),
|
||||
totalBought: acc.totalBought.toString(),
|
||||
totalSold: acc.totalSold.toString(),
|
||||
createdAt: acc.createdAt,
|
||||
updatedAt: acc.updatedAt,
|
||||
})),
|
||||
total: accounts.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue