feat: improve yuntu report sync flow
This commit is contained in:
@@ -1,33 +1,18 @@
|
||||
const INSERT_REPORT_SQL = `
|
||||
INSERT INTO public.yuntu_report_info (
|
||||
INSERT INTO public.segmented_market_reports (
|
||||
report_id,
|
||||
source_type,
|
||||
source_report_id,
|
||||
aadvid,
|
||||
name,
|
||||
price,
|
||||
rules,
|
||||
analysis_dims,
|
||||
categories,
|
||||
transaction_channels,
|
||||
start_time,
|
||||
end_time,
|
||||
period_type,
|
||||
user_name,
|
||||
payload
|
||||
report_info
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5,
|
||||
$6::jsonb, $7::jsonb, $8::jsonb, $9::jsonb, $10::jsonb,
|
||||
$11, $12, $13, $14, $15::jsonb
|
||||
$1, $2::jsonb
|
||||
)
|
||||
ON CONFLICT (report_id) DO NOTHING
|
||||
RETURNING id, report_id
|
||||
RETURNING report_id
|
||||
`;
|
||||
|
||||
const FIND_REPORT_SQL = `
|
||||
SELECT id, report_id
|
||||
FROM public.yuntu_report_info
|
||||
SELECT report_id
|
||||
FROM public.segmented_market_reports
|
||||
WHERE report_id = $1
|
||||
`;
|
||||
|
||||
@@ -36,20 +21,7 @@ function createRepository(pool) {
|
||||
async save(record) {
|
||||
const insertValues = [
|
||||
record.report_id,
|
||||
record.source_type,
|
||||
record.source_report_id,
|
||||
record.aadvid,
|
||||
record.name,
|
||||
JSON.stringify(record.price),
|
||||
JSON.stringify(record.rules),
|
||||
JSON.stringify(record.analysis_dims),
|
||||
JSON.stringify(record.categories),
|
||||
JSON.stringify(record.transaction_channels),
|
||||
record.start_time,
|
||||
record.end_time,
|
||||
record.period_type,
|
||||
record.user_name,
|
||||
JSON.stringify(record.payload),
|
||||
JSON.stringify(record.report_info),
|
||||
];
|
||||
|
||||
const insertResult = await pool.query(INSERT_REPORT_SQL, insertValues);
|
||||
@@ -57,7 +29,6 @@ function createRepository(pool) {
|
||||
if (insertResult.rows.length > 0) {
|
||||
const row = insertResult.rows[0];
|
||||
return {
|
||||
id: row.id,
|
||||
reportId: row.report_id,
|
||||
created: true,
|
||||
};
|
||||
@@ -71,7 +42,6 @@ function createRepository(pool) {
|
||||
}
|
||||
|
||||
return {
|
||||
id: existingRow.id,
|
||||
reportId: existingRow.report_id,
|
||||
created: false,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
const ALLOWED_SOURCE_TYPES = new Set(['MANUAL_CAPTURE', 'AUTO_COPY']);
|
||||
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
||||
|
||||
class ValidationError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
@@ -9,30 +6,6 @@ class ValidationError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNonEmptyString(value, fieldName) {
|
||||
if (typeof value !== 'string' || value.trim() === '') {
|
||||
throw new ValidationError(`${fieldName} is required`);
|
||||
}
|
||||
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
function ensureOptionalString(value, fieldName) {
|
||||
if (value == null || value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ensureNonEmptyString(value, fieldName);
|
||||
}
|
||||
|
||||
function ensureArray(value, fieldName) {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new ValidationError(`${fieldName} must be an array`);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function ensureObject(value, fieldName) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw new ValidationError(`${fieldName} must be an object`);
|
||||
@@ -41,75 +14,60 @@ function ensureObject(value, fieldName) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function ensureDateString(value, fieldName) {
|
||||
const normalized = ensureNonEmptyString(value, fieldName);
|
||||
|
||||
if (!DATE_PATTERN.test(normalized)) {
|
||||
throw new ValidationError(`${fieldName} must match YYYY-MM-DD`);
|
||||
function normalizeReportId(value) {
|
||||
if (typeof value === 'string' && value.trim() !== '') {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
return normalized;
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractReportId(reportInfo) {
|
||||
const directReportId =
|
||||
normalizeReportId(reportInfo.report_id) ||
|
||||
normalizeReportId(reportInfo.reportId);
|
||||
if (directReportId) {
|
||||
return directReportId;
|
||||
}
|
||||
|
||||
if (!reportInfo.data || typeof reportInfo.data !== 'object' || Array.isArray(reportInfo.data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
normalizeReportId(reportInfo.data.report_id) ||
|
||||
normalizeReportId(reportInfo.data.reportId)
|
||||
);
|
||||
}
|
||||
|
||||
function validateAndNormalizeReportInput(input) {
|
||||
const payload = ensureObject(input, 'body');
|
||||
const sourceType = ensureNonEmptyString(payload.sourceType, 'sourceType');
|
||||
const reportInfo = ensureObject(input, 'body');
|
||||
const reportId = extractReportId(reportInfo);
|
||||
|
||||
if (!ALLOWED_SOURCE_TYPES.has(sourceType)) {
|
||||
throw new ValidationError('sourceType must be MANUAL_CAPTURE or AUTO_COPY');
|
||||
}
|
||||
|
||||
const sourceReportId = ensureOptionalString(payload.sourceReportId, 'sourceReportId');
|
||||
|
||||
if (sourceType === 'MANUAL_CAPTURE' && sourceReportId !== null) {
|
||||
throw new ValidationError('sourceReportId must be empty for MANUAL_CAPTURE');
|
||||
}
|
||||
|
||||
if (sourceType === 'AUTO_COPY' && sourceReportId === null) {
|
||||
throw new ValidationError('sourceReportId is required when sourceType is AUTO_COPY');
|
||||
if (!reportId) {
|
||||
throw new ValidationError('report_id is required in response info');
|
||||
}
|
||||
|
||||
return {
|
||||
reportId: ensureNonEmptyString(payload.reportId, 'reportId'),
|
||||
sourceType,
|
||||
sourceReportId,
|
||||
aadvid: ensureNonEmptyString(payload.aadvid, 'aadvid'),
|
||||
name: ensureNonEmptyString(payload.name, 'name'),
|
||||
price: ensureArray(payload.price, 'price'),
|
||||
rules: ensureArray(payload.rules, 'rules'),
|
||||
analysisDims: ensureArray(payload.analysisDims, 'analysisDims'),
|
||||
categories: ensureArray(payload.categories, 'categories'),
|
||||
channels: ensureArray(payload.channels, 'channels'),
|
||||
startTime: ensureDateString(payload.startTime, 'startTime'),
|
||||
endTime: ensureDateString(payload.endTime, 'endTime'),
|
||||
periodType: ensureOptionalString(payload.periodType, 'periodType'),
|
||||
userName: ensureOptionalString(payload.userName, 'userName'),
|
||||
payload: ensureObject(payload.payload, 'payload'),
|
||||
reportId,
|
||||
reportInfo,
|
||||
};
|
||||
}
|
||||
|
||||
function toDatabaseRecord(normalizedReport) {
|
||||
return {
|
||||
report_id: normalizedReport.reportId,
|
||||
source_type: normalizedReport.sourceType,
|
||||
source_report_id: normalizedReport.sourceReportId,
|
||||
aadvid: normalizedReport.aadvid,
|
||||
name: normalizedReport.name,
|
||||
price: normalizedReport.price,
|
||||
rules: normalizedReport.rules,
|
||||
analysis_dims: normalizedReport.analysisDims,
|
||||
categories: normalizedReport.categories,
|
||||
transaction_channels: normalizedReport.channels,
|
||||
start_time: normalizedReport.startTime,
|
||||
end_time: normalizedReport.endTime,
|
||||
period_type: normalizedReport.periodType,
|
||||
user_name: normalizedReport.userName,
|
||||
payload: normalizedReport.payload,
|
||||
report_info: normalizedReport.reportInfo,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
ValidationError,
|
||||
extractReportId,
|
||||
validateAndNormalizeReportInput,
|
||||
toDatabaseRecord,
|
||||
};
|
||||
|
||||
@@ -35,7 +35,6 @@ function createApp({ repository, allowedOrigin }) {
|
||||
response.status(saved.created ? 201 : 200).json({
|
||||
success: true,
|
||||
data: {
|
||||
id: saved.id,
|
||||
reportId: saved.reportId,
|
||||
created: saved.created,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user