跳到主要内容

7-拼板过站接口修改

拼板过站是关键的一个接口,多个部门都在使用,且存在多设备使用的情况,即有并发场景

修改:

  1. 传参JSON
  2. 拼板过站前的校验
  3. 拼板重复过站校验

1.业务逻辑

设备传输时,根据传入的TYPE字段判断本次调用的目的: 校验:校验时类型TYPE传值为C,值不为NA的条码表单,到MESEXT_XLD_BAECODE表中的SFC逐一校验是否存在

上传数据:上传时,TYPE值需为I,看isPassing的值来判断是否过站,为N的时候不过站,对数据SFC不为NA的进行校验,并且保存,对NA的按照时间流水造一个SFC存入。为Y的时候执行上述的操作后进行过站

重复上传:TYPE值需为X,正常走上传数据的逻辑,但是走校验的时候,如果数据存在,checkCodeForm会返回exsit,会提示,不报错

2.拼板校验

校验工单工厂的空值,SFC表单的SFC数据是否存在,主板条码是否存在,设置标志位X为重复过站操作,遇到X的时候checkCodeForm校验存在SFC情况下返回exsit

其余情况下,只有完全校验通过才会返回success

3.条码保存

对校验通过的条码进行保存,批量保存,为了避免重复交互数据库,使用jdbc的批量保存:

String insertSql = "insert into mesext_xld_barcode (barcode, site, shop_order, azm, seq, status, create_date, create_user, sfc) " +
"values(?, ?, ?, ?, ?, ?, ?, ?, ?)";
BatchPreparedStatementSetter bs = new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, saveDataList.get(i).getBARCODE());
ps.setString(2, saveDataList.get(i).getSITE());
ps.setString(3, saveDataList.get(i).getSHOP_ORDER());
ps.setString(4, saveDataList.get(i).getAZM());
ps.setString(5, saveDataList.get(i).getSEQ());
ps.setString(6, saveDataList.get(i).getSTATUS());
ps.setString(7, saveDataList.get(i).getCREATE_DATE());
ps.setString(8, saveDataList.get(i).getCREATE_USER());
ps.setString(9, saveDataList.get(i).getSFC());
}
@Override
public int getBatchSize() {
return saveDataList.size();
}
};
jdbcTemplate.batchUpdate(insertSql, bs);

类似的,jdbc也提供了单条保存的方法,多看源码

4.拼板过站

使用原有的拼版过站方法即可

5.2024/10/31新增日志功能

记录请求和传出的数据,有AOP的方法和暴力的接口记录,这里还没去研究到AOP,先介绍暴力记录的方法:

在Controller的try-catch中新开事务

String message = e.getMessage();
Throwable cause = e.getCause();
status = "失败";
while (cause != null){
message = cause.getMessage();
cause = cause.getCause();
}
dataLog(startTime,parmas,message,status);

dataLog为记录日志的方法,在Impl中记录不起效,需要在Controller中新开事务才可以记录

6.2024/11/07报错记录

出现报错

ORA-00001: unique constraint (MESWIP.MESEXT_XLD_BARCODE_PK) violated

报错ORACLE的数据表唯一性错误,这是并发引起的多线程进入保存的函数,同时保存引起的问题,这里的问题频率不高,因为有并发的情况,并且不可加锁,所以暂不处理。介绍一下加锁的方法:

6.1 使用synchronized关键字

synchronized是内部提供的方法,可以动态的加锁和解锁,确保同一时刻只有一个线程进入代码块

6.2 使用ReentrantLock

ReentrantLock,也被称为“可重入锁”,是一个同步工具类,在java.util.concurrent.locks包下。这种锁的一个重要特点是,它允许一个线程多次获取同一个锁而不会产生死锁。这与synchronized关键字提供的锁定机制非常相似,但ReentrantLock提供了更高的扩展性。

  1. 可重入性:ReentrantLock的一个主要特点是它的名字所表示的含义——“可重入”。简单来说,如果一个线程已经持有了某个锁,那么它可以再次调用lock()方法而不会被阻塞。这在某些需要递归锁定的场景中非常有用。锁的持有计数会在每次成功调用lock()方法时递增,并在每次unlock()方法被调用时递减。
  2. 公平性:与内置的synchronized关键字不同,ReentrantLock提供了一个公平锁的选项。公平锁会按照线程请求锁的顺序来分配锁,而不是像非公平锁那样允许线程抢占已经等待的线程的锁。公平锁可以减少“饥饿”的情况,但也可能降低一些性能。
  3. 可中断性:ReentrantLock的获取锁操作(lockInterruptibly()方法)可以被中断。这提供了另一个相对于synchronized关键字的优势,因为synchronized不支持响应中断。
  4. 条件变量:ReentrantLock类中还包含一个Condition接口的实现,该接口允许线程在某些条件下等待或唤醒。这提供了一种比使用wait()和notify()更灵活和更安全的线程通信方式。
try{
ReentrantLock lock = new ReentrantLock();
lock.lock();
code
````````
}catch{
catch``````
}finally{
lock.unock();
}
`````````
目前测试依然无法避免,未知解决方案

### 6.3 使用RedisService实现的KeyExpirationEventMessageListener

项目里有实现KeyExpirationEventMessageListener的工具类,利用Redis也可以实现上锁机制。

```java
public boolean set(String key, String value,long time,TimeUnit timeUnit){
try {
redisTemplate.opsForValue().set(key, value,time,timeUnit);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}

通过set来对某一个键值上锁,设置时间和单位

public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}

通过get方法来尝试获取某一个键,如果取得到,抛出异常报错