Selaa lähdekoodia

优化SN分配

callm 3 viikkoa sitten
vanhempi
commit
496af85c6a

+ 7 - 0
src/main/java/com/uas/eis/dao/BaseDao.java

@@ -115,6 +115,13 @@ public class BaseDao{
 		jdbcTemplate.execute(sql);
 	}
 
+	/**
+	 * 执行更新并返回影响的行数,用于需要根据更新行数做判断的场景(如并发占用)
+	 */
+	public int executeUpdate(String sql) {
+		return jdbcTemplate.update(sql);
+	}
+
 	public synchronized boolean execute(String sql, Object... objs) {
 		try {
 			jdbcTemplate.update(sql, objs);

+ 79 - 28
src/main/java/com/uas/eis/serviceImpl/MESHelperServiceImpl.java

@@ -2052,6 +2052,58 @@ public class MESHelperServiceImpl implements MESHelperService {
 		return AE_MASTER.toString();
 	}
 
+	/**
+	 * 原子占用SN
+	 * <p>
+	 * 原实现是"先select符合条件的SN,再在循环里逐条update",两步之间没有互斥,两个请求时间接近时会读到
+	 * 同一批msl_status=0的记录,从而返回相同的SN。
+	 * 这里改成先占用再反查:
+	 * 1)占用用单条update完成,where里带nvl(msl_status,0)=0。并发请求在行锁等待结束(Oracle会阻塞后重试该行)
+	 *   会用当前数据重新评估该条件,已被占用的行会被跳过,因此同一行不会被占用两次;
+	 * 2)占用时写入唯一token,再按token反查,精确得到本次占用的SN,不再依赖时间或行号;
+	 * 3)占用不足时重新补占用,最终仍不足则归还本次已占用的行,避免占用泄漏。
+	 *
+	 * @param macode 工单号
+	 * @param mType  SN类型,传空表示不限类型
+	 * @param need   需要占用的数量
+	 * @return 本次占用的SN列表(每项含sn、id,按msl_sncode升序);数量不足时归还并返回null
+	 */
+	private List<Map<String,String>> claimSnCodes(String macode,String mType,int need){
+		String typeCond=(mType==null||"".equals(mType))?"1=1":"nvl(msl_type,' ')='"+mType+"'";
+		String token=UUID.randomUUID().toString().replace("-","");
+		List<Map<String,String>> list=new ArrayList<>();
+		while(list.size()<need){
+			int updated=baseDao.executeUpdate("update MakeSnList set msl_status=-1,msl_signtime=sysdate,msl_mainbarcode='"+token+"' " +
+					"where nvl(msl_status,0)=0 and msl_id in (select msl_id from (select msl_id from MakeSnList " +
+					"where msl_makecode='"+macode+"' and "+typeCond+" and nvl(msl_status,0)=0 order by msl_sncode) where rownum<="+(need-list.size())+")");
+			if(updated<=0){
+				break;
+			}
+			//token唯一,反查到的就是本次请求占用的行
+			SqlRowList rs=baseDao.queryForRowSet("select msl_sncode,msl_id from MakeSnList where msl_mainbarcode='"+token+"' and msl_status=-1 order by msl_sncode");
+			list.clear();
+			while(rs.next()){
+				Map<String,String> one=new HashMap<>();
+				one.put("sn",rs.getGeneralString("msl_sncode"));
+				one.put("id",rs.getGeneralString("msl_id"));
+				list.add(one);
+			}
+		}
+		if(list.size()<need){
+			//可用数量不足,归还本次已占用的行
+			StringBuilder ids=new StringBuilder();
+			for(Map<String,String> one:list){
+				ids.append(one.get("id")).append(",");
+			}
+			if(ids.length()>0){
+				ids.append("0");
+				baseDao.execute("update MakeSnList set msl_status=0,msl_signtime=null,msl_mainbarcode='' where msl_id in ("+ids+")");
+			}
+			return null;
+		}
+		return list;
+	}
+
 	@Override
 	public Map<Object,Object> getMESSnCode(String data) {
 		Map<String, Object> map=null;
@@ -2097,26 +2149,26 @@ public class MESHelperServiceImpl implements MESHelperService {
 			rmap.put("message","SN不足!");
 			return rmap;
 		}
-		List<String> sqls = new ArrayList<>();
+		//原子占用SN,防止并发请求拿到相同的SN
+		List<Map<String,String>> claimed=claimSnCodes(macode,"",(int)Math.ceil(sncount));
+		if(claimed==null){
+			rmap.put("code",-1);
+			rmap.put("message","SN不足!");
+			return rmap;
+		}
 		List<Map<String, Object>> maps = new ArrayList<>();
-		Map<String, Object> snmap=null;
-		SqlRowList rs = baseDao.queryForRowSet("select msl_sncode,msl_id from (select msl_sncode,msl_id from MakeSnList where msl_makecode='" + macode + "' and nvl(msl_status,0)=0 order by msl_sncode) where rownum<="+sncount +"  order by msl_sncode ");
-		String mainbarcode="";
+		//主条码取排序后的最后一条
+		String mainbarcode=claimed.get(claimed.size()-1).get("sn");
 		String msl_id="";
-		while (rs.next()){
-			snmap=new HashMap<String, Object>();
-			snmap.put("serialNo",rs.getGeneralString("msl_sncode"));
-			mainbarcode=rs.getGeneralString("msl_sncode");
+		for(Map<String,String> one:claimed){
+			Map<String, Object> snmap=new HashMap<String, Object>();
+			snmap.put("serialNo",one.get("sn"));
 			maps.add(snmap);
-			baseDao.execute("update MakeSnList set msl_signtime=sysdate,msl_status=-1 where msl_id="+rs.getGeneralInt("msl_id"));
-			msl_id+=rs.getGeneralInt("msl_id")+",";
+			msl_id+=one.get("id")+",";
 		}
 		msl_id+="0";
 		baseDao.execute("update MakeSnList set msl_mainbarcode='"+mainbarcode+"' where msl_id in ("+msl_id+")");
 
-//		if (sqls!=null&&sqls.size()>0){
-//			baseDao.execute(sqls);
-//		}
 		rmap.put("code",0);
 		rmap.put("data",maps);
 		return rmap;
@@ -2173,27 +2225,26 @@ public class MESHelperServiceImpl implements MESHelperService {
 			rmap.put("message","工单号"+macode+"SN不足!类型"+mType);
 			return rmap;
 		}
-		List<String> sqls = new ArrayList<>();
+		//原子占用SN,防止并发请求拿到相同的SN
+		List<Map<String,String>> claimed=claimSnCodes(macode,mType,(int)Math.ceil(sncount));
+		if(claimed==null){
+			rmap.put("code",-1);
+			rmap.put("message","工单号"+macode+"SN不足!类型"+mType);
+			return rmap;
+		}
 		List<Map<String, Object>> maps = new ArrayList<>();
-		Map<String, Object> snmap=null;
-		SqlRowList rs = baseDao.queryForRowSet("select msl_sncode,msl_id from (select msl_sncode,msl_id from MakeSnList where msl_makecode='" + macode + "' and nvl(msl_type,' ')='"+mType+"' and nvl(msl_status,0)=0 order by msl_sncode) where rownum<="+sncount +"  order by msl_sncode ");
-		String mainbarcode="";
+		//主条码取排序后的最后一条
+		String mainbarcode=claimed.get(claimed.size()-1).get("sn");
 		String msl_id="";
-		while (rs.next()){
-			snmap=new HashMap<String, Object>();
-			snmap.put("serialNo",rs.getGeneralString("msl_sncode"));
-			mainbarcode=rs.getGeneralString("msl_sncode");
+		for(Map<String,String> one:claimed){
+			Map<String, Object> snmap=new HashMap<String, Object>();
+			snmap.put("serialNo",one.get("sn"));
 			maps.add(snmap);
-			baseDao.execute("update MakeSnList set msl_signtime=sysdate,msl_status=-1 where msl_id="+rs.getGeneralInt("msl_id"));
-			msl_id+=rs.getGeneralInt("msl_id")+",";
-			baseDao.execute("insert into MACRELEASE(MR_ID,MR_SNCODE,MR_INDATE)values(MACRELEASE_seq.nextval,'"+mainbarcode+"',sysdate)");
+			baseDao.execute("insert into MACRELEASE(MR_ID,MR_SNCODE,MR_INDATE)values(MACRELEASE_seq.nextval,'"+one.get("sn")+"',sysdate)");
+			msl_id+=one.get("id")+",";
 		}
 		msl_id+="0";
 		baseDao.execute("update MakeSnList set msl_mainbarcode='"+mainbarcode+"' where msl_id in ("+msl_id+")");
-
-//		if (sqls!=null&&sqls.size()>0){
-//			baseDao.execute(sqls);
-//		}
 		rmap.put("code",0);
 		rmap.put("data",maps);
 		return rmap;