- 新增库存监控页面、后端服务与 API,支持分页筛选与 CSV 导出 - 库存取值优先聚水潭实时库存(jst_inventoryInfo),取不到回退日报快照(pm_stockMonitor) - 统计卡与预警分档统一全仓库口径,新增在库 SKU 款数 - 预留聚水潭加工单接入(jushuitanClient + 自检脚本),凭证到位后可算采购在途
164 lines
5.5 KiB
JavaScript
164 lines
5.5 KiB
JavaScript
'use strict';
|
||
|
||
// 聚水潭「加工单查询」接入自检脚本
|
||
//
|
||
// 用法:
|
||
// cd backend
|
||
// node scripts/jst-manufacture-probe.js # 查最近 7 天
|
||
// node scripts/jst-manufacture-probe.js 14 # 查最近 14 天(内部按 7 天分段)
|
||
// node scripts/jst-manufacture-probe.js 7 904533 # 额外按指定加工单号探测
|
||
//
|
||
// 作用:
|
||
// 1. 校验 JST_APP_KEY / JST_APP_SECRET / JST_ACCESS_TOKEN 是否可用
|
||
// 2. 自动探测 4 种签名变体,告诉你 JST_SIGN_VARIANT 该填哪个
|
||
// 3. 打印首屏加工单原始结构(含 items 明细的 sku_id / qty / io_qty),用于确认「在途数量」取值口径
|
||
|
||
const path = require('path');
|
||
|
||
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
|
||
|
||
const jushuitan = require('../src/services/jushuitanClient');
|
||
|
||
const MANUFACTURE_PATH = '/open/jushuitan/manufacture/query';
|
||
const PAGE_SIZE = 50;
|
||
|
||
function pad(value) {
|
||
return String(value).padStart(2, '0');
|
||
}
|
||
|
||
function formatDateTime(date) {
|
||
return (
|
||
`${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +
|
||
`${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
||
);
|
||
}
|
||
|
||
function printConfig() {
|
||
const info = jushuitan.describeConfig();
|
||
console.log('== 凭证 ==');
|
||
console.log(` API 地址 : ${info.baseUrl}`);
|
||
console.log(` app_key : ${info.appKey}`);
|
||
console.log(` app_secret : ${info.appSecret}`);
|
||
console.log(` access_token : ${info.accessToken}`);
|
||
console.log(` 当前签名变体 : ${info.signVariant}`);
|
||
console.log(` 是否已配置完整: ${info.configured}`);
|
||
console.log('');
|
||
if (!info.configured) {
|
||
console.log('✗ 凭证不完整,请先在 backend/.env 里补上 JST_APP_KEY / JST_APP_SECRET / JST_ACCESS_TOKEN');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function buildParams({ startTime, endTime, pageIndex = 1, poId = null }) {
|
||
const params = {
|
||
page_index: pageIndex,
|
||
page_size: PAGE_SIZE,
|
||
date_type: 2, // 2 = 加工日期
|
||
start_time: startTime,
|
||
end_time: endTime,
|
||
};
|
||
if (poId) {
|
||
delete params.start_time;
|
||
delete params.end_time;
|
||
params.po_id = poId;
|
||
}
|
||
return params;
|
||
}
|
||
|
||
function summarizePage(payload) {
|
||
const data = payload && payload.data ? payload.data : payload;
|
||
const list = (data && (data.datas || data.items || data.list)) || [];
|
||
console.log(` 返回条数: ${Array.isArray(list) ? list.length : '未知'}`);
|
||
if (!Array.isArray(list) || list.length === 0) return;
|
||
const first = list[0];
|
||
console.log(' 首条加工单字段:', Object.keys(first).join(', '));
|
||
console.log(' 首条摘要:', JSON.stringify({
|
||
po_id: first.po_id,
|
||
po_date: first.po_date,
|
||
seller: first.seller,
|
||
status: first.status,
|
||
sub_type: first.sub_type,
|
||
wms_co_id: first.wms_co_id,
|
||
receipt_wms_co_id: first.receipt_wms_co_id,
|
||
}));
|
||
const items = first.items || first.items_json || [];
|
||
if (Array.isArray(items) && items.length) {
|
||
console.log(' items 字段:', Object.keys(items[0]).join(', '));
|
||
console.log(' 首条 items 前 2 项:', JSON.stringify(items.slice(0, 2)));
|
||
} else {
|
||
console.log(' ⚠️ 首条没有 items 数组,完整结构如下(截断 800 字):');
|
||
console.log(JSON.stringify(first).slice(0, 800));
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
console.log('=== 聚水潭「加工单查询」自检 ===\n');
|
||
if (!printConfig()) process.exitCode = 1;
|
||
|
||
const days = Math.max(1, Number.parseInt(process.argv[2] || '7', 10) || 7);
|
||
const poId = (process.argv[3] || '').trim() || null;
|
||
|
||
const end = new Date();
|
||
const start = new Date(end.getTime() - days * 24 * 60 * 60 * 1000);
|
||
const startTime = formatDateTime(start);
|
||
const endTime = formatDateTime(end);
|
||
console.log(`== 查询窗口 == ${startTime} ~ ${endTime}(date_type=2 加工日期)\n`);
|
||
|
||
console.log('== 签名变体探测 ==');
|
||
let result;
|
||
try {
|
||
result = await jushuitan.probeSignVariants(
|
||
MANUFACTURE_PATH,
|
||
buildParams({ startTime, endTime, pageIndex: 1 })
|
||
);
|
||
} catch (error) {
|
||
console.log(`✗ 调用异常: ${error.message}`);
|
||
process.exitCode = 1;
|
||
return;
|
||
}
|
||
|
||
for (const attempt of result.attempts) {
|
||
const mark = attempt.ok ? '✓ 成功' : '✗ 失败';
|
||
console.log(` ${attempt.variant.padEnd(11)} ${mark} ${attempt.error ? `- ${attempt.error}` : ''}`);
|
||
}
|
||
|
||
if (!result.variant) {
|
||
console.log('\n✗ 4 种签名变体全部失败,请检查:app_key/app_secret 是否正确、access_token 是否过期、应用是否有「加工单查询」权限');
|
||
process.exitCode = 1;
|
||
return;
|
||
}
|
||
|
||
console.log(`\n✓ 有效签名变体: ${result.variant}`);
|
||
console.log(` 请在 backend/.env 写入: JST_SIGN_VARIANT=${result.variant}\n`);
|
||
|
||
console.log('== 数据预览 ==');
|
||
summarizePage(result.payload);
|
||
console.log('');
|
||
|
||
if (poId) {
|
||
console.log(`== 按加工单号探测 po_id=${poId} ==`);
|
||
try {
|
||
const byId = await jushuitan.callApi(MANUFACTURE_PATH, {
|
||
page_index: 1,
|
||
page_size: PAGE_SIZE,
|
||
po_id: poId,
|
||
});
|
||
summarizePage(byId);
|
||
} catch (error) {
|
||
console.log(` ✗ ${error.message}`);
|
||
}
|
||
console.log('');
|
||
}
|
||
|
||
console.log('== 下一步 ==');
|
||
console.log(' 1) 把有效签名变体写入 backend/.env 的 JST_SIGN_VARIANT');
|
||
console.log(' 2) 确认 items 里的 qty / io_qty 口径(在途 ≈ qty - io_qty)');
|
||
console.log(' 3) 告诉我结果,我接上库存明细表的「采购在途数」并做定时同步');
|
||
}
|
||
|
||
main().catch((error) => {
|
||
console.error(`脚本异常: ${error && error.stack ? error.stack : error}`);
|
||
process.exitCode = 1;
|
||
});
|