搜 索

ZAND IBAN Migration - Test Manual

  • 77阅读
  • 2026年03月23日
  • 0评论
首页 / Default / 正文

Visii Changes

Branch: fea-260301-iban-migrate

I. Change Overview

mindmap root((IBAN Migration
Visii Changes)) MQ Message Listeners KycFinishListener
KYC completion auto account opening KycDeleteListener
KYC deletion account closure Dubbo Interfaces retryOpenAccount
Counter retry account opening CGS Interfaces
Mini-program API VamAuthInit
Initialization query VamAuthActive
Activation account opening VamAuthInfoQuery
Account info query VamAuthInfoQueryV2
V2 version query Core Services ZandVaServiceImpl
ZAND account opening core logic VaConverter/VaBuilder
VA entity construction Infrastructure CmsClient
Whitelist service MemberClient
Added isVip/grcKycType RetryApplyVamHandler
Compensation retry

II. Use Case Diagram

graph TB subgraph Actors["Participants"] User["End User
(App)"] Counter["Counter Personnel
(Operations Portal)"] MQ["MQ Messages
(KYC System Push)"] end subgraph UseCases["Use Cases"] UC1["UC1: KYC Completion Auto Account Opening"] UC2["UC2: KYC Deletion Account Closure"] UC3["UC3: Query IBAN Account Opening Status"] UC4["UC4: Activate Account Opening"] UC5["UC5: Query IBAN Account Information"] UC6["UC6: Counter Batch Retry Account Opening"] end MQ --> UC1 MQ --> UC2 User --> UC3 User --> UC4 User --> UC5 Counter --> UC6 UC1 -. "When account opening fails" .-> UC6 UC4 -. "Depends on" .-> UC3

III. Detailed Module Descriptions

3.1 KYC Completion Auto Account Opening (KycFinishListener)

Change Description
ItemDescription
Class NameKycFinishListener
MQ Exchangeexchange.kyc.finish
Route Keykyc.eid.finish / kyc.eid.finish.vip
Queuekyc.visii.queue
Trigger ConditionKYC system pushes MQ message after KYC completion
Core LogicReceive message → Check switch/whitelist → Check if VA exists → Build VA → Async call ZAND account opening
Flowchart
flowchart TD A[Receive MQ message] --> B{Message empty?} B -- Yes --> Z1[Skip] B -- No --> C[Parse KycFinishInfo] C --> D{memberId empty?} D -- Yes --> Z1 D -- No --> E{autoOpen switch=Y
or whitelist hit?} E -- No --> Z2[Skip: Auto account opening not enabled] E -- Yes --> F{VA record exists?} F -- Yes --> Z3[Skip: Idempotent] F -- No --> G[VaBuilder build VA entity] G --> H[Transaction: Save VA + Save compensation event] H --> I[Async execute: Call ZAND account opening API] I --> J[Complete] style Z1 fill:#f9f,stroke:#333 style Z2 fill:#ff9,stroke:#333 style Z3 fill:#9ff,stroke:#333 style J fill:#9f9,stroke:#333
Configuration Items
Config KeyTypeDefaultDescription
visii.config.zand.customerAutoOpenStringYAuto account opening master switch
CMS Whitelist VISII_KYC_MID_WHITELIST--Whitelist hit also allows account opening
Test Points
Test ScenarioInputExpectedCorresponding Test Case
Empty messagenull / ""No exception thrown, skipKycFinishListenerTest#testHandle_emptyMessage
Switch offcustomerAutoOpen=N, memberId not in whitelistSkiptestHandle_switchOff
VA existsSame memberId sent twiceIdempotent, skip second timetestHandle_vaExists
Normal flowValid KYC messageVA record created, async account opening triggeredtestHandle_normalFlow

3.2 KYC Deletion Account Closure (KycDeleteListener)

Change Description
ItemDescription
Class NameKycDeleteListener
MQ Exchangeexchange.kyc.delete
Route Keykyc.delete.eid
Queuekyc.delete.visii.queue
Core LogicReceive delete message → Trigger MemberLogoutEventHandler to close VA
Flowchart
flowchart TD A[Receive MQ message] --> B{Message empty?} B -- Yes --> Z1[Skip] B -- No --> C[Parse KycDeleteInfo] C --> D{memberId empty?} D -- Yes --> Z1 D -- No --> E[Build MemberLogoutEvent
parameters: memberId, deleteMethod] E --> F[Save compensation event] F --> G[Async execute: Close VA] G --> H[Complete] style Z1 fill:#f9f,stroke:#333 style H fill:#9f9,stroke:#333
Message Example
{
    "memberId": "100021922209",
    "deleteMethod": "EID_BASIS_MANUAL_DELETE",
    "kycType": "EID"
}
Test Points
Test ScenarioInputExpectedCorresponding Test Case
Empty messagenull / ""No exception thrown, skipKycDeleteListenerTest#testHandle_emptyMessage
memberId empty{"deleteMethod":"X"}SkiptestHandle_emptyMemberId
No VA recordNon-existent memberIdNo error, save close eventtestHandle_noVaExists
Normal closureValid memberIdTrigger close VAtestHandle_normalFlow

3.3 Counter Batch Retry Account Opening (retryOpenAccount)

Change Description
ItemDescription
InterfaceCounterOperateFacade#retryOpenAccount
Input ParameterRetryOpenAccountRequest(bankCode, memberIdList)
Output ParameterCommonResponse
Core LogicIterate memberIdList → Query/create VA → Trigger compensation retry
Flowchart
flowchart TD A[Receive retryOpenAccount request] --> B[Parameter validation
bankCode: ZAND/PAYBY
memberIdList: non-empty] B -- Validation failed --> Z1[Return FAIL] B -- Validation passed --> C[Iterate memberIdList] C --> D{Query VA record} D -- VA exists and VALID --> E1[Skip: Already opened] D -- VA exists and CLOSED --> E2[Skip: Already closed] D -- VA exists and not VALID/CLOSED --> F[Trigger RetryApplyVamHandler] D -- VA does not exist --> G[vaConverter.initVa create VA] G --> G1{initVa succeeded?} G1 -- No --> E3[Skip: Cannot create] G1 -- Yes --> G2[insert VA record] G2 --> F F --> H[Save compensation event + async execute] H --> C E1 --> C E2 --> C E3 --> C C -- Iteration complete --> I[Return SUCCESS] style Z1 fill:#f66,stroke:#333 style I fill:#9f9,stroke:#333
Test Points
Test ScenarioInputExpectedCorresponding Test Case
bankCode emptybankCode=nullFAILCounterOperateFacadeImplTest#testRetryOpenAccount_validateFail
memberIdList emptymemberIdList=nullFAILSame as above
memberIdList empty listmemberIdList=[]FAILSame as above
member has no VARandom memberIdSUCCESS, create VA and retrytestRetryOpenAccount_noVaExists
Normal retrymemberId with existing VASUCCESStestRetryOpenAccount_normalFlow

3.4 Mini-program CGS Interfaces (VamAuth Series)

Interface List
ProviderServiceCodeMethodDescription
VamAuthInitProvider/visii/vam/v1/auth/initQuery account opening statusReturn openStatus + vipType
VamAuthActiveProvider/visii/vam/v1/auth/activeActivate account openingTrigger ZAND account opening flow
VamAuthInfoQueryProvider/visii/vam/v1/auth/query-infoQuery account informationReturn IBAN/bankName etc.
VamAuthInfoQueryV2Provider/visii/vam/v2/auth/query-infoV2 querySame as above, newVersion=true
VamAuthInit Flowchart
flowchart TD A["/visii/vam/v1/auth/init"] --> B[Get memberId] B --> C{Query VA record} C -- VA does not exist --> D1["openStatus = I (INIT)"] C -- VA exists --> D2["openStatus = VA actual status"] D1 --> E{isVip?} D2 --> E E -- Yes --> F1["vipType = VIP"] E -- No --> F2["vipType = NON_VIP"] F1 --> G[Return Response] F2 --> G
VamAuthActive Flowchart
flowchart TD A["/visii/vam/v1/auth/active"] --> B[Get memberId] B --> C{Query VA record} C -- "VA exists and VALID" --> D1["Return openStatus=V"] C -- "VA does not exist" --> E[vaConverter.initVa create VA] E --> E1{Creation succeeded?} E1 -- No --> D2["Return openStatus=F"] E1 -- Yes --> F[Set accountSeq/name] F --> G[insert VA] G -- Duplicate key --> G1[Re-query VA] G -- Success --> H G1 --> H{VA status=INIT/FAIL?} C -- "VA exists and not VALID" --> H H -- Yes --> I[Trigger RetryApplyVamHandler
async account opening] H -- No --> J I --> J["Return openStatus=VA status"]
VamAuthInfoQuery Flowchart
flowchart TD A["/visii/vam/v1/auth/query-info"] --> B[Query VA record] B -- VA does not exist --> C1[Return empty Response] B -- VA exists --> C2[Fill bankCode/bankName/accountType] C2 --> D{IBAN not empty?} D -- Yes --> E1[Set iban/accountNo] D -- No --> F E1 --> F{name not empty?} F -- Yes --> G[Decrypt name set accountHolder] F -- No --> H[Query GRC limit] G --> H H --> I{kycType not empty?} I -- Yes --> J[Query limitConfig
set vamLimit] I -- No --> K[Return Response] J --> K
Test Points
Test ScenarioInterfaceExpectedCorresponding Test Case
memberId emptyInit/Active/QueryFAILVamAuthProcessorTest#testVamAuthInit_memberIdNull etc.
No VA record query InitInitopenStatus=I, vipType has valuetestVamAuthInit_noVa
VA record exists query InitInitopenStatus=VA actual statustestVamAuthInit_withVa
New member activationActiveSUCCESS, DB creates VAtestVamAuthActive_newMember
Activation with nameActiveSUCCESStestVamAuthActive_withName
Duplicate activationActiveIdempotent, SUCCESStestVamAuthActive_idempotent
No VA query InfoQuerySUCCESS, bankCode=nulltestVamAuthInfoQuery_noVa
VA exists query InfoQuerybankCode=ZANDtestVamAuthInfoQuery_withVa
V2 version queryQueryV2bankCode=ZANDtestVamAuthInfoQuery_v2

IV. New/Modified File List

4.1 New Files

FileModuleDescription
KycFinishInfo.javacore/domain/dtoKYC completion MQ message DTO
KycDeleteInfo.javacore/domain/dtoKYC deletion MQ message DTO
KycFinishListener.javadomainservice/mqKYC completion MQ listener
KycDeleteListener.javadomainservice/mqKYC deletion MQ listener
VaBuilder.javadomainservice/builderBuild VA entity from KycFinishInfo
RetryOpenAccountRequest.javafacade/domain/requestRetry account opening request DTO
RetryOpenAccountProcessor.javadomainservice/processorRetry account opening business processor
VamAuthInitProcessor.javadomainservice/service/impl/vaInit interface processor
VamAuthActiveProcessor.javadomainservice/service/impl/vaActive interface processor
VamAuthInfoQueryProcessor.javadomainservice/service/impl/vaInfoQuery interface processor
VamAuthInitProvider.javaext/service/providerCGS Init interface Provider
VamAuthActiveProvider.javaext/service/providerCGS Active interface Provider
VamAuthInfoQueryProvider.javaext/service/providerCGS InfoQuery interface Provider
VamAuthInfoQueryV2Provider.javaext/service/providerCGS InfoQuery V2 Provider
CmsClient.java / CmsClientImpl.javaext/integration/internal/cmsCMS whitelist service client
BaseConverter.javadomainservice/converterCGS request basic info conversion

4.2 Modified Files

FileChanges
VisiiConstants.javaAdded Kyc/KycDelete/ServiceCode constant interfaces
ZandConfig.javaRemoved customerOpenPercent (changed to CMS whitelist control)
MemberClient.javaAdded isVip(memberId) / grcKycType(memberId)
MemberClientImpl.javaImplemented isVip/grcKycType, renamed private method isVip→isVipKycType
CounterOperateFacade.javaAdded retryOpenAccount method
CounterOperateFacadeImpl.javaInjected RetryOpenAccountProcessor and delegated
ZandVaServiceImpl.javaFully implemented vamAuthInit/vamAuthActive/vamAuthInfoQuery
VaConverter.javaAdded initVa(memberId) method

4.3 New Test Files

FileDescription
KycFinishListenerTest.javaKYC completion listener test (4 test cases)
KycDeleteListenerTest.javaKYC deletion listener test (4 test cases)
CounterOperateFacadeImplTest.javaCounter interface test (includes retryOpenAccount 5 test cases)
VamAuthProcessorTest.javaCGS interface processor test (11 test cases)

V. End-to-End Test Flow

sequenceDiagram participant Tester as Tester participant MQ as RabbitMQ participant Visii as Visii Service participant DB as Database(t_va) participant ZAND as ZAND API participant App as Mini-program rect rgb(230, 243, 255) Note over Tester,ZAND: Scenario 1: KYC Completion Auto Account Opening Tester->>MQ: Send KYC completion message to exchange.kyc.finish MQ->>Visii: KycFinishListener receives Visii->>DB: Check autoOpen switch + whitelist Visii->>DB: Create VA record(status=INIT) Visii->>ZAND: Async call CreateVA API ZAND-->>Visii: Return IBAN Visii->>DB: Update VA(status=VALID, iban=xxx) Tester->>DB: Verify: t_va has record, status=V end rect rgb(255, 230, 230) Note over Tester,ZAND: Scenario 2: KYC Deletion Account Closure Tester->>MQ: Send KYC deletion message to exchange.kyc.delete MQ->>Visii: KycDeleteListener receives Visii->>DB: Trigger MemberLogout close VA Visii->>DB: Update VA(status=CLOSED) Tester->>DB: Verify: t_va status=C end rect rgb(230, 255, 230) Note over Tester,App: Scenario 3: Mini-program Query and Account Opening App->>Visii: /visii/vam/v1/auth/init Visii-->>App: openStatus + vipType App->>Visii: /visii/vam/v1/auth/active Visii->>DB: Create VA + async account opening Visii-->>App: openStatus Note over Tester: Wait for ZAND callback completion App->>Visii: /visii/vam/v1/auth/query-info Visii-->>App: iban + bankName + vamLimit end rect rgb(245, 245, 245) Note over Tester,ZAND: Scenario 4: Counter Batch Retry Tester->>Visii: retryOpenAccount(ZAND, [mid1,mid2]) Visii->>DB: Query/create VA Visii->>ZAND: Async retry account opening Visii-->>Tester: SUCCESS end

VI. Key Verification Point Checklist

MQ Message Listening

  • [ ] exchange.kyc.finish messages can be consumed normally
  • [ ] exchange.kyc.delete messages can be consumed normally
  • [ ] Empty messages/malformed messages do not cause exceptions
  • [ ] When customerAutoOpen=N and not in whitelist, KYC messages are skipped
  • [ ] When customerAutoOpen=Y, normal account opening occurs
  • [ ] When whitelist hit, account opening works even if switch is off
  • [ ] Duplicate MQ messages are handled idempotently (no duplicate VA creation)

Counter Interface

  • [ ] retryOpenAccount parameter validation works (bankCode required and must be ZAND/PAYBY, memberIdList non-empty)
  • [ ] Already opened (VALID) members are skipped
  • [ ] Already closed (CLOSED) members are skipped
  • [ ] Members without VA records automatically create VA and retry
  • [ ] Partial failures in batch processing do not affect overall operation

CGS Interface (Provided to app)

  • [ ] /visii/vam/v1/auth/init returns openStatus=I when no VA
  • [ ] /visii/vam/v1/auth/init returns vipType=VIP for VIP users
  • [ ] /visii/vam/v1/auth/active triggers account opening for new users
  • [ ] /visii/vam/v1/auth/active is idempotent on duplicate calls
  • [ ] /visii/vam/v1/auth/active handles name parameter normally
  • [ ] /visii/vam/v1/auth/query-info returns empty Response when no VA
  • [ ] /visii/vam/v1/auth/query-info returns bankCode=ZAND when VA exists
  • [ ] /visii/vam/v2/auth/query-info returns with vamLimit limit information
  • [ ] Unauthenticated users (no token) are blocked by CGS gateway

Database Verification

  • [ ] t_va table new record fields are complete (memberId, bankCode, vaId, status, iban, name, extension etc.)
  • [ ] VA status transition correct: INIT → PROCESSING → VALID / FAIL
  • [ ] Status after closure: CLOSED

VII. Configuration Change Description

Change TypeConfiguration ItemDescription
Removedvisii.config.zand.customerOpenPercentNo longer use percentage control, changed to CMS whitelist
Retainedvisii.config.zand.customerAutoOpenMaster switch, default Y
AddedCMS Whitelist VISII_KYC_MID_WHITELISTUsed with autoOpen, whitelist hit can bypass switch

VIII. Transaction Status Change MQ Notification

8.1 Change Description

ItemDescription
Exchangeexchange.visii.trans
RoutingKey Formatstatus.${statusCode}.${memberId}
Trigger TimingTransaction first insert (WV), each status change (WV→V/MC, general transition, P→S)
Failure Strategytry-catch wrapped, failure only log.warn, does not affect main flow

8.2 Message Body TransStatusChangeMessage

FieldTypeDescription
orderIdLongOrder ID
memberIdStringMember ID
bankCodeStringBank code
statusStringCurrent status code
bankOrderNoStringBank order number
previousStatusStringPrevious status code (null for first insert)
amountBigDecimalTransaction amount
currencyStringCurrency
ibanStringIBAN
directionStringDirection (I/O)
gmtModifiedLocalDateTimeModification time

8.3 Publishing Trigger Points

Trigger PointClassMethodStatus Change
First receive transaction notificationNotifyTransProcessorsaveAndValidate4Banknull → WV
Post-validation status updateVamOrderRepositoryImplupdateStatus(VamOrder, VamOrderStatusEnum, VisiiUnityResultCodeEnum)WV → V/MC
General status transitionVamOrderRepositoryImplupdateStatus(VamOrder, DbResultCarrier)Various status transitions
Deposit successVamOrderRepositoryImplupdateToSuccess(VamOrder, DepositInfo)P → S

8.4 Flowchart

flowchart TD A[Transaction notification arrives] --> B[NotifyTransProcessor
insert order] B --> C["Publish MQ: status=WV, prev=null"] C --> D{Validation result} D -- Passed --> E[updateStatus WV→V] D -- Manual needed --> F[updateStatus WV→MC] E --> G["Publish MQ: status=V, prev=WV"] F --> H["Publish MQ: status=MC, prev=WV"] G --> I[Subsequent deposit flow] I --> J[updateToSuccess P→S] J --> K["Publish MQ: status=S, prev=P"] style C fill:#e6f3ff style G fill:#e6f3ff style H fill:#e6f3ff style K fill:#e6f3ff

8.5 Test Points

Test ScenarioExpectedCorresponding Test Case
First insert status (previousStatus=null)No exception thrown, MQ message sentTransStatusMqPublisherTest#testPublish_initialStatus
Status change WV→VNo exception thrown, routingKey=status.V.{memberId}testPublish_statusChange
Status change P→SNo exception thrown, routingKey=status.S.{memberId}testPublish_pendingToSuccess

8.6 New/Modified Files

FileOperationDescription
VisiiConstants.javaModifiedAdded TransNotify constant interface
TransStatusChangeMessage.javaCreatedMQ message body DTO
TransStatusMqPublisher.javaCreatedMQ publisher component
VamOrderRepositoryImpl.javaModified3 update methods call publisher
NotifyTransProcessor.javaModifiedCall publisher after insert
TransStatusMqPublisherTest.javaCreatedPublisher test cases

Vis Changes

1. Feature Overview

This change involves the following modules for IBAN migration functionality:

ModuleFeatureDescription
Automatic retry schedulingFeature 7Failed records auto retry (max 3 times, incremental delay)
Manual status modificationFeature 8.1Counter manually set migration record status
Manual retryFeature 8.2Counter manually retry failed records
Transaction notification consumptionFeature 9Receive transaction status change notifications, remove TodoCard
graph TB subgraph "Feature 7 - Automatic Retry Scheduling" F7_JOB[MigrateRetryJob
Scheduled task every 5 minutes] F7_QUERY[(Query FAILED records
retryCount < 3)] F7_DELAY{Delay check} F7_CAS[CAS: F → I] F7_DISPATCH{Dispatch by memberType} F7_CUSTOMER[CustomerMigrateService] F7_MERCHANT[MerchantMigrateService] F7_FF[Mark FINAL_FAILED] F7_JOB --> F7_QUERY F7_QUERY --> F7_DELAY F7_DELAY -->|Delay satisfied| F7_CAS F7_DELAY -->|Delay not reached| SKIP1[Skip] F7_CAS --> F7_DISPATCH F7_DISPATCH -->|CUSTOMER| F7_CUSTOMER F7_DISPATCH -->|MERCHANT| F7_MERCHANT F7_QUERY -->|retryCount >= 3| F7_FF end subgraph "Feature 8.1 - Manual Status Modification" F81_API[CounterFacade#putMigrateStatus] F81_PROC[PutMigrateStatusProcessor] F81_DB[(Update t_migrate_record)] F81_API --> F81_PROC --> F81_DB end subgraph "Feature 8.2 - Manual Retry" F82_API[CounterFacade#retryMigrate] F82_PROC[RetryMigrateProcessor] F82_DB[(Reset to PENDING)] F82_API --> F82_PROC --> F82_DB end subgraph "Feature 9 - Transaction Notification Consumption" F9_MQ[exchange.visii.trans
RabbitMQ] F9_HANDLER[TransStatusChangeHandler] F9_CHECK{memberId starts with 1
and status == WV?} F9_REMOVE[removeTodoCard] F9_IGNORE[Ignore] F9_MQ --> F9_HANDLER --> F9_CHECK F9_CHECK -->|Yes| F9_REMOVE F9_CHECK -->|No| F9_IGNORE end

2. Migration Status Enum MigrateStatus

CodeEnum ValueDescription
PPENDINGPending
IIN_PROGRESSIn Progress
CCOMPLETEDCompleted
FFAILEDFailed (retryable)
FFFINAL_FAILEDFinal Failed (exceeds retry limit)
SSKIPPEDSkipped
stateDiagram-v2 [*] --> P : File import P --> I : Engine pickup / Retry pickup I --> C : Migration success I --> F : Migration failed F --> I : Auto retry (retryCount < 3) F --> FF : Exceeds 3 retries FF --> P : Manual retry (retryMigrate) F --> P : Manual retry (retryMigrate) F --> C : Manual modify (putMigrateStatus) F --> S : Manual modify (putMigrateStatus) P --> S : Manual modify (putMigrateStatus)

3. Feature 7: Automatic Retry Scheduling (MigrateRetryJob)

3.1 Retry Strategy

retryCountDelay TimeAction
15 minutesFirst retry
230 minutesSecond retry
>= 3-Mark as FINAL_FAILED, no more auto retries

3.2 Processing Flow

flowchart TD START[MigrateRetryJob trigger
cron: Every 5 minutes] --> CHECK_ENABLED{enabled?} CHECK_ENABLED -->|No| END_DISABLED[Skip] CHECK_ENABLED -->|Yes| CHECK_WINDOW{Within time window?
startHour ~ endHour} CHECK_WINDOW -->|No| END_WINDOW[Skip] CHECK_WINDOW -->|Yes| QUERY[Query FAILED records
retryCount < 3
LIMIT batchSize] QUERY -->|Empty| END_EMPTY[No retryable records] QUERY -->|Has records| LOOP[Iterate each record] LOOP --> CHECK_RETRY{retryCount >= 3?} CHECK_RETRY -->|Yes| MARK_FF[CAS: F → FF
Mark final failed] CHECK_RETRY -->|No| CHECK_DELAY{gmtModified + delay <= now?} CHECK_DELAY -->|No| SKIP_DELAY[Delay not reached, skip] CHECK_DELAY -->|Yes| CAS[CAS: F → I] CAS -->|Success| DISPATCH{memberType?} CAS -->|Conflict| SKIP_CAS[CAS conflict, skip] DISPATCH -->|CUSTOMER| EXEC_C[migrateExecutor submit
customerMigrateService.migrateOne] DISPATCH -->|MERCHANT| EXEC_M[migrateExecutor submit
merchantMigrateService.migrateOne]

3.3 Configuration Items

Configuration ItemDescriptionDefault Value
elastic.job.task.cron.migrate.retryScheduled task cron0 0/5 * * * ? *
MigrateEngineConfiguration.enabledMaster switchtrue
MigrateEngineConfiguration.startHourExecution start hour allowed-
MigrateEngineConfiguration.endHourExecution end hour allowed-
MigrateEngineConfiguration.batchSizeBatch query size-

4. Feature 8.1: Manual Status Modification (putMigrateStatus)

4.1 Interface Definition

ItemDescription
InterfaceCounterFacade#putMigrateStatus
Request ClassPutMigrateStatusRequest
ProcessorPutMigrateStatusProcessor

4.2 Request Parameters

FieldTypeRequiredDescription
idListList\<Long\>YMigration record ID list
statusStringYTarget status code (P/C/F/FF/S)

4.3 Processing Logic

flowchart TD REQ[Request: idList + status] --> VALIDATE[Validate parameters
status must be valid enum value] VALIDATE --> QUERY[queryByIds query records] QUERY -->|Empty| SUCCESS_NOOP[Return SUCCESS
No records to update] QUERY -->|Has records| LOOP[Iterate each record] LOOP --> SET_STATUS[Set status = target status] SET_STATUS --> CHECK_PENDING{Target status == PENDING?} CHECK_PENDING -->|Yes| RESET[retryCount = 0
message = null] CHECK_PENDING -->|No| NO_RESET[Keep original retryCount and message] RESET --> UPDATE[updateById] NO_RESET --> UPDATE UPDATE --> SUCCESS[Return SUCCESS]

4.4 Test Cases

#Test CaseExpected Result
1Set FAILED record to COMPLETEDstatus=C, retryCount/message unchanged
2Set FINAL_FAILED record to PENDINGstatus=P, retryCount=0, message=null
3No records foundSUCCESS (no-op)
4Set FAILED record to SKIPPEDstatus=S, retryCount/message unchanged

5. Feature 8.2: Manual Retry (retryMigrate)

5.1 Interface Definition

ItemDescription
InterfaceCounterFacade#retryMigrate
Request ClassRetryMigrateRequest
ProcessorRetryMigrateProcessor

5.2 Request Parameters

FieldTypeRequiredDescription
idListList\<Long\>YMigration record ID list

5.3 Processing Logic

flowchart TD REQ[Request: idList] --> QUERY[queryByIds query records] QUERY -->|Empty| SUCCESS_NOOP[Return SUCCESS
No records to retry] QUERY -->|Has records| LOOP[Iterate each record] LOOP --> CHECK{status == FAILED
or FINAL_FAILED?} CHECK -->|Yes| RESET[status = PENDING
retryCount = 0
message = null] CHECK -->|No| SKIP[Skip this record] RESET --> UPDATE[updateById] UPDATE --> SUCCESS[Return SUCCESS]

5.4 Test Cases

#Test CaseExpected Result
1FAILED recordReset to PENDING, retryCount=0, message=null
2FINAL_FAILED recordReset to PENDING, retryCount=0, message=null
3COMPLETED recordSkip, no modification
4Mixed status (F + FF + C)Only F and FF reset, C skipped
5No records foundSUCCESS (no-op)

6. Feature 9: Transaction Notification Consumption (TransStatusChangeHandler)

6.1 MQ Configuration

ItemValue
Exchangeexchange.visii.trans
Exchange TypeTopic
Queuequeue.vis.transStatusChange
RoutingKey (sender)status.${statusCode}.${memberId}
RoutingKey (consumer)status.#
Trigger TimingTransaction first insert (WV), each status change
Failure Strategytry-catch wrapped, failure only log.warn

6.2 Message Body TransStatusChangeMessage

FieldTypeDescription
orderIdLongOrder ID
memberIdStringMember ID
bankCodeStringBank code
statusStringCurrent status code
bankOrderNoStringBank order number
previousStatusStringPrevious status code (null for first insert)
amountBigDecimalTransaction amount
currencyStringCurrency
ibanStringIBAN
directionStringDirection (I/O)
gmtModifiedStringModification time

6.3 Processing Flow

flowchart TD MQ[RabbitMQ message
exchange.visii.trans] --> PARSE[Parse JSON → TransStatusChangeMessage] PARSE -->|Parse failed| LOG_WARN[log.warn skip] PARSE -->|Success| CHECK_MEMBER{memberId empty?} CHECK_MEMBER -->|Yes| LOG_EMPTY[log.warn skip] CHECK_MEMBER -->|No| CHECK_PREFIX{memberId starts with 1?} CHECK_PREFIX -->|No Merchant| IGNORE[Ignore, do not process] CHECK_PREFIX -->|Yes Individual user| CHECK_STATUS{status == WV?} CHECK_STATUS -->|No| SKIP_STATUS[Not first transaction, skip] CHECK_STATUS -->|Yes| REMOVE[Call migrateNotifyService
.removeTodoCard-memberId-] REMOVE -->|Success| DONE[Processing complete] REMOVE -->|Exception| LOG_FAIL[log.warn does not affect main flow]

6.4 Core Judgment Logic

graph LR A[Receive message] --> B{memberId
starts with 1?} B -->|1xxxxxxx
Individual user| C{status == WV?} B -->|2xxxxxxx / 3xxxxxxx
Merchant| D[Directly ignore] C -->|WV First transaction| E[Remove TodoCard] C -->|V / MC / S / P
Other status| F[Skip]

6.5 Test Cases

#Test CaseMessage ContentExpected Result
1Individual user WV statusmemberId=1xxx, status=WVCall removeTodoCard
2Individual user WV + complete fieldsIncludes orderId/amount/iban etc.Call removeTodoCard
3Individual user V statusmemberId=1xxx, status=VDo not call removeTodoCard
4Individual user MC statusmemberId=1xxx, status=MCDo not call removeTodoCard
5Individual user S statusmemberId=1xxx, status=SDo not call removeTodoCard
6Merchant WV statusmemberId=2xxx, status=WVDo not call removeTodoCard
7Merchant starting with 3memberId=3xxx, status=WVDo not call removeTodoCard
8Empty message""Not processed
9null messagenullNot processed
10memberId emptymemberId="", status=WVNot processed
11memberId nullmemberId=null, status=WVNot processed
12status nullmemberId=1xxx, status=nullDo not call removeTodoCard
13Invalid JSON"invalid json"No exception thrown, not processed
14removeTodoCard throws exceptionmemberId=1xxx, status=WVException caught, not propagated

7. Modified File List

7.1 New Files

FileDescription
facade/domain/TransStatusChangeMessage.javaTransaction status change message body
facade/request/PutMigrateStatusRequest.javaManual status modification request
facade/request/RetryMigrateRequest.javaManual retry request
core/dal/configuration/TransStatusChangeConfiguration.javaTransaction notification MQ configuration
domainservice/mq/TransStatusChangeHandler.javaTransaction notification MQ consumer
ext/service/manage/PutMigrateStatusProcessor.javaManual status modification processor
ext/service/manage/RetryMigrateProcessor.javaManual retry processor
ext/daemon/MigrateRetryJob.javaAutomatic retry scheduled task

7.2 Modified Files

FileChange Description
facade/enums/MigrateStatus.javaAdded FINAL_FAILED("FF") enum value
facade/CounterFacade.javaAdded putMigrateStatus / retryMigrate interface methods
ext/service/CounterFacadeImpl.javaAdded putMigrateStatus / retryMigrate implementation
core/common/VisConstants.javaAdded MIGRATE_MAX_RETRY_COUNT = 3
domainservice/repo/MigrateRecordRepository.javaAdded queryRetryableRecords / queryByIds methods
domainservice/repo/impl/MigrateRecordRepositoryImpl.javaImplemented above methods
domainservice/service/MigrateRecordService.javaAdded queryRetryableRecords / queryByIds methods
domainservice/service/impl/MigrateRecordServiceImpl.javaImplemented above methods

7.3 Test Files

FileCase CountCoverage
MigrateRetryJobTest.java8Switch/time window/delay/retry/FINAL_FAILED/CAS conflict
PutMigrateStatusProcessorTest.java4Set to C/P/S, no records
RetryMigrateProcessorTest.java5F reset/FF reset/C skip/mixed/no records
TransStatusChangeHandlerTest.java14Individual WV/non-WV/merchant/empty message/exception

8. Test Guide

8.1 Feature 7 Automatic Retry - Test Steps

Prerequisite: There are FAILED status migration records

StepOperationVerification Point
1Confirm MigrateEngineConfiguration.enabled = trueScheduled task running
2Prepare status=F, retryCount=1 record, gmtModified 6 minutes ago5-minute delay satisfied
3Wait for scheduled task trigger (every 5 minutes)Record status changes to I, then to C or F
4Prepare retryCount=2 record, gmtModified 31 minutes ago30-minute delay satisfied
5Wait for scheduled task triggerRecord is retried
6Prepare retryCount=3 FAILED recordNo more retries
7Wait for scheduled task triggerRecord status changes to FF (FINAL_FAILED)
8Set enabled to falseScheduled task skips, does not query any records

8.2 Feature 8.1 Manual Status Modification - Test Steps

Interface: CounterFacade#putMigrateStatus

StepOperationVerification Point
1Call putMigrateStatus, idList=[record ID], status="C"Record status becomes C, retryCount and message unchanged
2Call putMigrateStatus, idList=[record ID], status="P"Record status becomes P, retryCount=0, message=null
3Call putMigrateStatus, idList=[record ID], status="S"Record status becomes S, retryCount and message unchanged
4Call putMigrateStatus, idList=[non-existent ID], status="C"Return SUCCESS, no records modified
5Call putMigrateStatus, idList=nullReturn FAIL (parameter validation failed)
6Call putMigrateStatus, status="INVALID"Return FAIL (invalid status code)

8.3 Feature 8.2 Manual Retry - Test Steps

Interface: CounterFacade#retryMigrate

StepOperationVerification Point
1Prepare FAILED record, call retryMigrate(idList)Record becomes PENDING, retryCount=0, message=null
2Prepare FINAL_FAILED record, call retryMigrate(idList)Same as above, FF can also be reset
3Prepare COMPLETED record, call retryMigrate(idList)Record unchanged, still COMPLETED
4Pass mixed status record idListOnly F and FF reset, C/S/P unchanged
5Wait for engine pickup after resetPENDING records picked up and executed by engine

8.4 Feature 9 Transaction Notification - Test Steps

Prerequisite: Visii system sends transaction status change messages to exchange.visii.trans

StepOperationVerification Point
1Send message: memberId=1xxx, status=WVVIS calls removeTodoCard, user TodoCard removed
2Send message: memberId=1xxx, status=VVIS receives message but does not process (not WV status)
3Send message: memberId=2xxx, status=WVVIS receives message but ignores (merchant)
4Send message: memberId=3xxx, status=WVVIS receives message but ignores (merchant)
5Send message: User has no migration record or TodoCard already removedremoveTodoCard internally skips, no side effects
6Simulate CSimple service exceptionVIS only prints warn log, does not affect transaction main flow
7Send empty message or invalid JSONVIS does not throw exception, prints log and skips

8.5 End-to-End Scenario

sequenceDiagram participant Counter as Counter App participant VIS as VIS System participant DB as t_migrate_record participant Engine as MigrateRetryJob participant Visii as Visii participant MQ as RabbitMQ participant CS as CSimple Note over DB: Initial: status=F, retryCount=1 rect rgb(255, 245, 230) Note over Engine: Scenario A: Auto retry success Engine->>DB: Query FAILED & retryCount<3 Engine->>DB: CAS F→I Engine->>Engine: migrateOne(record) Engine->>DB: status=C, migration success Engine->>CS: addTodoCard + sendMessage end rect rgb(230, 245, 255) Note over Visii: Scenario B: User receives first transaction Visii->>MQ: TransStatusChangeMessage
memberId=1xxx, status=WV MQ->>VIS: TransStatusChangeHandler VIS->>VIS: memberId starts with 1 & status==WV VIS->>CS: removeTodoCard(memberId) end rect rgb(255, 230, 230) Note over Engine: Scenario C: Auto retry 3 times then final failure Engine->>DB: Query retryCount>=3 Engine->>DB: CAS F→FF end rect rgb(230, 255, 230) Note over Counter: Scenario D: Counter manual retry Counter->>VIS: retryMigrate(idList) VIS->>DB: FF→P, retryCount=0 Note over DB: Wait for engine pickup end rect rgb(245, 230, 255) Note over Counter: Scenario E: Counter manual status modification Counter->>VIS: putMigrateStatus(idList, "S") VIS->>DB: status=S (skipped) end

Counter Changes

This change branch: fea-260301-iban-migrate, involves dependency upgrades and four functional points.

Dependency Upgrades

DependencyOld VersionNew Version
vis-service-facade1.1.2-SNAPSHOT1.1.4-SNAPSHOT
visii-service-facade1.0.0-SNAPSHOT1.0.1-SNAPSHOT
  • vis-service-facade 1.1.4 added CUSTOMER_MIGRATE_FILE and MERCHANT_MIGRATE_FILE two file type enums
  • visii-service-facade 1.0.1 added retryOpenAccount interface
  • vis-service-facade 1.1.4 added CounterFacade#putMigrateStatus and CounterFacade#retryMigrate interfaces

Change 1: File Upload (/upload) Added Two Migration File Types

vis-service-facade's FileType enum added:

Enum ValueCodeDescription
CUSTOMER_MIGRATE_FILECMGCMG-Individual migration file
MERCHANT_MIGRATE_FILEMMGMMG-Merchant migration file

Since the file upload page (/vis/upload) dynamically renders dropdown options via FileType.values(), it automatically takes effect after upgrading dependencies, no additional code changes needed.

Change 2: Added Retry Open Account Function

Added "Retry Open Account" button on VIS Virtual Account page to support batch retry VAM account opening.

Modified File List:

FileChange TypeDescription
VisiiClient.javaInterface addedAdded retryOpenAccount(memberIds, bankCode) method
VisiiClientImpl.javaImplementation addedParse comma-separated memberIds, call counterOperateFacade.retryOpenAccount()
VisAccountController.javaInterface addedAdded GET (popup page) and POST (submit processing) two interfaces
account.htmlFrontend buttonAdded "Retry Open Account" button
account.jsFrontend logicAdded button click event, popup retry account opening form
retry_open_account.htmlNew filePopup form: memberIds input + bankCode dropdown
retry_open_account.jsNew fileForm submission logic: validation + AJAX POST

Change 3: Added Put Migrate Status Function

Added "Put Migrate Status" button on VIS Virtual Account page to support manual batch set migration record status.

Modified File List:

FileChange TypeDescription
VisClient.javaInterface addedAdded putMigrateStatus(idList, status) method
VisClientImpl.javaImplementation addedParse comma-separated idList to List<Long>, call counterFacade.putMigrateStatus()
VisAccountController.javaInterface addedAdded GET (popup page) and POST (submit processing) two interfaces
account.htmlFrontend buttonAdded "Put Migrate Status" button
account.jsFrontend logicAdded button click event, popup set migration status form
put_migrate_status.htmlNew filePopup form: idList input + status input
put_migrate_status.jsNew fileForm submission logic: validation + AJAX POST

Change 4: Added Retry Migrate Record Function

Added "Retry Migrate Record" button on VIS Virtual Account page to support manual batch retry migration records.

Modified File List:

FileChange TypeDescription
VisClient.javaInterface addedAdded retryMigrate(idList) method
VisClientImpl.javaImplementation addedParse comma-separated idList to List<Long>, call counterFacade.retryMigrate()
VisAccountController.javaInterface addedAdded GET (popup page) and POST (submit processing) two interfaces
account.htmlFrontend buttonAdded "Retry Migrate Record" button
account.jsFrontend logicAdded button click event, popup retry migration record form
retry_migrate_record.htmlNew filePopup form: idList input
retry_migrate_record.jsNew fileForm submission logic: validation + AJAX POST

Use Case Diagram

graph LR subgraph Operators A((Test/Operations Personnel)) end subgraph Counter System - VIS Module B[Upload Migration File
CMG/MMG] C[Retry VAM Account Opening
Retry Open Account] F[Manual Set Migration Status
Put Migrate Status] G[Retry Migration Record
Retry Migrate Record] end A --> B A --> C A --> F A --> G B --> |Select file type CMG or MMG
Upload file| D[(VIS Service)] C --> |Submit memberIds + bankCode| E[(VISII Service)] F --> |Submit idList + status| D G --> |Submit idList| D

Overall Flow Diagrams

Change 1: Upload Migration File

flowchart TB A1[Enter VIS File Flow page] --> A2[Click upload button] A2 --> A3[In fileType dropdown
select CMG or MMG] A3 --> A4[Select file and fill in remarks] A4 --> A5[Submit upload] A5 --> A6{Upload result} A6 --> |Success| A7[Return success message] A6 --> |Failed| A8[Return error message]

Change 2: Retry VAM Account Opening

flowchart TB B1[Enter VIS Virtual Account page] --> B2[Click Retry Open Account button] B2 --> B3[Popup form window] B3 --> B4[Input memberIds
comma-separated multiple] B4 --> B5[Select bankCode
ZAND or PAYBY] B5 --> B6[Click submit] B6 --> B7{Frontend validation} B7 --> |Validation failed| B8[Prompt field cannot be empty] B7 --> |Validation passed| B9[POST /vis/virtualAccount/
retryOpenAccountSubmit] B9 --> B10[VisiiClientImpl
Parse memberIds to List] B10 --> B11[Call counterOperateFacade
.retryOpenAccount] B11 --> B12{Call result} B12 --> |SUCCESS| B13[Return success message] B12 --> |FAIL| B14[Return error message]

Change 3: Manual Set Migration Status

flowchart TB C1[Enter VIS Virtual Account page] --> C2[Click Put Migrate Status button] C2 --> C3[Popup form window] C3 --> C4[Input idList
comma-separated multiple] C4 --> C5[Input status] C5 --> C6[Click submit] C6 --> C7{Frontend validation} C7 --> |Validation failed| C8[Prompt field cannot be empty] C7 --> |Validation passed| C9[POST /vis/migrate/
putMigrateStatusSubmit] C9 --> C10[VisClientImpl
Parse idList to List] C10 --> C11[Call counterFacade
.putMigrateStatus] C11 --> C12{Call result} C12 --> |SUCCESS| C13[Return success message] C12 --> |FAIL| C14[Return error message]

Change 4: Retry Migration Record

flowchart TB D1[Enter VIS Virtual Account page] --> D2[Click Retry Migrate Record button] D2 --> D3[Popup form window] D3 --> D4[Input idList
comma-separated multiple] D4 --> D5[Click submit] D5 --> D6{Frontend validation} D6 --> |Validation failed| D7[Prompt idList cannot be empty] D6 --> |Validation passed| D8[POST /vis/migrate/
retryMigrateSubmit] D8 --> D9[VisClientImpl
Parse idList to List] D9 --> D10[Call counterFacade
.retryMigrate] D10 --> D11{Call result} D11 --> |SUCCESS| D12[Return success message] D11 --> |FAIL| D13[Return error message]

Test 1: File Upload - Added CMG/MMG File Types

Prerequisite: Environment deployed with vis-service-facade:1.1.4-SNAPSHOT

StepOperationExpected Result
1Enter VIS File Flow Management pagePage loads normally
2Click upload button, open file upload popupPopup opens normally
3Expand fileType dropdownDropdown contains CMG-Individual migration file and MMG-Merchant migration file two new options
4Select CMG, upload a valid individual migration file, fill in remarks, submitSubmit success, file enters processing flow
5Select MMG, upload a valid merchant migration file, fill in remarks, submitSubmit success, file enters processing flow
6Upload a file with incorrect formatReturn clear error message

Test 2: Retry Open Account

Prerequisite: Environment deployed with visii-service-facade:1.0.1-SNAPSHOT, and there are failed account opening member records

2.1 Page Display

StepOperationExpected Result
1Enter VIS Virtual Account pagePage loads normally
2Observe toolbar buttonsNext to Retry Trans and Write Off buttons, Retry Open Account button appears
3Click Retry Open Account button750x550 form popup appears, title is "Retry Open Account"

2.2 Form Validation

StepOperationExpected Result
1Do not fill any fields, directly click submitPrompt bankCode cannot be empty
2Fill memberIds, do not select bankCode (if dropdown allows empty selection)Prompt bankCode cannot be empty
3Do not fill memberIds, select bankCode = ZAND, click submitPrompt memberIds cannot be empty
4Click reset buttonForm clears and restores

2.3 Normal Submission

StepOperationExpected Result
1Input single memberId (e.g., 100001), bankCode select ZAND, submitReturn success message "Success!"
2Input multiple memberId, comma-separated (e.g., 100001,100002,100003), bankCode select PAYBY, submitReturn success message "Success!"
3Input memberId with spaces (e.g., 100001, 100002 , 100003), submitReturn success message (backend auto trims spaces)

2.4 Exception Scenarios

StepOperationExpected Result
1Input non-existent memberId, submitReturn clear error message
2Input already successfully opened memberId, submitReturn corresponding business message (not system exception)
3Submit when backend service unavailableReturn "Vis retryOpenAccount failed!" error message

Test 3: Put Migrate Status

Prerequisite: Environment deployed with vis-service-facade:1.1.4-SNAPSHOT, and there are migration records

3.1 Page Display

StepOperationExpected Result
1Enter VIS Virtual Account pagePage loads normally
2Observe toolbar buttonsNext to Retry Open Account button, Put Migrate Status button appears
3Click Put Migrate Status button750x550 form popup appears, title is "Put Migrate Status"

3.2 Form Validation

StepOperationExpected Result
1Do not fill any fields, directly click submitPrompt status cannot be empty
2Do not fill idList, fill status, click submitPrompt idList cannot be empty
3Fill idList, do not fill status, click submitPrompt status cannot be empty
4Click reset buttonForm clears and restores

3.3 Normal Submission

StepOperationExpected Result
1Input single id (e.g., 100001), fill status, submitReturn success message "Success!"
2Input multiple id, comma-separated (e.g., 100001,100002,100003), fill status, submitReturn success message "Success!"
3Input id with spaces (e.g., 100001, 100002 , 100003), submitReturn success message (backend auto trims spaces)

3.4 Exception Scenarios

StepOperationExpected Result
1Input non-existent id, submitReturn clear error message
2Input non-numeric id, submitReturn error message
3Submit when backend service unavailableReturn "Vis putMigrateStatus failed!" error message

Test 4: Retry Migrate Record

Prerequisite: Environment deployed with vis-service-facade:1.1.4-SNAPSHOT, and there are migration records needing retry

4.1 Page Display

StepOperationExpected Result
1Enter VIS Virtual Account pagePage loads normally
2Observe toolbar buttonsNext to Put Migrate Status button, Retry Migrate Record button appears
3Click Retry Migrate Record button750x550 form popup appears, title is "Retry Migrate Record"

4.2 Form Validation

StepOperationExpected Result
1Do not fill idList, directly click submitPrompt idList cannot be empty
2Click reset buttonForm clears and restores

4.3 Normal Submission

StepOperationExpected Result
1Input single id (e.g., 100001), submitReturn success message "Success!"
2Input multiple id, comma-separated (e.g., 100001,100002,100003), submitReturn success message "Success!"
3Input id with spaces (e.g., 100001, 100002 , 100003), submitReturn success message (backend auto trims spaces)

4.4 Exception Scenarios

StepOperationExpected Result
1Input non-existent id, submitReturn clear error message
2Input non-numeric id, submitReturn error message
3Submit when backend service unavailableReturn "Vis retryMigrate failed!" error message

API Interface Reference

InterfaceMethodPathParameters
Popup pageGET/vis/virtualAccount/retryOpenAccountNone
Submit processingPOST/vis/virtualAccount/retryOpenAccountSubmitmemberIds(String, comma-separated), bankCode(String, ZAND/PAYBY)
Popup pageGET/vis/migrate/putMigrateStatusNone
Submit processingPOST/vis/migrate/putMigrateStatusSubmitidList(String, comma-separated), status(String)
Popup pageGET/vis/migrate/retryMigrateRecordNone
Submit processingPOST/vis/migrate/retryMigrateSubmitidList(String, comma-separated)
无标签
评论区
暂无评论
avatar