feat(refdata): Q22 basicdata 映射与 ReferenceDataProcessor(ACM2-93)
闭合 Q22;V6 建 basicdata 表组;RefData 走真处理器(DNLD/RESP/ADD/UPD/DEL/RSTA)。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -69,6 +69,9 @@ class OutboundRequestService(
|
||||
fun completeRqfdResponse(operationDay: LocalDate = currentOperationDay()): Boolean =
|
||||
reqTrack.completeLatest(OutboundRequestKeys.RQFD_REQ_TYPE, operationDay, OutboundRequestKeys.SENDER)
|
||||
|
||||
fun completeRqrdResponse(operationDay: LocalDate = currentOperationDay()): Boolean =
|
||||
reqTrack.completeLatest(OutboundRequestKeys.RQRD_REQ_TYPE, operationDay, OutboundRequestKeys.SENDER)
|
||||
|
||||
fun dispatchPending(limit: Int = 10): Int {
|
||||
var sent = 0
|
||||
reqTrack.listPendingDispatch(limit).forEach { req ->
|
||||
|
||||
@@ -124,6 +124,7 @@ class MessageProcessor(
|
||||
private val flopProcessor: FlopProcessor,
|
||||
private val fdelProcessor: FdelProcessor,
|
||||
private val adftProcessor: AdftProcessor,
|
||||
private val referenceDataProcessor: ReferenceDataProcessor,
|
||||
private val outbound: OutboundRequestService,
|
||||
private val procFailure: ProcFailure,
|
||||
private val props: PipelineProps,
|
||||
@@ -237,10 +238,9 @@ class MessageProcessor(
|
||||
flopProcessor.apply(head, decoded, payload)
|
||||
}
|
||||
is MsgKind.RefData -> {
|
||||
// 参考数据必须走 US-13(implementation.md「分派」),不得按"不支持"跳过;
|
||||
// ReferenceDataProcessor 尚未实装(ACM2-93,待 Q22),先按可重试失败留队并告警。
|
||||
log.warn("refdata without handler -> FAILED(UNSUPPORTED) msgId={} type={}", head.msgId, kind.type)
|
||||
return procFailure.fail(head, ErrorClass.UNSUPPORTED, "refdata-pending:${kind.type}")
|
||||
val body = decoded.body as? com.gzzn.omms.msgexchange.domain.ref.RefDataBody // validated below
|
||||
if (body == null) return deadMalformed(head, "missing-refdata-body")
|
||||
referenceDataProcessor.apply(head, decoded, kind.type)
|
||||
}
|
||||
MsgKind.Eror -> {
|
||||
val payload = decoded.body as? ErorPayload
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.gzzn.omms.msgexchange.processing
|
||||
|
||||
import com.gzzn.omms.msgexchange.domain.ProcState
|
||||
import com.gzzn.omms.msgexchange.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.PipelineLockRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.PipelineTransactionManager
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.ProcStateRepository
|
||||
import jakarta.inject.Singleton
|
||||
import java.time.Clock
|
||||
|
||||
/** 静态参考数据单事务提交:落库 + 终态 + 回填意图(无 Redis / MSG_EVENT,`US-13`)。 */
|
||||
@Singleton
|
||||
class RefDataCommit(
|
||||
private val txManager: PipelineTransactionManager,
|
||||
private val lock: PipelineLockRepository,
|
||||
private val procState: ProcStateRepository,
|
||||
private val clock: Clock,
|
||||
) {
|
||||
fun commit(head: ProcState, work: () -> Unit) {
|
||||
lock.holdAcrossTransactions {
|
||||
txManager.inTransaction {
|
||||
lock.lock()
|
||||
work()
|
||||
procState.markTerminal(head.msgId, ProcStatus.SUCCEEDED, now = clock.instant())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.gzzn.omms.msgexchange.processing
|
||||
|
||||
import com.gzzn.omms.msgexchange.domain.DecodedMessage
|
||||
import com.gzzn.omms.msgexchange.domain.ProcState
|
||||
import com.gzzn.omms.msgexchange.domain.ref.RefDataBody
|
||||
import com.gzzn.omms.msgexchange.domain.ref.RefDataFieldMapping
|
||||
import com.gzzn.omms.msgexchange.domain.ref.RefDataMode
|
||||
import com.gzzn.omms.msgexchange.domain.ref.RefDataValidator
|
||||
import com.gzzn.omms.msgexchange.domain.ref.refDataModeOf
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.RefDataRepository
|
||||
import jakarta.inject.Singleton
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
@Singleton
|
||||
class ReferenceDataProcessor(
|
||||
private val commit: RefDataCommit,
|
||||
private val refData: RefDataRepository,
|
||||
private val outbound: OutboundRequestService,
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(ReferenceDataProcessor::class.java)
|
||||
|
||||
fun apply(head: ProcState, msg: DecodedMessage, category: String): ApplyResult {
|
||||
val body = msg.body as? RefDataBody ?: return ApplyResult.DeadProtocol("missing-refdata-body")
|
||||
val mode = refDataModeOf(body.styp) ?: return ApplyResult.DeadProtocol("refdata-styp:${body.styp}")
|
||||
val reason = RefDataValidator.validate(category, body, mode)
|
||||
if (reason != null) {
|
||||
log.warn("refdata validation failed msgId={} category={} reason={}", head.msgId, category, reason)
|
||||
return ApplyResult.DeadProtocol(reason)
|
||||
}
|
||||
|
||||
commit.commit(head) {
|
||||
if (category == "RSTA") {
|
||||
refData.applyResourceStatuses(body.resourceStatuses)
|
||||
} else {
|
||||
when (mode) {
|
||||
RefDataMode.FULL_REPLACE -> refData.replaceCategory(category, body.records)
|
||||
RefDataMode.ADD, RefDataMode.UPDATE ->
|
||||
body.records.forEach { refData.upsertCategory(category, it) }
|
||||
RefDataMode.DELETE ->
|
||||
body.records.forEach {
|
||||
refData.deleteCategory(category, RefDataFieldMapping.businessKey(category, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (body.styp.equals("RESP", ignoreCase = true)) {
|
||||
outbound.completeRqrdResponse()
|
||||
}
|
||||
return ApplyResult.Succeeded
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user