From 89c9e6a0667bfebff565f29bd5fbf5e96fb63745 Mon Sep 17 00:00:00 2001 From: marsal Date: Wed, 8 Jul 2026 16:07:26 +0800 Subject: [PATCH] fix(channel): add null guard when loading person during sync --- .../service/ChannelAccountService.java | 171 +++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java index 488195e..0b44fef 100644 --- a/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java +++ b/backend/mci-server/src/main/java/com/sino/mci/channel/service/ChannelAccountService.java @@ -1,11 +1,17 @@ package com.sino.mci.channel.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.sino.mci.channel.adapter.registry.ChannelAdapterRegistry; +import com.sino.mci.channel.common.dto.ChannelInitRequest; +import com.sino.mci.channel.common.dto.ChannelInitResult; +import com.sino.mci.channel.common.model.AccountType; import com.sino.mci.channel.common.model.ChannelType; +import com.sino.mci.channel.common.spi.ChannelAdapter; import com.sino.mci.channel.dto.ChannelAccountInitRequest; import com.sino.mci.channel.dto.CreateChannelAccountRequest; import com.sino.mci.channel.dto.HeartbeatRequest; import com.sino.mci.channel.dto.SyncResult; +import com.sino.mci.channel.dto.UpdateStatusRequest; import com.sino.mci.channel.entity.ChannelAccount; import com.sino.mci.channel.entity.ChannelAccountConversation; import com.sino.mci.channel.entity.ChannelSubject; @@ -34,7 +40,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Slf4j @Service @@ -66,6 +74,7 @@ public class ChannelAccountService { private final ConversationParticipantMapper conversationParticipantMapper; private final ChannelAccountConversationMapper accountConversationMapper; private final WeComSyncClient weComSyncClient; + private final ChannelAdapterRegistry adapterRegistry; public List listAccounts(String tenantCode) { return accountMapper.selectList( @@ -75,6 +84,14 @@ public class ChannelAccountService { .orderByDesc(ChannelAccount::getId)); } + public ChannelAccount getAccount(Long id, String tenantCode) { + ChannelAccount account = accountMapper.selectById(id); + if (account == null || !tenantCode.equals(account.getTenantCode())) { + throw new BusinessException("渠道账号不存在: " + id); + } + return account; + } + @Transactional public ChannelAccount createAccount(String tenantCode, CreateChannelAccountRequest request) { if (request.getSubjectId() != null) { @@ -111,6 +128,59 @@ public class ChannelAccountService { return account; } + @Transactional + public ChannelAccount updateAccount(Long id, String tenantCode, CreateChannelAccountRequest request) { + ChannelAccount account = getAccount(id, tenantCode); + + if (request.getSubjectId() != null) { + ChannelSubject subject = subjectMapper.selectById(request.getSubjectId()); + if (subject == null) { + throw new BusinessException("渠道主体不存在"); + } + } + + if (!account.getAccountId().equals(request.getAccountId())) { + ChannelAccount existing = accountMapper.selectOne( + new LambdaQueryWrapper() + .eq(ChannelAccount::getTenantCode, tenantCode) + .eq(ChannelAccount::getChannelType, request.getChannelType()) + .eq(ChannelAccount::getAccountId, request.getAccountId())); + if (existing != null) { + throw new BusinessException("渠道账号已存在: " + request.getAccountId()); + } + } + + account.setAccountType(request.getAccountType()); + account.setChannelType(request.getChannelType()); + account.setSubjectId(request.getSubjectId()); + account.setAccountId(request.getAccountId()); + account.setAccountName(request.getAccountName()); + account.setSessionArchiveEnabled(request.getSessionArchiveEnabled() != null ? request.getSessionArchiveEnabled() : 0); + account.setServerHost(request.getServerHost()); + account.setExtInfo(request.getExtInfo()); + accountMapper.updateById(account); + return account; + } + + @Transactional + public void deleteAccount(Long id, String tenantCode) { + ChannelAccount account = getAccount(id, tenantCode); + account.setStatus(0); + accountMapper.updateById(account); + } + + @Transactional + public ChannelAccount updateAccountStatus(Long id, String tenantCode, UpdateStatusRequest request) { + ChannelAccount account = getAccount(id, tenantCode); + account.setStatus(request.getStatus()); + accountMapper.updateById(account); + return account; + } + + public ChannelAccount getAccountStatus(Long id, String tenantCode) { + return getAccount(id, tenantCode); + } + @Transactional public ChannelAccount heartbeat(String accountCode, HeartbeatRequest request) { ChannelAccount account = accountMapper.selectOne( @@ -156,9 +226,100 @@ public class ChannelAccountService { return offlineCount; } - public ChannelAccount initAccount(String tenantCode, ChannelAccountInitRequest request) { - // Phase 2 placeholder: actual adapter dispatch will be implemented later - throw new BusinessException("渠道账号初始化适配器尚未实现: " + request.getChannelType()); + @Transactional + public ChannelInitResult initAccount(String tenantCode, ChannelAccountInitRequest request) { + ChannelType channelType; + try { + channelType = ChannelType.valueOf(request.getChannelType().toUpperCase()); + } catch (IllegalArgumentException | NullPointerException e) { + throw new BusinessException("不支持的渠道类型: " + request.getChannelType()); + } + + ChannelAdapter adapter = adapterRegistry.getAdapter(channelType); + if (adapter == null) { + throw new BusinessException("不支持的渠道类型: " + request.getChannelType()); + } + + ChannelAccount account = null; + if (request.getAccountId() != null) { + account = accountMapper.selectById(request.getAccountId()); + if (account != null && !tenantCode.equals(account.getTenantCode())) { + account = null; + } + } + + Map config = new HashMap<>(); + if (request.getConfig() != null) { + config.putAll(request.getConfig()); + } + + Long subjectId = request.getSubjectId(); + if (account != null) { + config.putIfAbsent("account_id", account.getAccountId()); + config.putIfAbsent("account_name", account.getAccountName()); + if (account.getSubjectId() != null) { + subjectId = account.getSubjectId(); + } + } + + if (subjectId != null) { + ChannelSubject subject = subjectMapper.selectById(subjectId); + if (subject != null) { + if (subject.getCorpId() != null) { + config.putIfAbsent("corp_id", subject.getCorpId()); + } + if (subject.getCorpSecret() != null) { + config.putIfAbsent("secret", subject.getCorpSecret()); + } + if (subject.getAgentId() != null) { + config.putIfAbsent("agent_id", subject.getAgentId()); + } + } + } + + ChannelInitRequest initRequest = new ChannelInitRequest(); + initRequest.setChannelType(channelType); + initRequest.setAccountType(AccountType.of(request.getAccountType())); + initRequest.setSubjectId(subjectId); + initRequest.setConfig(config); + + ChannelInitResult result = adapter.initAccount(initRequest); + if (!result.isSuccess()) { + return result; + } + + String resultAccountId = result.getAccountId(); + if (account != null) { + account.setAccountName(result.getAccountName() != null ? result.getAccountName() : account.getAccountName()); + account.setLoginStatus(1); + accountMapper.updateById(account); + } else if (resultAccountId != null) { + ChannelAccount existing = accountMapper.selectOne( + new LambdaQueryWrapper() + .eq(ChannelAccount::getTenantCode, tenantCode) + .eq(ChannelAccount::getChannelType, request.getChannelType()) + .eq(ChannelAccount::getAccountId, resultAccountId)); + if (existing != null) { + existing.setAccountName(result.getAccountName() != null ? result.getAccountName() : existing.getAccountName()); + existing.setLoginStatus(1); + accountMapper.updateById(existing); + } else { + ChannelAccount newAccount = new ChannelAccount(); + newAccount.setTenantCode(tenantCode); + newAccount.setAccountType(request.getAccountType()); + newAccount.setChannelType(request.getChannelType()); + newAccount.setSubjectId(subjectId); + newAccount.setAccountId(resultAccountId); + newAccount.setAccountName(result.getAccountName() != null ? result.getAccountName() : resultAccountId); + newAccount.setLoginStatus(1); + newAccount.setOnlineStatus(0); + newAccount.setRiskLevel(0); + newAccount.setStatus(1); + accountMapper.insert(newAccount); + } + } + + return result; } @Transactional @@ -232,6 +393,10 @@ public class ChannelAccountService { Person person; if (identity != null) { person = personMapper.selectById(identity.getPersonId()); + if (person == null) { + log.warn("ChannelIdentity {} references missing person {}", identity.getId(), identity.getPersonId()); + return; + } Long originalInstitutionId = person.getInstitutionId(); Long originalSupplierId = person.getSupplierId(); Integer originalPersonType = person.getPersonType();