Files
dukang/server/dukang-api/src/modules/common/resource.service.ts
T
2026-07-01 14:36:50 +08:00

99 lines
3.7 KiB
TypeScript

import { Injectable, NotFoundException } from '@nestjs/common';
import type { ResourceBizType, ResourceMediaType, ResourceOwnerType } from '@prisma/client';
import { Prisma } from '@prisma/client';
import { randomUUID } from 'crypto';
import { PrismaService } from '../../common/prisma/prisma.module';
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
import type { ResourceListQueryDto } from './dto/common-query.dto';
import type { RegisterResourceDto, UpdateResourceDto, UploadTokenDto } from './dto/common-mutate.dto';
@Injectable()
export class ResourceService {
constructor(private readonly prisma: PrismaService) {}
getUploadToken(dto: UploadTokenDto) {
const bucket = process.env.OSS_BUCKET || 'mock-dukang';
const ext = dto.fileName.includes('.') ? dto.fileName.split('.').pop() : 'bin';
const key = `uploads/${dto.bizType.toLowerCase()}/${Date.now()}-${randomUUID().slice(0, 8)}.${ext}`;
const cdnBase = process.env.OSS_CDN_BASE || 'https://mock-cdn.dukang.local';
return {
bucket,
region: process.env.OSS_REGION || 'oss-cn-hangzhou',
ossKey: key,
url: `${cdnBase}/${key}`,
mock: true,
expireAt: new Date(Date.now() + 15 * 60 * 1000).toISOString(),
mediaType: dto.mediaType,
bizType: dto.bizType,
};
}
async register(dto: RegisterResourceDto) {
const resource = await this.prisma.commonResource.create({
data: {
ownerType: dto.ownerType as ResourceOwnerType,
ownerId: BigInt(dto.ownerId),
bizType: dto.bizType as ResourceBizType,
mediaType: dto.mediaType as ResourceMediaType,
ossBucket: dto.ossBucket ?? process.env.OSS_BUCKET ?? 'mock-dukang',
ossKey: dto.ossKey,
url: dto.url,
fileName: dto.fileName,
sortOrder: dto.sortOrder ?? 0,
},
});
return serializeBigInt(resource);
}
async list(query: ResourceListQueryDto) {
const page = query.page ?? 1;
const pageSize = query.pageSize ?? 20;
const where: Prisma.CommonResourceWhereInput = {
status: (query.status ?? 'ACTIVE') as Prisma.EnumResourceStatusFilter['equals'],
};
if (query.ownerType) where.ownerType = query.ownerType as Prisma.EnumResourceOwnerTypeFilter['equals'];
if (query.ownerId) where.ownerId = BigInt(query.ownerId);
if (query.bizType) where.bizType = query.bizType as Prisma.EnumResourceBizTypeFilter['equals'];
const [items, total] = await Promise.all([
this.prisma.commonResource.findMany({
where,
orderBy: [{ sortOrder: 'asc' }, { createdAt: 'desc' }],
skip: (page - 1) * pageSize,
take: pageSize,
}),
this.prisma.commonResource.count({ where }),
]);
return serializeBigInt({ items, total, page, pageSize });
}
async detail(id: bigint) {
const resource = await this.prisma.commonResource.findUnique({ where: { id } });
if (!resource) throw new NotFoundException('资源不存在');
return serializeBigInt(resource);
}
async update(id: bigint, dto: UpdateResourceDto) {
await this.detail(id);
const resource = await this.prisma.commonResource.update({
where: { id },
data: {
...(dto.url !== undefined ? { url: dto.url, ossKey: dto.url } : {}),
...(dto.mediaType !== undefined ? { mediaType: dto.mediaType as 'IMAGE' | 'VIDEO' | 'FILE' } : {}),
...(dto.sortOrder !== undefined ? { sortOrder: dto.sortOrder } : {}),
...(dto.status !== undefined ? { status: dto.status as 'ACTIVE' | 'DELETED' } : {}),
},
});
return serializeBigInt(resource);
}
async remove(id: bigint) {
await this.detail(id);
await this.prisma.commonResource.update({
where: { id },
data: { status: 'DELETED' },
});
return { ok: true };
}
}