需求:配件库存监控、MKH-BOM 资料两个页面各加一个同步按钮,任何时候点击都能 立即拉取最新数据,不必等凌晨 00:00 的定时刷新。 后端: - mkhBomService 新增 clearMkhBomCache():清空本页全部服务端缓存 (businessBom / officialBomIndex / officialBomDetail / productBom / mkhProduct / replenishIndex / workOrderIndex / pendingOrderIndex / componentStock / componentLocation),返回 syncedAt + 各缓存清除条目数。 - modularStockMonitorService 新增 syncModularStock():本页无进程内缓存 (每次请求直连副本库),同步动作 = 重新解析并返回最新监控日,前端据此重拉列表。 - 新增接口 POST /api/mkh-bom/sync、POST /api/modular-stock-monitor/sync。 前端: - api/mkhBom.js 新增 syncMkhBom;api/modularStockMonitor.js 新增 syncModularStock。 - 两个页面工具栏各加「同步最新数据」主按钮(SyncOutlined,带 loading 态), 点击 → 调后端 sync → 成功后重新拉列表(MKH-BOM 页同时清空展开明细缓存 detailCache,避免看到旧的配件明细)→ 提示「已同步最新数据」; 失败时展示后端返回的错误信息。 口径说明(重要): - 两页数据来自副本库 JSReportReplica,由上游聚水潭同步;应用层无法触发上游拉取。 本按钮的作用是「清服务端缓存 + 直连副本库重取最新」,保证看到的不是 2~15 分钟前的缓存结果,而不是重新跑一遍上游同步。 - 后端已内置的 dailyRefresh(cron 0 0 * * *,Asia/Shanghai)刷的是 summary_daily_* / summary_monthly_* 汇总表,不覆盖这两页数据源, 与本按钮既不重复也不冲突,可并存。 验证: - 后端 4 个改动文件 node --check 全部通过。 - 前端 4 个改动文件(含 2 个 jsx)用 @babel/parser 解析全部通过。 - 实测:syncModularStock 返回 monitorDate=2026-09-16; clearMkhBomCache 返回 syncedAt 与各缓存清除条目数。 - 单测 mkhBomService.test.js 40/40 通过,无回归。 - 前端 lint/build 未运行:frontend/node_modules 仍是之前被中断 npm ci 破坏的 坏目录(frontend/node_modules_broken_0917/,未纳入提交)。 合并前需在本机 npm ci 恢复后,打开两个页面确认按钮渲染与点击效果。 - 后端改动需重启服务后才生效。
116 lines
3.7 KiB
JavaScript
116 lines
3.7 KiB
JavaScript
'use strict';
|
||
|
||
const express = require('express');
|
||
const {
|
||
getMkhBomList,
|
||
getMkhBomDetail,
|
||
listMkhBomExportRows,
|
||
clearMkhBomCache,
|
||
} = require('../services/mkhBomService');
|
||
const {
|
||
getMkhReplenishAlertAdminConfig,
|
||
resetMkhReplenishAlertState,
|
||
saveMkhReplenishAlertConfig,
|
||
} = require('../services/mkhReplenishAlertSettings');
|
||
const { checkMkhReplenishAlerts } = require('../jobs/mkhReplenishAlertJob');
|
||
const {
|
||
saveYydNotificationCenterConfig,
|
||
} = require('../services/yydNotificationCenterSettings');
|
||
|
||
const router = express.Router();
|
||
|
||
function sendError(res, error, fallback) {
|
||
const status = Number(error?.statusCode) || 500;
|
||
console.error('[MkhBom]', {
|
||
message: error?.message || fallback,
|
||
code: error?.code || null,
|
||
status,
|
||
});
|
||
res.status(status).json({ success: false, error: error.message || fallback });
|
||
}
|
||
|
||
// MKH-BOM 资料列表(模块化组合商品 + 配件汇总)
|
||
router.get('/list', async (req, res) => {
|
||
try {
|
||
res.json({ success: true, data: await getMkhBomList(req.query || {}) });
|
||
} catch (error) {
|
||
sendError(res, error, '加载 MKH-BOM 资料列表失败');
|
||
}
|
||
});
|
||
|
||
// 单个 MKH 组合商品的 BOM 配件明细(打印用)
|
||
router.get('/detail', async (req, res) => {
|
||
try {
|
||
const skuId = req.query?.skuId || req.query?.comboSkuId;
|
||
res.json({ success: true, data: await getMkhBomDetail(skuId) });
|
||
} catch (error) {
|
||
sendError(res, error, '加载 MKH-BOM 明细失败');
|
||
}
|
||
});
|
||
|
||
// 导出:当前筛选条件下的全部数据(不分页)
|
||
// mode=products(默认)→ 商品清单;mode=components → 配件明细摊平
|
||
router.get('/export', async (req, res) => {
|
||
try {
|
||
res.json({ success: true, data: await listMkhBomExportRows(req.query || {}) });
|
||
} catch (error) {
|
||
sendError(res, error, '导出 MKH-BOM 资料失败');
|
||
}
|
||
});
|
||
|
||
// 补货预警推送设置(接收人 / 开关 / 推送范围)
|
||
router.get('/alert-config', async (req, res) => {
|
||
try {
|
||
res.json({ success: true, data: await getMkhReplenishAlertAdminConfig() });
|
||
} catch (error) {
|
||
sendError(res, error, '加载补货预警推送设置失败');
|
||
}
|
||
});
|
||
|
||
// 保存补货预警推送设置
|
||
router.put('/alert-config', async (req, res) => {
|
||
try {
|
||
const data = await saveMkhReplenishAlertConfig(req.body || {});
|
||
res.json({ success: true, data });
|
||
} catch (error) {
|
||
sendError(res, error, '保存补货预警推送设置失败');
|
||
}
|
||
});
|
||
|
||
// 立即检查并推送(dryRun: 只预览不发送;force: 忽略「只推新增」推全量;reset: 先清空已推送记录)
|
||
router.post('/alert-check', async (req, res) => {
|
||
try {
|
||
const body = req.body || {};
|
||
if (body.reset === true) await resetMkhReplenishAlertState();
|
||
const result = await checkMkhReplenishAlerts({
|
||
dryRun: body.dryRun === true,
|
||
force: body.force === true,
|
||
});
|
||
res.json({ success: true, data: result });
|
||
} catch (error) {
|
||
sendError(res, error, '执行补货预警推送失败');
|
||
}
|
||
});
|
||
|
||
// 保存统一通知中心凭据(补货预警的推送通道)
|
||
router.put('/notification-center', async (req, res) => {
|
||
try {
|
||
await saveYydNotificationCenterConfig(req.body || {});
|
||
res.json({ success: true, data: await getMkhReplenishAlertAdminConfig() });
|
||
} catch (error) {
|
||
sendError(res, error, '保存统一通知中心配置失败');
|
||
}
|
||
});
|
||
|
||
// 手动同步:清空本页服务端缓存(BOM / 补货索引 / 加工单 / 待发货 / 配件库存 / 仓位),
|
||
// 前端「同步」按钮调用后再拉一次列表即可直连副本库取最新快照
|
||
router.post('/sync', async (_req, res) => {
|
||
try {
|
||
res.json({ success: true, data: clearMkhBomCache() });
|
||
} catch (error) {
|
||
sendError(res, error, '同步 MKH-BOM 数据失败');
|
||
}
|
||
});
|
||
|
||
module.exports = router;
|