Files
yara724api/src/expert-blame/expert-blame.v2.controller.ts
SepehrYahyaee 2bdd0d507e YARA-951
2026-05-24 13:34:43 +03:30

182 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
Body,
Controller,
Get,
HttpException,
InternalServerErrorException,
Param,
Post,
Put,
Query,
UseGuards,
} from "@nestjs/common";
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from "@nestjs/swagger";
import { ExpertFileAssignResultDto } from "src/common/dto/expert-file-assign-result.dto";
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
import { RolesGuard } from "src/auth/guards/role.guard";
import { Roles } from "src/decorators/roles.decorator";
import { CurrentUser } from "src/decorators/user.decorator";
import { RoleEnum } from "src/Types&Enums/role.enum";
import { ExpertBlameService } from "./expert-blame.service";
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
import { AllRequestDtoRsV2 } from "./dto/all-request.dto";
import { SubmitReplyDto } from "./dto/reply.dto";
import { ResendRequestDto } from "./dto/resend.dto";
@ApiTags("expert-blame-panel (v2)")
@Controller("v2/expert-blame")
@ApiBearerAuth()
@UseGuards(LocalActorAuthGuard, RolesGuard)
@Roles(RoleEnum.EXPERT, RoleEnum.FIELD_EXPERT)
export class ExpertBlameV2Controller {
constructor(private readonly expertBlameService: ExpertBlameService) { }
@Get()
@ApiOperation({
summary: "List blame cases for field expert (V2)",
description:
"Optional query: `search`, `sortBy` (publicId | createdAt | requestNo | status), `sortOrder`, `page`, `limit`. Without `page`/`limit`, returns the full filtered list.",
})
async findAll(
@CurrentUser() actor: any,
@Query() query: ListQueryV2Dto,
): Promise<AllRequestDtoRsV2> {
try {
return await this.expertBlameService.findAllV2(actor, query);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to list blame cases",
);
}
}
@Get("report/status-counts")
@ApiOperation({
summary: "Count blame cases by grouped status bucket (this field expert)",
description:
"Counts only files in the acting experts portfolio: expertFileActivities (checked or handled), workflow lock, expert decision, or expert-initiated blame. IN_PROGRESS groups OPEN and WAITING_FOR_SECOND_PARTY. Other keys match CaseStatus. Does not include the open WAITING_FOR_EXPERT queue unless this expert has touched the file.",
})
async getStatusReportBucketsV2(@CurrentUser() actor: any) {
try {
return await this.expertBlameService.getStatusReportBucketsV2(actor);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to load blame status report",
);
}
}
@Get(":id")
@ApiParam({ name: "id", description: "Blame case request id" })
async findOne(@Param("id") id: string, @CurrentUser() actor: any) {
try {
return await this.expertBlameService.findOneV2(id, actor);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to get blame case details",
);
}
}
@Post(":id/assign")
@ApiOperation({
summary: "Check availability and assign blame case to this expert",
description:
"Call before opening a case from the list. Returns `assigned` when the case was free and is now locked to you, `already_assigned_to_you` when you already hold the lock, or **409** with `status=locked` when another expert is reviewing. Does not replace PUT lock — use this for list UI availability.",
})
@ApiParam({ name: "id", description: "Blame case request id" })
@ApiResponse({ status: 200, type: ExpertFileAssignResultDto })
@ApiResponse({
status: 409,
description: "Another expert is processing this case",
type: ExpertFileAssignResultDto,
})
async assignForReview(@Param("id") id: string, @CurrentUser() actor: any) {
try {
return await this.expertBlameService.assignBlameCaseForReviewV2(id, actor);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to assign blame case",
);
}
}
@Put("lock/:id")
@ApiOperation({
summary: "Lock blame case for review (legacy)",
description:
"Same assignment logic as POST `:id/assign`, but returns the legacy `{ _id, lock }` shape and maps conflicts to 400 instead of 409.",
})
@ApiParam({ name: "id", description: "Blame case request id" })
async lockRequest(@Param("id") id: string, @CurrentUser() actor: any) {
try {
return await this.expertBlameService.lockRequestV2(id, actor);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to lock blame case",
);
}
}
@Put("reply/resend/:id")
@ApiParam({ name: "id", description: "Blame case request id" })
@ApiBody({ type: ResendRequestDto })
async resendRequest(
@Param("id") id: string,
@Body() body: ResendRequestDto,
@CurrentUser() actor: any,
) {
try {
return await this.expertBlameService.resendRequestV2(id, body, actor);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error
? error.message
: "Failed to request document resend",
);
}
}
@Put("reply/submit/:id")
@ApiParam({ name: "id", description: "Blame case request id" })
@ApiBody({ type: SubmitReplyDto })
async submitReply(
@Param("id") id: string,
@Body() body: SubmitReplyDto,
@CurrentUser() actor: any,
) {
try {
return await this.expertBlameService.replyRequestV2(id, body, actor);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to submit expert reply",
);
}
}
@Put("reply/inPerson/:id")
@ApiParam({ name: "id", description: "Blame case request id" })
@ApiBody({ type: SubmitReplyDto })
async submitInPerson(
@Param("id") id: string,
@Body() body: SubmitReplyDto,
@CurrentUser() actor: any,
) {
try {
return await this.expertBlameService.submitInPerson(id, actor.sub);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to submit expert reply for in person",
);
}
}
}