YARA-836 + 2 more sms

This commit is contained in:
SepehrYahyaee
2026-04-25 16:53:56 +03:30
parent 9c87927e6c
commit 5d2227b00b
7 changed files with 156 additions and 4 deletions

View File

@@ -25,7 +25,7 @@ export class AiService implements OnModuleInit {
username: process.env.AI_USERNAME,
password: process.env.AI_PASSWORD,
},
timeout: 30000, // 30 second timeout
timeout: 1000, // 30 second timeout
};
private get profileOptions(): AxiosRequestConfig {

View File

@@ -1122,7 +1122,31 @@ export class ExpertBlameService {
requestId,
);
// TODO: Send notifications to parties (SMS/Push) about required documents
const requestIdToken = String(request._id);
const partyPhoneByUserId = new Map<string, string>();
for (const p of request.parties || []) {
const uid = p?.person?.userId ? String(p.person.userId) : "";
const phone = p?.person?.phoneNumber;
if (uid && typeof phone === "string" && phone.length > 0) {
partyPhoneByUserId.set(uid, phone);
}
}
for (const p of partyResendRequests) {
const uid = String((p as any).partyId);
const phone = partyPhoneByUserId.get(uid);
if (!phone) continue;
const role =
String(request.parties?.find((x: any) => String(x?.person?.userId) === uid)?.role) ===
"SECOND"
? "SECOND"
: "FIRST";
await this.smsOrchestrationService.sendResendDocumentsNotice({
receptor: phone,
fileKind: "blame",
publicId: request.publicId,
link: this.smsOrchestrationService.buildBlamePartyLink(requestIdToken, role),
});
}
return {
requestId: String(request._id),
@@ -1277,6 +1301,27 @@ export class ExpertBlameService {
);
}
if (request.type === BlameRequestType.THIRD_PARTY) {
const requestIdToken = String(request._id);
const expertLastName =
actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس";
for (const p of request.parties || []) {
const phone = p?.person?.phoneNumber;
if (!phone || typeof phone !== "string") continue;
const role = String(p?.role) === "SECOND" ? "SECOND" : "FIRST";
await this.smsOrchestrationService.sendSignatureReviewNotice({
receptor: phone,
fileKind: "blame",
publicId: request.publicId,
expertLastName,
link: this.smsOrchestrationService.buildBlamePartyLink(
requestIdToken,
role,
),
});
}
}
return {
requestId: String(request._id),
status: CaseStatus.WAITING_FOR_SIGNATURES,

View File

@@ -13,6 +13,7 @@ import { ClientModule } from "src/client/client.module";
import { ExpertBlameModule } from "src/expert-blame/expert-blame.module";
import { RequestManagementModule } from "src/request-management/request-management.module";
import { SandHubModule } from "src/sand-hub/sand-hub.module";
import { SmsOrchestrationModule } from "src/sms-orchestration/sms-orchestration.module";
import { UsersModule } from "src/users/users.module";
import { ExpertClaimController } from "./expert-claim.controller";
import { ExpertClaimV2Controller } from "./expert-claim.v2.controller";
@@ -31,6 +32,7 @@ import { ExpertClaimService } from "./expert-claim.service";
ExpertBlameModule,
AiModule,
RequestManagementModule,
SmsOrchestrationModule,
UsersModule,
ClientModule,
],

View File

@@ -65,6 +65,7 @@ import {
import { resendRequestHasPayload } from "src/helpers/claim-expert-resend";
import { snapshotFromDamageExpert } from "src/helpers/expert-profile-snapshot";
import { DamageExpertModel } from "src/users/entities/schema/damage-expert.schema";
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
@Injectable()
export class ExpertClaimService {
@@ -187,6 +188,7 @@ export class ExpertClaimService {
private readonly claimFactorsImageDbService: ClaimFactorsImageDbService,
private readonly claimRequiredDocumentDbService: ClaimRequiredDocumentDbService,
private readonly branchDbService: BranchDbService,
private readonly smsOrchestrationService: SmsOrchestrationService,
) { }
/** Load immutable profile fields from `damage-expert` for the acting expert. */
@@ -282,13 +284,16 @@ export class ExpertClaimService {
private blameFileContextForExpert(blame: any): {
blameRequestType?: BlameRequestType;
carBodyFirstForm?: { car?: boolean; object?: boolean };
blameStatus?: string
} {
if (!blame?.type) return {};
const blameRequestType = blame.type as BlameRequestType;
const out: {
blameRequestType?: BlameRequestType;
carBodyFirstForm?: { car?: boolean; object?: boolean };
blameStatus?: string;
} = { blameRequestType };
out.blameStatus = blame.blameStatus;
if (blameRequestType !== BlameRequestType.CAR_BODY) return out;
const parties = blame.parties;
@@ -297,6 +302,7 @@ export class ExpertClaimService {
const first =
parties.find((p: any) => p?.role === PartyRole.FIRST) ?? parties[0];
const cbf = first?.carBodyFirstForm;
delete out.blameStatus
if (cbf && typeof cbf === "object") {
out.carBodyFirstForm = {
...(typeof cbf.car === "boolean" ? { car: cbf.car } : {}),
@@ -2219,6 +2225,30 @@ export class ExpertClaimService {
},
});
if (claim.blameRequestId) {
const blame = await this.blameRequestDbService.findById(
claim.blameRequestId.toString(),
);
if (blame?.type === BlameRequestType.THIRD_PARTY) {
const ownerUserId = claim.owner?.userId ? String(claim.owner.userId) : "";
const ownerParty = (blame.parties || []).find(
(p: any) =>
p?.person?.userId && String(p.person.userId) === ownerUserId,
);
const ownerPhone = ownerParty?.person?.phoneNumber;
if (ownerPhone && typeof ownerPhone === "string") {
await this.smsOrchestrationService.sendResendDocumentsNotice({
receptor: ownerPhone,
fileKind: "claim",
publicId: claim.publicId,
link: this.smsOrchestrationService.buildClaimLink(
String(claim._id),
),
});
}
}
}
return {
claimRequestId,
status: ClaimCaseStatus.WAITING_FOR_USER_RESEND,
@@ -2371,6 +2401,33 @@ export class ExpertClaimService {
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, updatePayload);
if (!needsFactorUpload && claim.blameRequestId) {
const blame = await this.blameRequestDbService.findById(
claim.blameRequestId.toString(),
);
if (blame?.type === BlameRequestType.THIRD_PARTY) {
const ownerUserId = claim.owner?.userId ? String(claim.owner.userId) : "";
const ownerParty = (blame.parties || []).find(
(p: any) =>
p?.person?.userId && String(p.person.userId) === ownerUserId,
);
const ownerPhone = ownerParty?.person?.phoneNumber;
if (ownerPhone && typeof ownerPhone === "string") {
const expertLastName =
actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس";
await this.smsOrchestrationService.sendSignatureReviewNotice({
receptor: ownerPhone,
fileKind: "claim",
publicId: claim.publicId,
expertLastName,
link: this.smsOrchestrationService.buildClaimLink(
String(claim._id),
),
});
}
}
}
return {
claimRequestId,
status: nextCaseStatus,
@@ -2519,7 +2576,7 @@ export class ExpertClaimService {
blameIds.length > 0
? ((await this.blameRequestDbService.find(
{ _id: { $in: blameIds.map((id) => new Types.ObjectId(id)) } },
{ lean: true, select: "type parties expert.decision" },
{ lean: true, select: "type parties expert.decision blameStatus" },
)) as any[])
: [];
const blameById = new Map<string, any>(

View File

@@ -38,7 +38,12 @@ async function bootstrap() {
.addBearerAuth()
.build();
const docs = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("/docs", app, docs);
SwaggerModule.setup("/docs", app, docs, {
swaggerOptions: {
persistAuthorization: true,
docExpansion: "none",
},
});
await app.listen(process.env.PORT);
}

View File

@@ -22,6 +22,7 @@ export interface VerifyLookUpMessage {
token: string;
token2?: string;
token3?: string;
token10?: string;
receptor: string;
}

View File

@@ -10,6 +10,7 @@ type TemplateArgs = {
token: string;
token2?: string;
token3?: string;
token10?: string;
};
@Injectable()
@@ -40,6 +41,15 @@ export class SmsOrchestrationService implements OnModuleInit {
return `${process.env.URL}/${frontendRoute}?token=${requestId}`;
}
buildBlamePartyLink(requestId: string, partyRole: "FIRST" | "SECOND"): string {
const route = partyRole === "SECOND" ? "user2" : "user";
return `${process.env.URL}/${route}?token=${requestId}`;
}
buildClaimLink(claimRequestId: string): string {
return `${process.env.URL}/claim?token=${claimRequestId}`;
}
async sendInviteLink(phoneNumber: string, link: string): Promise<boolean> {
return this.sendTemplate({
template: "yara724-invite-link",
@@ -124,6 +134,38 @@ export class SmsOrchestrationService implements OnModuleInit {
});
}
async sendResendDocumentsNotice(params: {
receptor: string;
fileKind: "blame" | "claim";
publicId: string;
link: string;
}): Promise<boolean> {
return this.sendTemplate({
template: "yara-resend-documents",
receptor: params.receptor,
token: params.fileKind === "blame" ? "تصادف" : "خسارت",
token2: params.publicId,
token3: params.link,
});
}
async sendSignatureReviewNotice(params: {
receptor: string;
fileKind: "blame" | "claim";
publicId: string;
expertLastName: string;
link: string;
}): Promise<boolean> {
return this.sendTemplate({
template: "yara-signature",
receptor: params.receptor,
token: params.fileKind === "blame" ? "تصادف" : "خسارت",
token2: params.publicId,
token3: params.expertLastName || "کارشناس",
token10: params.link,
});
}
private async sendTemplate(args: TemplateArgs): Promise<boolean> {
try {
await this.smsGatewayService.verifyLookUp(args);