v3数据表更改
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { IamModule } from '../iam/iam.module';
|
||||
import { ResourceService } from './resource.service';
|
||||
import { EventService } from './event.service';
|
||||
import { TicketService } from './ticket.service';
|
||||
import { ThirdPartyLogService } from './third-party-log.service';
|
||||
import { ResourceController } from './resource.controller';
|
||||
import { EventController } from './event.controller';
|
||||
import { TicketController } from './ticket.controller';
|
||||
import { ThirdPartyLogController } from './third-party-log.controller';
|
||||
|
||||
@Module({
|
||||
imports: [IamModule],
|
||||
controllers: [ResourceController, EventController, TicketController, ThirdPartyLogController],
|
||||
providers: [ResourceService, EventService, TicketService, ThirdPartyLogService],
|
||||
exports: [ResourceService, EventService, TicketService],
|
||||
})
|
||||
export class CommonModule {}
|
||||
@@ -0,0 +1,178 @@
|
||||
import { IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class UploadTokenDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
bizType: string;
|
||||
|
||||
@IsString()
|
||||
@IsIn(['IMAGE', 'VIDEO', 'FILE'])
|
||||
mediaType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export class RegisterResourceDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
ownerType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
ownerId: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
bizType: string;
|
||||
|
||||
@IsString()
|
||||
@IsIn(['IMAGE', 'VIDEO', 'FILE'])
|
||||
mediaType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
ossKey: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
url: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
ossBucket?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
fileName?: string;
|
||||
|
||||
@IsOptional()
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export class UpdateResourceDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
url?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['IMAGE', 'VIDEO', 'FILE'])
|
||||
mediaType?: string;
|
||||
|
||||
@IsOptional()
|
||||
sortOrder?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['ACTIVE', 'DELETED'])
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export class CreateEventDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
eventType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
refType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
refId: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
actorType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
actorId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param1?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param1Desc?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param2?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param2Desc?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param3?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param3Desc?: string;
|
||||
|
||||
@IsOptional()
|
||||
amount1?: number;
|
||||
|
||||
@IsOptional()
|
||||
amount2?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
|
||||
@IsOptional()
|
||||
extraJson?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export class CreateTicketDto {
|
||||
@IsString()
|
||||
@IsIn(['REFUND', 'RESHIPMENT', 'ALERT'])
|
||||
ticketType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
refType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
refId: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param1?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
param1Desc?: string;
|
||||
}
|
||||
|
||||
export class UpdateTicketStatusDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
status: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class AssignTicketDto {
|
||||
@IsString()
|
||||
@IsIn(['HQ', 'PARTNER', 'SYSTEM'])
|
||||
operatorType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
operatorId: string;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsIn, IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
|
||||
|
||||
export class PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page?: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(100)
|
||||
pageSize?: number = 20;
|
||||
}
|
||||
|
||||
export class ResourceListQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
ownerType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
ownerId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
bizType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(['ACTIVE', 'DELETED'])
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export class EventListQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
refType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
refId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
eventType?: string;
|
||||
}
|
||||
|
||||
export class EventTimelineQueryDto {
|
||||
@IsString()
|
||||
refType: string;
|
||||
|
||||
@IsString()
|
||||
refId: string;
|
||||
}
|
||||
|
||||
export class TicketListQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
ticketType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
status?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
refType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
refId?: string;
|
||||
}
|
||||
|
||||
export class ThirdPartyLogQueryDto extends PaginationQueryDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
provider?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
scene?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
refType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
refId?: string;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { EventService } from './event.service';
|
||||
import { EventListQueryDto, EventTimelineQueryDto } from './dto/common-query.dto';
|
||||
import { CreateEventDto } from './dto/common-mutate.dto';
|
||||
|
||||
@Controller('common/events')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class EventController {
|
||||
constructor(private readonly service: EventService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateEventDto) {
|
||||
return this.service.create(dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: EventListQueryDto) {
|
||||
return this.service.list(query);
|
||||
}
|
||||
|
||||
@Get('timeline')
|
||||
timeline(@Query() query: EventTimelineQueryDto) {
|
||||
return this.service.timeline(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import type { ActorType, EventType, ResourceBizType, ResourceMediaType, ResourceOwnerType } from '@prisma/client';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import type { EventListQueryDto, EventTimelineQueryDto } from './dto/common-query.dto';
|
||||
import type { CreateEventDto } from './dto/common-mutate.dto';
|
||||
|
||||
@Injectable()
|
||||
export class EventService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async create(dto: CreateEventDto) {
|
||||
const event = await this.prisma.commonEvent.create({
|
||||
data: {
|
||||
eventType: dto.eventType as EventType,
|
||||
refType: dto.refType,
|
||||
refId: BigInt(dto.refId),
|
||||
actorType: dto.actorType as ActorType | undefined,
|
||||
actorId: dto.actorId ? BigInt(dto.actorId) : undefined,
|
||||
status: dto.status,
|
||||
param1: dto.param1,
|
||||
param1Desc: dto.param1Desc,
|
||||
param2: dto.param2,
|
||||
param2Desc: dto.param2Desc,
|
||||
param3: dto.param3,
|
||||
param3Desc: dto.param3Desc,
|
||||
amount1: dto.amount1,
|
||||
amount2: dto.amount2,
|
||||
remark: dto.remark,
|
||||
extraJson: dto.extraJson as Prisma.InputJsonValue | undefined,
|
||||
},
|
||||
});
|
||||
return serializeBigInt(event);
|
||||
}
|
||||
|
||||
async list(query: EventListQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.CommonEventWhereInput = {};
|
||||
if (query.refType) where.refType = query.refType;
|
||||
if (query.refId) where.refId = BigInt(query.refId);
|
||||
if (query.eventType) where.eventType = query.eventType as Prisma.EnumEventTypeFilter['equals'];
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.commonEvent.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.commonEvent.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
}
|
||||
|
||||
async timeline(query: EventTimelineQueryDto) {
|
||||
const items = await this.prisma.commonEvent.findMany({
|
||||
where: { refType: query.refType, refId: BigInt(query.refId) },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
take: 200,
|
||||
});
|
||||
return serializeBigInt(items);
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const event = await this.prisma.commonEvent.findUnique({ where: { id } });
|
||||
if (!event) throw new NotFoundException('事件不存在');
|
||||
return serializeBigInt(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Body, Controller, Delete, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { ResourceService } from './resource.service';
|
||||
import { ResourceListQueryDto } from './dto/common-query.dto';
|
||||
import { RegisterResourceDto, UpdateResourceDto, UploadTokenDto } from './dto/common-mutate.dto';
|
||||
|
||||
@Controller('common/resources')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class ResourceController {
|
||||
constructor(private readonly service: ResourceService) {}
|
||||
|
||||
@Post('upload-token')
|
||||
uploadToken(@Body() dto: UploadTokenDto) {
|
||||
return this.service.getUploadToken(dto);
|
||||
}
|
||||
|
||||
@Post()
|
||||
register(@Body() dto: RegisterResourceDto) {
|
||||
return this.service.register(dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: ResourceListQueryDto) {
|
||||
return this.service.list(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
update(@Param('id') id: string, @Body() dto: UpdateResourceDto) {
|
||||
return this.service.update(BigInt(id), dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string) {
|
||||
return this.service.remove(BigInt(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { ThirdPartyLogService } from './third-party-log.service';
|
||||
import { ThirdPartyLogQueryDto } from './dto/common-query.dto';
|
||||
|
||||
@Controller('common/third-party-logs')
|
||||
@UseGuards(HqAuthGuard)
|
||||
export class ThirdPartyLogController {
|
||||
constructor(private readonly service: ThirdPartyLogService) {}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: ThirdPartyLogQueryDto) {
|
||||
return this.service.list(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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 type { ThirdPartyLogQueryDto } from './dto/common-query.dto';
|
||||
|
||||
@Injectable()
|
||||
export class ThirdPartyLogService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async list(query: ThirdPartyLogQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.LogThirdPartyWhereInput = {};
|
||||
if (query.provider) where.provider = query.provider as Prisma.EnumThirdPartyProviderFilter['equals'];
|
||||
if (query.scene) where.scene = { contains: query.scene };
|
||||
if (query.refType) where.refType = query.refType;
|
||||
if (query.refId) where.refId = BigInt(query.refId);
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.logThirdParty.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.logThirdParty.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const log = await this.prisma.logThirdParty.findUnique({ where: { id } });
|
||||
if (!log) throw new NotFoundException('日志不存在');
|
||||
return serializeBigInt(log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Body, Controller, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { TicketService } from './ticket.service';
|
||||
import { TicketListQueryDto } from './dto/common-query.dto';
|
||||
import { AssignTicketDto, CreateTicketDto, UpdateTicketStatusDto } from './dto/common-mutate.dto';
|
||||
|
||||
@Controller('common/tickets')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class TicketController {
|
||||
constructor(private readonly service: TicketService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() dto: CreateTicketDto) {
|
||||
return this.service.create(dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: TicketListQueryDto) {
|
||||
return this.service.list(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
}
|
||||
|
||||
@Put(':id/status')
|
||||
updateStatus(@Param('id') id: string, @Body() dto: UpdateTicketStatusDto) {
|
||||
return this.service.updateStatus(BigInt(id), dto);
|
||||
}
|
||||
|
||||
@Put(':id/assign')
|
||||
@UseGuards(HqAuthGuard)
|
||||
assign(@Param('id') id: string, @Body() dto: AssignTicketDto) {
|
||||
return this.service.assign(BigInt(id), dto);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import type { ActorType, TicketType } from '@prisma/client';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import type { TicketListQueryDto } from './dto/common-query.dto';
|
||||
import type { AssignTicketDto, CreateTicketDto, UpdateTicketStatusDto } from './dto/common-mutate.dto';
|
||||
|
||||
function generateTicketNo() {
|
||||
return `TK${Date.now()}${Math.floor(Math.random() * 900 + 100)}`;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TicketService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async create(dto: CreateTicketDto) {
|
||||
const ticket = await this.prisma.commonTicket.create({
|
||||
data: {
|
||||
ticketNo: generateTicketNo(),
|
||||
ticketType: dto.ticketType as TicketType,
|
||||
refType: dto.refType,
|
||||
refId: BigInt(dto.refId),
|
||||
remark: dto.remark,
|
||||
param1: dto.param1,
|
||||
param1Desc: dto.param1Desc,
|
||||
},
|
||||
});
|
||||
return serializeBigInt(ticket);
|
||||
}
|
||||
|
||||
async list(query: TicketListQueryDto) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
const where: Prisma.CommonTicketWhereInput = {};
|
||||
if (query.ticketType) where.ticketType = query.ticketType as Prisma.EnumTicketTypeFilter['equals'];
|
||||
if (query.status) where.status = query.status;
|
||||
if (query.refType) where.refType = query.refType;
|
||||
if (query.refId) where.refId = BigInt(query.refId);
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.commonTicket.findMany({
|
||||
where,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
}),
|
||||
this.prisma.commonTicket.count({ where }),
|
||||
]);
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
}
|
||||
|
||||
async detail(id: bigint) {
|
||||
const ticket = await this.prisma.commonTicket.findUnique({ where: { id } });
|
||||
if (!ticket) throw new NotFoundException('工单不存在');
|
||||
return serializeBigInt(ticket);
|
||||
}
|
||||
|
||||
async updateStatus(id: bigint, dto: UpdateTicketStatusDto) {
|
||||
await this.detail(id);
|
||||
const ticket = await this.prisma.commonTicket.update({
|
||||
where: { id },
|
||||
data: {
|
||||
status: dto.status,
|
||||
remark: dto.remark,
|
||||
completedAt: ['COMPLETED', 'CLOSED', 'RESOLVED'].includes(dto.status) ? new Date() : undefined,
|
||||
},
|
||||
});
|
||||
return serializeBigInt(ticket);
|
||||
}
|
||||
|
||||
async assign(id: bigint, dto: AssignTicketDto) {
|
||||
await this.detail(id);
|
||||
const ticket = await this.prisma.commonTicket.update({
|
||||
where: { id },
|
||||
data: {
|
||||
operatorType: dto.operatorType as ActorType,
|
||||
operatorId: BigInt(dto.operatorId),
|
||||
},
|
||||
});
|
||||
return serializeBigInt(ticket);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user