java開發(fā)WMS倉(cāng)庫(kù)商品預(yù)警需求示例解析
1.預(yù)警需求
為了更好的管理商品日期,需要對(duì)倉(cāng)庫(kù)的商品進(jìn)行預(yù)警管理,對(duì)商品的保質(zhì)期控制在一個(gè)范圍內(nèi)提示出來,也可以通過該功能間接的展示出一個(gè)商品的的銷量程度和對(duì)下次進(jìn)貨做個(gè)考量!
1. 預(yù)警需求分析
- 前端界面需要設(shè)置商品的預(yù)警天數(shù)
- 后端保存預(yù)警天數(shù)
- 數(shù)據(jù)庫(kù)有字段存放商品需要預(yù)警的天數(shù)
- 通過定時(shí)器運(yùn)行指點(diǎn)方法算出對(duì)應(yīng)那些商品的批次存低于設(shè)置的預(yù)警天數(shù)
- 查詢出來在wms首頁(yè)展出
2.數(shù)據(jù)庫(kù)表
對(duì)于前端界面的開發(fā)不做過多的代碼分析,本次重點(diǎn)展示商品預(yù)警實(shí)現(xiàn)思路!!
數(shù)據(jù)庫(kù)用到到字段會(huì)截取出來,便于理解!
商品表數(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
如:

由此我們就可以通過函數(shù)算出商品距離過期的天數(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)樾枰故境鰜碓谇岸丝梢越o用戶點(diǎn)擊查看貨物存放的地方,所以查詢出來的數(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í)器,通過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)在是全表查詢出來)
// 這里把商品預(yù)警信息都查詢出來為了以后擴(kuò)展做庫(kù)存預(yù)警設(shè)置
List<BasicProductGp> basicProductGps = productGpMapper.selectByExample(basicProductGpExample);
//查詢商品批次表:商品批次和天數(shù) 商品的批次是唯一的,存在多個(gè)商品貨號(hào)(所以現(xiàn)在是全表查詢出來)
// 以后擴(kuò)展商品庫(kù)存預(yù)警只要在這個(gè)查詢里面添加關(guān)聯(lián)庫(kù)存數(shù)量查詢出來,然后在過濾里面添加預(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("沒有需要預(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è)問題,如果批次被打上預(yù)警標(biāo)識(shí),此時(shí)客戶更改了商品的預(yù)警日期,那么商品的預(yù)警可能就沒有到達(dá),但是頁(yè)面查詢預(yù)警也能被查詢出來,解決方案,使用mq來做處理,當(dāng)客戶更改了商品編碼預(yù)警信息,那么程序就把更改的商品編碼存到mq中我們?cè)趯憘€(gè)消費(fè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),下面通過實(shí)例代碼給大家分享springmvc+shiro+maven 實(shí)現(xiàn)登錄認(rèn)證與權(quán)限授權(quán)管理,感興趣的朋友一起看看吧2017-09-09
基于自定義BufferedReader中的read和readLine方法
下面小編就為大家分享一篇基于自定義BufferedReader中的read和readLine方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12
Java使用組件編寫窗口實(shí)現(xiàn)網(wǎng)上文件下載
這篇文章主要為大家詳細(xì)介紹了Java使用組件編寫窗口實(shí)現(xiàn)網(wǎng)上文件下載的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02
Javaweb EL自定義函數(shù)開發(fā)及代碼實(shí)例
這篇文章主要介紹了Javaweb EL自定義函數(shù)開發(fā)及代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Java8中 LocalDate和java.sql.Date的相互轉(zhuǎn)換操作
這篇文章主要介紹了Java8中 LocalDate和java.sql.Date的相互轉(zhuǎn)換操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Java集合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-09
RocketMQ消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制源碼分析
這篇文章主要介紹了RocketMQ源碼分析之消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05

