- 应用骨架: Solon + MyBatis-Plus + PostgreSQL + Redis + Sa-Token - 核心链路: 回调网关 → 消息落库(幂等) → 分发队列(FOR UPDATE SKIP LOCKED) → worker消费/重试/死信 - 多租户隔离: 前缀法账号映射, 所有业务表带 tenant_id - 管理后台: FreeMarker 渲染, 租户/授权/队列/用量管理页面 - 历史消息补拉: 水位线驱动, getRoamMsg/getGroupMsg 增量拉取, 兜底回调丢失 - msg_key 统一算法(回调/补拉共享, 跨路径去重) Co-Authored-By: Claude <noreply@anthropic.com>
61 lines
2.7 KiB
Java
61 lines
2.7 KiB
Java
package com.imutil.mapper;
|
||
|
||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||
import com.imutil.entity.UsageStat;
|
||
import org.apache.ibatis.annotations.Insert;
|
||
import org.apache.ibatis.annotations.Param;
|
||
|
||
import java.time.OffsetDateTime;
|
||
|
||
/**
|
||
* 用量统计 Mapper
|
||
* <p>
|
||
* 提供按小时窗口的聚合 upsert(PG ON CONFLICT 幂等,可重复跑)。
|
||
* 两个数据源各自 upsert 指定列,避免互相覆盖:
|
||
* - im_message → im_msg_count / im_dau
|
||
* - api_call_log → api_call_count
|
||
*
|
||
* @author imutil
|
||
*/
|
||
public interface UsageStatMapper extends BaseMapper<UsageStat> {
|
||
|
||
/**
|
||
* 从 im_message 聚合某小时窗口的消息量与活跃用户数,upsert 到 usage_stat(stat_level=1 小时)
|
||
* <p>
|
||
* 仅更新 im_msg_count / im_dau,不触碰已有 api_call_count。
|
||
*
|
||
* @param start 窗口起点(含)
|
||
* @param end 窗口终点(不含)
|
||
* @param statTime 统计时刻(=窗口起点整点,作为 usage_stat.stat_time)
|
||
* @return 受影响租户数
|
||
*/
|
||
@Insert("INSERT INTO usage_stat (tenant_id, stat_time, stat_level, im_msg_count, im_dau, " +
|
||
"api_call_count, trtc_duration_sec, trtc_max_concurrent_room, created_at) " +
|
||
"SELECT tenant_id, #{statTime}, 1, count(*), count(DISTINCT from_account), 0, 0, 0, now() " +
|
||
"FROM im_message WHERE created_at >= #{start} AND created_at < #{end} " +
|
||
"GROUP BY tenant_id " +
|
||
"ON CONFLICT (tenant_id, stat_time, stat_level) DO UPDATE SET " +
|
||
"im_msg_count = EXCLUDED.im_msg_count, im_dau = EXCLUDED.im_dau")
|
||
int aggregateMsgHour(@Param("start") OffsetDateTime start,
|
||
@Param("end") OffsetDateTime end,
|
||
@Param("statTime") OffsetDateTime statTime);
|
||
|
||
/**
|
||
* 从 api_call_log 聚合某小时窗口的腾讯 API 调用次数,upsert 到 usage_stat(stat_level=1 小时)
|
||
* <p>
|
||
* 仅更新 api_call_count,不触碰已有 im_msg_count / im_dau。
|
||
*
|
||
* @return 受影响租户数
|
||
*/
|
||
@Insert("INSERT INTO usage_stat (tenant_id, stat_time, stat_level, im_msg_count, im_dau, " +
|
||
"api_call_count, trtc_duration_sec, trtc_max_concurrent_room, created_at) " +
|
||
"SELECT tenant_id, #{statTime}, 1, 0, 0, count(*), 0, 0, now() " +
|
||
"FROM api_call_log WHERE called_at >= #{start} AND called_at < #{end} " +
|
||
"GROUP BY tenant_id " +
|
||
"ON CONFLICT (tenant_id, stat_time, stat_level) DO UPDATE SET " +
|
||
"api_call_count = EXCLUDED.api_call_count")
|
||
int aggregateApiCallHour(@Param("start") OffsetDateTime start,
|
||
@Param("end") OffsetDateTime end,
|
||
@Param("statTime") OffsetDateTime statTime);
|
||
}
|