forked from lunanting/cangku
281 lines
9.7 KiB
JavaScript
281 lines
9.7 KiB
JavaScript
'use strict';
|
|
|
|
const dayjs = require('dayjs');
|
|
const cloudDb = require('../config/cloudDatabase');
|
|
const database = require('../config/database');
|
|
const { applyAftersalesRefundExportOverrides } = require('./aftersalesRefundExportOverride');
|
|
|
|
const SYNC_VERSION = 'aftersales-refund-union-v2';
|
|
const INSERT_BATCH_SIZE = 500;
|
|
|
|
const TARGET_COLUMNS = [
|
|
'stat_date',
|
|
'source_key',
|
|
'source_table',
|
|
'as_id',
|
|
'outer_as_id',
|
|
'o_id',
|
|
'so_id',
|
|
'type',
|
|
'node',
|
|
'order_labels',
|
|
'shop_name',
|
|
'freight',
|
|
'refund',
|
|
'payment',
|
|
'free_amount',
|
|
'total_refund_money',
|
|
'shop_status',
|
|
'question_type',
|
|
'process_result',
|
|
'status',
|
|
'created',
|
|
'modified',
|
|
'l_id',
|
|
'confirm_date',
|
|
'creator_name',
|
|
'original_order_no',
|
|
'original_courier_name',
|
|
'original_waybill_no',
|
|
];
|
|
|
|
function normalizeDate(value, fallback = dayjs().subtract(1, 'day').format('YYYY-MM-DD')) {
|
|
const parsed = dayjs(value || fallback);
|
|
if (!parsed.isValid()) {
|
|
throw new Error(`Invalid date: ${value}`);
|
|
}
|
|
return parsed.format('YYYY-MM-DD');
|
|
}
|
|
|
|
function listDatesInRange(startDate, endDate) {
|
|
const start = dayjs(normalizeDate(startDate));
|
|
const end = dayjs(normalizeDate(endDate || startDate));
|
|
if (end.isBefore(start, 'day')) {
|
|
throw new Error(`Invalid date range: ${startDate} -> ${endDate}`);
|
|
}
|
|
|
|
const dates = [];
|
|
let cursor = start;
|
|
while (!cursor.isAfter(end, 'day')) {
|
|
dates.push(cursor.format('YYYY-MM-DD'));
|
|
cursor = cursor.add(1, 'day');
|
|
}
|
|
return dates;
|
|
}
|
|
|
|
function decimalOrZero(value) {
|
|
const number = Number(value);
|
|
return Number.isFinite(number) ? number : 0;
|
|
}
|
|
|
|
function textOrEmpty(value) {
|
|
return value === null || value === undefined ? '' : String(value);
|
|
}
|
|
|
|
function toInsertRow(row) {
|
|
return TARGET_COLUMNS.map((column) => {
|
|
if (['freight', 'refund', 'payment', 'free_amount', 'total_refund_money'].includes(column)) {
|
|
return decimalOrZero(row[column]);
|
|
}
|
|
if (['created', 'modified', 'confirm_date'].includes(column)) {
|
|
return row[column] || null;
|
|
}
|
|
if (column === 'stat_date') {
|
|
return normalizeDate(row[column]);
|
|
}
|
|
return textOrEmpty(row[column]);
|
|
});
|
|
}
|
|
|
|
const UNION_SQL = `
|
|
WITH unified_refunds AS (
|
|
SELECT
|
|
CONVERT(varchar(10), CONVERT(date, created), 120) AS stat_date,
|
|
N'jst_returnAndRefundInfo' AS source_key,
|
|
N'dbo.jst_returnAndRefundInfo' AS source_table,
|
|
CAST(as_id AS nvarchar(80)) AS as_id,
|
|
CAST(outer_as_id AS nvarchar(120)) AS outer_as_id,
|
|
CAST(o_id AS nvarchar(80)) AS o_id,
|
|
CAST(so_id AS nvarchar(120)) AS so_id,
|
|
CAST(type AS nvarchar(50)) AS type,
|
|
CAST(node AS nvarchar(100)) AS node,
|
|
CAST(order_labelsText AS nvarchar(max)) AS order_labels,
|
|
CAST(shop_name AS nvarchar(255)) AS shop_name,
|
|
TRY_CONVERT(decimal(18,2), freight) AS freight,
|
|
TRY_CONVERT(decimal(18,2), refund) AS refund,
|
|
TRY_CONVERT(decimal(18,2), payment) AS payment,
|
|
TRY_CONVERT(decimal(18,2), free_amount) AS free_amount,
|
|
(
|
|
COALESCE(TRY_CONVERT(decimal(18,2), freight), 0)
|
|
+ COALESCE(TRY_CONVERT(decimal(18,2), refund), 0)
|
|
- COALESCE(TRY_CONVERT(decimal(18,2), payment), 0)
|
|
- COALESCE(TRY_CONVERT(decimal(18,2), free_amount), 0)
|
|
) AS total_refund_money,
|
|
CAST(shop_status AS nvarchar(100)) AS shop_status,
|
|
CAST(question_type AS nvarchar(255)) AS question_type,
|
|
CAST(result AS nvarchar(255)) AS process_result,
|
|
CAST(status AS nvarchar(100)) AS status,
|
|
CONVERT(varchar(19), created, 120) AS created,
|
|
CONVERT(varchar(19), modified, 120) AS modified,
|
|
CAST(l_id AS nvarchar(120)) AS l_id,
|
|
CONVERT(varchar(19), confirm_date, 120) AS confirm_date,
|
|
CAST(creator_name AS nvarchar(100)) AS creator_name,
|
|
CAST(N'' AS nvarchar(255)) AS original_order_no,
|
|
CAST(N'' AS nvarchar(120)) AS original_courier_name,
|
|
CAST(N'' AS nvarchar(120)) AS original_waybill_no
|
|
FROM dbo.jst_returnAndRefundInfo WITH (NOLOCK)
|
|
WHERE shop_site <> N'拼多多'
|
|
AND CONVERT(date, created) = @statDate
|
|
|
|
UNION ALL
|
|
|
|
SELECT
|
|
CONVERT(varchar(10), CONVERT(date, created), 120) AS stat_date,
|
|
N'jst_qmcust_refundlist' AS source_key,
|
|
N'dbo.jst_qmcust_refundlist' AS source_table,
|
|
CAST(as_id AS nvarchar(80)) AS as_id,
|
|
CAST(outer_as_id AS nvarchar(120)) AS outer_as_id,
|
|
CAST(o_id AS nvarchar(80)) AS o_id,
|
|
CAST(so_id AS nvarchar(120)) AS so_id,
|
|
CAST(type AS nvarchar(50)) AS type,
|
|
CAST(node AS nvarchar(100)) AS node,
|
|
CAST(order_label AS nvarchar(max)) AS order_labels,
|
|
CAST(shop_name AS nvarchar(255)) AS shop_name,
|
|
TRY_CONVERT(decimal(18,2), freight) AS freight,
|
|
TRY_CONVERT(decimal(18,2), refund) AS refund,
|
|
TRY_CONVERT(decimal(18,2), payment) AS payment,
|
|
TRY_CONVERT(decimal(18,2), free_amount) AS free_amount,
|
|
(
|
|
COALESCE(TRY_CONVERT(decimal(18,2), freight), 0)
|
|
+ COALESCE(TRY_CONVERT(decimal(18,2), refund), 0)
|
|
- COALESCE(TRY_CONVERT(decimal(18,2), payment), 0)
|
|
- COALESCE(TRY_CONVERT(decimal(18,2), free_amount), 0)
|
|
) AS total_refund_money,
|
|
CAST(shop_status AS nvarchar(100)) AS shop_status,
|
|
CAST(question_type AS nvarchar(255)) AS question_type,
|
|
CAST(N'' AS nvarchar(255)) AS process_result,
|
|
CAST(status AS nvarchar(100)) AS status,
|
|
CONVERT(varchar(19), created, 120) AS created,
|
|
CONVERT(varchar(19), modified, 120) AS modified,
|
|
CAST(l_id AS nvarchar(120)) AS l_id,
|
|
CONVERT(varchar(19), confirm_date, 120) AS confirm_date,
|
|
CAST(creator_name AS nvarchar(100)) AS creator_name,
|
|
CAST(N'' AS nvarchar(255)) AS original_order_no,
|
|
CAST(N'' AS nvarchar(120)) AS original_courier_name,
|
|
CAST(N'' AS nvarchar(120)) AS original_waybill_no
|
|
FROM dbo.jst_qmcust_refundlist WITH (NOLOCK)
|
|
WHERE CONVERT(date, created) = @statDate
|
|
|
|
UNION ALL
|
|
|
|
SELECT
|
|
CONVERT(varchar(10), CONVERT(date, created), 120) AS stat_date,
|
|
N'jst_refund_pdd_fz_view' AS source_key,
|
|
N'dbo.jst_refund_pdd_fz_view' AS source_table,
|
|
CAST(as_id AS nvarchar(80)) AS as_id,
|
|
CAST(outer_as_id AS nvarchar(120)) AS outer_as_id,
|
|
CAST(o_id AS nvarchar(80)) AS o_id,
|
|
CAST(so_id AS nvarchar(120)) AS so_id,
|
|
CAST(type AS nvarchar(50)) AS type,
|
|
CAST(node AS nvarchar(100)) AS node,
|
|
CAST(order_labels_str AS nvarchar(max)) AS order_labels,
|
|
CAST(shop_name AS nvarchar(255)) AS shop_name,
|
|
TRY_CONVERT(decimal(18,2), freight) AS freight,
|
|
TRY_CONVERT(decimal(18,2), refund) AS refund,
|
|
TRY_CONVERT(decimal(18,2), payment) AS payment,
|
|
TRY_CONVERT(decimal(18,2), free_amount) AS free_amount,
|
|
(
|
|
COALESCE(TRY_CONVERT(decimal(18,2), freight), 0)
|
|
+ COALESCE(TRY_CONVERT(decimal(18,2), refund), 0)
|
|
- COALESCE(TRY_CONVERT(decimal(18,2), payment), 0)
|
|
- COALESCE(TRY_CONVERT(decimal(18,2), free_amount), 0)
|
|
) AS total_refund_money,
|
|
CAST(shop_status AS nvarchar(100)) AS shop_status,
|
|
CAST(question_type AS nvarchar(255)) AS question_type,
|
|
CAST(result AS nvarchar(255)) AS process_result,
|
|
CAST(status AS nvarchar(100)) AS status,
|
|
CONVERT(varchar(19), created, 120) AS created,
|
|
CONVERT(varchar(19), modified, 120) AS modified,
|
|
CAST(l_id AS nvarchar(120)) AS l_id,
|
|
CONVERT(varchar(19), confirm_date, 120) AS confirm_date,
|
|
CAST(creator_name AS nvarchar(100)) AS creator_name,
|
|
CAST(N'' AS nvarchar(255)) AS original_order_no,
|
|
CAST(N'' AS nvarchar(120)) AS original_courier_name,
|
|
CAST(N'' AS nvarchar(120)) AS original_waybill_no
|
|
FROM dbo.jst_refund_pdd_fz_view
|
|
WHERE CONVERT(date, created) = @statDate
|
|
)
|
|
SELECT *
|
|
FROM unified_refunds
|
|
ORDER BY created ASC, source_key ASC, as_id ASC;
|
|
`;
|
|
|
|
async function writeSyncLog({ statDate, rowCount, status, errorMessage = '' }) {
|
|
await database.local.query(
|
|
`INSERT INTO aftersales_refund_detail_sync
|
|
(stat_date, sync_version, row_count, status, error_message, synced_at)
|
|
VALUES (?, ?, ?, ?, ?, NOW())
|
|
ON DUPLICATE KEY UPDATE
|
|
sync_version = VALUES(sync_version),
|
|
row_count = VALUES(row_count),
|
|
status = VALUES(status),
|
|
error_message = VALUES(error_message),
|
|
synced_at = NOW()`,
|
|
[statDate, SYNC_VERSION, rowCount, status, errorMessage.slice(0, 1000)]
|
|
);
|
|
}
|
|
|
|
async function insertRows(rows) {
|
|
if (rows.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const columnSql = TARGET_COLUMNS.map((column) => `\`${column}\``).join(', ');
|
|
for (let index = 0; index < rows.length; index += INSERT_BATCH_SIZE) {
|
|
const batch = rows.slice(index, index + INSERT_BATCH_SIZE).map(toInsertRow);
|
|
await database.local.query(
|
|
`INSERT INTO aftersales_refund_detail_read (${columnSql}) VALUES ?`,
|
|
[batch]
|
|
);
|
|
}
|
|
}
|
|
|
|
async function refreshAftersalesRefundDetailDate(inputDate) {
|
|
const statDate = normalizeDate(inputDate);
|
|
console.log(`[aftersales-refund-detail] sync start ${statDate}`);
|
|
|
|
try {
|
|
const rows = await cloudDb.query(UNION_SQL, { statDate });
|
|
await database.local.query('DELETE FROM aftersales_refund_detail_read WHERE stat_date = ?', [statDate]);
|
|
await insertRows(rows);
|
|
await applyAftersalesRefundExportOverrides(statDate);
|
|
await writeSyncLog({ statDate, rowCount: rows.length, status: 'success' });
|
|
console.log(`[aftersales-refund-detail] sync done ${statDate}, rows=${rows.length}`);
|
|
return { statDate, rowCount: rows.length };
|
|
} catch (error) {
|
|
await writeSyncLog({
|
|
statDate,
|
|
rowCount: 0,
|
|
status: 'failed',
|
|
errorMessage: error.message || String(error),
|
|
}).catch((logError) => {
|
|
console.warn('[aftersales-refund-detail] sync log failed:', logError.message || logError);
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function refreshAftersalesRefundDetailRange(startDate, endDate = startDate) {
|
|
const results = [];
|
|
for (const statDate of listDatesInRange(startDate, endDate)) {
|
|
results.push(await refreshAftersalesRefundDetailDate(statDate));
|
|
}
|
|
return results;
|
|
}
|
|
|
|
module.exports = {
|
|
SYNC_VERSION,
|
|
refreshAftersalesRefundDetailDate,
|
|
refreshAftersalesRefundDetailRange,
|
|
};
|