package com.imutil.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.imutil.entity.CrossTenantAudit; import com.imutil.entity.CrossTenantGrant; import com.imutil.mapper.CrossTenantAuditMapper; import com.imutil.mapper.CrossTenantGrantMapper; import com.imutil.service.CrossTenantService; import lombok.extern.slf4j.Slf4j; import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Inject; import java.time.OffsetDateTime; /** * 跨租户通讯授权服务实现 *

* 授权匹配:正向(from→to) 优先,未命中再查反向双向(to→from 且 direction=1)。 * user 字段 NULL 表示通配;permissions 用 LIKE 匹配是否含 send_msg。 * * @author imutil */ @Slf4j @Component public class CrossTenantServiceImpl implements CrossTenantService { private static final String PERM_SEND_MSG = "send_msg"; @Inject private CrossTenantGrantMapper grantMapper; @Inject private CrossTenantAuditMapper auditMapper; @Override public Long checkSendMsgGrant(String fromTenant, String fromUser, String toTenant, String toUser) { // 同租户或任一租户缺失,不算跨租户通讯 if (fromTenant == null || toTenant == null || fromTenant.equals(toTenant)) { return null; } // 1. 正向授权 from→to Long gid = matchDirected(fromTenant, fromUser, toTenant, toUser, false); if (gid != null) { return gid; } // 2. 反向双向授权:记录方向为 to→from 且 direction=1 return matchDirected(toTenant, toUser, fromTenant, fromUser, true); } @Override public void audit(Long grantId, String msgKey, String fromUser, String toUser) { try { CrossTenantAudit a = new CrossTenantAudit(); a.setGrantId(grantId); a.setMsgKey(msgKey); a.setFromImUserId(fromUser); a.setToImUserId(toUser); a.setActionTime(OffsetDateTime.now()); auditMapper.insert(a); } catch (Exception e) { // 审计写入失败不阻塞主流程 log.warn("跨租户审计写入失败 grant={} msgKey={} : {}", grantId, msgKey, e.getMessage()); } } /** * 按指定方向匹配一条 send_msg 授权 * * @param matchFromTenant 查询条件 from_tenant * @param matchFromUser 查询条件 from_im_user(NULL 通配由 SQL 处理) * @param matchToTenant 查询条件 to_tenant * @param matchToUser 查询条件 to_im_user * @param now 当前时间(校验有效期) * @param requireBidir 是否要求 direction=1(反向匹配时) * @return 命中的 grantId,未命中返回 null */ private Long matchDirected(String matchFromTenant, String matchFromUser, String matchToTenant, String matchToUser, boolean requireBidir) { CrossTenantGrant g = grantMapper.selectOne(Wrappers.lambdaQuery() .eq(CrossTenantGrant::getFromTenantId, matchFromTenant) .eq(CrossTenantGrant::getToTenantId, matchToTenant) .eq(CrossTenantGrant::getStatus, 1) .eq(requireBidir, CrossTenantGrant::getDirection, 1) .like(CrossTenantGrant::getPermissions, PERM_SEND_MSG) .and(w -> w.isNull(CrossTenantGrant::getStartAt).or().apply("start_at <= now()")) .and(w -> w.isNull(CrossTenantGrant::getEndAt).or().apply("end_at >= now()")) .and(w -> w.isNull(CrossTenantGrant::getFromImUserId).or().eq(CrossTenantGrant::getFromImUserId, matchFromUser)) .and(w -> w.isNull(CrossTenantGrant::getToImUserId).or().eq(CrossTenantGrant::getToImUserId, matchToUser)) .last("LIMIT 1")); return g == null ? null : g.getGrantId(); } }