package jnpf.dmsBusinessOccupancy; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; import java.util.Objects; /** Number lock precedes document/resource locks. Does not change any original expiry condition. */ public final class BusinessFileExpiryGate { private final BusinessOccupancyTransactions transactions; private final BusinessFileOccupancyCoordinator coordinator; public BusinessFileExpiryGate(SqlSessionTemplate sessions) { transactions = new BusinessOccupancyTransactions(sessions); coordinator = new BusinessFileOccupancyCoordinator(sessions); } /** False means WAIT, never EXPIRED. Caller rereads deadline and executes in this transaction. */ public boolean tryEnter(String tenantId, String centerVersionId) { Objects.requireNonNull(centerVersionId); return transactions.execute(mapper -> { List before = mapper.expiryTargets(tenantId, centerVersionId); if (before.isEmpty() && !mapper.hasCurrentBusinessReference(tenantId, centerVersionId)) { // A CREATE can be center-effective before its resource projection commits. // With no occupancy this still enters the original center/historical path. String number=mapper.centerFileNumber(tenantId,centerVersionId); if(number==null) return true; if(!coordinator.tryEnterExpiry(tenantId,List.of(number))) return false; return Objects.equals(number,mapper.centerFileNumber(tenantId,centerVersionId)) && !mapper.hasCurrentBusinessReference(tenantId,centerVersionId); } if (before.size() != 1) throw new IllegalStateException("到期文件当前业务映射异常,需核查映射"); BusinessFileTarget target = before.get(0); if (!coordinator.tryEnterExpiry(tenantId, List.of(target.getFileNumber()))) return false; // Discovery gives only the lock key; a mapping change must not redirect the expiry. List after = mapper.expiryTargets(tenantId, centerVersionId); return before.equals(after); }); } }