diff --git a/database_migration_add_inventory_threshold.sql b/database_migration_add_inventory_threshold.sql new file mode 100644 index 0000000..74db1bd --- /dev/null +++ b/database_migration_add_inventory_threshold.sql @@ -0,0 +1,8 @@ +-- 药品库存智能预警功能数据库升级脚本 +-- 添加最低库存阈值字段 + +-- 为药品信息表添加最低库存阈值字段 +ALTER TABLE `yaopinxinxi` ADD COLUMN `zuidikucunyuzhi` int(11) NULL DEFAULT 0 COMMENT '最低库存阈值' AFTER `kucun`; + +-- 更新现有记录,设置默认最低库存阈值为库存的20%(可根据实际需求调整) +UPDATE `yaopinxinxi` SET `zuidikucunyuzhi` = FLOOR(`kucun` * 0.2) WHERE `zuidikucunyuzhi` = 0 AND `kucun` > 0; diff --git a/manage_code/src/views/yaopinxinxi/formModel.vue b/manage_code/src/views/yaopinxinxi/formModel.vue index f0e561a..3208731 100644 --- a/manage_code/src/views/yaopinxinxi/formModel.vue +++ b/manage_code/src/views/yaopinxinxi/formModel.vue @@ -55,6 +55,14 @@ + + + + + + 药品库存统计 + + + + 库存预警 + + + + 采购建议 @@ -111,6 +119,17 @@ {{scope.row.kucun}} + + + { @@ -439,6 +458,51 @@ }) }) } + //显示低库存预警 + const showLowStockAlert = () => { + context.$http({ + url: `${tableName}/lowstock`, + method: 'get' + }).then(res=>{ + let lowStockList = res.data.data + if(lowStockList.length === 0) { + context.$message.success('当前所有药品库存均在正常范围内') + return + } + let message = '以下药品库存低于阈值:\n\n' + lowStockList.forEach(item => { + message += `药品名称:${item.yaopinmingcheng}\n当前库存:${item.kucun}\n最低阈值:${item.zuidikucunyuzhi}\n\n` + }) + context.$alert(message, '低库存详情', { + confirmButtonText: '确定', + type: 'warning' + }) + }) + } + //显示采购建议 + const showPurchaseSuggestions = () => { + context.$http({ + url: `${tableName}/purchase/suggestions`, + method: 'get' + }).then(res=>{ + let suggestions = res.data.data + if(suggestions.length === 0) { + context.$message.success('当前所有药品库存充足,无需采购') + return + } + let message = '药品采购建议清单:\n\n' + let totalCost = 0 + suggestions.forEach(item => { + message += `药品名称:${item.yaopinmingcheng}\n规格:${item.guige}\n当前库存:${item.currentStock}\n最低阈值:${item.threshold}\n建议采购数量:${item.suggestedQuantity}\n预计费用:¥${item.estimatedCost.toFixed(2)}\n\n` + totalCost += item.estimatedCost + }) + message += `预计总费用:¥${totalCost.toFixed(2)}` + context.$alert(message, '采购建议详情', { + confirmButtonText: '确定', + type: 'success' + }) + }) + } import rukujiluFormModel from '@/views/rukujilu/formModel' const rukujiluFormModelRef = ref(null) const rukujiluCrossAddOrUpdateHandler = (row,type,crossOptAudit,crossOptPay,statusColumnName,tips,statusColumnValue) => { @@ -473,15 +537,31 @@ } //初始化 const init = () => { - let urlkucun = 'yaopinxinxi/remind/kucun/1' - urlkucun+=`?remindend=5` - urlkucun+=`&remindstart=0` context.$http({ - url: urlkucun, + url: `${tableName}/lowstock/count`, method: 'get' }).then(res=>{ if(res.data.count>0) { - context?.$alert(`库存有${res.data.count}条记录达到预警`) + context.$alert(`库存有${res.data.count}条记录达到预警`, '库存预警提示', { + confirmButtonText: '查看详情', + callback: (action) => { + if (action === 'confirm') { + context.$http({ + url: `${tableName}/lowstock`, + method: 'get' + }).then(res=>{ + let lowStockList = res.data.data + let message = '以下药品库存低于阈值:\n\n' + lowStockList.forEach(item => { + message += `药品名称:${item.yaopinmingcheng}\n当前库存:${item.kucun}\n最低阈值:${item.zuidikucunyuzhi}\n\n` + }) + context.$alert(message, '低库存详情', { + confirmButtonText: '确定' + }) + }) + } + } + }) } }) getList() diff --git a/server_code/src/main/java/com/cl/controller/ChukujiluController.java b/server_code/src/main/java/com/cl/controller/ChukujiluController.java index 39cece5..b36ece1 100644 --- a/server_code/src/main/java/com/cl/controller/ChukujiluController.java +++ b/server_code/src/main/java/com/cl/controller/ChukujiluController.java @@ -28,12 +28,15 @@ import com.cl.entity.view.ChukujiluView; import com.cl.service.ChukujiluService; +import com.cl.service.YaopinxinxiService; import com.cl.service.TokenService; import com.cl.utils.PageUtils; import com.cl.utils.R; import com.cl.utils.MPUtil; import com.cl.utils.MapUtils; import com.cl.utils.CommonUtil; +import com.cl.entity.YaopinxinxiEntity; +import com.baomidou.mybatisplus.mapper.EntityWrapper; /** * 出库记录 @@ -47,6 +50,9 @@ public class ChukujiluController { @Autowired private ChukujiluService chukujiluService; + + @Autowired + private YaopinxinxiService yaopinxinxiService; @@ -139,9 +145,25 @@ public R detail(@PathVariable("id") Long id){ */ @RequestMapping("/save") @SysLog("新增出库记录") + @Transactional public R save(@RequestBody ChukujiluEntity chukujilu, HttpServletRequest request){ //ValidatorUtils.validateEntity(chukujilu); chukujiluService.insert(chukujilu); + + YaopinxinxiEntity drug = yaopinxinxiService.selectOne(new EntityWrapper() + .eq("yaopinmingcheng", chukujilu.getYaopinmingcheng())); + + if (drug != null) { + Integer currentStock = drug.getKucun() != null ? drug.getKucun() : 0; + Integer outboundQuantity = chukujilu.getKucun() != null ? chukujilu.getKucun() : 0; + Integer newStock = currentStock - outboundQuantity; + if (newStock < 0) { + newStock = 0; + } + drug.setKucun(newStock); + yaopinxinxiService.updateById(drug); + } + return R.ok(); } diff --git a/server_code/src/main/java/com/cl/controller/RukujiluController.java b/server_code/src/main/java/com/cl/controller/RukujiluController.java index 065aeb5..6bab179 100644 --- a/server_code/src/main/java/com/cl/controller/RukujiluController.java +++ b/server_code/src/main/java/com/cl/controller/RukujiluController.java @@ -28,12 +28,15 @@ import com.cl.entity.view.RukujiluView; import com.cl.service.RukujiluService; +import com.cl.service.YaopinxinxiService; import com.cl.service.TokenService; import com.cl.utils.PageUtils; import com.cl.utils.R; import com.cl.utils.MPUtil; import com.cl.utils.MapUtils; import com.cl.utils.CommonUtil; +import com.cl.entity.YaopinxinxiEntity; +import com.baomidou.mybatisplus.mapper.EntityWrapper; /** * 入库记录 @@ -47,6 +50,9 @@ public class RukujiluController { @Autowired private RukujiluService rukujiluService; + + @Autowired + private YaopinxinxiService yaopinxinxiService; @@ -139,9 +145,21 @@ public R detail(@PathVariable("id") Long id){ */ @RequestMapping("/save") @SysLog("新增入库记录") + @Transactional public R save(@RequestBody RukujiluEntity rukujilu, HttpServletRequest request){ //ValidatorUtils.validateEntity(rukujilu); rukujiluService.insert(rukujilu); + + YaopinxinxiEntity drug = yaopinxinxiService.selectOne(new EntityWrapper() + .eq("yaopinmingcheng", rukujilu.getYaopinmingcheng())); + + if (drug != null) { + Integer currentStock = drug.getKucun() != null ? drug.getKucun() : 0; + Integer inboundQuantity = rukujilu.getKucun() != null ? rukujilu.getKucun() : 0; + drug.setKucun(currentStock + inboundQuantity); + yaopinxinxiService.updateById(drug); + } + return R.ok(); } diff --git a/server_code/src/main/java/com/cl/controller/YaopinxinxiController.java b/server_code/src/main/java/com/cl/controller/YaopinxinxiController.java index 1729cb2..521e441 100644 --- a/server_code/src/main/java/com/cl/controller/YaopinxinxiController.java +++ b/server_code/src/main/java/com/cl/controller/YaopinxinxiController.java @@ -381,6 +381,22 @@ public R count(@RequestParam Map params,YaopinxinxiEntity yaopin return R.ok().put("data", count); } + @RequestMapping("/lowstock") + public R getLowStockDrugs() { + List lowStockDrugs = yaopinxinxiService.selectLowStockDrugs(); + return R.ok().put("data", lowStockDrugs).put("count", lowStockDrugs.size()); + } + + @RequestMapping("/lowstock/count") + public R getLowStockCount() { + List lowStockDrugs = yaopinxinxiService.selectLowStockDrugs(); + return R.ok().put("count", lowStockDrugs.size()); + } + @RequestMapping("/purchase/suggestions") + public R getPurchaseSuggestions() { + List> suggestions = yaopinxinxiService.generatePurchaseSuggestions(); + return R.ok().put("data", suggestions).put("count", suggestions.size()); + } } diff --git a/server_code/src/main/java/com/cl/entity/YaopinxinxiEntity.java b/server_code/src/main/java/com/cl/entity/YaopinxinxiEntity.java index e971761..2ad3792 100644 --- a/server_code/src/main/java/com/cl/entity/YaopinxinxiEntity.java +++ b/server_code/src/main/java/com/cl/entity/YaopinxinxiEntity.java @@ -89,6 +89,12 @@ public YaopinxinxiEntity(T t) { private Integer kucun; + /** + * 最低库存阈值 + */ + + private Integer zuidikucunyuzhi; + /** * 新增时间 */ @@ -193,6 +199,18 @@ public void setKucun(Integer kucun) { public Integer getKucun() { return kucun; } + /** + * 设置:最低库存阈值 + */ + public void setZuidikucunyuzhi(Integer zuidikucunyuzhi) { + this.zuidikucunyuzhi = zuidikucunyuzhi; + } + /** + * 获取:最低库存阈值 + */ + public Integer getZuidikucunyuzhi() { + return zuidikucunyuzhi; + } /** * 设置:新增时间 */ diff --git a/server_code/src/main/java/com/cl/service/YaopinxinxiService.java b/server_code/src/main/java/com/cl/service/YaopinxinxiService.java index 9b59cf7..9c952a0 100644 --- a/server_code/src/main/java/com/cl/service/YaopinxinxiService.java +++ b/server_code/src/main/java/com/cl/service/YaopinxinxiService.java @@ -34,7 +34,9 @@ public interface YaopinxinxiService extends IService { List> selectGroup(Map params,Wrapper wrapper); - + List selectLowStockDrugs(); + + List> generatePurchaseSuggestions(); } diff --git a/server_code/src/main/java/com/cl/service/impl/YaopinxinxiServiceImpl.java b/server_code/src/main/java/com/cl/service/impl/YaopinxinxiServiceImpl.java index b42feef..48b1166 100644 --- a/server_code/src/main/java/com/cl/service/impl/YaopinxinxiServiceImpl.java +++ b/server_code/src/main/java/com/cl/service/impl/YaopinxinxiServiceImpl.java @@ -65,7 +65,45 @@ public List> selectGroup(Map params, Wrapper return baseMapper.selectGroup(params, wrapper); } + @Override + public List selectLowStockDrugs() { + return baseMapper.selectList(new EntityWrapper() + .isNotNull("zuidikucunyuzhi") + .gt("zuidikucunyuzhi", 0) + .lt("kucun", "zuidikucunyuzhi")); + } + @Override + public List> generatePurchaseSuggestions() { + List lowStockDrugs = selectLowStockDrugs(); + List> suggestions = new ArrayList<>(); + + for (YaopinxinxiEntity drug : lowStockDrugs) { + Map suggestion = new HashMap<>(); + suggestion.put("id", drug.getId()); + suggestion.put("yaopinmingcheng", drug.getYaopinmingcheng()); + suggestion.put("guige", drug.getGuige()); + suggestion.put("danjia", drug.getDanjia()); + suggestion.put("currentStock", drug.getKucun()); + suggestion.put("threshold", drug.getZuidikucunyuzhi()); + + Integer threshold = drug.getZuidikucunyuzhi() != null ? drug.getZuidikucunyuzhi() : 0; + Integer currentStock = drug.getKucun() != null ? drug.getKucun() : 0; + Integer suggestedQuantity = threshold * 3 - currentStock; + if (suggestedQuantity <= 0) { + suggestedQuantity = threshold * 2; + } + suggestion.put("suggestedQuantity", suggestedQuantity); + + Double price = drug.getDanjia() != null ? drug.getDanjia() : 0.0; + Double estimatedCost = price * suggestedQuantity; + suggestion.put("estimatedCost", estimatedCost); + + suggestions.add(suggestion); + } + + return suggestions; + } }