@@ -14,6 +14,12 @@ import { contractMediaType, normalizeContractUrls } from '../../common/store-med
|
||||
import type { AdminStoreAccountsQueryDto, AdminStoreMediaQueryDto, AdminStoresQueryDto } from './dto/admin-query.dto';
|
||||
import { PartnerCityService } from '../city-scope/partner-city.service';
|
||||
import { StoreCategoryService } from '../store/store-category.service';
|
||||
import {
|
||||
attachStoreCategories,
|
||||
parseUniqueCategoryIds,
|
||||
storeCategoryLinkInclude,
|
||||
syncStoreCategoryLinks,
|
||||
} from '../store/store-category-link.util';
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
import type {
|
||||
CreateStoreAccountDto,
|
||||
@@ -123,6 +129,7 @@ export class AdminStoresService {
|
||||
cityRef: { select: { id: true, name: true, code: true } },
|
||||
partnerAccount: { select: { id: true, companyName: true, name: true, phone: true } },
|
||||
category: { select: { id: true, name: true, parentId: true } },
|
||||
...storeCategoryLinkInclude,
|
||||
bindings: {
|
||||
where: { storeAccount: { isPrimary: 1 } },
|
||||
take: 1,
|
||||
@@ -169,7 +176,7 @@ export class AdminStoresService {
|
||||
return serializeBigInt({
|
||||
items: items.map((s) => {
|
||||
const { visibilityPhones, ...rest } = s;
|
||||
return mapStoreCompat({
|
||||
return mapStoreCompat(attachStoreCategories({
|
||||
...rest,
|
||||
visibilityWhitelistEnabled: s.visibilityWhitelistEnabled,
|
||||
visibilityPhones: visibilityPhones.map((p) => p.phone),
|
||||
@@ -180,7 +187,7 @@ export class AdminStoresService {
|
||||
partner: s.partnerAccount,
|
||||
account: s.bindings[0]?.storeAccount ?? null,
|
||||
bindings: undefined,
|
||||
});
|
||||
}));
|
||||
}),
|
||||
total,
|
||||
page,
|
||||
@@ -196,6 +203,7 @@ export class AdminStoresService {
|
||||
cityRef: true,
|
||||
partnerAccount: true,
|
||||
category: true,
|
||||
...storeCategoryLinkInclude,
|
||||
bindings: {
|
||||
where: { storeAccount: { isPrimary: 1 } },
|
||||
take: 1,
|
||||
@@ -219,7 +227,7 @@ export class AdminStoresService {
|
||||
}),
|
||||
]);
|
||||
const { visibilityPhones, ...rest } = store;
|
||||
return serializeBigInt(mapStoreCompat({
|
||||
return serializeBigInt(mapStoreCompat(attachStoreCategories({
|
||||
...rest,
|
||||
visibilityWhitelistEnabled: store.visibilityWhitelistEnabled,
|
||||
visibilityPhones: visibilityPhones.map((p) => p.phone),
|
||||
@@ -235,7 +243,7 @@ export class AdminStoresService {
|
||||
redeemCount: store._count.redeemRecords,
|
||||
ratingCount: store._count.ratings,
|
||||
_count: undefined,
|
||||
}));
|
||||
})));
|
||||
}
|
||||
|
||||
async updateStoreStatus(id: bigint, dto: UpdateStoreStatusDto, actorId: bigint) {
|
||||
@@ -430,11 +438,22 @@ export class AdminStoresService {
|
||||
}
|
||||
|
||||
let categoryId: bigint | undefined;
|
||||
if (dto.categoryId !== undefined) {
|
||||
let categoryIds: bigint[] | undefined;
|
||||
if (dto.categoryIds !== undefined) {
|
||||
categoryIds = parseUniqueCategoryIds(dto.categoryIds);
|
||||
if (!categoryIds.length) {
|
||||
throw new BadRequestException('请选择门店分类');
|
||||
}
|
||||
for (const id of categoryIds) {
|
||||
await this.storeCategoryService.assertLeafCategoryId(id);
|
||||
}
|
||||
categoryId = categoryIds[0];
|
||||
} else if (dto.categoryId !== undefined) {
|
||||
if (!dto.categoryId?.trim()) {
|
||||
throw new BadRequestException('请选择门店分类');
|
||||
}
|
||||
categoryId = BigInt(dto.categoryId);
|
||||
categoryIds = [categoryId];
|
||||
await this.storeCategoryService.assertLeafCategoryId(categoryId);
|
||||
}
|
||||
|
||||
@@ -661,6 +680,10 @@ export class AdminStoresService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (categoryIds !== undefined) {
|
||||
await syncStoreCategoryLinks(tx, id, categoryIds);
|
||||
}
|
||||
});
|
||||
|
||||
return this.detailStore(id, actorId);
|
||||
@@ -693,11 +716,20 @@ export class AdminStoresService {
|
||||
if (!city) throw new BadRequestException('开城城市不存在');
|
||||
await this.partnerCityService.assertPartnerAccountBoundToCity(partnerAccountId, city.id);
|
||||
|
||||
if (!dto.categoryId?.trim()) {
|
||||
if (!dto.categoryId?.trim() && (!dto.categoryIds || !dto.categoryIds.length)) {
|
||||
throw new BadRequestException('请选择门店分类');
|
||||
}
|
||||
const categoryId = BigInt(dto.categoryId);
|
||||
await this.storeCategoryService.assertLeafCategoryId(categoryId);
|
||||
const categoryIds = parseUniqueCategoryIds(dto.categoryIds);
|
||||
if (!categoryIds.length && dto.categoryId?.trim()) {
|
||||
categoryIds.push(BigInt(dto.categoryId));
|
||||
}
|
||||
if (!categoryIds.length) {
|
||||
throw new BadRequestException('请选择门店分类');
|
||||
}
|
||||
for (const id of categoryIds) {
|
||||
await this.storeCategoryService.assertLeafCategoryId(id);
|
||||
}
|
||||
const categoryId = categoryIds[0];
|
||||
|
||||
const latitude = dto.latitude != null ? Number(dto.latitude) : null;
|
||||
const longitude = dto.longitude != null ? Number(dto.longitude) : null;
|
||||
@@ -775,6 +807,8 @@ export class AdminStoresService {
|
||||
},
|
||||
});
|
||||
|
||||
await syncStoreCategoryLinks(this.prisma, store.id, categoryIds);
|
||||
|
||||
if (dto.coverUrl) {
|
||||
const cover = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
|
||||
@@ -45,9 +45,15 @@ export class CreateStoreDto {
|
||||
@IsString()
|
||||
contactPhone?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
categoryId: string;
|
||||
categoryId?: string;
|
||||
|
||||
/** 多选二级分类;至少选一项 */
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
categoryIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@@ -240,6 +246,12 @@ export class UpdateStoreDto {
|
||||
@IsString()
|
||||
categoryId?: string;
|
||||
|
||||
/** 多选二级分类;传此项时覆盖 categoryId */
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
categoryIds?: string[];
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
|
||||
Reference in New Issue
Block a user