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