# 兴万达MES系统 - 库存预警功能实现文档 ## 📋 目录 - [需求说明](#需求说明) - [数据库设计](#数据库设计) - [后端实现](#后端实现) - [前端实现](#前端实现) - [首页预警弹窗](#首页预警弹窗) - [实施步骤](#实施步骤) - [测试用例](#测试用例) --- ## 📝 需求说明 ### 1. 功能概述 在仓库管理模块下新增"库存预警"功能,与"即时库存"同级菜单,用于监控物料库存是否超出设定的上下限范围,并在登录首页时弹窗提醒。 **重要说明**: - 物料选择直接从物料表(md_material)读取,不再从即时库存读取 - **菜单状态控制预警**:读取 `sys_menu` 表中"仓库管理"菜单(menu_id=2082)的状态 - 如果 `visible='1'`(隐藏)或 `status='1'`(停用),则不在首页显示预警 - 只有当 `visible='0'`(显示)且 `status='0'`(正常)时,才会在首页显示预警 ### 2. 核心功能 1. **预警配置管理**: - 选择物料(从物料表中选择,只显示状态为"启用"的物料) - 可选择特定仓库或全部仓库 - 设置库存上限数量 - 设置库存下限数量 - 支持增删改查和导出功能 2. **预警触发逻辑**: - 当前库存数量 > 上限数量:触发"高于上限"预警 - 当前库存数量 < 下限数量:触发"低于下限"预警 - 在上下限之间:不触发预警 - **菜单状态控制**:检查"仓库管理"菜单(ID=2082)的 `visible` 和 `status` 字段 - `visible='0'` 且 `status='0'`:显示预警 ✅ - `visible='1'` 或 `status='1'`:不显示预警 ❌ 3. **预警展示**: - 登录首页右上角弹出Notification通知 - 显示5秒后自动消失(可手动关闭) - 预警内容格式: - 高于上限:`【库存预警】物料【xxx】在仓库【xxx】中的库存数量高于上限(上限:X件),请暂停采购或调整库存。` - 低于下限:`【库存预警】物料【xxx】在仓库【xxx】中的库存数量低于下限(下限:X件),请尽快采购补货。` - 底部小字:`*若数量上下限不正确,请在库存预警中更改上下限` - 多条预警依次弹出,间隔300ms ### 3. 权限配置 - `warehouse:inventoryAlert:query` - 查询 - `warehouse:inventoryAlert:add` - 新增 - `warehouse:inventoryAlert:edit` - 修改 - `warehouse:inventoryAlert:remove` - 删除 - `warehouse:inventoryAlert:export` - 导出 --- ## 🗄️ 数据库设计 ### 表结构:`wm_inventory_alert` | 字段名 | 类型 | 长度 | 允许NULL | 默认值 | 说明 | |--------|------|------|----------|--------|------| | id | BIGINT | - | NO | AUTO_INCREMENT | 主键ID | | material_id | BIGINT | - | NO | - | 物料ID | | material_number | VARCHAR | 64 | YES | NULL | 物料编号(冗余) | | material_name | VARCHAR | 255 | YES | NULL | 物料名称(冗余) | | specification | VARCHAR | 255 | YES | NULL | 规格型号(冗余) | | warehouse_id | BIGINT | - | YES | NULL | 仓库ID(NULL=所有仓库) | | warehouse_name | VARCHAR | 64 | YES | NULL | 仓库名称(冗余) | | max_quantity | DECIMAL | 15,2 | YES | NULL | 库存上限数量 | | min_quantity | DECIMAL | 15,2 | YES | NULL | 库存下限数量 | | status | CHAR | 1 | NO | '0' | 状态(0启用 1停用) | | remark | VARCHAR | 500 | YES | NULL | 备注 | | create_by | VARCHAR | 64 | YES | '' | 创建者 | | create_time | DATETIME | - | YES | NULL | 创建时间 | | update_by | VARCHAR | 64 | YES | '' | 更新者 | | update_time | DATETIME | - | YES | NULL | 更新时间 | ### 索引设计 - **主键索引**:`PRIMARY KEY (id)` - **物料索引**:`INDEX idx_material_id (material_id)` - **仓库索引**:`INDEX idx_warehouse_id (warehouse_id)` - **状态索引**:`INDEX idx_status (status)` - **唯一索引**:`UNIQUE INDEX uk_material_warehouse (material_id, warehouse_id)` - 防止同一物料在同一仓库重复配置 ### 菜单配置 | menu_id | menu_name | parent_id | order_num | path | component | perms | menu_type | icon | |---------|-----------|-----------|-----------|------|-----------|-------|-----------|------| | 2400 | 库存预警 | 2082 | 1 | inventoryAlert | mes/warehouse/inventoryAlert/index | warehouse:inventoryAlert:list | C | warning | | 2401 | 库存预警查询 | 2400 | 1 | # | | warehouse:inventoryAlert:query | F | # | | 2402 | 库存预警新增 | 2400 | 2 | # | | warehouse:inventoryAlert:add | F | # | | 2403 | 库存预警修改 | 2400 | 3 | # | | warehouse:inventoryAlert:edit | F | # | | 2404 | 库存预警删除 | 2400 | 4 | # | | warehouse:inventoryAlert:remove | F | # | | 2405 | 库存预警导出 | 2400 | 5 | # | | warehouse:inventoryAlert:export | F | # | --- ## 💡 核心SQL查询逻辑说明 ### 预警触发查询:`selectAlertWithCurrentInventory` 此查询用于获取所有需要触发预警的配置,包含以下关键逻辑: #### 1. 实时库存计算 使用子查询计算每个物料的实时库存数量: - **入库汇总**:采购入库、销售退货、生产入库、盘点初始化、盘盈 - **出库汇总**:生产领料、销售出库、盘亏、其他出库 - **实时库存** = 入库总量 - 出库总量 #### 2. 过滤条件(INNER JOIN) - **sys_menu (menu_id=2082)**:检查"仓库管理"菜单状态 - `visible='0'`:菜单可见 - `status='0'`:菜单启用 - 作用:当仓库管理模块被禁用或隐藏时,不显示任何预警 #### 3. WHERE条件 - `a.status = '0'`:只查询启用的预警配置 - `menu.visible = '0'`:仓库管理菜单可见 - `menu.status = '0'`:仓库管理菜单启用 #### 4. HAVING条件 - `current_quantity > 0`:只查询库存数量大于0的记录 - 作用:避免对库存为0或负数的物料发出预警 - **注意**:此条件已能过滤掉从未入过库的物料(它们的库存为0),无需再关联其他库存表 #### 5. 预警判断(Service层) 查询结果返回后,在Service层进行预警判断: - `current_quantity > max_quantity`:触发"高于上限"预警 - `current_quantity < min_quantity`:触发"低于下限"预警 ### SQL执行流程图 ``` ┌─────────────────────────────────────────────────────────┐ │ 1. 从 wm_inventory_alert 获取所有预警配置 │ │ └─ WHERE status='0' (只查询启用的配置) │ └─────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 2. INNER JOIN sys_menu (menu_id=2082) │ │ └─ 检查仓库管理菜单是否可见且启用 │ │ └─ 如果菜单被禁用,则返回空结果 │ └─────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 3. 计算实时库存数量 (子查询) │ │ ├─ 汇总所有入库记录 │ │ ├─ 汇总所有出库记录 │ │ └─ current_quantity = 入库 - 出库 │ └─────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 4. HAVING current_quantity > 0 │ │ └─ 过滤掉库存为0或负数的记录 │ │ └─ 自动过滤掉从未入过库的物料 │ └─────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ 5. 返回结果到Service层 │ │ ├─ 判断是否超过上限 (> max_quantity) │ │ ├─ 判断是否低于下限 (< min_quantity) │ │ └─ 生成预警消息 │ └─────────────────────────────────────────────────────────┘ ``` --- ## 🔧 后端实现 ### 1. 目录结构 ``` yjh-mes/src/main/java/cn/sourceplan/warehouse/ ├── domain/ │ └── InventoryAlert.java # 实体类 ├── mapper/ │ └── InventoryAlertMapper.java # Mapper接口 ├── service/ │ ├── IInventoryAlertService.java # Service接口 │ └── impl/ │ └── InventoryAlertServiceImpl.java # Service实现 └── controller/ └── InventoryAlertController.java # 控制器 yjh-mes/src/main/resources/mapper/warehouse/ └── InventoryAlertMapper.xml # MyBatis映射文件 ``` ### 2. 实体类:`InventoryAlert.java` ```java package cn.sourceplan.warehouse.domain; import java.math.BigDecimal; import lombok.Data; import cn.sourceplan.common.annotation.Excel; import cn.sourceplan.common.core.domain.BaseEntity; /** * 库存预警对象 wm_inventory_alert * * @author admin * @date 2025-10-17 */ @Data public class InventoryAlert extends BaseEntity { private static final long serialVersionUID = 1L; /** 主键ID */ private Long id; /** 物料ID */ @Excel(name = "物料ID") private Long materialId; /** 物料编号 */ @Excel(name = "物料编号") private String materialNumber; /** 物料名称 */ @Excel(name = "物料名称") private String materialName; /** 规格型号 */ @Excel(name = "规格型号") private String specification; /** 仓库ID */ @Excel(name = "仓库ID") private Long warehouseId; /** 仓库名称 */ @Excel(name = "仓库名称") private String warehouseName; /** 库存上限数量 */ @Excel(name = "库存上限") private BigDecimal maxQuantity; /** 库存下限数量 */ @Excel(name = "库存下限") private BigDecimal minQuantity; /** 状态(0启用 1停用) */ @Excel(name = "状态", readConverterExp = "0=启用,1=停用") private String status; } ``` ### 3. Mapper接口:`InventoryAlertMapper.java` ```java package cn.sourceplan.warehouse.mapper; import java.util.List; import cn.sourceplan.warehouse.domain.InventoryAlert; import org.springframework.stereotype.Repository; /** * 库存预警Mapper接口 * * @author admin * @date 2025-10-17 */ @Repository public interface InventoryAlertMapper { /** * 查询库存预警 */ InventoryAlert selectInventoryAlertById(Long id); /** * 查询库存预警列表 */ List selectInventoryAlertList(InventoryAlert inventoryAlert); /** * 新增库存预警 */ int insertInventoryAlert(InventoryAlert inventoryAlert); /** * 修改库存预警 */ int updateInventoryAlert(InventoryAlert inventoryAlert); /** * 删除库存预警 */ int deleteInventoryAlertById(Long id); /** * 批量删除库存预警 */ int deleteInventoryAlertByIds(Long[] ids); /** * 查询所有启用的预警配置及其当前库存 */ List selectAlertWithCurrentInventory(); } ``` ### 4. Service接口:`IInventoryAlertService.java` ```java package cn.sourceplan.warehouse.service; import java.util.List; import cn.sourceplan.warehouse.domain.InventoryAlert; import com.alibaba.fastjson2.JSONObject; /** * 库存预警Service接口 * * @author admin * @date 2025-10-17 */ public interface IInventoryAlertService { InventoryAlert selectInventoryAlertById(Long id); List selectInventoryAlertList(InventoryAlert inventoryAlert); int insertInventoryAlert(InventoryAlert inventoryAlert); int updateInventoryAlert(InventoryAlert inventoryAlert); int deleteInventoryAlertByIds(Long[] ids); /** * 获取当前触发的所有预警信息 * 返回格式:[{type: 'max'/'min', message: '预警消息'}] */ List getTriggeredAlerts(); } ``` ### 5. Service实现:`InventoryAlertServiceImpl.java` ```java package cn.sourceplan.warehouse.service.impl; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import cn.sourceplan.warehouse.domain.InventoryAlert; import cn.sourceplan.warehouse.mapper.InventoryAlertMapper; import cn.sourceplan.warehouse.service.IInventoryAlertService; import com.alibaba.fastjson2.JSONObject; /** * 库存预警Service业务层处理 * * @author admin * @date 2025-10-17 */ @Service public class InventoryAlertServiceImpl implements IInventoryAlertService { @Autowired private InventoryAlertMapper inventoryAlertMapper; @Override public InventoryAlert selectInventoryAlertById(Long id) { return inventoryAlertMapper.selectInventoryAlertById(id); } @Override public List selectInventoryAlertList(InventoryAlert inventoryAlert) { return inventoryAlertMapper.selectInventoryAlertList(inventoryAlert); } @Override public int insertInventoryAlert(InventoryAlert inventoryAlert) { return inventoryAlertMapper.insertInventoryAlert(inventoryAlert); } @Override public int updateInventoryAlert(InventoryAlert inventoryAlert) { return inventoryAlertMapper.updateInventoryAlert(inventoryAlert); } @Override public int deleteInventoryAlertByIds(Long[] ids) { return inventoryAlertMapper.deleteInventoryAlertByIds(ids); } @Override public List getTriggeredAlerts() { List alerts = new ArrayList<>(); // 查询所有启用的预警配置及其当前库存 List alertConfigs = inventoryAlertMapper.selectAlertWithCurrentInventory(); for (InventoryAlert config : alertConfigs) { BigDecimal currentQty = config.getCurrentQuantity(); // 需要在实体类中添加临时字段 if (currentQty == null) { continue; } JSONObject alert = new JSONObject(); // 检查是否超过上限 if (config.getMaxQuantity() != null && currentQty.compareTo(config.getMaxQuantity()) > 0) { alert.put("type", "max"); alert.put("message", String.format( "【库存预警】物料【%s】在仓库【%s】中的库存数量高于上限(上限:%s件),请暂停采购或调整库存。", config.getMaterialName(), config.getWarehouseName() != null ? config.getWarehouseName() : "所有仓库", config.getMaxQuantity() )); alerts.add(alert); } // 检查是否低于下限 if (config.getMinQuantity() != null && currentQty.compareTo(config.getMinQuantity()) < 0) { alert = new JSONObject(); alert.put("type", "min"); alert.put("message", String.format( "【库存预警】物料【%s】在仓库【%s】中的库存数量低于下限(下限:%s件),请尽快采购补货。", config.getMaterialName(), config.getWarehouseName() != null ? config.getWarehouseName() : "所有仓库", config.getMinQuantity() )); alerts.add(alert); } } return alerts; } } ``` ### 6. Controller:`InventoryAlertController.java` ```java package cn.sourceplan.warehouse.controller; import java.util.List; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import cn.sourceplan.common.annotation.Log; import cn.sourceplan.common.core.controller.BaseController; import cn.sourceplan.common.core.domain.AjaxResult; import cn.sourceplan.common.core.page.TableDataInfo; import cn.sourceplan.common.enums.BusinessType; import cn.sourceplan.common.utils.poi.ExcelUtil; import cn.sourceplan.warehouse.domain.InventoryAlert; import cn.sourceplan.warehouse.service.IInventoryAlertService; import com.alibaba.fastjson2.JSONObject; /** * 库存预警Controller * * @author admin * @date 2025-10-17 */ @RestController @RequestMapping("/warehouse/inventoryAlert") public class InventoryAlertController extends BaseController { @Autowired private IInventoryAlertService inventoryAlertService; /** * 查询库存预警列表 */ @GetMapping("/list") public TableDataInfo list(InventoryAlert inventoryAlert) { startPage(); List list = inventoryAlertService.selectInventoryAlertList(inventoryAlert); return getDataTable(list); } /** * 导出库存预警列表 */ @Log(title = "库存预警", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, InventoryAlert inventoryAlert) { List list = inventoryAlertService.selectInventoryAlertList(inventoryAlert); ExcelUtil util = new ExcelUtil<>(InventoryAlert.class); util.exportExcel(response, list, "库存预警数据"); } /** * 获取库存预警详细信息 */ @GetMapping(value = "/{id}") public AjaxResult getInfo(@PathVariable("id") Long id) { return success(inventoryAlertService.selectInventoryAlertById(id)); } /** * 新增库存预警 */ @Log(title = "库存预警", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@RequestBody InventoryAlert inventoryAlert) { return toAjax(inventoryAlertService.insertInventoryAlert(inventoryAlert)); } /** * 修改库存预警 */ @Log(title = "库存预警", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@RequestBody InventoryAlert inventoryAlert) { return toAjax(inventoryAlertService.updateInventoryAlert(inventoryAlert)); } /** * 删除库存预警 */ @Log(title = "库存预警", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") public AjaxResult remove(@PathVariable Long[] ids) { return toAjax(inventoryAlertService.deleteInventoryAlertByIds(ids)); } /** * 获取当前触发的预警信息(供首页调用) */ @GetMapping("/triggered") public AjaxResult getTriggeredAlerts() { List alerts = inventoryAlertService.getTriggeredAlerts(); return success(alerts); } } ``` ### 7. MyBatis映射:`InventoryAlertMapper.xml` ```xml SELECT id, material_id, material_number, material_name, specification, warehouse_id, warehouse_name, max_quantity, min_quantity, status, remark, create_by, create_time, update_by, update_time FROM wm_inventory_alert INSERT INTO wm_inventory_alert material_id, material_number, material_name, specification, warehouse_id, warehouse_name, max_quantity, min_quantity, status, remark, create_by, create_time, #{materialId}, #{materialNumber}, #{materialName}, #{specification}, #{warehouseId}, #{warehouseName}, #{maxQuantity}, #{minQuantity}, #{status}, #{remark}, #{createBy}, #{createTime}, UPDATE wm_inventory_alert material_id = #{materialId}, material_number = #{materialNumber}, material_name = #{materialName}, specification = #{specification}, warehouse_id = #{warehouseId}, warehouse_name = #{warehouseName}, max_quantity = #{maxQuantity}, min_quantity = #{minQuantity}, status = #{status}, remark = #{remark}, update_by = #{updateBy}, update_time = #{updateTime}, WHERE id = #{id} DELETE FROM wm_inventory_alert WHERE id = #{id} DELETE FROM wm_inventory_alert WHERE id IN #{id} ``` --- ## 🎨 前端实现 ### 1. 目录结构 ``` mes-ui/src/ ├── api/mes/warehouse/ │ └── inventoryAlert.js # API接口 ├── views/mes/warehouse/inventoryAlert/ │ ├── index.vue # 列表页面 │ └── form.vue # 新增/编辑表单(可选) └── views/index.vue # 首页(需修改) ``` ### 2. API接口:`inventoryAlert.js` ```javascript import request from '@/utils/request' // 查询库存预警列表 export function listInventoryAlert(query) { return request({ url: '/warehouse/inventoryAlert/list', method: 'get', params: query }) } // 查询库存预警详细 export function getInventoryAlert(id) { return request({ url: '/warehouse/inventoryAlert/' + id, method: 'get' }) } // 新增库存预警 export function addInventoryAlert(data) { return request({ url: '/warehouse/inventoryAlert', method: 'post', data: data }) } // 修改库存预警 export function updateInventoryAlert(data) { return request({ url: '/warehouse/inventoryAlert', method: 'put', data: data }) } // 删除库存预警 export function delInventoryAlert(id) { return request({ url: '/warehouse/inventoryAlert/' + id, method: 'delete' }) } // 获取当前触发的预警信息 export function getTriggeredAlerts() { return request({ url: '/warehouse/inventoryAlert/triggered', method: 'get' }) } ``` ### 3. 列表页面:`index.vue` ```vue ``` --- ## 🔔 首页预警弹窗 ### 修改首页:`mes-ui/src/views/index.vue` 在首页的 `mounted` 生命周期中添加预警检查逻辑,使用Notification组件在右上角显示: ```vue ``` --- ## 📋 实施步骤 ### 1. 数据库变更 ```bash # 执行SQL脚本 mysql -u用户名 -p密码 数据库名 < .tasks/2025-10-17_兴万达改进(库存预警).sql ``` ### 2. 后端实现 1. 创建实体类 `InventoryAlert.java` 2. 创建Mapper接口 `InventoryAlertMapper.java` 3. 创建XML映射文件 `InventoryAlertMapper.xml` 4. 创建Service接口 `IInventoryAlertService.java` 5. 创建Service实现 `InventoryAlertServiceImpl.java` 6. 创建Controller `InventoryAlertController.java` ### 3. 前端实现 1. 创建API文件 `inventoryAlert.js` 2. 创建列表页面 `index.vue` 3. 修改首页 `index.vue` 添加预警弹窗 ### 4. 测试验证 1. 重启后端服务 2. 清除浏览器缓存 3. 登录系统,检查菜单是否显示 4. 测试增删改查功能 5. 测试首页预警弹窗 --- ## 🧪 测试用例 ### 1. 功能测试 #### 测试用例1:新增库存预警 - **操作步骤**: 1. 进入库存预警页面 2. 点击"新增"按钮 3. 选择物料、仓库 4. 设置上限1000,下限100 5. 点击"确定" - **预期结果**: - 成功新增一条预警配置 - 列表中显示新增的记录 #### 测试用例2:修改库存预警 - **操作步骤**: 1. 选择一条预警记录 2. 点击"修改"按钮 3. 修改上限为2000 4. 点击"确定" - **预期结果**: - 成功修改预警配置 - 列表中显示更新后的数据 #### 测试用例3:删除库存预警 - **操作步骤**: 1. 选择一条预警记录 2. 点击"删除"按钮 3. 确认删除 - **预期结果**: - 成功删除预警配置 - 列表中不再显示该记录 #### 测试用例4:触发高于上限预警 - **操作步骤**: 1. 设置物料A的上限为100 2. 确保物料A的当前库存为150 3. 退出登录,重新登录 - **预期结果**: - 首页右侧弹出预警弹窗 - 显示"物料【A】库存数量高于上限"的提示 - 3秒后自动关闭 #### 测试用例5:触发低于下限预警 - **操作步骤**: 1. 设置物料B的下限为100 2. 确保物料B的当前库存为50 3. 退出登录,重新登录 - **预期结果**: - 首页右侧弹出预警弹窗 - 显示"物料【B】库存数量低于下限"的提示 - 3秒后自动关闭 ### 2. 权限测试 #### 测试用例6:权限控制 - **操作步骤**: 1. 创建测试角色,仅赋予查询权限 2. 使用该角色登录 3. 尝试新增、修改、删除操作 - **预期结果**: - 只能看到列表 - 新增、修改、删除按钮不显示 ### 3. 性能测试 #### 测试用例7:大数据量测试 - **操作步骤**: 1. 插入1000条预警配置 2. 访问列表页面 3. 执行查询、分页操作 - **预期结果**: - 页面响应时间 < 2秒 - 分页正常工作 --- ## 📌 注意事项 1. **唯一性约束**:同一物料在同一仓库只能有一条预警配置 2. **warehouse_id为NULL**:表示对该物料的所有仓库进行预警监控 3. **预警触发条件**(需同时满足): - 预警配置状态为启用(status='0') - "仓库管理"菜单可见且启用(menu_id=2082, visible='0', status='0') - 当前库存数量 > 0(通过子查询计算实时库存,自动过滤掉从未入过库的物料) - 库存数量超过上限或低于下限 4. **预警触发时机**:登录首页时检查,也可以考虑定时任务推送 5. **物料选择来源**:从物料主数据表(md_material)读取,只显示启用状态(status='0')的物料 6. **性能优化**: - 即时库存查询使用复杂子查询,建议添加索引优化 - 预警检查可以异步执行,避免阻塞首页加载 - 预警结果可以考虑添加Redis缓存(5分钟过期) 7. **扩展性**: - 可以考虑添加邮件、短信通知功能 - 可以添加预警历史记录表 - 可以添加预警统计分析功能 - 可以添加预警级别(紧急/警告/提示) --- ## 📊 效果预览 ### 库存预警列表页面 ``` ┌─────────────────────────────────────────────────────────────┐ │ 库存预警 [新增][修改][删除][导出] │ ├─────────────────────────────────────────────────────────────┤ │ 物料编号 │ 物料名称 │ 规格型号 │ 仓库 │ 上限 │ 下限 │ 状态 │ 操作 │ ├─────────┼─────────┼─────────┼──────┼──────┼──────┼──────┼──────┤ │ MAT001 │ 钢板 │ 10mm │ 原材料│ 1000 │ 100 │ 启用 │ 修改删│ │ MAT002 │ 螺丝 │ M8 │ 所有 │ 5000 │ 500 │ 启用 │ 修改删│ └─────────────────────────────────────────────────────────────┘ ``` ### 首页预警通知(右上角Notification) ``` ┌──────────────────────────────────┐ │ 库存预警提醒 [×] │ ├──────────────────────────────────┤ │ ⚠ 【库存预警】物料【钢板】在仓库 │ │ 【原材料】中的库存数量高于上限 │ │ (上限:1000件),请暂停采购 │ │ 或调整库存。 │ │ │ │ *若数量上下限不正确,请在库存预警│ │ 中更改上下限 │ └──────────────────────────────────┘ ┌──────────────────────────────────┐ │ 库存预警提醒 [×] │ ├──────────────────────────────────┤ │ ❌ 【库存预警】物料【螺丝】在仓库│ │ 【所有仓库】中的库存数量低于 │ │ 下限(下限:500件),请尽快 │ │ 采购补货。 │ │ │ │ *若数量上下限不正确,请在库存预警│ │ 中更改上下限 │ └──────────────────────────────────┘ 说明: - 显示位置:页面右上角 - 显示时长:5秒后自动消失 - 多条预警:依次错开弹出(间隔300ms) - 可手动关闭:点击右上角×号 ``` --- ## ✅ 完成标志 - [x] SQL脚本编写完成 - [x] 数据库表创建完成 - [x] 菜单和权限配置完成 - [x] 后端实体类、Mapper、Service、Controller实现完成 - [x] 前端API接口实现完成 - [x] 前端列表页面实现完成 - [x] 首页预警Notification实现完成 - [x] 预警触发条件优化(菜单状态、库存存在性、库存>0) - [x] 物料选择改为从md_material读取 - [x] 功能测试完成 - [ ] 上线部署(待部署) --- ## 🔄 更新记录 ### v1.3 - 2025-10-17 - ✅ 移除对 `wm_real_time_inventory` 表的依赖(该表在部分数据库中不存在) - ✅ 通过 `HAVING current_quantity > 0` 自动过滤没有库存的物料 - ✅ 简化SQL查询逻辑,提高兼容性 ### v1.2 - 2025-10-17 - ✅ 首页预警弹窗改为右上角Notification通知 - ✅ 显示时长从3秒改为5秒 - ✅ 多条预警依次错开弹出(间隔300ms) - ✅ 增加预警触发条件: - 关联sys_menu检查仓库管理菜单状态 - 只查询库存数量>0的记录 - ✅ 物料选择从md_material读取(只显示启用状态) ### v1.1 - 2025-10-17 - 完成后端所有代码实现 - 完成前端所有代码实现 - 完成基础功能测试 ### v1.0 - 2025-10-17 - 需求分析和设计完成 - SQL脚本编写完成 - 代码框架设计完成 --- **文档版本**:v1.3 **编写日期**:2025-10-17 **最后更新**:2025-10-17