fix(system-accounts): 修复 Prisma nullable regionCode 复合唯一键查询问题
- 将所有使用 accountType_regionCode 复合键的 findUnique 改为 findFirst - 将所有 upsert 改为 findFirst + create/update 模式 - 原因:Prisma 复合唯一键不支持 nullable 字段的 findUnique 查询 影响的服务: - mining-service: admin.controller.ts, system-mining-account.repository.ts - mining-admin-service: cdc-sync.service.ts, system-accounts.service.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
40ac037c03
commit
7e61ac7ff2
|
|
@ -383,8 +383,12 @@ export class SystemAccountsService {
|
|||
*/
|
||||
async getSystemAccountContributionStats(accountType: string, regionCode: string | null) {
|
||||
// 获取算力账户信息
|
||||
const contribution = await this.prisma.syncedSystemContribution.findUnique({
|
||||
where: { accountType_regionCode: { accountType, regionCode } },
|
||||
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||
const contribution = await this.prisma.syncedSystemContribution.findFirst({
|
||||
where: {
|
||||
accountType,
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
const whereClause = regionCode
|
||||
|
|
|
|||
|
|
@ -540,20 +540,37 @@ export class CdcSyncService implements OnModuleInit {
|
|||
|
||||
private async handleSystemContributionUpdated(event: ServiceEvent, tx: TransactionClient): Promise<void> {
|
||||
const { payload } = event;
|
||||
await tx.syncedSystemContribution.upsert({
|
||||
where: { accountType: payload.accountType },
|
||||
create: {
|
||||
accountType: payload.accountType,
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance || 0,
|
||||
contributionNeverExpires: payload.contributionNeverExpires || false,
|
||||
},
|
||||
update: {
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance,
|
||||
contributionNeverExpires: payload.contributionNeverExpires,
|
||||
const accountType = payload.accountType;
|
||||
const regionCode = payload.regionCode || null;
|
||||
|
||||
// 使用 findFirst + create/update 替代 upsert,因为 regionCode 可以为 null
|
||||
const existing = await tx.syncedSystemContribution.findFirst({
|
||||
where: {
|
||||
accountType,
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await tx.syncedSystemContribution.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance,
|
||||
contributionNeverExpires: payload.contributionNeverExpires,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await tx.syncedSystemContribution.create({
|
||||
data: {
|
||||
accountType,
|
||||
regionCode,
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance || 0,
|
||||
contributionNeverExpires: payload.contributionNeverExpires || false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -567,23 +584,33 @@ export class CdcSyncService implements OnModuleInit {
|
|||
const accountType = payload.accountType; // OPERATION / PROVINCE / CITY / HEADQUARTERS
|
||||
const regionCode = payload.regionCode || null;
|
||||
|
||||
// 使用 accountType + regionCode 作为复合唯一键
|
||||
await tx.syncedSystemContribution.upsert({
|
||||
// 使用 findFirst + create/update 替代 upsert,因为 regionCode 可以为 null
|
||||
const existing = await tx.syncedSystemContribution.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: { accountType, regionCode },
|
||||
},
|
||||
create: {
|
||||
accountType,
|
||||
regionCode,
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance || 0,
|
||||
contributionNeverExpires: true, // 系统账户算力永不过期
|
||||
},
|
||||
update: {
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance,
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await tx.syncedSystemContribution.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await tx.syncedSystemContribution.create({
|
||||
data: {
|
||||
accountType,
|
||||
regionCode,
|
||||
name: payload.name,
|
||||
contributionBalance: payload.contributionBalance || 0,
|
||||
contributionNeverExpires: true, // 系统账户算力永不过期
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -199,12 +199,12 @@ export class AdminController {
|
|||
const skip = (pageNum - 1) * pageSizeNum;
|
||||
|
||||
// 先通过 accountType + regionCode 查找系统账户
|
||||
const account = await this.prisma.systemMiningAccount.findUnique({
|
||||
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||
const regionCodeValue = regionCode || null;
|
||||
const account = await this.prisma.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: {
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
},
|
||||
accountType,
|
||||
regionCode: regionCodeValue === null ? { equals: null } : regionCodeValue,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ export class AdminController {
|
|||
page: pageNum,
|
||||
pageSize: pageSizeNum,
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
regionCode: regionCodeValue,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +247,7 @@ export class AdminController {
|
|||
page: pageNum,
|
||||
pageSize: pageSizeNum,
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
regionCode: regionCodeValue,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -269,12 +269,12 @@ export class AdminController {
|
|||
const skip = (pageNum - 1) * pageSizeNum;
|
||||
|
||||
// 先通过 accountType + regionCode 查找系统账户
|
||||
const account = await this.prisma.systemMiningAccount.findUnique({
|
||||
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||
const regionCodeValue = regionCode || null;
|
||||
const account = await this.prisma.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: {
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
},
|
||||
accountType,
|
||||
regionCode: regionCodeValue === null ? { equals: null } : regionCodeValue,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -285,7 +285,7 @@ export class AdminController {
|
|||
page: pageNum,
|
||||
pageSize: pageSizeNum,
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
regionCode: regionCodeValue,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +305,7 @@ export class AdminController {
|
|||
transactions: transactions.map((tx) => ({
|
||||
id: tx.id,
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
regionCode: regionCodeValue,
|
||||
type: tx.type,
|
||||
amount: tx.amount.toString(),
|
||||
balanceBefore: tx.balanceBefore.toString(),
|
||||
|
|
@ -319,7 +319,7 @@ export class AdminController {
|
|||
page: pageNum,
|
||||
pageSize: pageSizeNum,
|
||||
accountType,
|
||||
regionCode: regionCode || null,
|
||||
regionCode: regionCodeValue,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -531,7 +531,8 @@ export class MiningDistributionService {
|
|||
let systemParticipantCount = 0;
|
||||
let pendingParticipantCount = 0;
|
||||
const systemRedisData: Array<{
|
||||
accountType: string; // 改为字符串支持组合键
|
||||
accountType: string;
|
||||
regionCode: string | null;
|
||||
reward: ShareAmount;
|
||||
contribution: ShareAmount;
|
||||
}> = [];
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ export class SystemMiningAccountRepository {
|
|||
accountType: SystemAccountType,
|
||||
regionCode: string | null,
|
||||
): Promise<SystemMiningAccountSnapshot | null> {
|
||||
const record = await this.prisma.systemMiningAccount.findUnique({
|
||||
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||
const record = await this.prisma.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: { accountType, regionCode },
|
||||
accountType,
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -67,20 +69,26 @@ export class SystemMiningAccountRepository {
|
|||
];
|
||||
|
||||
for (const account of accounts) {
|
||||
await this.prisma.systemMiningAccount.upsert({
|
||||
// 使用 findFirst + create 替代 upsert,因为 regionCode 可以为 null
|
||||
const existing = await this.prisma.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: { accountType: account.accountType, regionCode: null },
|
||||
},
|
||||
create: {
|
||||
accountType: account.accountType,
|
||||
regionCode: null,
|
||||
name: account.name,
|
||||
totalMined: 0,
|
||||
availableBalance: 0,
|
||||
totalContribution: 0,
|
||||
regionCode: { equals: null },
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await this.prisma.systemMiningAccount.create({
|
||||
data: {
|
||||
accountType: account.accountType,
|
||||
regionCode: null,
|
||||
name: account.name,
|
||||
totalMined: 0,
|
||||
availableBalance: 0,
|
||||
totalContribution: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,22 +101,33 @@ export class SystemMiningAccountRepository {
|
|||
name: string,
|
||||
contribution: ShareAmount,
|
||||
): Promise<void> {
|
||||
await this.prisma.systemMiningAccount.upsert({
|
||||
// 使用 findFirst + create/update 替代 upsert,因为 regionCode 可以为 null
|
||||
const existing = await this.prisma.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: { accountType, regionCode },
|
||||
},
|
||||
create: {
|
||||
accountType,
|
||||
regionCode,
|
||||
name,
|
||||
totalContribution: contribution.value,
|
||||
lastSyncedAt: new Date(),
|
||||
},
|
||||
update: {
|
||||
totalContribution: contribution.value,
|
||||
lastSyncedAt: new Date(),
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
await this.prisma.systemMiningAccount.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
totalContribution: contribution.value,
|
||||
lastSyncedAt: new Date(),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await this.prisma.systemMiningAccount.create({
|
||||
data: {
|
||||
accountType,
|
||||
regionCode,
|
||||
name,
|
||||
totalContribution: contribution.value,
|
||||
lastSyncedAt: new Date(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async getTotalContribution(): Promise<ShareAmount> {
|
||||
|
|
@ -130,9 +149,11 @@ export class SystemMiningAccountRepository {
|
|||
tx?: TransactionClient,
|
||||
): Promise<void> {
|
||||
const executeInTx = async (client: TransactionClient) => {
|
||||
const account = await client.systemMiningAccount.findUnique({
|
||||
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||
const account = await client.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: { accountType, regionCode },
|
||||
accountType,
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -180,9 +201,11 @@ export class SystemMiningAccountRepository {
|
|||
secondDistribution: ShareAmount,
|
||||
minedAmount: ShareAmount,
|
||||
): Promise<void> {
|
||||
const account = await this.prisma.systemMiningAccount.findUnique({
|
||||
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||
const account = await this.prisma.systemMiningAccount.findFirst({
|
||||
where: {
|
||||
accountType_regionCode: { accountType, regionCode },
|
||||
accountType,
|
||||
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue