forked from Yara724/api
script updated , inquiry field added to blame case and claim case
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
/*
|
||||
* One-time script to replace mocked blameCases party vehicle inquiry data.
|
||||
* THIRD_PARTY cases use Tejarat block inquiry; CAR_BODY cases use both
|
||||
* Tejarat third-party block inquiry and car-body inquiry.
|
||||
* Tejarat third-party block inquiry and car-body inquiry. All cases also
|
||||
* refresh available party personal inquiries into inquiries.person.
|
||||
*
|
||||
* Defaults to DRY_RUN=true. Set DRY_RUN=false to write changes.
|
||||
*/
|
||||
@@ -61,10 +62,15 @@ const config = {
|
||||
carBodyUrl:
|
||||
process.env.TEJARAT_CAR_BODY_URL ||
|
||||
"http://82.99.202.245:3027/block-inquiry-tejarat/badane",
|
||||
personUrl:
|
||||
process.env.TEJARAT_PERSON_URL ||
|
||||
"http://82.99.202.245:3027/personal-inquiry/tejarat-no",
|
||||
thirdPartyToken:
|
||||
process.env.TEJARAT_THIRD_PARTY_TOKEN || process.env.TEJARAT_TOKEN || "",
|
||||
carBodyToken:
|
||||
process.env.TEJARAT_CAR_BODY_TOKEN || process.env.TEJARAT_TOKEN || "",
|
||||
personToken:
|
||||
process.env.TEJARAT_PERSON_TOKEN || process.env.TEJARAT_TOKEN || "",
|
||||
rateLimitPerMinute: Number(process.env.RATE_LIMIT_PER_MINUTE || 5),
|
||||
retryEnabled: String(process.env.RETRY_ENABLED ?? "true").toLowerCase() !== "false",
|
||||
retryCount: Number(process.env.RETRY_COUNT || 3),
|
||||
@@ -124,6 +130,7 @@ async function main() {
|
||||
requestNo: 1,
|
||||
type: 1,
|
||||
parties: 1,
|
||||
inquiries: 1,
|
||||
},
|
||||
})
|
||||
.sort({ createdAt: 1, _id: 1 });
|
||||
@@ -136,6 +143,9 @@ async function main() {
|
||||
partiesInquired: 0,
|
||||
partiesSkipped: 0,
|
||||
partiesFailed: 0,
|
||||
personInquired: 0,
|
||||
personSkipped: 0,
|
||||
personFailed: 0,
|
||||
};
|
||||
|
||||
for await (const doc of cursor) {
|
||||
@@ -145,7 +155,9 @@ async function main() {
|
||||
console.log(`currently inquiry for doc with ${label} and requestId ${requestId}`);
|
||||
|
||||
const parties = Array.isArray(doc.parties) ? clone(doc.parties) : [];
|
||||
const inquiries = doc.inquiries && typeof doc.inquiries === "object" ? clone(doc.inquiries) : {};
|
||||
let docChanged = false;
|
||||
let inquiriesChanged = false;
|
||||
|
||||
for (let index = 0; index < parties.length; index += 1) {
|
||||
const party = parties[index];
|
||||
@@ -155,61 +167,101 @@ async function main() {
|
||||
if (!input.ok) {
|
||||
summary.partiesSkipped += 1;
|
||||
console.warn(`[skip] ${partyLabel}: ${input.reason}`);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let nextParty = party;
|
||||
let partyChanged = false;
|
||||
|
||||
let nextParty = party;
|
||||
let partyChanged = false;
|
||||
|
||||
for (const request of input.requests) {
|
||||
console.log(
|
||||
`[request] ${partyLabel} type=${request.type} body=${JSON.stringify(request.body)}`,
|
||||
);
|
||||
|
||||
try {
|
||||
const inquiryResponse = await inquiryWithRetry(request.type, request.body);
|
||||
const successful = isInquirySuccessful(request.type, inquiryResponse);
|
||||
for (const request of input.requests) {
|
||||
console.log(
|
||||
`[response] ${partyLabel} type=${request.type} successful=${successful} body=${JSON.stringify(inquiryResponse)}`,
|
||||
`[request] ${partyLabel} type=${request.type} body=${JSON.stringify(request.body)}`,
|
||||
);
|
||||
|
||||
if (!successful) {
|
||||
summary.partiesFailed += 1;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const inquiryResponse = await inquiryWithRetry(request.type, request.body);
|
||||
const successful = isInquirySuccessful(request.type, inquiryResponse);
|
||||
console.log(
|
||||
`[response] ${partyLabel} type=${request.type} successful=${successful} body=${JSON.stringify(inquiryResponse)}`,
|
||||
);
|
||||
|
||||
nextParty = applyInquiryToParty(request.type, nextParty, inquiryResponse, input.plate);
|
||||
summary.partiesInquired += 1;
|
||||
partyChanged = true;
|
||||
} catch (error) {
|
||||
summary.partiesFailed += 1;
|
||||
console.error(`[error] ${partyLabel} type=${request.type}: ${error.message}`);
|
||||
if (!successful) {
|
||||
summary.partiesFailed += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
nextParty = applyInquiryToParty(request.type, nextParty, inquiryResponse, input.plate);
|
||||
summary.partiesInquired += 1;
|
||||
partyChanged = true;
|
||||
} catch (error) {
|
||||
summary.partiesFailed += 1;
|
||||
console.error(`[error] ${partyLabel} type=${request.type}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (partyChanged) {
|
||||
parties[index] = nextParty;
|
||||
docChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (partyChanged) {
|
||||
parties[index] = nextParty;
|
||||
docChanged = true;
|
||||
const personInput = buildPersonInquiryInput(party);
|
||||
if (!personInput.ok) {
|
||||
summary.personSkipped += 1;
|
||||
console.warn(`[skip] ${partyLabel} person: ${personInput.reason}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`[request] ${partyLabel} type=PERSON body=${JSON.stringify(personInput.body)}`);
|
||||
try {
|
||||
const personResponse = await inquiryWithRetry("PERSON", personInput.body);
|
||||
const successful = isInquirySuccessful("PERSON", personResponse);
|
||||
console.log(
|
||||
`[response] ${partyLabel} type=PERSON successful=${successful} body=${JSON.stringify(personResponse)}`,
|
||||
);
|
||||
|
||||
if (!successful) {
|
||||
summary.personFailed += 1;
|
||||
applyPersonInquiryToCaseInquiries(inquiries, party, index, false, {}, personResponse);
|
||||
inquiriesChanged = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
applyPersonInquiryToCaseInquiries(
|
||||
inquiries,
|
||||
party,
|
||||
index,
|
||||
true,
|
||||
normalizePersonResponse(personResponse),
|
||||
);
|
||||
summary.personInquired += 1;
|
||||
inquiriesChanged = true;
|
||||
} catch (error) {
|
||||
summary.personFailed += 1;
|
||||
applyPersonInquiryToCaseInquiries(inquiries, party, index, false, {}, error);
|
||||
inquiriesChanged = true;
|
||||
console.error(`[error] ${partyLabel} type=PERSON: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!docChanged) {
|
||||
if (!docChanged && !inquiriesChanged) {
|
||||
console.log(`[doc] ${label} no changes`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.dryRun) {
|
||||
console.log(`[dry-run] ${label} would update parties array`);
|
||||
console.log(`[dry-run] ${label} would update parties/inquiries`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const updateSet = {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
if (docChanged) updateSet.parties = parties;
|
||||
if (inquiriesChanged) updateSet.inquiries = inquiries;
|
||||
|
||||
const result = await collection.updateOne(
|
||||
{ _id: doc._id },
|
||||
{
|
||||
$set: {
|
||||
parties,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
$set: updateSet,
|
||||
},
|
||||
);
|
||||
summary.docsChanged += result.modifiedCount;
|
||||
@@ -285,6 +337,40 @@ function buildCarBodyRequest(plate, serialLetter, nationalCode) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildPersonInquiryInput(party) {
|
||||
if (!party || typeof party !== "object") return { ok: false, reason: "party is empty" };
|
||||
const person = party.person && typeof party.person === "object" ? party.person : null;
|
||||
if (!person) return { ok: false, reason: "party.person is missing" };
|
||||
|
||||
const nationalCode =
|
||||
cleanString(person.nationalCodeOfInsurer) ||
|
||||
cleanString(person.nationalCodeOfDriver);
|
||||
const birthDate = firstPresent(
|
||||
person.insurerBirthday,
|
||||
person.driverBirthday,
|
||||
person.birthday,
|
||||
);
|
||||
const gregorianBirthdate = jalaliToGregorianDate(birthDate);
|
||||
|
||||
if (!nationalCode) {
|
||||
return { ok: false, reason: "nationalCodeOfInsurer/nationalCodeOfDriver missing" };
|
||||
}
|
||||
if (!gregorianBirthdate) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: `invalid insurerBirthday/driverBirthday=${cleanString(birthDate)}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
body: {
|
||||
nationalCode,
|
||||
birthdate: gregorianBirthdate,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function parsePlateId(plateId) {
|
||||
const value = cleanString(plateId);
|
||||
if (!value) return null;
|
||||
@@ -352,8 +438,10 @@ function extractPlate(party) {
|
||||
}
|
||||
|
||||
async function inquiryWithRetry(type, body) {
|
||||
const url = type === "CAR_BODY" ? config.carBodyUrl : config.thirdPartyUrl;
|
||||
const token = type === "CAR_BODY" ? config.carBodyToken : config.thirdPartyToken;
|
||||
const endpoint = getInquiryEndpoint(type);
|
||||
const url = endpoint.url;
|
||||
const token = endpoint.token;
|
||||
const accept = endpoint.accept;
|
||||
let lastError;
|
||||
|
||||
const maxAttempts = config.retryEnabled ? config.retryCount : 1;
|
||||
@@ -362,7 +450,7 @@ async function inquiryWithRetry(type, body) {
|
||||
await waitForRateLimit();
|
||||
try {
|
||||
console.log("[http] " + type + " attempt=" + attempt + "/" + maxAttempts);
|
||||
return await postJson(url, token, body);
|
||||
return await postJson(url, token, body, accept);
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
console.error(`[retry] ${type} attempt=${attempt} failed: ${error.message}`);
|
||||
@@ -375,11 +463,11 @@ async function inquiryWithRetry(type, body) {
|
||||
throw lastError;
|
||||
}
|
||||
|
||||
async function postJson(url, token, body) {
|
||||
async function postJson(url, token, body, accept = "application/json") {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
accept,
|
||||
authorization: `Bearer ${token}`,
|
||||
"content-type": "application/json",
|
||||
},
|
||||
@@ -395,7 +483,10 @@ async function postJson(url, token, body) {
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${JSON.stringify(data)}`);
|
||||
const error = new Error(`HTTP ${response.status}: ${JSON.stringify(data)}`);
|
||||
error.status = response.status;
|
||||
error.data = data;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -403,9 +494,20 @@ async function postJson(url, token, body) {
|
||||
|
||||
function isInquirySuccessful(type, response) {
|
||||
if (type === "CAR_BODY") return response && response.isSuccess === true && response.data;
|
||||
if (type === "PERSON") return response && response.status === 200 && response.data;
|
||||
return response && response.resultStatus === true;
|
||||
}
|
||||
|
||||
function getInquiryEndpoint(type) {
|
||||
if (type === "CAR_BODY") {
|
||||
return { url: config.carBodyUrl, token: config.carBodyToken, accept: "application/json" };
|
||||
}
|
||||
if (type === "PERSON") {
|
||||
return { url: config.personUrl, token: config.personToken, accept: "*/*" };
|
||||
}
|
||||
return { url: config.thirdPartyUrl, token: config.thirdPartyToken, accept: "application/json" };
|
||||
}
|
||||
|
||||
function applyInquiryToParty(type, party, response, originalPlate) {
|
||||
if (type === "CAR_BODY") return applyCarBodyInquiryToParty(party, response, originalPlate);
|
||||
return applyThirdPartyInquiryToParty(party, response, originalPlate);
|
||||
@@ -486,6 +588,51 @@ function applyCarBodyInquiryToParty(party, response, originalPlate) {
|
||||
return next;
|
||||
}
|
||||
|
||||
function applyPersonInquiryToCaseInquiries(inquiries, party, index, has, data, error) {
|
||||
if (!inquiries.person || typeof inquiries.person !== "object") {
|
||||
inquiries.person = {};
|
||||
}
|
||||
|
||||
const existingData =
|
||||
inquiries.person.data && typeof inquiries.person.data === "object" && !Array.isArray(inquiries.person.data)
|
||||
? inquiries.person.data
|
||||
: {};
|
||||
const existingError =
|
||||
inquiries.person.error && typeof inquiries.person.error === "object" && !Array.isArray(inquiries.person.error)
|
||||
? inquiries.person.error
|
||||
: {};
|
||||
const roleKey = party && party.role ? party.role : `party_${index}`;
|
||||
const nextData = { ...existingData };
|
||||
const nextError = { ...existingError };
|
||||
|
||||
if (has) {
|
||||
nextData[roleKey] = data || {};
|
||||
delete nextError[roleKey];
|
||||
} else {
|
||||
nextError[roleKey] = normalizeInquiryError(error);
|
||||
}
|
||||
|
||||
inquiries.person = {
|
||||
has: Object.keys(nextData).length > 0,
|
||||
data: nextData,
|
||||
...(Object.keys(nextError).length > 0 ? { error: nextError } : {}),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizePersonResponse(response) {
|
||||
return response && response.data ? response.data : response;
|
||||
}
|
||||
|
||||
function normalizeInquiryError(error) {
|
||||
if (!error) return undefined;
|
||||
return {
|
||||
message: error.message || String(error),
|
||||
status: error.status,
|
||||
data: error.data,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPlateId(plate) {
|
||||
if (!plate) return "";
|
||||
const serialLetter = NUMBER_TO_LETTER[String(plate.Plk2)] || cleanString(plate.Plk2);
|
||||
@@ -555,6 +702,111 @@ function normalizeCarBodyResponse(response, originalPlate) {
|
||||
};
|
||||
}
|
||||
|
||||
function jalaliToGregorianDate(input) {
|
||||
if (input === null || input === undefined) return null;
|
||||
|
||||
const raw = normalizeDigits(typeof input === "number" ? String(input) : String(input).trim());
|
||||
if (!raw) return null;
|
||||
|
||||
let year = 0;
|
||||
let month = 0;
|
||||
let day = 0;
|
||||
|
||||
const separated = raw.match(/^(\d{4})[\-/](\d{1,2})[\-/](\d{1,2})$/);
|
||||
if (separated) {
|
||||
year = parseInt(separated[1], 10);
|
||||
month = parseInt(separated[2], 10);
|
||||
day = parseInt(separated[3], 10);
|
||||
} else {
|
||||
const digits = raw.replace(/\D/g, "");
|
||||
if (digits.length !== 8) return null;
|
||||
year = parseInt(digits.slice(0, 4), 10);
|
||||
month = parseInt(digits.slice(4, 6), 10);
|
||||
day = parseInt(digits.slice(6, 8), 10);
|
||||
}
|
||||
|
||||
if (!year || !month || !day) return null;
|
||||
|
||||
if (year >= 1900) {
|
||||
const mm = String(month).padStart(2, "0");
|
||||
const dd = String(day).padStart(2, "0");
|
||||
const result = `${year}-${mm}-${dd}`;
|
||||
return isNaN(new Date(result).getTime()) ? null : result;
|
||||
}
|
||||
|
||||
return jalaliPartsToGregorian(year, month, day);
|
||||
}
|
||||
|
||||
function jalaliPartsToGregorian(jYear, jMonth, jDay) {
|
||||
const jy = jYear - 979;
|
||||
const jm = jMonth - 1;
|
||||
const jd = jDay - 1;
|
||||
|
||||
let jDayNo =
|
||||
365 * jy + Math.floor(jy / 33) * 8 + Math.floor(((jy % 33) + 3) / 4);
|
||||
|
||||
const jalaliMonthDays = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
|
||||
for (let i = 0; i < jm; i += 1) {
|
||||
jDayNo += jalaliMonthDays[i];
|
||||
}
|
||||
jDayNo += jd;
|
||||
|
||||
let gDayNo = jDayNo + 79;
|
||||
|
||||
let gy = 1600 + 400 * Math.floor(gDayNo / 146097);
|
||||
gDayNo %= 146097;
|
||||
|
||||
let leap = true;
|
||||
if (gDayNo >= 36525) {
|
||||
gDayNo -= 1;
|
||||
gy += 100 * Math.floor(gDayNo / 36524);
|
||||
gDayNo %= 36524;
|
||||
if (gDayNo >= 365) gDayNo += 1;
|
||||
else leap = false;
|
||||
}
|
||||
|
||||
gy += 4 * Math.floor(gDayNo / 1461);
|
||||
gDayNo %= 1461;
|
||||
|
||||
if (gDayNo >= 366) {
|
||||
leap = false;
|
||||
gDayNo -= 1;
|
||||
gy += Math.floor(gDayNo / 365);
|
||||
gDayNo %= 365;
|
||||
}
|
||||
|
||||
const gregorianMonthDays = [
|
||||
31,
|
||||
leap ? 29 : 28,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
30,
|
||||
31,
|
||||
];
|
||||
|
||||
let gm = 0;
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
if (gDayNo < gregorianMonthDays[i]) {
|
||||
gm = i + 1;
|
||||
break;
|
||||
}
|
||||
gDayNo -= gregorianMonthDays[i];
|
||||
}
|
||||
|
||||
const gd = gDayNo + 1;
|
||||
const mm = String(gm).padStart(2, "0");
|
||||
const dd = String(gd).padStart(2, "0");
|
||||
const result = `${gy}-${mm}-${dd}`;
|
||||
|
||||
return isNaN(new Date(result).getTime()) ? null : result;
|
||||
}
|
||||
|
||||
async function waitForRateLimit() {
|
||||
const minDelayMs = Math.ceil(60000 / config.rateLimitPerMinute);
|
||||
const elapsed = Date.now() - lastRequestAt;
|
||||
@@ -580,6 +832,9 @@ function validateConfig() {
|
||||
if (!config.carBodyToken) {
|
||||
throw new Error("TEJARAT_CAR_BODY_TOKEN or TEJARAT_TOKEN is required");
|
||||
}
|
||||
if (!config.personToken) {
|
||||
throw new Error("TEJARAT_PERSON_TOKEN or TEJARAT_TOKEN is required");
|
||||
}
|
||||
}
|
||||
|
||||
function loadEnvFiles(filePath) {
|
||||
|
||||
Reference in New Issue
Block a user