package jnpf.dmsService.recordprint; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jnpf.exception.DataException; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Component; import java.util.Arrays; /** * 使用 Redis 保存短期打印预占,避免预览阶段写入业务表。 */ @Component @RequiredArgsConstructor public class RedisDmsRecordPrintReservationStore implements DmsRecordPrintReservationStore { private static final String KEY_PREFIX = "dms:record-print:reservation:"; private static final DefaultRedisScript CREATE_SCRIPT = new DefaultRedisScript<>( "if redis.call('exists', KEYS[1]) == 1 then return 0 end " + "redis.call('psetex', KEYS[1], ARGV[1], ARGV[2]) " + "redis.call('psetex', KEYS[2], ARGV[1], ARGV[3]) " + "return 1", Long.class); private static final DefaultRedisScript CONFIRM_SCRIPT = new DefaultRedisScript<>( "redis.call('psetex', KEYS[1], ARGV[1], ARGV[2]) " + "redis.call('psetex', KEYS[2], ARGV[1], ARGV[3]) " + "return 1", Long.class); private final StringRedisTemplate redisTemplate; private final ObjectMapper objectMapper; @Override public DmsRecordPrintReservation findByPrintItem(String tenantId, String printItemId) throws DataException { try { String itemKey = itemKey(tenantId, printItemId); String reservationId = redisTemplate.opsForValue().get(itemKey); if (reservationId == null || reservationId.trim().isEmpty()) { return null; } DmsRecordPrintReservation reservation = read( redisTemplate.opsForValue().get(idKey(tenantId, reservationId))); if (reservation == null) { redisTemplate.delete(itemKey); } return reservation; } catch (RuntimeException exception) { throw new DataException("读取打印流水号预占失败"); } } @Override public DmsRecordPrintReservation findById(String tenantId, String reservationId) throws DataException { try { return read(redisTemplate.opsForValue().get(idKey(tenantId, reservationId))); } catch (RuntimeException exception) { throw new DataException("读取打印流水号预占失败"); } } @Override public void create(DmsRecordPrintReservation reservation, long ttlMillis) throws DataException { try { String json = objectMapper.writeValueAsString(reservation); Long created = redisTemplate.execute(CREATE_SCRIPT, Arrays.asList(itemKey(reservation.getTenantId(), reservation.getPrintItemId()), idKey(reservation.getTenantId(), reservation.getReservationId())), String.valueOf(ttlMillis), reservation.getReservationId(), json); if (!Long.valueOf(1L).equals(created)) { throw new DataException("该打印申请已经存在有效预占,请刷新后重试"); } } catch (JsonProcessingException exception) { throw new DataException("保存打印流水号预占失败"); } catch (RuntimeException exception) { if (exception instanceof DataException) { throw exception; } throw new DataException("保存打印流水号预占失败"); } } @Override public void markConfirmed(DmsRecordPrintReservation reservation, long ttlMillis) throws DataException { try { String json = objectMapper.writeValueAsString(reservation); redisTemplate.execute(CONFIRM_SCRIPT, Arrays.asList(itemKey(reservation.getTenantId(), reservation.getPrintItemId()), idKey(reservation.getTenantId(), reservation.getReservationId())), String.valueOf(ttlMillis), reservation.getReservationId(), json); } catch (JsonProcessingException | RuntimeException exception) { throw new DataException("更新打印流水号预占状态失败"); } } private DmsRecordPrintReservation read(String json) throws DataException { if (json == null || json.trim().isEmpty()) { return null; } try { return objectMapper.readValue(json, DmsRecordPrintReservation.class); } catch (JsonProcessingException exception) { throw new DataException("打印流水号预占数据格式无效"); } } private String itemKey(String tenantId, String printItemId) { return KEY_PREFIX + "item:{" + tenantId + "}:" + printItemId; } private String idKey(String tenantId, String reservationId) { return KEY_PREFIX + "id:{" + tenantId + "}:" + reservationId; } }