refactor: isolate issuance source currency usage

agent/payment-training-progress
chenyuan 2 weeks ago
parent dcd01e57be
commit 1e111da5bb

@ -11,6 +11,8 @@ import com.yau.digitalrmb.shared.exception.BusinessException;
import com.yau.digitalrmb.issuance.domain.model.DenominationItem;
import com.yau.digitalrmb.issuance.domain.model.DraftDigitalCurrency;
import com.yau.digitalrmb.issuance.domain.model.IssuanceAuditActor;
import com.yau.digitalrmb.issuance.infrastructure.persistence.entity.IssuanceSourceCurrencyUsageEntity;
import com.yau.digitalrmb.issuance.infrastructure.persistence.mapper.IssuanceSourceCurrencyUsageMapper;
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyBatchEntity;
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyBatchMapper;
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyEntity;
@ -37,6 +39,7 @@ public class DigitalCurrencyGenerationModuleGateway {
private final ControlSystemSignatureService controlSignatureService;
private final StandardCurrencyBatchMapper standardCurrencyBatchMapper;
private final StandardCurrencyMapper standardCurrencyMapper;
private final IssuanceSourceCurrencyUsageMapper sourceUsageMapper;
private final JdbcTemplate jdbcTemplate;
@Autowired
@ -45,19 +48,21 @@ public class DigitalCurrencyGenerationModuleGateway {
ControlSystemSignatureService controlSignatureService,
StandardCurrencyBatchMapper standardCurrencyBatchMapper,
StandardCurrencyMapper standardCurrencyMapper,
IssuanceSourceCurrencyUsageMapper sourceUsageMapper,
JdbcTemplate jdbcTemplate) {
this.keyService = keyService;
this.quotaService = quotaService;
this.controlSignatureService = controlSignatureService;
this.standardCurrencyBatchMapper = standardCurrencyBatchMapper;
this.standardCurrencyMapper = standardCurrencyMapper;
this.sourceUsageMapper = sourceUsageMapper;
this.jdbcTemplate = jdbcTemplate;
}
public DigitalCurrencyGenerationModuleGateway(InstitutionKeyService keyService,
QuotaControlBitService quotaService,
ControlSystemSignatureService controlSignatureService) {
this(keyService, quotaService, controlSignatureService, null, null, null);
this(keyService, quotaService, controlSignatureService, null, null, null, null);
}
public String signIssuanceDigest(InstitutionKeySubject subject, String digest) {
@ -82,7 +87,7 @@ public class DigitalCurrencyGenerationModuleGateway {
UUID requestId,
List<DenominationItem> denominations,
IssuanceAuditActor actor) {
if (standardCurrencyBatchMapper == null || standardCurrencyMapper == null) {
if (standardCurrencyBatchMapper == null || standardCurrencyMapper == null || sourceUsageMapper == null) {
throw new BusinessException(ErrorCode.INTERNAL_ERROR, "生成模块币串库存服务尚未配置");
}
requiredSubject(subject);
@ -102,39 +107,47 @@ public class DigitalCurrencyGenerationModuleGateway {
if (batch == null) {
throw validation("生成模块步骤九尚未生成与本次额度控制位对应的标准币串");
}
List<StandardCurrencyEntity> alreadyReserved = standardCurrencyMapper.selectList(
new LambdaQueryWrapper<StandardCurrencyEntity>()
.eq(StandardCurrencyEntity::getBatchId, batch.getId())
.eq(StandardCurrencyEntity::getIssuanceRequestId, requestId.toString())
.eq(StandardCurrencyEntity::getStatus, "发行锁定")
.eq(StandardCurrencyEntity::getDeleted, false)
.orderByAsc(StandardCurrencyEntity::getSequenceNumber));
List<StandardCurrencyEntity> alreadyReserved = sourceUsageMapper.selectReservedSources(
batch.getId(), requestId.toString());
if (!alreadyReserved.isEmpty()) {
assertExactQuantities(alreadyReserved, requested);
return stockItems(alreadyReserved);
}
List<StandardCurrencyEntity> available = standardCurrencyMapper.selectList(
new LambdaQueryWrapper<StandardCurrencyEntity>()
.eq(StandardCurrencyEntity::getBatchId, batch.getId())
.eq(StandardCurrencyEntity::getStatus, "待生效")
.eq(StandardCurrencyEntity::getDeleted, false)
.orderByAsc(StandardCurrencyEntity::getSequenceNumber)
.last("FOR UPDATE"));
List<StandardCurrencyEntity> available = sourceUsageMapper.selectAvailableSourcesForUpdate(batch.getId());
List<StandardCurrencyEntity> selected = selectExactQuantities(available, requested);
LocalDateTime now = LocalDateTime.now();
for (StandardCurrencyEntity currency : selected) {
int updated = standardCurrencyMapper.update(null, new LambdaUpdateWrapper<StandardCurrencyEntity>()
.eq(StandardCurrencyEntity::getId, currency.getId())
.eq(StandardCurrencyEntity::getStatus, "待生效")
.eq(StandardCurrencyEntity::getDeleted, false)
.set(StandardCurrencyEntity::getStatus, "发行锁定")
.set(StandardCurrencyEntity::getIssuanceRequestId, requestId.toString())
.set(StandardCurrencyEntity::getReservedAt, now)
.set(StandardCurrencyEntity::getReservedByUserId, actor.getUserId())
.set(StandardCurrencyEntity::getReservedBy, actor.getUsername())
.set(StandardCurrencyEntity::getUpdatedAt, now)
.set(StandardCurrencyEntity::getUpdatedBy, actor.getUsername()));
if (updated != 1) throw validation("币串库存已变化,请重新获取后再发行");
IssuanceSourceCurrencyUsageEntity existing = sourceUsageMapper.selectBySourceCurrencyIdForUpdate(
currency.getId());
if (existing == null) {
IssuanceSourceCurrencyUsageEntity usage = new IssuanceSourceCurrencyUsageEntity();
usage.setSourceCurrencyId(currency.getId());
usage.setSourceBatchId(currency.getBatchId());
usage.setIssuanceRequestId(requestId.toString());
usage.setStatus("RESERVED");
usage.setReservedAt(now);
usage.setAuditUserId(actor.getUserId());
usage.setAuditUsername(actor.getUsername());
usage.setCreatedAt(now);
usage.setUpdatedAt(now);
if (sourceUsageMapper.insert(usage) != 1) {
throw validation("币串库存已变化,请重新获取后再发行");
}
} else {
int reused = sourceUsageMapper.update(null,
new LambdaUpdateWrapper<IssuanceSourceCurrencyUsageEntity>()
.eq(IssuanceSourceCurrencyUsageEntity::getId, existing.getId())
.eq(IssuanceSourceCurrencyUsageEntity::getStatus, "RELEASED")
.set(IssuanceSourceCurrencyUsageEntity::getIssuanceRequestId, requestId.toString())
.set(IssuanceSourceCurrencyUsageEntity::getStatus, "RESERVED")
.set(IssuanceSourceCurrencyUsageEntity::getReservedAt, now)
.set(IssuanceSourceCurrencyUsageEntity::getReleasedAt, null)
.set(IssuanceSourceCurrencyUsageEntity::getIssuedAt, null)
.set(IssuanceSourceCurrencyUsageEntity::getAuditUserId, actor.getUserId())
.set(IssuanceSourceCurrencyUsageEntity::getAuditUsername, actor.getUsername())
.set(IssuanceSourceCurrencyUsageEntity::getUpdatedAt, now));
if (reused != 1) throw validation("币串库存已变化,请重新获取后再发行");
}
}
return stockItems(selected);
}
@ -143,7 +156,8 @@ public class DigitalCurrencyGenerationModuleGateway {
public void activateGeneratedCurrencies(InstitutionKeySubject subject, UUID requestId, String bankCode,
String institutionIdentifier, List<DraftDigitalCurrency> confirmedCoins,
IssuanceAuditActor actor) {
if (standardCurrencyBatchMapper == null || standardCurrencyMapper == null || jdbcTemplate == null) {
if (standardCurrencyBatchMapper == null || standardCurrencyMapper == null
|| sourceUsageMapper == null || jdbcTemplate == null) {
throw new BusinessException(ErrorCode.INTERNAL_ERROR, "生成模块币串发行服务尚未配置");
}
requiredSubject(subject);
@ -162,15 +176,16 @@ public class DigitalCurrencyGenerationModuleGateway {
if (!requiredInstitution.equals(coin.getOrganizationId())) throw validation("发行币串机构标识不一致");
if (currencyCode == null) currencyCode = required(coin.getCurrency(), "发行币种");
if (!currencyCode.equals(coin.getCurrency())) throw validation("同一发行批次币种不一致");
StandardCurrencyEntity source = standardCurrencyMapper.selectOne(
new LambdaQueryWrapper<StandardCurrencyEntity>()
.eq(StandardCurrencyEntity::getId, coin.getSourceCurrencyId())
.eq(StandardCurrencyEntity::getBatchId, coin.getSourceCurrencyBatchId())
.eq(StandardCurrencyEntity::getIssuanceRequestId, requestId.toString())
.eq(StandardCurrencyEntity::getStatus, "发行锁定")
.eq(StandardCurrencyEntity::getDeleted, false)
.last("FOR UPDATE"));
StandardCurrencyEntity source = standardCurrencyMapper.selectById(coin.getSourceCurrencyId());
IssuanceSourceCurrencyUsageEntity usage = sourceUsageMapper.selectBySourceCurrencyIdForUpdate(
coin.getSourceCurrencyId());
if (source == null || !coin.getCoinId().equals(source.getCurrencyId())
|| !coin.getSourceCurrencyBatchId().equals(source.getBatchId())
|| Boolean.TRUE.equals(source.getDeleted())
|| usage == null
|| !source.getBatchId().equals(usage.getSourceBatchId())
|| !requestId.toString().equals(usage.getIssuanceRequestId())
|| !"RESERVED".equals(usage.getStatus())
|| !requiredInstitution.equals(source.getInstitutionIdentifier())
|| !coin.getSourceCompleteCurrency().equals(source.getCompleteCurrency())) {
throw validation("生成模块来源币串与发行记录不一致");
@ -187,19 +202,16 @@ public class DigitalCurrencyGenerationModuleGateway {
for (int index = 0; index < sources.size(); index++) {
StandardCurrencyEntity source = sources.get(index);
DraftDigitalCurrency coin = confirmedCoins.get(index);
int updated = standardCurrencyMapper.update(null, new LambdaUpdateWrapper<StandardCurrencyEntity>()
.eq(StandardCurrencyEntity::getId, source.getId())
.eq(StandardCurrencyEntity::getStatus, "发行锁定")
.eq(StandardCurrencyEntity::getIssuanceRequestId, requestId.toString())
.eq(StandardCurrencyEntity::getDeleted, false)
.set(StandardCurrencyEntity::getStatus, "可用")
.set(StandardCurrencyEntity::getIssuedAt, now)
.set(StandardCurrencyEntity::getIssuedByUserId, actor.getUserId())
.set(StandardCurrencyEntity::getIssuedBy, actor.getUsername())
.set(StandardCurrencyEntity::getOwnershipSignature, coin.getOwnershipSignature())
.set(StandardCurrencyEntity::getIssuedCompleteCurrency, coin.getCompleteCurrency())
.set(StandardCurrencyEntity::getUpdatedAt, now)
.set(StandardCurrencyEntity::getUpdatedBy, actor.getUsername()));
int updated = sourceUsageMapper.update(null,
new LambdaUpdateWrapper<IssuanceSourceCurrencyUsageEntity>()
.eq(IssuanceSourceCurrencyUsageEntity::getSourceCurrencyId, source.getId())
.eq(IssuanceSourceCurrencyUsageEntity::getIssuanceRequestId, requestId.toString())
.eq(IssuanceSourceCurrencyUsageEntity::getStatus, "RESERVED")
.set(IssuanceSourceCurrencyUsageEntity::getStatus, "ISSUED")
.set(IssuanceSourceCurrencyUsageEntity::getIssuedAt, now)
.set(IssuanceSourceCurrencyUsageEntity::getAuditUserId, actor.getUserId())
.set(IssuanceSourceCurrencyUsageEntity::getAuditUsername, actor.getUsername())
.set(IssuanceSourceCurrencyUsageEntity::getUpdatedAt, now));
if (updated != 1) throw validation("币串发行状态已变化,不能重复确权");
jdbcTemplate.update("INSERT INTO commercial_bank_currency "
+ "(currency_id, source_currency_id, source_batch_id, issuance_request_id, bank_code, "
@ -217,6 +229,24 @@ public class DigitalCurrencyGenerationModuleGateway {
}
}
@Transactional
public void releaseGeneratedCurrencies(UUID requestId, IssuanceAuditActor actor) {
if (sourceUsageMapper == null) {
throw new BusinessException(ErrorCode.INTERNAL_ERROR, "生成模块币串库存服务尚未配置");
}
if (requestId == null) throw validation("发行申请标识不能为空");
requiredActor(actor);
LocalDateTime now = LocalDateTime.now();
sourceUsageMapper.update(null, new LambdaUpdateWrapper<IssuanceSourceCurrencyUsageEntity>()
.eq(IssuanceSourceCurrencyUsageEntity::getIssuanceRequestId, requestId.toString())
.eq(IssuanceSourceCurrencyUsageEntity::getStatus, "RESERVED")
.set(IssuanceSourceCurrencyUsageEntity::getStatus, "RELEASED")
.set(IssuanceSourceCurrencyUsageEntity::getReleasedAt, now)
.set(IssuanceSourceCurrencyUsageEntity::getAuditUserId, actor.getUserId())
.set(IssuanceSourceCurrencyUsageEntity::getAuditUsername, actor.getUsername())
.set(IssuanceSourceCurrencyUsageEntity::getUpdatedAt, now));
}
private void assertOwnedBatch(Long batchId, InstitutionKeySubject subject) {
StandardCurrencyBatchEntity batch = standardCurrencyBatchMapper.selectById(batchId);
if (batch == null || !subject.getUserId().equals(batch.getUserId())

@ -0,0 +1,29 @@
package com.yau.digitalrmb.issuance.infrastructure.persistence.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@TableName("issuance_source_currency_usage")
public class IssuanceSourceCurrencyUsageEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@TableField("source_currency_id") private Long sourceCurrencyId;
@TableField("source_batch_id") private Long sourceBatchId;
@TableField("issuance_request_id") private String issuanceRequestId;
private String status;
@TableField("reserved_at") private LocalDateTime reservedAt;
@TableField("released_at") private LocalDateTime releasedAt;
@TableField("issued_at") private LocalDateTime issuedAt;
@TableField("audit_user_id") private String auditUserId;
@TableField("audit_username") private String auditUsername;
@TableField("created_at") private LocalDateTime createdAt;
@TableField("updated_at") private LocalDateTime updatedAt;
}

@ -0,0 +1,32 @@
package com.yau.digitalrmb.issuance.infrastructure.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyEntity;
import com.yau.digitalrmb.issuance.infrastructure.persistence.entity.IssuanceSourceCurrencyUsageEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface IssuanceSourceCurrencyUsageMapper extends BaseMapper<IssuanceSourceCurrencyUsageEntity> {
@Select("SELECT sc.* FROM standard_currency sc "
+ "JOIN issuance_source_currency_usage u ON u.source_currency_id = sc.id "
+ "WHERE sc.batch_id = #{batchId} AND sc.deleted = FALSE "
+ "AND u.issuance_request_id = #{requestId} AND u.status = 'RESERVED' "
+ "ORDER BY sc.sequence_number")
List<StandardCurrencyEntity> selectReservedSources(@Param("batchId") Long batchId,
@Param("requestId") String requestId);
@Select("SELECT sc.* FROM standard_currency sc WHERE sc.batch_id = #{batchId} "
+ "AND sc.status = '待生效' AND sc.deleted = FALSE "
+ "AND NOT EXISTS (SELECT 1 FROM issuance_source_currency_usage u "
+ "WHERE u.source_currency_id = sc.id AND u.status IN ('RESERVED','ISSUED')) "
+ "ORDER BY sc.sequence_number FOR UPDATE")
List<StandardCurrencyEntity> selectAvailableSourcesForUpdate(@Param("batchId") Long batchId);
@Select("SELECT * FROM issuance_source_currency_usage WHERE source_currency_id = #{sourceCurrencyId} FOR UPDATE")
IssuanceSourceCurrencyUsageEntity selectBySourceCurrencyIdForUpdate(
@Param("sourceCurrencyId") Long sourceCurrencyId);
}

@ -39,3 +39,20 @@ CREATE TABLE IF NOT EXISTS training_experiment_action (
CONSTRAINT fk_experiment_action_attempt
FOREIGN KEY (attempt_id) REFERENCES training_experiment_attempt(id)
);
CREATE TABLE IF NOT EXISTS issuance_source_currency_usage (
id BIGINT PRIMARY KEY,
source_currency_id BIGINT NOT NULL,
source_batch_id BIGINT NOT NULL,
issuance_request_id CHAR(36) NOT NULL,
status VARCHAR(16) NOT NULL,
reserved_at TIMESTAMP NOT NULL,
released_at TIMESTAMP NULL,
issued_at TIMESTAMP NULL,
audit_user_id VARCHAR(36) NOT NULL,
audit_username VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
CONSTRAINT uk_issuance_source_currency UNIQUE (source_currency_id),
INDEX idx_issuance_source_request (issuance_request_id, status)
);

@ -309,6 +309,23 @@ CREATE TABLE IF NOT EXISTS digital_currency_ownership_confirmation (
CONSTRAINT fk_digital_currency_ownership_batch FOREIGN KEY (batch_id) REFERENCES digital_currency_production_batch(batch_id)
);
CREATE TABLE IF NOT EXISTS issuance_source_currency_usage (
id BIGINT PRIMARY KEY,
source_currency_id BIGINT NOT NULL,
source_batch_id BIGINT NOT NULL,
issuance_request_id CHAR(36) NOT NULL,
status VARCHAR(16) NOT NULL,
reserved_at TIMESTAMP NOT NULL,
released_at TIMESTAMP NULL,
issued_at TIMESTAMP NULL,
audit_user_id VARCHAR(36) NOT NULL,
audit_username VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
CONSTRAINT uk_issuance_source_currency UNIQUE (source_currency_id),
INDEX idx_issuance_source_request (issuance_request_id, status)
);
CREATE TABLE IF NOT EXISTS commercial_bank_currency (
currency_id VARCHAR(96) PRIMARY KEY,
source_currency_id BIGINT NOT NULL UNIQUE,

@ -14,6 +14,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.time.Instant;
@ -38,6 +39,7 @@ class DigitalCurrencyGenerationStockIntegrationTest {
"stock-user", 1001L, 2001L);
insertCoin(8901L, batchId, 1, "100.00", "DC_SOURCE_001", "SOURCE_CURRENCY_001");
insertCoin(8902L, batchId, 2, "50.00", "DC_SOURCE_002", "SOURCE_CURRENCY_002");
List<Map<String, Object>> sourceBefore = sourceState(batchId);
List<GeneratedCurrencyStockItem> reserved = gateway.reserveGeneratedCurrencies(
new InstitutionKeySubject("stock-user", 1001L, 2001L), 81L, requestId,
@ -47,9 +49,27 @@ class DigitalCurrencyGenerationStockIntegrationTest {
assertThat(reserved).extracting(GeneratedCurrencyStockItem::getCurrencyId)
.containsExactly("DC_SOURCE_001", "DC_SOURCE_002");
assertThat(sourceState(batchId)).isEqualTo(sourceBefore);
assertThat(jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM standard_currency WHERE batch_id = ? AND status = '发行锁定' AND issuance_request_id = ?",
"SELECT COUNT(*) FROM issuance_source_currency_usage WHERE source_batch_id = ? "
+ "AND issuance_request_id = ? AND status = 'RESERVED'",
Integer.class, batchId, requestId.toString())).isEqualTo(2);
IssuanceAuditActor actor = new IssuanceAuditActor("stock-user", "库存测试员");
gateway.releaseGeneratedCurrencies(requestId, actor);
assertThat(jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM issuance_source_currency_usage WHERE source_batch_id = ? AND status = 'RELEASED'",
Integer.class, batchId)).isEqualTo(2);
UUID retryRequestId = UUID.fromString("00000000-0000-0000-0000-000000008811");
gateway.reserveGeneratedCurrencies(new InstitutionKeySubject("stock-user", 1001L, 2001L), 81L,
retryRequestId, Arrays.asList(new DenominationItem(new BigDecimal("100.00"), 1),
new DenominationItem(new BigDecimal("50.00"), 1)), actor);
assertThat(jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM issuance_source_currency_usage WHERE source_batch_id = ? "
+ "AND issuance_request_id = ? AND status = 'RESERVED'",
Integer.class, batchId, retryRequestId.toString())).isEqualTo(2);
assertThat(sourceState(batchId)).isEqualTo(sourceBefore);
}
@Test
@ -63,6 +83,7 @@ class DigitalCurrencyGenerationStockIntegrationTest {
batchId, 82L, "20260807_802", new BigDecimal("100.00"), 1, "100.00:1",
"stock-user", 1001L, 2001L);
insertCoin(8903L, batchId, 1, "100.00", "DC_SOURCE_003", "SOURCE_CURRENCY_003");
List<Map<String, Object>> sourceBefore = sourceState(batchId);
InstitutionKeySubject subject = new InstitutionKeySubject("stock-user", 1001L, 2001L);
IssuanceAuditActor actor = new IssuanceAuditActor("stock-user", "库存测试员");
gateway.reserveGeneratedCurrencies(subject, 82L, requestId,
@ -81,10 +102,13 @@ class DigitalCurrencyGenerationStockIntegrationTest {
gateway.activateGeneratedCurrencies(subject, requestId, "BKCHCNBJ00001", "ORG_001",
Arrays.asList(confirmed), actor);
assertThat(jdbcTemplate.queryForObject("SELECT status FROM standard_currency WHERE id = 8903", String.class))
.isEqualTo("可用");
assertThat(jdbcTemplate.queryForObject("SELECT issued_complete_currency FROM standard_currency WHERE id = 8903", String.class))
.isEqualTo("SOURCE_CURRENCY_003|OWNERSHIP_SIGNATURE");
assertThat(sourceState(batchId)).isEqualTo(sourceBefore);
assertThat(jdbcTemplate.queryForObject(
"SELECT status FROM issuance_source_currency_usage WHERE source_currency_id = 8903",
String.class)).isEqualTo("ISSUED");
assertThat(jdbcTemplate.queryForObject(
"SELECT issuance_request_id FROM issuance_source_currency_usage WHERE source_currency_id = 8903",
String.class)).isEqualTo(requestId.toString());
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM commercial_bank_currency WHERE currency_id = 'DC_SOURCE_003'",
Integer.class)).isEqualTo(1);
assertThat(jdbcTemplate.queryForObject(
@ -104,4 +128,10 @@ class DigitalCurrencyGenerationStockIntegrationTest {
+ "CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 'tester', 'tester', FALSE)",
id, batchId, sequence, new BigDecimal(denomination), currencyId, completeCurrency);
}
private List<Map<String, Object>> sourceState(long batchId) {
return jdbcTemplate.queryForList("SELECT id, status, issuance_request_id, reserved_at, reserved_by_user_id, "
+ "reserved_by, issued_at, issued_by_user_id, issued_by, ownership_signature, issued_complete_currency "
+ "FROM standard_currency WHERE batch_id = ? ORDER BY id", batchId);
}
}

@ -186,6 +186,23 @@ CREATE TABLE IF NOT EXISTS standard_currency (
deleted BOOLEAN NOT NULL DEFAULT FALSE
);
CREATE TABLE IF NOT EXISTS issuance_source_currency_usage (
id BIGINT PRIMARY KEY,
source_currency_id BIGINT NOT NULL,
source_batch_id BIGINT NOT NULL,
issuance_request_id CHAR(36) NOT NULL,
status VARCHAR(16) NOT NULL,
reserved_at TIMESTAMP NOT NULL,
released_at TIMESTAMP NULL,
issued_at TIMESTAMP NULL,
audit_user_id VARCHAR(36) NOT NULL,
audit_username VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL,
CONSTRAINT uk_issuance_source_currency UNIQUE (source_currency_id),
INDEX idx_issuance_source_request (issuance_request_id, status)
);
CREATE TABLE IF NOT EXISTS institution_sm2_key_audit (
id BIGINT PRIMARY KEY,
key_record_id BIGINT NOT NULL,

Loading…
Cancel
Save