fix: 跨租户授权联调修复 + 新增分区自建模块
- CrossTenantServiceImpl: 有效期判断改用 apply("start_at <= now()"),规避
OffsetDateTime 绑定 timestamptz 比较异常(见实施记录 6.5)
- CallbackServiceImpl: 集成 C2C 跨租户授权检查(额外分发+审计+is_cross_tenant),
提取 enqueueIfConfigured
- 新增 PartitionService/Task: im_message 按月 RANGE 分区滚动建表
(当月+下月,@Init 启动自检 + 每日 0:10)
- 新增 CrossTenantService: 正反向授权匹配 + 跨租户审计
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import com.imutil.mapper.GroupMappingMapper;
|
||||
import com.imutil.mapper.ImMessageMapper;
|
||||
import com.imutil.mapper.PullWatermarkMapper;
|
||||
import com.imutil.service.CallbackService;
|
||||
import com.imutil.service.CrossTenantService;
|
||||
import com.imutil.service.TenantService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.noear.snack4.ONode;
|
||||
@@ -52,6 +53,9 @@ public class CallbackServiceImpl implements CallbackService {
|
||||
@Inject
|
||||
private TenantService tenantService;
|
||||
|
||||
@Inject
|
||||
private CrossTenantService crossTenantService;
|
||||
|
||||
@Override
|
||||
@Tran
|
||||
public String handleCallback(String callbackCommand, String body) {
|
||||
@@ -79,10 +83,25 @@ public class CallbackServiceImpl implements CallbackService {
|
||||
// 不依赖 MsgTimeStamp(避免腾讯回调时间戳偏差导致漏判)。
|
||||
// DB 主键 (msg_key, msg_time) 因分区表约束保留 msg_time,作兜底防护。
|
||||
String msgKey = null;
|
||||
// 跨租户授权命中的对端账号(非空表示该消息为授权放行的跨租户消息,需额外分发+审计)
|
||||
String crossToImUser = null;
|
||||
Long crossGrantId = null;
|
||||
if (isMessageCallback(callbackCommand)) {
|
||||
ImMessage msg = parseMessage(callbackCommand, node, tenantId);
|
||||
if (msg != null) {
|
||||
msgKey = msg.getMsgKey();
|
||||
// 跨租户授权检查(仅 C2C;群消息跨租户语义为群成员维度,本设计不处理)
|
||||
if (msg.getConvType() != null && msg.getConvType() == 1) {
|
||||
String toTenant = parsePrefix(msg.getConvId());
|
||||
if (toTenant != null && !toTenant.equals(tenantId)) {
|
||||
crossGrantId = crossTenantService.checkSendMsgGrant(
|
||||
tenantId, msg.getFromAccount(), toTenant, msg.getConvId());
|
||||
if (crossGrantId != null) {
|
||||
msg.setIsCrossTenant(true);
|
||||
crossToImUser = msg.getConvId();
|
||||
}
|
||||
}
|
||||
}
|
||||
long exists = imMessageMapper.selectCount(Wrappers.<ImMessage>lambdaQuery()
|
||||
.eq(ImMessage::getMsgKey, msg.getMsgKey()));
|
||||
if (exists == 0) {
|
||||
@@ -94,23 +113,45 @@ public class CallbackServiceImpl implements CallbackService {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 写分发队列(payload=原始回调体,供业务系统消费)
|
||||
Tenant tenant = tenantService.getById(tenantId);
|
||||
if (tenant != null && tenant.getCallbackUrl() != null && !tenant.getCallbackUrl().isEmpty()) {
|
||||
DistQueue q = new DistQueue();
|
||||
q.setMsgKey(msgKey);
|
||||
q.setTenantId(tenantId);
|
||||
q.setConvId(extractConvId(callbackCommand, node));
|
||||
q.setTargetUrl(tenant.getCallbackUrl());
|
||||
q.setPayload(body);
|
||||
q.setStatus(0);
|
||||
q.setRetryCount(0);
|
||||
q.setNextRetryAt(OffsetDateTime.now());
|
||||
distQueueMapper.insert(q);
|
||||
// 3. 分发给 from 租户(payload=原始回调体,供业务系统消费)
|
||||
String convId = extractConvId(callbackCommand, node);
|
||||
enqueueIfConfigured(tenantId, msgKey, convId, body);
|
||||
|
||||
// 4. 跨租户授权放行:额外分发给 to 租户 + 记录审计
|
||||
if (crossGrantId != null && crossToImUser != null) {
|
||||
String toTenant = parsePrefix(crossToImUser);
|
||||
enqueueIfConfigured(toTenant, msgKey, convId, body);
|
||||
crossTenantService.audit(crossGrantId, msgKey,
|
||||
node.get("FromAccount").getString(), crossToImUser);
|
||||
}
|
||||
return ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 租户配置了回调地址则入队分发
|
||||
*
|
||||
* @param tenantId 目标租户
|
||||
* @param msgKey 消息键(非消息类回调可为 null)
|
||||
* @param convId 会话ID(保序分桶用)
|
||||
* @param payload 分发载荷(原始回调体)
|
||||
*/
|
||||
private void enqueueIfConfigured(String tenantId, String msgKey, String convId, String payload) {
|
||||
Tenant tenant = tenantService.getById(tenantId);
|
||||
if (tenant == null || tenant.getCallbackUrl() == null || tenant.getCallbackUrl().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
DistQueue q = new DistQueue();
|
||||
q.setMsgKey(msgKey);
|
||||
q.setTenantId(tenantId);
|
||||
q.setConvId(convId);
|
||||
q.setTargetUrl(tenant.getCallbackUrl());
|
||||
q.setPayload(payload);
|
||||
q.setStatus(0);
|
||||
q.setRetryCount(0);
|
||||
q.setNextRetryAt(OffsetDateTime.now());
|
||||
distQueueMapper.insert(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别租户:优先 FromAccount 前缀,群回调用 GroupId 查 group_mapping
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user