v3数据表更改
This commit is contained in:
@@ -2,6 +2,8 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/comm
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { buildBenefitLedgerEvent, benefitLedgerWhere } from '../../common/event/event.helpers';
|
||||
import { mapBenefitLedgerCompat } from '../../common/compat/v31-compat';
|
||||
import type { AdminBenefitCouponsQueryDto, AdminBenefitLedgersQueryDto } from './dto/admin-query.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -38,12 +40,16 @@ export class AdminBenefitService {
|
||||
include: {
|
||||
user: { select: { id: true, userNo: true, phone: true, nickname: true } },
|
||||
order: { select: { id: true, orderNo: true, status: true, payAmount: true } },
|
||||
ledgers: { orderBy: { createdAt: 'desc' }, take: 20 },
|
||||
redeemRecords: { orderBy: { createdAt: 'desc' }, take: 10, include: { store: { select: { id: true, name: true } } } },
|
||||
},
|
||||
});
|
||||
if (!coupon) throw new NotFoundException('权益券不存在');
|
||||
return serializeBigInt(coupon);
|
||||
const ledgers = await this.prisma.commonEvent.findMany({
|
||||
where: benefitLedgerWhere(undefined, id),
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 20,
|
||||
});
|
||||
return serializeBigInt({ ...coupon, ledgers });
|
||||
}
|
||||
|
||||
async voidCoupon(id: bigint) {
|
||||
@@ -57,8 +63,8 @@ export class AdminBenefitService {
|
||||
data: { status: 'VOID', balance: 0 },
|
||||
});
|
||||
if (Number(coupon.balance) > 0) {
|
||||
await tx.benefitLedger.create({
|
||||
data: {
|
||||
await tx.commonEvent.create({
|
||||
data: buildBenefitLedgerEvent({
|
||||
userId: coupon.userId,
|
||||
couponId: coupon.id,
|
||||
type: 'ADJUST',
|
||||
@@ -66,7 +72,7 @@ export class AdminBenefitService {
|
||||
balanceAfter: 0,
|
||||
refType: 'ADMIN_VOID',
|
||||
remark: 'HQ 手动作废',
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
return row;
|
||||
@@ -77,24 +83,47 @@ export class AdminBenefitService {
|
||||
async listLedgers(query: AdminBenefitLedgersQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.BenefitLedgerWhereInput = {};
|
||||
if (query.userId) where.userId = BigInt(query.userId);
|
||||
if (query.couponId) where.couponId = BigInt(query.couponId);
|
||||
if (query.type) where.type = query.type as Prisma.EnumBenefitLedgerTypeFilter['equals'];
|
||||
const where: Prisma.CommonEventWhereInput = {
|
||||
eventType: 'BENEFIT_LEDGER',
|
||||
...(query.userId ? { actorType: 'USER', actorId: BigInt(query.userId) } : {}),
|
||||
...(query.couponId ? { param2: BigInt(query.couponId).toString() } : {}),
|
||||
...(query.type ? { param1: query.type } : {}),
|
||||
};
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.benefitLedger.findMany({
|
||||
this.prisma.commonEvent.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
include: {
|
||||
user: { select: { id: true, userNo: true } },
|
||||
coupon: { select: { id: true, couponNo: true } },
|
||||
},
|
||||
}),
|
||||
this.prisma.benefitLedger.count({ where }),
|
||||
this.prisma.commonEvent.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
|
||||
const userIds = [...new Set(items.map((i) => i.actorId).filter(Boolean))] as bigint[];
|
||||
const couponIds = [...new Set(items.map((i) => i.param2).filter(Boolean))].map((id) => BigInt(id!));
|
||||
const [users, coupons] = await Promise.all([
|
||||
userIds.length
|
||||
? this.prisma.user.findMany({ where: { id: { in: userIds } }, select: { id: true, userNo: true } })
|
||||
: Promise.resolve([] as { id: bigint; userNo: string | null }[]),
|
||||
couponIds.length
|
||||
? this.prisma.benefitCoupon.findMany({ where: { id: { in: couponIds } }, select: { id: true, couponNo: true } })
|
||||
: Promise.resolve([] as { id: bigint; couponNo: string }[]),
|
||||
]);
|
||||
const userMap = new Map(users.map((u) => [u.id.toString(), u] as const));
|
||||
const couponMap = new Map(coupons.map((c) => [c.id.toString(), c] as const));
|
||||
|
||||
return serializeBigInt({
|
||||
items: items.map((e) =>
|
||||
mapBenefitLedgerCompat(
|
||||
e,
|
||||
e.actorId ? userMap.get(e.actorId.toString()) : null,
|
||||
e.param2 ? couponMap.get(e.param2) : null,
|
||||
),
|
||||
),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,14 @@ export class AdminCitiesService {
|
||||
async list(query: AdminCitiesQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.CityWhereInput = {};
|
||||
const where: Prisma.CommonCityWhereInput = {};
|
||||
if (query.name) where.name = { contains: query.name };
|
||||
if (query.code) where.code = { contains: query.code };
|
||||
if (query.status) where.status = query.status as Prisma.EnumCityStatusFilter['equals'];
|
||||
if (query.partnerId) where.partnerId = BigInt(query.partnerId);
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.city.findMany({
|
||||
this.prisma.commonCity.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
@@ -29,7 +29,7 @@ export class AdminCitiesService {
|
||||
_count: { select: { stores: true, orders: true } },
|
||||
},
|
||||
}),
|
||||
this.prisma.city.count({ where }),
|
||||
this.prisma.commonCity.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({
|
||||
items: items.map((c) => ({
|
||||
@@ -45,7 +45,7 @@ export class AdminCitiesService {
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const city = await this.prisma.city.findUnique({
|
||||
const city = await this.prisma.commonCity.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
partner: true,
|
||||
@@ -58,9 +58,9 @@ export class AdminCitiesService {
|
||||
}
|
||||
|
||||
async create(dto: CreateCityDto) {
|
||||
const exists = await this.prisma.city.findUnique({ where: { code: dto.code } });
|
||||
const exists = await this.prisma.commonCity.findUnique({ where: { code: dto.code } });
|
||||
if (exists) throw new BadRequestException('城市编码已存在');
|
||||
const city = await this.prisma.city.create({
|
||||
const city = await this.prisma.commonCity.create({
|
||||
data: {
|
||||
code: dto.code,
|
||||
name: dto.name,
|
||||
@@ -79,7 +79,7 @@ export class AdminCitiesService {
|
||||
}
|
||||
|
||||
async update(id: bigint, dto: UpdateCityDto) {
|
||||
const city = await this.prisma.city.update({
|
||||
const city = await this.prisma.commonCity.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(dto.name !== undefined ? { name: dto.name } : {}),
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { orderStatusLogWhere } from '../../common/event/event.helpers';
|
||||
import { mapOrderCompat, mapStatusLogCompat } from '../../common/compat/v31-compat';
|
||||
import type { AdminOrdersQueryDto } from './dto/admin-query.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -54,17 +56,24 @@ export class AdminOrdersService {
|
||||
phoneVerifiedAt: true,
|
||||
},
|
||||
},
|
||||
items: true,
|
||||
delivery: true,
|
||||
payment: true,
|
||||
statusLogs: { orderBy: { createdAt: 'asc' } },
|
||||
benefitCoupons: {
|
||||
benefitCoupon: {
|
||||
select: { id: true, couponNo: true, balance: true, status: true },
|
||||
},
|
||||
city: { select: { id: true, name: true, code: true } },
|
||||
product: { select: { id: true, name: true, skuCode: true, barcode69: true } },
|
||||
imageResource: { select: { id: true, url: true } },
|
||||
},
|
||||
});
|
||||
if (!order) throw new NotFoundException('订单不存在');
|
||||
return serializeBigInt(order);
|
||||
const statusLogs = await this.prisma.commonEvent.findMany({
|
||||
where: orderStatusLogWhere(id),
|
||||
orderBy: { createdAt: 'asc' },
|
||||
});
|
||||
return serializeBigInt(mapOrderCompat({
|
||||
...order,
|
||||
statusLogs: mapStatusLogCompat(statusLogs),
|
||||
benefitCoupons: order.benefitCoupon ? [order.benefitCoupon] : [],
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Body, Controller, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { AdminProductsService } from './admin-products.service';
|
||||
import { AdminProductsQueryDto } from './dto/admin-query.dto';
|
||||
import { CreateProductDto, UpdateProductDto } from './dto/admin-mutate.dto';
|
||||
|
||||
@Controller('admin/products')
|
||||
@UseGuards(HqAuthGuard)
|
||||
export class AdminProductsController {
|
||||
constructor(private readonly service: AdminProductsService) {}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: AdminProductsQueryDto) {
|
||||
return this.service.list(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateProductDto) {
|
||||
return this.service.create(dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateProductDto) {
|
||||
return this.service.update(BigInt(id), dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import type { AdminProductsQueryDto } from './dto/admin-query.dto';
|
||||
import type { CreateProductDto, UpdateProductDto } from './dto/admin-mutate.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AdminProductsService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(query: AdminProductsQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.CommonProductItemWhereInput = {};
|
||||
if (query.name) where.name = { contains: query.name };
|
||||
if (query.status) where.status = query.status as Prisma.EnumProductStatusFilter['equals'];
|
||||
if (query.aromaType) where.aromaType = query.aromaType as Prisma.EnumAromaTypeFilter['equals'];
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.commonProductItem.findMany({
|
||||
where,
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
include: { coverResource: true },
|
||||
}),
|
||||
this.prisma.commonProductItem.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({
|
||||
items: items.map((p) => ({
|
||||
...p,
|
||||
mainImageUrl: p.coverResource?.url ?? null,
|
||||
})),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const product = await this.prisma.commonProductItem.findUnique({
|
||||
where: { id },
|
||||
include: { coverResource: true },
|
||||
});
|
||||
if (!product) throw new NotFoundException('商品不存在');
|
||||
return serializeBigInt({ ...product, mainImageUrl: product.coverResource?.url ?? null });
|
||||
}
|
||||
|
||||
async create(dto: CreateProductDto) {
|
||||
const exists = await this.prisma.commonProductItem.findFirst({
|
||||
where: { OR: [{ skuCode: dto.skuCode }, { barcode69: dto.barcode69 }] },
|
||||
});
|
||||
if (exists) throw new BadRequestException('SKU 或 69 码已存在');
|
||||
|
||||
const product = await this.prisma.commonProductItem.create({
|
||||
data: {
|
||||
skuCode: dto.skuCode,
|
||||
barcode69: dto.barcode69,
|
||||
name: dto.name,
|
||||
subtitle: dto.subtitle,
|
||||
aromaType: dto.aromaType as 'QINGXIANG' | 'JIANGXIANG' | 'NONGXIANG',
|
||||
spec: dto.spec,
|
||||
price: dto.price,
|
||||
benefitAmount: dto.benefitAmount ?? dto.price,
|
||||
status: (dto.status ?? 'DRAFT') as 'DRAFT' | 'ON_SALE' | 'OFF_SALE',
|
||||
sortOrder: dto.sortOrder ?? 0,
|
||||
},
|
||||
});
|
||||
|
||||
if (dto.coverUrl) {
|
||||
const cover = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
ownerType: 'PRODUCT',
|
||||
ownerId: product.id,
|
||||
bizType: 'COVER',
|
||||
mediaType: 'IMAGE',
|
||||
ossBucket: 'legacy',
|
||||
ossKey: dto.coverUrl,
|
||||
url: dto.coverUrl,
|
||||
},
|
||||
});
|
||||
await this.prisma.commonProductItem.update({
|
||||
where: { id: product.id },
|
||||
data: { coverResourceId: cover.id },
|
||||
});
|
||||
}
|
||||
|
||||
return this.detail(product.id);
|
||||
}
|
||||
|
||||
async update(id: bigint, dto: UpdateProductDto) {
|
||||
await this.detail(id);
|
||||
await this.prisma.commonProductItem.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(dto.name !== undefined ? { name: dto.name } : {}),
|
||||
...(dto.subtitle !== undefined ? { subtitle: dto.subtitle } : {}),
|
||||
...(dto.spec !== undefined ? { spec: dto.spec } : {}),
|
||||
...(dto.price !== undefined ? { price: dto.price } : {}),
|
||||
...(dto.benefitAmount !== undefined ? { benefitAmount: dto.benefitAmount } : {}),
|
||||
...(dto.status !== undefined ? { status: dto.status as 'DRAFT' | 'ON_SALE' | 'OFF_SALE' } : {}),
|
||||
...(dto.sortOrder !== undefined ? { sortOrder: dto.sortOrder } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
if (dto.coverUrl) {
|
||||
const product = await this.prisma.commonProductItem.findUniqueOrThrow({ where: { id } });
|
||||
if (product.coverResourceId) {
|
||||
await this.prisma.commonResource.update({
|
||||
where: { id: product.coverResourceId },
|
||||
data: { url: dto.coverUrl, ossKey: dto.coverUrl },
|
||||
});
|
||||
} else {
|
||||
const cover = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
ownerType: 'PRODUCT',
|
||||
ownerId: id,
|
||||
bizType: 'COVER',
|
||||
mediaType: 'IMAGE',
|
||||
ossBucket: 'legacy',
|
||||
ossKey: dto.coverUrl,
|
||||
url: dto.coverUrl,
|
||||
},
|
||||
});
|
||||
await this.prisma.commonProductItem.update({
|
||||
where: { id },
|
||||
data: { coverResourceId: cover.id },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return this.detail(id);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import type { DeliveryProvider } from '@prisma/client';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
@@ -42,7 +43,6 @@ export class AdminRedeemService {
|
||||
store: { include: { partner: { select: { id: true, companyName: true } } } },
|
||||
coupon: true,
|
||||
payout: true,
|
||||
commissions: true,
|
||||
},
|
||||
});
|
||||
if (!record) throw new NotFoundException('核销记录不存在');
|
||||
@@ -58,7 +58,7 @@ export class AdminDeliveriesService {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.OrderDeliveryWhereInput = {};
|
||||
if (query.provider) where.provider = query.provider;
|
||||
if (query.provider) where.provider = query.provider as DeliveryProvider;
|
||||
if (query.trackingNo) where.trackingNo = { contains: query.trackingNo };
|
||||
if (query.orderNo) {
|
||||
where.order = { orderNo: { contains: query.orderNo } };
|
||||
@@ -79,6 +79,8 @@ export class AdminDeliveriesService {
|
||||
receiverName: true,
|
||||
receiverPhone: true,
|
||||
deliveryType: true,
|
||||
productName: true,
|
||||
quantity: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -95,7 +97,7 @@ export class AdminDeliveriesService {
|
||||
order: {
|
||||
include: {
|
||||
user: { select: { id: true, userNo: true, phone: true } },
|
||||
items: true,
|
||||
imageResource: { select: { url: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -108,7 +110,7 @@ export class AdminDeliveriesService {
|
||||
const delivery = await this.prisma.orderDelivery.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(dto.provider !== undefined ? { provider: dto.provider } : {}),
|
||||
...(dto.provider !== undefined ? { provider: dto.provider as DeliveryProvider } : {}),
|
||||
...(dto.providerOrderNo !== undefined ? { providerOrderNo: dto.providerOrderNo } : {}),
|
||||
...(dto.trackingNo !== undefined ? { trackingNo: dto.trackingNo } : {}),
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/comm
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { mapStoreCompat } from '../../common/compat/v31-compat';
|
||||
import type { AdminStoreAccountsQueryDto, AdminStoreMediaQueryDto, AdminStoresQueryDto } from './dto/admin-query.dto';
|
||||
import type {
|
||||
CreateStoreAccountDto,
|
||||
@@ -37,11 +38,17 @@ export class AdminStoresService {
|
||||
cityRef: { select: { id: true, name: true, code: true } },
|
||||
partner: { select: { id: true, companyName: true } },
|
||||
account: { select: { id: true, phone: true, name: true, status: true } },
|
||||
coverResource: { select: { id: true, url: true } },
|
||||
},
|
||||
}),
|
||||
this.prisma.store.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
return serializeBigInt({
|
||||
items: items.map((s) => mapStoreCompat(s)),
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
});
|
||||
}
|
||||
|
||||
async detailStore(id: bigint) {
|
||||
@@ -52,18 +59,30 @@ export class AdminStoresService {
|
||||
partner: true,
|
||||
category: true,
|
||||
account: true,
|
||||
media: { orderBy: { sortOrder: 'asc' } },
|
||||
audits: { orderBy: { submittedAt: 'desc' }, take: 5 },
|
||||
coverResource: true,
|
||||
_count: { select: { redeemRecords: true, ratings: true } },
|
||||
},
|
||||
});
|
||||
if (!store) throw new NotFoundException('门店不存在');
|
||||
return serializeBigInt({
|
||||
const [media, audits] = await Promise.all([
|
||||
this.prisma.commonResource.findMany({
|
||||
where: { ownerType: 'STORE', ownerId: id, status: 'ACTIVE' },
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
}),
|
||||
this.prisma.commonEvent.findMany({
|
||||
where: { eventType: 'STORE_AUDIT', refType: 'STORE', refId: id },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5,
|
||||
}),
|
||||
]);
|
||||
return serializeBigInt(mapStoreCompat({
|
||||
...store,
|
||||
media,
|
||||
audits,
|
||||
redeemCount: store._count.redeemRecords,
|
||||
ratingCount: store._count.ratings,
|
||||
_count: undefined,
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
async updateStoreStatus(id: bigint, dto: UpdateStoreStatusDto) {
|
||||
@@ -81,18 +100,41 @@ export class AdminStoresService {
|
||||
...(dto.name !== undefined ? { name: dto.name } : {}),
|
||||
...(dto.phone !== undefined ? { phone: dto.phone } : {}),
|
||||
...(dto.intro !== undefined ? { intro: dto.intro } : {}),
|
||||
...(dto.coverUrl !== undefined ? { coverUrl: dto.coverUrl } : {}),
|
||||
...(dto.address !== undefined ? { address: dto.address } : {}),
|
||||
...(dto.district !== undefined ? { district: dto.district } : {}),
|
||||
},
|
||||
});
|
||||
return serializeBigInt(store);
|
||||
|
||||
if (dto.coverUrl) {
|
||||
const current = await this.prisma.store.findUniqueOrThrow({ where: { id } });
|
||||
if (current.coverResourceId) {
|
||||
await this.prisma.commonResource.update({
|
||||
where: { id: current.coverResourceId },
|
||||
data: { url: dto.coverUrl, ossKey: dto.coverUrl },
|
||||
});
|
||||
} else {
|
||||
const cover = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
ownerType: 'STORE',
|
||||
ownerId: id,
|
||||
bizType: 'COVER',
|
||||
mediaType: 'IMAGE',
|
||||
ossBucket: 'legacy',
|
||||
ossKey: dto.coverUrl,
|
||||
url: dto.coverUrl,
|
||||
},
|
||||
});
|
||||
await this.prisma.store.update({ where: { id }, data: { coverResourceId: cover.id } });
|
||||
}
|
||||
}
|
||||
|
||||
return this.detailStore(id);
|
||||
}
|
||||
|
||||
async createStore(dto: CreateStoreDto) {
|
||||
const partner = await this.prisma.partner.findUnique({ where: { id: BigInt(dto.partnerId) } });
|
||||
if (!partner) throw new BadRequestException('开城合伙人不存在');
|
||||
const city = await this.prisma.city.findUnique({ where: { id: BigInt(dto.cityId) } });
|
||||
const city = await this.prisma.commonCity.findUnique({ where: { id: BigInt(dto.cityId) } });
|
||||
if (!city) throw new BadRequestException('开城城市不存在');
|
||||
|
||||
const store = await this.prisma.store.create({
|
||||
@@ -107,7 +149,6 @@ export class AdminStoresService {
|
||||
district: dto.district ?? '',
|
||||
address: dto.address,
|
||||
intro: dto.intro ?? null,
|
||||
coverUrl: dto.coverUrl ?? null,
|
||||
status: 'OPEN',
|
||||
},
|
||||
});
|
||||
@@ -139,19 +180,21 @@ export class AdminStoresService {
|
||||
async listStoreMedia(query: AdminStoreMediaQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.StoreMediaWhereInput = {};
|
||||
if (query.storeId) where.storeId = BigInt(query.storeId);
|
||||
if (query.mediaType) where.mediaType = query.mediaType;
|
||||
const where: Prisma.CommonResourceWhereInput = {
|
||||
ownerType: 'STORE',
|
||||
status: 'ACTIVE',
|
||||
};
|
||||
if (query.storeId) where.ownerId = BigInt(query.storeId);
|
||||
if (query.mediaType) where.mediaType = query.mediaType as Prisma.EnumResourceMediaTypeFilter['equals'];
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.storeMedia.findMany({
|
||||
this.prisma.commonResource.findMany({
|
||||
where,
|
||||
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
include: { store: { select: { id: true, name: true } } },
|
||||
}),
|
||||
this.prisma.storeMedia.count({ where }),
|
||||
this.prisma.commonResource.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
}
|
||||
@@ -159,10 +202,14 @@ export class AdminStoresService {
|
||||
async createStoreMedia(dto: CreateStoreMediaDto) {
|
||||
const store = await this.prisma.store.findUnique({ where: { id: BigInt(dto.storeId) } });
|
||||
if (!store) throw new BadRequestException('门店不存在');
|
||||
const media = await this.prisma.storeMedia.create({
|
||||
const media = await this.prisma.commonResource.create({
|
||||
data: {
|
||||
storeId: store.id,
|
||||
mediaType: dto.mediaType,
|
||||
ownerType: 'STORE',
|
||||
ownerId: store.id,
|
||||
bizType: 'ENV',
|
||||
mediaType: dto.mediaType as 'IMAGE' | 'VIDEO',
|
||||
ossBucket: 'legacy',
|
||||
ossKey: dto.url,
|
||||
url: dto.url,
|
||||
sortOrder: dto.sortOrder ?? 0,
|
||||
},
|
||||
@@ -171,11 +218,11 @@ export class AdminStoresService {
|
||||
}
|
||||
|
||||
async updateStoreMedia(id: bigint, dto: UpdateStoreMediaDto) {
|
||||
const media = await this.prisma.storeMedia.update({
|
||||
const media = await this.prisma.commonResource.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(dto.url !== undefined ? { url: dto.url } : {}),
|
||||
...(dto.mediaType !== undefined ? { mediaType: dto.mediaType } : {}),
|
||||
...(dto.url !== undefined ? { url: dto.url, ossKey: dto.url } : {}),
|
||||
...(dto.mediaType !== undefined ? { mediaType: dto.mediaType as 'IMAGE' | 'VIDEO' } : {}),
|
||||
...(dto.sortOrder !== undefined ? { sortOrder: dto.sortOrder } : {}),
|
||||
},
|
||||
});
|
||||
@@ -183,7 +230,10 @@ export class AdminStoresService {
|
||||
}
|
||||
|
||||
async deleteStoreMedia(id: bigint) {
|
||||
await this.prisma.storeMedia.delete({ where: { id } });
|
||||
await this.prisma.commonResource.update({
|
||||
where: { id },
|
||||
data: { status: 'DELETED' },
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export class AdminUsersService {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
cityPref: true,
|
||||
cityPreference: true,
|
||||
mergedInto: { select: { id: true, userNo: true, phone: true, nickname: true } },
|
||||
orders: {
|
||||
orderBy: { createdAt: 'desc' },
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { IsIn, IsNotEmpty, IsNumber, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateStoreStatusDto {
|
||||
@IsString()
|
||||
@@ -311,3 +311,81 @@ export class UpdateHqAccountDto {
|
||||
@IsIn(['ACTIVE', 'DISABLED'])
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export class CreateProductDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
skuCode: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
barcode69: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
subtitle?: string;
|
||||
|
||||
@IsIn(['QINGXIANG', 'JIANGXIANG', 'NONGXIANG'])
|
||||
aromaType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
spec: string;
|
||||
|
||||
@IsNumber()
|
||||
price: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
benefitAmount?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['DRAFT', 'ON_SALE', 'OFF_SALE'])
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
sortOrder?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
coverUrl?: string;
|
||||
}
|
||||
|
||||
export class UpdateProductDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
subtitle?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
spec?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
price?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
benefitAmount?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['DRAFT', 'ON_SALE', 'OFF_SALE'])
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
sortOrder?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
coverUrl?: string;
|
||||
}
|
||||
|
||||
@@ -213,6 +213,20 @@ export class AdminCitiesQueryDto extends PaginationQueryDto {
|
||||
partnerId?: string;
|
||||
}
|
||||
|
||||
export class AdminProductsQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
aromaType?: string;
|
||||
}
|
||||
|
||||
export class AdminStoreMediaQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
|
||||
@@ -18,6 +18,8 @@ import { AdminRedeemRecordsController, AdminDeliveriesController } from './admin
|
||||
import { AdminRedeemService, AdminDeliveriesService } from './admin-redeem.service';
|
||||
import { AdminHqAccountsController } from './admin-hq-accounts.controller';
|
||||
import { AdminHqAccountsService } from './admin-hq-accounts.service';
|
||||
import { AdminProductsController } from './admin-products.controller';
|
||||
import { AdminProductsService } from './admin-products.service';
|
||||
import { SuperAdminGuard } from '../../common/guards/super-admin.guard';
|
||||
|
||||
@Module({
|
||||
@@ -37,6 +39,7 @@ import { SuperAdminGuard } from '../../common/guards/super-admin.guard';
|
||||
AdminRedeemRecordsController,
|
||||
AdminDeliveriesController,
|
||||
AdminHqAccountsController,
|
||||
AdminProductsController,
|
||||
],
|
||||
providers: [
|
||||
AdminDashboardService,
|
||||
@@ -49,6 +52,7 @@ import { SuperAdminGuard } from '../../common/guards/super-admin.guard';
|
||||
AdminRedeemService,
|
||||
AdminDeliveriesService,
|
||||
AdminHqAccountsService,
|
||||
AdminProductsService,
|
||||
SuperAdminGuard,
|
||||
],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user