From 725fb80f80d89ed85f36f2474066faee0d61b80e Mon Sep 17 00:00:00 2001 From: hailin Date: Sat, 17 Jan 2026 00:28:30 -0800 Subject: [PATCH] =?UTF-8?q?refactor(frontend):=20=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=88=91=E7=9A=84=E9=A1=B5=E9=9D=A2=E4=B8=AD=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=AF=86=E7=A0=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.5 --- .../services/mining-distribution.service.ts | 2 + .../persistence/prisma/prisma.module.ts | 5 +- .../repositories/mining-account.repository.ts | 24 +++++-- .../system-mining-account.repository.ts | 6 +- .../persistence/unit-of-work/unit-of-work.ts | 62 +++++++++++++++++++ .../pages/profile/profile_page.dart | 12 ---- 6 files changed, 88 insertions(+), 23 deletions(-) create mode 100644 backend/services/mining-service/src/infrastructure/persistence/unit-of-work/unit-of-work.ts diff --git a/backend/services/mining-service/src/application/services/mining-distribution.service.ts b/backend/services/mining-service/src/application/services/mining-distribution.service.ts index b56eff95..b754a76c 100644 --- a/backend/services/mining-service/src/application/services/mining-distribution.service.ts +++ b/backend/services/mining-service/src/application/services/mining-distribution.service.ts @@ -6,6 +6,7 @@ import { PriceSnapshotRepository } from '../../infrastructure/persistence/reposi import { OutboxRepository } from '../../infrastructure/persistence/repositories/outbox.repository'; import { SystemMiningAccountRepository } from '../../infrastructure/persistence/repositories/system-mining-account.repository'; import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service'; +import { UnitOfWork, TransactionClient } from '../../infrastructure/persistence/unit-of-work/unit-of-work'; import { RedisService } from '../../infrastructure/redis/redis.service'; import { MiningCalculatorService } from '../../domain/services/mining-calculator.service'; import { ShareAmount } from '../../domain/value-objects/share-amount.vo'; @@ -36,6 +37,7 @@ export class MiningDistributionService { private readonly outboxRepository: OutboxRepository, private readonly systemMiningAccountRepository: SystemMiningAccountRepository, private readonly prisma: PrismaService, + private readonly unitOfWork: UnitOfWork, private readonly redis: RedisService, private readonly configService: ConfigService, ) {} diff --git a/backend/services/mining-service/src/infrastructure/persistence/prisma/prisma.module.ts b/backend/services/mining-service/src/infrastructure/persistence/prisma/prisma.module.ts index 7207426f..05cee4bc 100644 --- a/backend/services/mining-service/src/infrastructure/persistence/prisma/prisma.module.ts +++ b/backend/services/mining-service/src/infrastructure/persistence/prisma/prisma.module.ts @@ -1,9 +1,10 @@ import { Global, Module } from '@nestjs/common'; import { PrismaService } from './prisma.service'; +import { UnitOfWork } from '../unit-of-work/unit-of-work'; @Global() @Module({ - providers: [PrismaService], - exports: [PrismaService], + providers: [PrismaService, UnitOfWork], + exports: [PrismaService, UnitOfWork], }) export class PrismaModule {} diff --git a/backend/services/mining-service/src/infrastructure/persistence/repositories/mining-account.repository.ts b/backend/services/mining-service/src/infrastructure/persistence/repositories/mining-account.repository.ts index 89578e9b..d7481de7 100644 --- a/backend/services/mining-service/src/infrastructure/persistence/repositories/mining-account.repository.ts +++ b/backend/services/mining-service/src/infrastructure/persistence/repositories/mining-account.repository.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { MiningAccountAggregate, MiningTransactionType } from '../../../domain/aggregates/mining-account.aggregate'; import { ShareAmount } from '../../../domain/value-objects/share-amount.vo'; +import { TransactionClient } from '../unit-of-work/unit-of-work'; @Injectable() export class MiningAccountRepository { @@ -32,13 +33,18 @@ export class MiningAccountRepository { return result; } - async save(aggregate: MiningAccountAggregate): Promise { + /** + * 保存账户(带外部事务支持) + * @param aggregate 账户聚合 + * @param tx 可选的外部事务客户端,如果不传则自动创建事务 + */ + async save(aggregate: MiningAccountAggregate, tx?: TransactionClient): Promise { const snapshot = aggregate.toSnapshot(); const transactions = aggregate.pendingTransactions; - await this.prisma.$transaction(async (tx) => { + const executeInTx = async (client: TransactionClient) => { // 保存账户 - await tx.miningAccount.upsert({ + await client.miningAccount.upsert({ where: { accountSequence: snapshot.accountSequence }, create: { accountSequence: snapshot.accountSequence, @@ -59,7 +65,7 @@ export class MiningAccountRepository { // 保存交易流水 if (transactions.length > 0) { - await tx.miningTransaction.createMany({ + await client.miningTransaction.createMany({ data: transactions.map((t) => ({ accountSequence: snapshot.accountSequence, type: t.type, @@ -72,7 +78,15 @@ export class MiningAccountRepository { })), }); } - }); + }; + + if (tx) { + // 使用外部事务 + await executeInTx(tx); + } else { + // 自动创建事务(向后兼容) + await this.prisma.$transaction(executeInTx); + } aggregate.clearPendingTransactions(); } diff --git a/backend/services/mining-service/src/infrastructure/persistence/repositories/system-mining-account.repository.ts b/backend/services/mining-service/src/infrastructure/persistence/repositories/system-mining-account.repository.ts index cc5df892..846a8e78 100644 --- a/backend/services/mining-service/src/infrastructure/persistence/repositories/system-mining-account.repository.ts +++ b/backend/services/mining-service/src/infrastructure/persistence/repositories/system-mining-account.repository.ts @@ -1,10 +1,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { ShareAmount } from '../../../domain/value-objects/share-amount.vo'; -import { SystemAccountType, Prisma } from '@prisma/client'; - -// 事务客户端类型 -type TransactionClient = Prisma.TransactionClient; +import { SystemAccountType } from '@prisma/client'; +import { TransactionClient } from '../unit-of-work/unit-of-work'; export interface SystemMiningAccountSnapshot { accountType: SystemAccountType; diff --git a/backend/services/mining-service/src/infrastructure/persistence/unit-of-work/unit-of-work.ts b/backend/services/mining-service/src/infrastructure/persistence/unit-of-work/unit-of-work.ts new file mode 100644 index 00000000..c679d2e2 --- /dev/null +++ b/backend/services/mining-service/src/infrastructure/persistence/unit-of-work/unit-of-work.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { Prisma } from '@prisma/client'; + +export type TransactionClient = Omit< + PrismaService, + '$connect' | '$disconnect' | '$on' | '$transaction' | '$use' | '$extends' +>; + +/** + * 工作单元模式 + * 用于管理事务边界,确保多个仓库操作在同一事务中执行 + */ +@Injectable() +export class UnitOfWork { + private transactionClient: TransactionClient | null = null; + + constructor(private readonly prisma: PrismaService) {} + + /** + * 获取当前事务客户端,如果没有活跃事务则返回普通客户端 + */ + getClient(): TransactionClient { + return this.transactionClient || this.prisma; + } + + /** + * 在事务中执行操作 + * 使用较长的超时时间以支持挖矿分配等大批量操作 + */ + async executeInTransaction( + operation: (client: TransactionClient) => Promise, + options?: { + maxWait?: number; + timeout?: number; + isolationLevel?: Prisma.TransactionIsolationLevel; + }, + ): Promise { + return this.prisma.$transaction( + async (tx) => { + this.transactionClient = tx as TransactionClient; + try { + return await operation(tx as TransactionClient); + } finally { + this.transactionClient = null; + } + }, + { + maxWait: options?.maxWait ?? 10000, // 默认10秒等待获取事务 + timeout: options?.timeout ?? 60000, // 默认60秒事务超时(挖矿分配可能需要较长时间) + isolationLevel: options?.isolationLevel ?? Prisma.TransactionIsolationLevel.ReadCommitted, + }, + ); + } + + /** + * 是否在事务中 + */ + isInTransaction(): boolean { + return this.transactionClient !== null; + } +} diff --git a/frontend/mining-app/lib/presentation/pages/profile/profile_page.dart b/frontend/mining-app/lib/presentation/pages/profile/profile_page.dart index 08be54b3..a368cd0c 100644 --- a/frontend/mining-app/lib/presentation/pages/profile/profile_page.dart +++ b/frontend/mining-app/lib/presentation/pages/profile/profile_page.dart @@ -313,18 +313,6 @@ class ProfilePage extends ConsumerWidget { icon: Icons.security, label: '账户安全', onTap: () => context.push(Routes.changePassword), - ), - _buildSettingItem( - icon: Icons.lock_outline, - label: '支付密码', - trailing: const Text( - '已设置', - style: TextStyle( - fontSize: 14, - color: _green, - ), - ), - onTap: () {}, showDivider: false, ), ],