- Make AccountSelector.isHealthy public static so SendService can reuse it.
- Use isHealthy for the resolved/fallback conversation account instead of
a weaker existence/status/channel check, covering risk level and PYWECHAT
heartbeat/online status.
- Add null-safe channel-type comparison in resolvePersonTarget.
- Add @Min(0) to BindConversationAccountRequest.sortOrder.
- Rename misleading conversation fallback test and add tests for
resolvePersonTarget disabled/cross-tenant/channel-mismatch cases.
- Remove unused java.util.Arrays import from test file.
- SendService.selectSendAccount now delegates to AccountSelector with
extractStrategy(channelStrategy) instead of an inline filter that only
checked tenant/status, restoring risk/online/heartbeat checks.
- ConversationService.bindAccount validates the account exists, belongs
to the same tenant, and matches the conversation channel type before
creating the binding.
- Add unit tests for ConversationService.bindAccount validation and
SendService.resolveConversationTarget strategy/health behaviors.
- BindConversationAccountRequest: restrict bindingType to 1-4
- ConversationService.bindAccount: upsert binding by account+conversation+type with sortOrder and status=1
- SendService.selectSendAccount: prefer send-primary(1), fallback send-backup(2), status=1, tenant/channel match, sortOrder asc
- SendService.resolveConversationTarget/resolvePersonTarget: verify account tenant, channel type and status; ensure person identity channel type matches for single chat
- Strengthen WeComSyncTest personType assertion by seeding a value (3)
that differs from what resolvePersonType(user001/INTERNAL) would assign (1),
so the test actually detects an overwrite.
- Remove redundant personMapper/conversationMapper selectById calls in
upsertContact and upsertConversation; preserve original business fields
by reading them from the already-loaded entity before mutation.
- Delete the manual regression shell script; the unit tests are the primary
verification and the script required manual DB edits and only covered
conversation fields.