java開發(fā)WMS倉(cāng)庫(kù)商品預(yù)警需求示例解析
1.預(yù)警需求
為了更好的管理商品日期,需要對(duì)倉(cāng)庫(kù)的商品進(jìn)行預(yù)警管理,對(duì)商品的保質(zhì)期控制在一個(gè)范圍內(nèi)提示出來(lái),也可以通過(guò)該功能間接的展示出一個(gè)商品的的銷量程度和對(duì)下次進(jìn)貨做個(gè)考量!
1. 預(yù)警需求分析
- 前端界面需要設(shè)置商品的預(yù)警天數(shù)
- 后端保存預(yù)警天數(shù)
- 數(shù)據(jù)庫(kù)有字段存放商品需要預(yù)警的天數(shù)
- 通過(guò)定時(shí)器運(yùn)行指點(diǎn)方法算出對(duì)應(yīng)那些商品的批次存低于設(shè)置的預(yù)警天數(shù)
- 查詢出來(lái)在wms首頁(yè)展出
2.數(shù)據(jù)庫(kù)表
對(duì)于前端界面的開發(fā)不做過(guò)多的代碼分析,本次重點(diǎn)展示商品預(yù)警實(shí)現(xiàn)思路!!
數(shù)據(jù)庫(kù)用到到字段會(huì)截取出來(lái),便于理解!
商品表數(shù)據(jù):
預(yù)警表數(shù)據(jù):
商品批次表:
商品批次表中添加預(yù)警字段:后續(xù)查詢對(duì)應(yīng)的預(yù)警信息作為標(biāo)志
Mysql使用到的函數(shù)
//查詢當(dāng)前時(shí)間 select now(); //獲取時(shí)間相減 獲取到天數(shù) 參數(shù)是前面-后面 select DATEDIFF("2022-9-10", now()) as day
如:
由此我們就可以通過(guò)函數(shù)算出商品距離過(guò)期的天數(shù)
查詢語(yǔ)句:
SELECT bb.id as batchId, bb.product_id as productId, DATEDIFF(bb.ed, now()) as warnDate FROM `商品批次表` bb
需要注意: 查詢或者修改表數(shù)據(jù)我們?nèi)绻灰褂媚硞€(gè)字段去修改,盡量查詢下該表更新的字段數(shù)據(jù)是否有重復(fù)的數(shù)據(jù)并且其他字段可能跟我們預(yù)想不一致,必須要修改的,我們就應(yīng)該使用多字段去查詢修改
查詢重復(fù)數(shù)據(jù)
select * from 表 GROUP BY 字段 HAVING count(*)>1
查詢預(yù)警批次數(shù)量: 因?yàn)樾枰故境鰜?lái)在前端可以給用戶點(diǎn)擊查看貨物存放的地方,所以查詢出來(lái)的數(shù)據(jù)庫(kù)存數(shù)量要大于0
select IFNULL(count(*),0) from basic_batch bb LEFT JOIN handle_stock hs on bb.id=hs.batch_id where bb.is_warn_date=1 and hs.reality_number>0
2.后端代碼實(shí)現(xiàn):
1. 定時(shí)器任務(wù)
使用Scheduled作為定時(shí)器,通過(guò)cron語(yǔ)法指定運(yùn)行時(shí)間,每3小時(shí)運(yùn)行一次表達(dá)式如下
代碼詳情注釋寫的也比較清楚
/** * 每3小時(shí)運(yùn)行一次 */ @Scheduled(cron = "0 0 0/3 * * ?") public void goodsWarn(){ BasicProductGpExample basicProductGpExample = new BasicProductGpExample(); List<String> warnBatchIds=new ArrayList<>(); //查詢商品預(yù)警表信息 商品的貨號(hào)是唯一的 (所以現(xiàn)在是全表查詢出來(lái)) // 這里把商品預(yù)警信息都查詢出來(lái)為了以后擴(kuò)展做庫(kù)存預(yù)警設(shè)置 List<BasicProductGp> basicProductGps = productGpMapper.selectByExample(basicProductGpExample); //查詢商品批次表:商品批次和天數(shù) 商品的批次是唯一的,存在多個(gè)商品貨號(hào)(所以現(xiàn)在是全表查詢出來(lái)) // 以后擴(kuò)展商品庫(kù)存預(yù)警只要在這個(gè)查詢里面添加關(guān)聯(lián)庫(kù)存數(shù)量查詢出來(lái),然后在過(guò)濾里面添加預(yù)警數(shù)量判斷 List<BasicProductWarn> warnGoodsDay = basicBatchMapper.selectWarnGoodsDay(); Long start=System.currentTimeMillis(); for (BasicProductGp basicProductGp : basicProductGps) { //商品預(yù)警日期 Integer warnDate = basicProductGp.getWarnDate(); //商品貨號(hào)編碼 Integer productId = basicProductGp.getProductId(); //查詢出符合預(yù)警的商品批次 List<String> warnBatchId = warnGoodsDay.stream().filter(warnGood -> warnGood.getProductId().equals(productId) && warnGood.getWarnDate() <= warnDate) .map(BasicProductWarn::getBatchId).collect(Collectors.toList()); warnBatchIds.addAll(warnBatchId); } Long end=System.currentTimeMillis(); log.info("消耗時(shí)長(zhǎng):"+(end-start)/1000+"秒"); //獲取出所有需要提醒的批次號(hào) log.info(Arrays.toString(warnBatchIds.toArray())); //數(shù)組為空時(shí)不更新 if (CollectionUtils.isEmpty(warnBatchIds)){ log.info("沒(méi)有需要預(yù)警的商品批次!"); return; } //修改批次狀態(tài)為1標(biāo)識(shí)預(yù)警日期已經(jīng)到達(dá) //使用boolean類型保存數(shù)據(jù)庫(kù)會(huì)自動(dòng)把true轉(zhuǎn)換成1 int count = basicBatchMapper.updateByBatchWarn(warnBatchIds, true); log.info("更新預(yù)警數(shù)量:{}",count); }
這里面還存在一個(gè)問(wèn)題,如果批次被打上預(yù)警標(biāo)識(shí),此時(shí)客戶更改了商品的預(yù)警日期,那么商品的預(yù)警可能就沒(méi)有到達(dá),但是頁(yè)面查詢預(yù)警也能被查詢出來(lái),解決方案,使用mq來(lái)做處理,當(dāng)客戶更改了商品編碼預(yù)警信息,那么程序就把更改的商品編碼存到mq中我們?cè)趯憘€(gè)消費(fèi)方法來(lái)消費(fèi)更改后的商品編碼單獨(dú)走波預(yù)警方法
2.優(yōu)化加入隊(duì)列
先不考慮全局預(yù)警設(shè)置,只對(duì)勾選多個(gè)商品進(jìn)行添加隊(duì)列
使用了springBoot的rabbitMq模板類RabbitTemplate
pom.xml引入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
生產(chǎn)者添加消息到隊(duì)列中
//存入隊(duì)列 routingkey名稱 存入的數(shù)據(jù) rabbitTemplate.convertAndSend("warnGoodsQueue",ids);
消費(fèi)者:
/** * 消費(fèi)預(yù)警隊(duì)列信息 */ @Configuration @RabbitListener(bindings = @QueueBinding(value = @Queue(value = "warnGoodsQueue", durable = "true"), exchange = @Exchange(value = "warnGoodsExchange", ignoreDeclarationExceptions = "true", type = ExchangeTypes.TOPIC), key = {"warnGoodsQueue"})) public class WarnGoodsQueue { @RabbitHandler public void process(List<String> ids) { if (ids == null) { System.out.println("空"); } System.out.println(ids); } }
3.注意:關(guān)于Mq
生產(chǎn)者,生產(chǎn)的數(shù)據(jù)類型一定要要和消費(fèi)者獲取的類型要一致,否則會(huì)無(wú)限循環(huán)報(bào)錯(cuò)
Listener threw exception No method found for class [B
4.測(cè)試是否成功
生產(chǎn)者存入隊(duì)列:
消費(fèi)者獲取數(shù)據(jù):
如果消費(fèi)方法報(bào)錯(cuò)不try/catch的話,隊(duì)列就會(huì)一直重試該條數(shù)據(jù)
可以看出已經(jīng)獲取到修改預(yù)警商品的商品編號(hào),這個(gè)時(shí)候我們只要寫方法做相應(yīng)的處理就行了
我們也可以去RabbitMq管理界面查看隊(duì)列信息
消費(fèi)方法代碼:
public void updateWarnGoods(List<Integer> ids){ if (CollectionUtils.isEmpty(ids)){ return; } BasicProductGpExample example = new BasicProductGpExample(); example.createCriteria().andProductIdIn(ids); //查詢商品信息 商品的貨號(hào)是唯一的 (查詢變動(dòng)的商品貨號(hào)) List<BasicProductGp> basicProductGps = productGpMapper.selectByExample(example); //商品批次和天數(shù) 商品的批次是唯一的,存在多個(gè)商品貨號(hào)(查詢變動(dòng)的商品貨號(hào)) List<BasicProductWarn> warnGoodsDay = basicBatchMapper.selectWarnGoodsDayList(ids); //需要預(yù)警的集合 List<String> warnBatchIds=new ArrayList<>(); //不需要預(yù)警的集合 List<String> notWarnBatchIds=new ArrayList<>(); Long start=System.currentTimeMillis(); for (BasicProductGp basicProductGp : basicProductGps) { //商品預(yù)警日期 Integer warnDate = basicProductGp.getWarnDate(); //商品貨號(hào)編碼 Integer productId = basicProductGp.getProductId(); //查詢出符合預(yù)警的商品批次 List<String> warnBatchId = warnGoodsDay.stream().filter(warnGood -> { if (warnGood.getProductId().equals(productId)){ //匹配到的商品數(shù)據(jù) //2種情況:一種要預(yù)警另外一種不要預(yù)警 if (warnGood.getWarnDate() <= warnDate){ //預(yù)警的返回 return true; } //不要預(yù)警的添加到不預(yù)警集合用于更新 notWarnBatchIds.add(warnGood.getBatchId()); } return false; }).map(BasicProductWarn::getBatchId).collect(Collectors.toList()); //把預(yù)警集合添加到預(yù)警大集合中 warnBatchIds.addAll(warnBatchId); } long end=System.currentTimeMillis(); log.info("更新預(yù)警商品耗時(shí):{}秒",(end-start)/1000); //結(jié)束后會(huì)得到2種集合:不預(yù)警和要預(yù)警集合 //更新未預(yù)警商品 if (!CollectionUtils.isEmpty(notWarnBatchIds)){ int count = basicBatchMapper.updateByBatchWarn(notWarnBatchIds, false); log.info("更新預(yù)警數(shù)量:{}",count); } //更新預(yù)警商品 if (!CollectionUtils.isEmpty(warnBatchIds)){ int count = basicBatchMapper.updateByBatchWarn(warnBatchIds, true); log.info("更新預(yù)警數(shù)量:{}",count); } }
關(guān)于一些代碼存在冗余我們可以提取成一個(gè)公共方法進(jìn)行調(diào)用,后續(xù)也對(duì)全局修改預(yù)警Mq隊(duì)列走波運(yùn)算預(yù)警的商品
重載方法:
/*** * 重載方法 * 用于運(yùn)算全局商品類型 * 用于更新商品預(yù)警時(shí)間 */ public void updateWarnGoods(){ BasicProductGpExample example = new BasicProductGpExample(); //查詢商品信息 商品的貨號(hào)是唯一的 (查詢變動(dòng)的商品貨號(hào)) List<BasicProductGp> basicProductGps = productGpMapper.selectByExample(example); //商品批次和天數(shù) 商品的批次是唯一的,存在多個(gè)商品貨號(hào)(查詢變動(dòng)的商品貨號(hào)) List<BasicProductWarn> warnGoodsDay = basicBatchMapper.selectWarnGoodsDay(); //需要預(yù)警的集合 List<String> warnBatchIds=new ArrayList<>(); //不需要預(yù)警的集合 List<String> notWarnBatchIds=new ArrayList<>(); Long start=System.currentTimeMillis(); for (BasicProductGp basicProductGp : basicProductGps) { //商品預(yù)警日期 Integer warnDate = basicProductGp.getWarnDate(); //商品貨號(hào)編碼 Integer productId = basicProductGp.getProductId(); //查詢出符合預(yù)警的商品批次 List<String> warnBatchId = warnGoodsDay.stream().filter(warnGood -> { if (warnGood.getProductId().equals(productId)){ //匹配到的商品數(shù)據(jù) //2種情況:一種要預(yù)警另外一種不要預(yù)警 if (warnGood.getWarnDate() <= warnDate){ //預(yù)警的返回 return true; } //不要預(yù)警的添加到不預(yù)警集合用于更新 notWarnBatchIds.add(warnGood.getBatchId()); } return false; }).map(BasicProductWarn::getBatchId).collect(Collectors.toList()); //把預(yù)警集合添加到預(yù)警大集合中 warnBatchIds.addAll(warnBatchId); } long end=System.currentTimeMillis(); log.info("更新預(yù)警商品耗時(shí):{}秒",(end-start)/1000); //結(jié)束后會(huì)得到2種集合:不預(yù)警和要預(yù)警集合 //更新未預(yù)警商品 if (!CollectionUtils.isEmpty(notWarnBatchIds)){ int count = basicBatchMapper.updateByBatchWarn(notWarnBatchIds, false); log.info("更新預(yù)警數(shù)量:{}",count); } //更新預(yù)警商品 if (!CollectionUtils.isEmpty(warnBatchIds)){ int count = basicBatchMapper.updateByBatchWarn(warnBatchIds, true); log.info("更新預(yù)警數(shù)量:{}",count); } }
3.前端提一嘴
給商品添加預(yù)警日期,需要用戶提些數(shù)據(jù),為了避免出現(xiàn)英文空格等我們需要,對(duì)輸入的數(shù)據(jù)進(jìn)行正則,只能輸入數(shù)字
效果:
英文:
但是可以輸入0,如果預(yù)警日期設(shè)置成了0你們就意味著廢棄該商品預(yù)警提示功能
前端展示:
首頁(yè):
預(yù)警商品列表:
以上就是java開發(fā)WMS倉(cāng)庫(kù)商品預(yù)警需求示例解析的詳細(xì)內(nèi)容,更多關(guān)于java開發(fā)WMS倉(cāng)庫(kù)商品預(yù)警的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springmvc+shiro+maven 實(shí)現(xiàn)登錄認(rèn)證與權(quán)限授權(quán)管理
Shiro 是一個(gè) Apache 下的一開源項(xiàng)目項(xiàng)目,旨在簡(jiǎn)化身份驗(yàn)證和授權(quán),下面通過(guò)實(shí)例代碼給大家分享springmvc+shiro+maven 實(shí)現(xiàn)登錄認(rèn)證與權(quán)限授權(quán)管理,感興趣的朋友一起看看吧2017-09-09基于自定義BufferedReader中的read和readLine方法
下面小編就為大家分享一篇基于自定義BufferedReader中的read和readLine方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12Java使用組件編寫窗口實(shí)現(xiàn)網(wǎng)上文件下載
這篇文章主要為大家詳細(xì)介紹了Java使用組件編寫窗口實(shí)現(xiàn)網(wǎng)上文件下載的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Javaweb EL自定義函數(shù)開發(fā)及代碼實(shí)例
這篇文章主要介紹了Javaweb EL自定義函數(shù)開發(fā)及代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java8中 LocalDate和java.sql.Date的相互轉(zhuǎn)換操作
這篇文章主要介紹了Java8中 LocalDate和java.sql.Date的相互轉(zhuǎn)換操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Java集合List和Map互轉(zhuǎn)的方法總結(jié)
有時(shí)候我們需要將給定的List轉(zhuǎn)換為Map,或者M(jìn)ap轉(zhuǎn)換為L(zhǎng)ist,本文主要介紹了Java集合List和Map互轉(zhuǎn)的方法總結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09RocketMQ消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制源碼分析
這篇文章主要介紹了RocketMQ源碼分析之消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05