cangku/backend/src/services/jxStockSelectionQuery.js

277 lines
6.8 KiB
JavaScript

'use strict';
const dataSource = require('../config/database');
const localDb = dataSource.local;
const RESULT_TABLE = 'op_JxBarcodeChannelDecisionResult';
function buildStockSelectionWhereClause({
statDate,
poolType,
ruleCode,
barcode,
}) {
const whereParts = [ 'r.`statDate` = ?' ];
const params = [ statDate ];
if (poolType) {
whereParts.push('r.`poolType` = ?');
params.push(poolType);
}
if (ruleCode) {
whereParts.push('r.`ruleCode` = ?');
params.push(ruleCode);
}
if (barcode) {
whereParts.push('CAST(r.`barcode` AS CHAR) LIKE ?');
params.push(`%${barcode}%`);
}
return {
whereSql: `WHERE ${whereParts.join(' AND ')}`,
params,
};
}
async function getLatestJxStockSelectionStatDate() {
const rows = await localDb.query(
`SELECT MAX(statDate) AS latestStatDate FROM \`${RESULT_TABLE}\``
);
return rows?.[0]?.latestStatDate || null;
}
async function getJxStockSelectionDataDates() {
const rows = await localDb.query(
`
SELECT
statDate
FROM \`${RESULT_TABLE}\`
GROUP BY statDate
ORDER BY statDate DESC
LIMIT 30
`
);
return (rows || []).map((row) => ({
statDate: row.statDate,
}));
}
async function getJxStockSelectionOverview({
statDate,
poolType,
ruleCode,
barcode,
}) {
const latestStatDate = statDate || await getLatestJxStockSelectionStatDate();
if (!latestStatDate) {
return {
statDate: null,
totals: {
totalCount: 0,
pickPoolCount: 0,
goodPickPoolCount: 0,
clearPoolCount: 0,
banPoolCount: 0,
reviewPoolCount: 0,
naCount: 0,
},
poolDistribution: [],
ruleDistribution: [],
summaryRows: [],
ruleOptions: [],
poolOptions: [],
};
}
const { whereSql, params } = buildStockSelectionWhereClause({
statDate: latestStatDate,
poolType,
ruleCode,
barcode,
});
const totalsRows = await localDb.query(
`
SELECT
? AS statDate,
COUNT(1) AS totalCount,
SUM(CASE WHEN r.\`poolType\` = '选品待选池' THEN 1 ELSE 0 END) AS pickPoolCount,
SUM(CASE WHEN r.\`poolType\` = '优质选品待选池' THEN 1 ELSE 0 END) AS goodPickPoolCount,
SUM(CASE WHEN r.\`poolType\` = '清货待选池' THEN 1 ELSE 0 END) AS clearPoolCount,
SUM(CASE WHEN r.\`poolType\` = '禁止选品' THEN 1 ELSE 0 END) AS banPoolCount,
SUM(CASE WHEN r.\`poolType\` = '企划复盘' THEN 1 ELSE 0 END) AS reviewPoolCount,
SUM(CASE WHEN r.\`ruleCode\` = 'NA' THEN 1 ELSE 0 END) AS naCount
FROM \`${RESULT_TABLE}\` r
${whereSql}
`,
[latestStatDate, ...params]
);
const poolDistribution = await localDb.query(
`
SELECT
r.\`poolType\` AS name,
COUNT(1) AS value
FROM \`${RESULT_TABLE}\` r
${whereSql}
GROUP BY r.\`poolType\`
ORDER BY COUNT(1) DESC, r.\`poolType\`
`,
params
);
const ruleDistribution = await localDb.query(
`
SELECT
r.\`ruleCode\` AS name,
COUNT(1) AS value
FROM \`${RESULT_TABLE}\` r
${whereSql}
GROUP BY r.\`ruleCode\`
ORDER BY COUNT(1) DESC, r.\`ruleCode\`
`,
params
);
const summaryRowsRaw = await localDb.query(
`
SELECT
r.\`ruleCode\` AS ruleCode,
r.\`poolType\` AS poolType,
MAX(r.\`ruleTag\`) AS ruleTag,
COUNT(1) AS hitCount
FROM \`${RESULT_TABLE}\` r
${whereSql}
GROUP BY r.\`ruleCode\`, r.\`poolType\`
ORDER BY COUNT(1) DESC, r.\`ruleCode\`, r.\`poolType\`
`,
params
);
const ruleOptionsRows = await localDb.query(
`
SELECT DISTINCT r.\`ruleCode\` AS ruleCode
FROM \`${RESULT_TABLE}\` r
WHERE r.\`statDate\` = ?
ORDER BY r.\`ruleCode\`
`,
[latestStatDate]
);
const poolOptionsRows = await localDb.query(
`
SELECT DISTINCT r.\`poolType\` AS poolType
FROM \`${RESULT_TABLE}\` r
WHERE r.\`statDate\` = ?
ORDER BY r.\`poolType\`
`,
[latestStatDate]
);
const totals = totalsRows?.[0] || {};
const ruleOptions = (ruleOptionsRows || []).map((row) => row.ruleCode).filter(Boolean);
const poolOptions = (poolOptionsRows || []).map((row) => row.poolType).filter(Boolean);
const totalCount = Number(totals.totalCount || 0);
const summaryRows = summaryRowsRaw.map((row, index) => ({
key: `${row.ruleCode || 'NA'}-${row.poolType || 'NA'}-${index}`,
ruleCode: row.ruleCode,
poolType: row.poolType,
ruleTag: row.ruleTag,
hitCount: Number(row.hitCount || 0),
hitRate: totalCount > 0 ? Number(row.hitCount || 0) / totalCount : 0,
}));
return {
statDate: latestStatDate,
totals: {
totalCount: Number(totals.totalCount || 0),
pickPoolCount: Number(totals.pickPoolCount || 0),
goodPickPoolCount: Number(totals.goodPickPoolCount || 0),
clearPoolCount: Number(totals.clearPoolCount || 0),
banPoolCount: Number(totals.banPoolCount || 0),
reviewPoolCount: Number(totals.reviewPoolCount || 0),
naCount: Number(totals.naCount || 0),
},
poolDistribution: poolDistribution.map((row) => ({ name: row.name || '未分类', value: Number(row.value || 0) })),
ruleDistribution: ruleDistribution.map((row) => ({ name: row.name || 'NA', value: Number(row.value || 0) })),
summaryRows,
ruleOptions,
poolOptions,
};
}
async function getJxStockSelectionList({
statDate,
poolType,
ruleCode,
barcode,
page = 1,
pageSize = 20,
}) {
const latestStatDate = statDate || await getLatestJxStockSelectionStatDate();
if (!latestStatDate) {
return {
statDate: null,
page,
pageSize,
total: 0,
rows: [],
};
}
const { whereSql, params } = buildStockSelectionWhereClause({
statDate: latestStatDate,
poolType,
ruleCode,
barcode,
});
const totalRows = await localDb.query(
`
SELECT COUNT(1) AS total
FROM \`${RESULT_TABLE}\` r
${whereSql}
`,
params
);
const total = Number(totalRows?.[0]?.total || 0);
const rows = total > 0
? await localDb.query(
`
SELECT
r.\`statDate\` AS statDate,
r.\`channelName\` AS channelName,
r.\`barcode\` AS barcode,
r.\`ruleCode\` AS ruleCode,
r.\`poolType\` AS poolType,
r.\`ruleTag\` AS ruleTag,
r.\`ruleExplain\` AS ruleExplain
FROM \`${RESULT_TABLE}\` r
${whereSql}
ORDER BY
CASE WHEN r.\`ruleCode\` = 'NA' THEN 1 ELSE 0 END,
r.\`poolType\` ASC,
r.\`ruleCode\` ASC,
r.\`barcode\` ASC
LIMIT ?
OFFSET ?
`,
[ ...params, pageSize, (page - 1) * pageSize ]
)
: [];
return {
statDate: latestStatDate,
page,
pageSize,
total,
rows: rows || [],
};
}
module.exports = {
getLatestJxStockSelectionStatDate,
getJxStockSelectionDataDates,
getJxStockSelectionOverview,
getJxStockSelectionList,
};