cangku/backend/scripts/jst-manufacture-probe.js
wangjie 51b9f9eeed feat: 新增模块化库存监控(全仓口径 + 实时库存)
- 新增库存监控页面、后端服务与 API,支持分页筛选与 CSV 导出
- 库存取值优先聚水潭实时库存(jst_inventoryInfo),取不到回退日报快照(pm_stockMonitor)
- 统计卡与预警分档统一全仓库口径,新增在库 SKU 款数
- 预留聚水潭加工单接入(jushuitanClient + 自检脚本),凭证到位后可算采购在途
2026-09-24 15:34:57 +08:00

164 lines
5.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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;
});