feat(ops): v4.0.4 开发任务多行与版本状态同步、合伙人/门店删除、账单合伙人列
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -56,6 +56,16 @@ export class AdminPartnersController {
|
||||
return this.service.updatePartner(BigInt(id), dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@HqOperation({
|
||||
action: HqOperationAction.PARTNER_DELETE,
|
||||
refType: 'PARTNER',
|
||||
refIdParam: 'id',
|
||||
})
|
||||
remove(@Param('id') id: string) {
|
||||
return this.service.deletePartner(BigInt(id));
|
||||
}
|
||||
|
||||
@Get(':id/assoc')
|
||||
assocSummary(@Param('id') id: string) {
|
||||
return this.assoc.getSummary(BigInt(id));
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma, CityPartnerScopeType, CityPartnerStatus } from '@prisma/client';
|
||||
import { resolveMaxPartnerCommissionRate, validatePartnerCommissionRates } from '@dukang/domain';
|
||||
import {
|
||||
assertCanDeleteCityPartner,
|
||||
resolveMaxPartnerCommissionRate,
|
||||
validatePartnerCommissionRates,
|
||||
} from '@dukang/domain';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { PartnerCityService } from '../city-scope/partner-city.service';
|
||||
@@ -502,6 +506,62 @@ export class AdminPartnersService {
|
||||
return serializeBigInt(account);
|
||||
}
|
||||
|
||||
async deletePartner(id: bigint) {
|
||||
const account = await this.prisma.partnerAccount.findFirst({
|
||||
where: { id, ...PRIMARY_WHERE },
|
||||
include: { children: { select: { id: true } } },
|
||||
});
|
||||
if (!account) throw new NotFoundException('开城合伙人不存在');
|
||||
|
||||
const accountIds = [id, ...account.children.map((c) => c.id)];
|
||||
const [storeCount, orderCount, billCount, redeemCount] = await Promise.all([
|
||||
this.prisma.store.count({ where: { partnerAccountId: { in: accountIds } } }),
|
||||
this.prisma.order.count({
|
||||
where: {
|
||||
OR: [
|
||||
{ partnerAccountIdAtPay: { in: accountIds } },
|
||||
{ proxyPartnerAccountId: { in: accountIds } },
|
||||
],
|
||||
},
|
||||
}),
|
||||
this.prisma.partnerBill.count({ where: { partnerAccountId: { in: accountIds } } }),
|
||||
this.prisma.redeemRecord.count({
|
||||
where: { store: { partnerAccountId: { in: accountIds } } },
|
||||
}),
|
||||
]);
|
||||
|
||||
const check = assertCanDeleteCityPartner({
|
||||
storeCount,
|
||||
orderCount: orderCount + billCount,
|
||||
redeemCount,
|
||||
});
|
||||
if (!check.ok) throw new BadRequestException(check.message);
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.user.updateMany({
|
||||
where: { assocPartnerAccountId: { in: accountIds } },
|
||||
data: { assocPartnerAccountId: null, assocBoundAt: null },
|
||||
});
|
||||
await tx.partnerUserNote.deleteMany({ where: { partnerAccountId: { in: accountIds } } });
|
||||
await tx.logPartnerAnalytics.deleteMany({ where: { partnerAccountId: { in: accountIds } } });
|
||||
await tx.cityWarehouse.updateMany({
|
||||
where: { partnerAccountId: { in: accountIds } },
|
||||
data: { partnerAccountId: null },
|
||||
});
|
||||
await tx.partnerAccount.updateMany({
|
||||
where: { id: { in: accountIds } },
|
||||
data: { managedWarehouseId: null, assocQrcodeResourceId: null, activityPosterId: null },
|
||||
});
|
||||
const childIds = account.children.map((c) => c.id);
|
||||
if (childIds.length) {
|
||||
await tx.partnerAccount.deleteMany({ where: { id: { in: childIds } } });
|
||||
}
|
||||
await tx.partnerAccount.delete({ where: { id } });
|
||||
});
|
||||
|
||||
return { ok: true, message: '城市合伙人已删除' };
|
||||
}
|
||||
|
||||
async deletePartnerSubAccount(id: bigint) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({ where: { id } });
|
||||
if (!account) throw new NotFoundException('合伙人账号不存在');
|
||||
|
||||
@@ -52,6 +52,12 @@ export class AdminStoresController {
|
||||
return this.service.updateStore(BigInt(id), dto, user.actorId);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@HqOperation({ action: HqOperationAction.STORE_DELETE, refType: 'STORE', refIdParam: 'id' })
|
||||
remove(@CurrentUser() user: AuthUser, @Param('id') id: string) {
|
||||
return this.service.deleteStore(BigInt(id), user.actorId);
|
||||
}
|
||||
|
||||
@Put(':id/status')
|
||||
@HqOperation({ action: HqOperationAction.STORE_STATUS, refType: 'STORE', refIdParam: 'id', includeBody: true })
|
||||
updateStatus(@CurrentUser() user: AuthUser, @Param('id') id: string, @Body() dto: UpdateStoreStatusDto) {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { isMobilePhone, isStoreContactPhone, STORE_CONTACT_PHONE_HINT, validateBusinessHours } from '@dukang/domain';
|
||||
import {
|
||||
assertCanDeleteStore,
|
||||
isMobilePhone,
|
||||
isStoreContactPhone,
|
||||
STORE_CONTACT_PHONE_HINT,
|
||||
validateBusinessHours,
|
||||
} from '@dukang/domain';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { mapStoreCompat } from '../../common/compat/v31-compat';
|
||||
@@ -241,6 +247,88 @@ export class AdminStoresService {
|
||||
return serializeBigInt(store);
|
||||
}
|
||||
|
||||
async deleteStore(id: bigint, actorId: bigint) {
|
||||
await this.hqPermissions.assertStoreIdInScope(actorId, id);
|
||||
const store = await this.prisma.store.findUnique({
|
||||
where: { id },
|
||||
include: { bindings: { select: { storeAccountId: true } } },
|
||||
});
|
||||
if (!store) throw new NotFoundException('门店不存在');
|
||||
|
||||
const [redeemCount, pendingCount, payoutCount, billCount, ratingCount, withdrawCount] =
|
||||
await Promise.all([
|
||||
this.prisma.redeemRecord.count({ where: { storeId: id } }),
|
||||
this.prisma.redeemPendingRecord.count({ where: { storeId: id } }),
|
||||
this.prisma.storePayout.count({ where: { storeId: id } }),
|
||||
this.prisma.storeBill.count({ where: { storeId: id } }),
|
||||
this.prisma.storeRating.count({ where: { storeId: id } }),
|
||||
this.prisma.storeWithdrawRequest.count({ where: { storeId: id } }),
|
||||
]);
|
||||
const check = assertCanDeleteStore({
|
||||
orderCount: 0,
|
||||
redeemCount:
|
||||
redeemCount + pendingCount + payoutCount + billCount + ratingCount + withdrawCount,
|
||||
});
|
||||
if (!check.ok) throw new BadRequestException(check.message);
|
||||
|
||||
const accountIds = [...new Set(store.bindings.map((b) => b.storeAccountId))];
|
||||
const exclusiveAccountIds: bigint[] = [];
|
||||
for (const accountId of accountIds) {
|
||||
const otherBindings = await this.prisma.storeAccountStore.count({
|
||||
where: { storeAccountId: accountId, NOT: { storeId: id } },
|
||||
});
|
||||
if (otherBindings === 0) exclusiveAccountIds.push(accountId);
|
||||
}
|
||||
|
||||
const staffToDelete: bigint[] = [];
|
||||
if (exclusiveAccountIds.length) {
|
||||
const staff = await this.prisma.storeAccount.findMany({
|
||||
where: { parentAccountId: { in: exclusiveAccountIds }, isPrimary: 0 },
|
||||
select: { id: true },
|
||||
});
|
||||
for (const row of staff) {
|
||||
const other = await this.prisma.storeAccountStore.count({
|
||||
where: { storeAccountId: row.id, NOT: { storeId: id } },
|
||||
});
|
||||
if (other > 0) continue;
|
||||
const pending = await this.prisma.redeemPendingRecord.count({
|
||||
where: { storeAccountId: row.id },
|
||||
});
|
||||
if (pending > 0) {
|
||||
throw new BadRequestException('该账号下有关联核销单,禁止删除');
|
||||
}
|
||||
staffToDelete.push(row.id);
|
||||
}
|
||||
}
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
await tx.store.update({ where: { id }, data: { coverResourceId: null } });
|
||||
await tx.commonResource.updateMany({
|
||||
where: { ownerType: 'STORE', ownerId: id },
|
||||
data: { status: 'DELETED' },
|
||||
});
|
||||
await tx.logStoreAnalytics.deleteMany({ where: { storeId: id } });
|
||||
await tx.store.delete({ where: { id } });
|
||||
|
||||
if (staffToDelete.length) {
|
||||
await tx.logStoreAnalytics.deleteMany({ where: { storeAccountId: { in: staffToDelete } } });
|
||||
await tx.storeAccount.deleteMany({ where: { id: { in: staffToDelete } } });
|
||||
}
|
||||
if (exclusiveAccountIds.length) {
|
||||
await tx.logStoreAnalytics.deleteMany({
|
||||
where: { storeAccountId: { in: exclusiveAccountIds } },
|
||||
});
|
||||
await tx.storeAccount.updateMany({
|
||||
where: { parentAccountId: { in: exclusiveAccountIds } },
|
||||
data: { parentAccountId: null },
|
||||
});
|
||||
await tx.storeAccount.deleteMany({ where: { id: { in: exclusiveAccountIds } } });
|
||||
}
|
||||
});
|
||||
|
||||
return { ok: true, message: '门店及门店账号已删除' };
|
||||
}
|
||||
|
||||
async auditStore(id: bigint, dto: { approved: boolean; remark?: string }, actorId: bigint) {
|
||||
await this.hqPermissions.assertStoreIdInScope(actorId, id);
|
||||
const store = await this.prisma.store.findUnique({ where: { id } });
|
||||
|
||||
Reference in New Issue
Block a user