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) {
|
async getSystemAccountContributionStats(accountType: string, regionCode: string | null) {
|
||||||
// 获取算力账户信息
|
// 获取算力账户信息
|
||||||
const contribution = await this.prisma.syncedSystemContribution.findUnique({
|
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||||
where: { accountType_regionCode: { accountType, regionCode } },
|
const contribution = await this.prisma.syncedSystemContribution.findFirst({
|
||||||
|
where: {
|
||||||
|
accountType,
|
||||||
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const whereClause = regionCode
|
const whereClause = regionCode
|
||||||
|
|
|
||||||
|
|
@ -540,20 +540,37 @@ export class CdcSyncService implements OnModuleInit {
|
||||||
|
|
||||||
private async handleSystemContributionUpdated(event: ServiceEvent, tx: TransactionClient): Promise<void> {
|
private async handleSystemContributionUpdated(event: ServiceEvent, tx: TransactionClient): Promise<void> {
|
||||||
const { payload } = event;
|
const { payload } = event;
|
||||||
await tx.syncedSystemContribution.upsert({
|
const accountType = payload.accountType;
|
||||||
where: { accountType: payload.accountType },
|
const regionCode = payload.regionCode || null;
|
||||||
create: {
|
|
||||||
accountType: payload.accountType,
|
// 使用 findFirst + create/update 替代 upsert,因为 regionCode 可以为 null
|
||||||
name: payload.name,
|
const existing = await tx.syncedSystemContribution.findFirst({
|
||||||
contributionBalance: payload.contributionBalance || 0,
|
where: {
|
||||||
contributionNeverExpires: payload.contributionNeverExpires || false,
|
accountType,
|
||||||
},
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
update: {
|
|
||||||
name: payload.name,
|
|
||||||
contributionBalance: payload.contributionBalance,
|
|
||||||
contributionNeverExpires: payload.contributionNeverExpires,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 accountType = payload.accountType; // OPERATION / PROVINCE / CITY / HEADQUARTERS
|
||||||
const regionCode = payload.regionCode || null;
|
const regionCode = payload.regionCode || null;
|
||||||
|
|
||||||
// 使用 accountType + regionCode 作为复合唯一键
|
// 使用 findFirst + create/update 替代 upsert,因为 regionCode 可以为 null
|
||||||
await tx.syncedSystemContribution.upsert({
|
const existing = await tx.syncedSystemContribution.findFirst({
|
||||||
where: {
|
where: {
|
||||||
accountType_regionCode: { accountType, regionCode },
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
accountType,
|
accountType,
|
||||||
regionCode,
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
name: payload.name,
|
|
||||||
contributionBalance: payload.contributionBalance || 0,
|
|
||||||
contributionNeverExpires: true, // 系统账户算力永不过期
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: payload.name,
|
|
||||||
contributionBalance: payload.contributionBalance,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
const skip = (pageNum - 1) * pageSizeNum;
|
||||||
|
|
||||||
// 先通过 accountType + regionCode 查找系统账户
|
// 先通过 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: {
|
where: {
|
||||||
accountType_regionCode: {
|
accountType,
|
||||||
accountType,
|
regionCode: regionCodeValue === null ? { equals: null } : regionCodeValue,
|
||||||
regionCode: regionCode || null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -215,7 +215,7 @@ export class AdminController {
|
||||||
page: pageNum,
|
page: pageNum,
|
||||||
pageSize: pageSizeNum,
|
pageSize: pageSizeNum,
|
||||||
accountType,
|
accountType,
|
||||||
regionCode: regionCode || null,
|
regionCode: regionCodeValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,7 +247,7 @@ export class AdminController {
|
||||||
page: pageNum,
|
page: pageNum,
|
||||||
pageSize: pageSizeNum,
|
pageSize: pageSizeNum,
|
||||||
accountType,
|
accountType,
|
||||||
regionCode: regionCode || null,
|
regionCode: regionCodeValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,12 +269,12 @@ export class AdminController {
|
||||||
const skip = (pageNum - 1) * pageSizeNum;
|
const skip = (pageNum - 1) * pageSizeNum;
|
||||||
|
|
||||||
// 先通过 accountType + regionCode 查找系统账户
|
// 先通过 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: {
|
where: {
|
||||||
accountType_regionCode: {
|
accountType,
|
||||||
accountType,
|
regionCode: regionCodeValue === null ? { equals: null } : regionCodeValue,
|
||||||
regionCode: regionCode || null,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -285,7 +285,7 @@ export class AdminController {
|
||||||
page: pageNum,
|
page: pageNum,
|
||||||
pageSize: pageSizeNum,
|
pageSize: pageSizeNum,
|
||||||
accountType,
|
accountType,
|
||||||
regionCode: regionCode || null,
|
regionCode: regionCodeValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,7 +305,7 @@ export class AdminController {
|
||||||
transactions: transactions.map((tx) => ({
|
transactions: transactions.map((tx) => ({
|
||||||
id: tx.id,
|
id: tx.id,
|
||||||
accountType,
|
accountType,
|
||||||
regionCode: regionCode || null,
|
regionCode: regionCodeValue,
|
||||||
type: tx.type,
|
type: tx.type,
|
||||||
amount: tx.amount.toString(),
|
amount: tx.amount.toString(),
|
||||||
balanceBefore: tx.balanceBefore.toString(),
|
balanceBefore: tx.balanceBefore.toString(),
|
||||||
|
|
@ -319,7 +319,7 @@ export class AdminController {
|
||||||
page: pageNum,
|
page: pageNum,
|
||||||
pageSize: pageSizeNum,
|
pageSize: pageSizeNum,
|
||||||
accountType,
|
accountType,
|
||||||
regionCode: regionCode || null,
|
regionCode: regionCodeValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -531,7 +531,8 @@ export class MiningDistributionService {
|
||||||
let systemParticipantCount = 0;
|
let systemParticipantCount = 0;
|
||||||
let pendingParticipantCount = 0;
|
let pendingParticipantCount = 0;
|
||||||
const systemRedisData: Array<{
|
const systemRedisData: Array<{
|
||||||
accountType: string; // 改为字符串支持组合键
|
accountType: string;
|
||||||
|
regionCode: string | null;
|
||||||
reward: ShareAmount;
|
reward: ShareAmount;
|
||||||
contribution: ShareAmount;
|
contribution: ShareAmount;
|
||||||
}> = [];
|
}> = [];
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ export class SystemMiningAccountRepository {
|
||||||
accountType: SystemAccountType,
|
accountType: SystemAccountType,
|
||||||
regionCode: string | null,
|
regionCode: string | null,
|
||||||
): Promise<SystemMiningAccountSnapshot | null> {
|
): Promise<SystemMiningAccountSnapshot | null> {
|
||||||
const record = await this.prisma.systemMiningAccount.findUnique({
|
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||||
|
const record = await this.prisma.systemMiningAccount.findFirst({
|
||||||
where: {
|
where: {
|
||||||
accountType_regionCode: { accountType, regionCode },
|
accountType,
|
||||||
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -67,20 +69,26 @@ export class SystemMiningAccountRepository {
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
await this.prisma.systemMiningAccount.upsert({
|
// 使用 findFirst + create 替代 upsert,因为 regionCode 可以为 null
|
||||||
|
const existing = await this.prisma.systemMiningAccount.findFirst({
|
||||||
where: {
|
where: {
|
||||||
accountType_regionCode: { accountType: account.accountType, regionCode: null },
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
accountType: account.accountType,
|
accountType: account.accountType,
|
||||||
regionCode: null,
|
regionCode: { equals: null },
|
||||||
name: account.name,
|
|
||||||
totalMined: 0,
|
|
||||||
availableBalance: 0,
|
|
||||||
totalContribution: 0,
|
|
||||||
},
|
},
|
||||||
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,
|
name: string,
|
||||||
contribution: ShareAmount,
|
contribution: ShareAmount,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.prisma.systemMiningAccount.upsert({
|
// 使用 findFirst + create/update 替代 upsert,因为 regionCode 可以为 null
|
||||||
|
const existing = await this.prisma.systemMiningAccount.findFirst({
|
||||||
where: {
|
where: {
|
||||||
accountType_regionCode: { accountType, regionCode },
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
accountType,
|
accountType,
|
||||||
regionCode,
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
name,
|
|
||||||
totalContribution: contribution.value,
|
|
||||||
lastSyncedAt: new Date(),
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
totalContribution: contribution.value,
|
|
||||||
lastSyncedAt: new Date(),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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> {
|
async getTotalContribution(): Promise<ShareAmount> {
|
||||||
|
|
@ -130,9 +149,11 @@ export class SystemMiningAccountRepository {
|
||||||
tx?: TransactionClient,
|
tx?: TransactionClient,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const executeInTx = async (client: TransactionClient) => {
|
const executeInTx = async (client: TransactionClient) => {
|
||||||
const account = await client.systemMiningAccount.findUnique({
|
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||||
|
const account = await client.systemMiningAccount.findFirst({
|
||||||
where: {
|
where: {
|
||||||
accountType_regionCode: { accountType, regionCode },
|
accountType,
|
||||||
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -180,9 +201,11 @@ export class SystemMiningAccountRepository {
|
||||||
secondDistribution: ShareAmount,
|
secondDistribution: ShareAmount,
|
||||||
minedAmount: ShareAmount,
|
minedAmount: ShareAmount,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const account = await this.prisma.systemMiningAccount.findUnique({
|
// 使用 findFirst 替代 findUnique,因为 regionCode 可以为 null
|
||||||
|
const account = await this.prisma.systemMiningAccount.findFirst({
|
||||||
where: {
|
where: {
|
||||||
accountType_regionCode: { accountType, regionCode },
|
accountType,
|
||||||
|
regionCode: regionCode === null ? { equals: null } : regionCode,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue