package com.imutil.service.impl; import com.imutil.mapper.PartitionMapper; import com.imutil.service.PartitionService; import lombok.extern.slf4j.Slf4j; import org.noear.solon.annotation.Component; import org.noear.solon.annotation.Init; import org.noear.solon.annotation.Inject; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; /** * im_message 分区管理服务实现 *

* 每次建当月+下月分区(CREATE TABLE IF NOT EXISTS 幂等),保证: * - 启动时当月分区就绪(init.sql 仅建了 default 兜底分区) * - 跨月前下月分区已存在,消息不落 default 分区(避免查询性能退化) * 分区按自然月 RANGE(msg_time),+08 时区边界。 * * @author imutil */ @Slf4j @Component public class PartitionServiceImpl implements PartitionService { private static final ZoneId ZONE = ZoneId.of("+08"); private static final DateTimeFormatter MONTH_FMT = DateTimeFormatter.ofPattern("yyyyMM"); /** PG timestamptz 字面量格式:2026-07-01 00:00:00+08:00 */ private static final DateTimeFormatter TS_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxxx"); @Inject private PartitionMapper partitionMapper; /** * 应用启动后自检一次:首次启动当月分区缺失则补建 */ @Init public void init() { try { createCurrentAndNextMonth(); log.info("启动分区自检完成(当月+下月)"); } catch (Throwable e) { log.error("启动分区自检异常", e); } } @Override public void createCurrentAndNextMonth() { ZonedDateTime now = ZonedDateTime.now(ZONE); ensureMonth(now.getYear(), now.getMonthValue()); // 下月:用 plusMonths 计算避免年底跨月 ZonedDateTime next = now.plusMonths(1); ensureMonth(next.getYear(), next.getMonthValue()); } /** * 确保指定年月的分区存在(不存在则创建,幂等) */ private void ensureMonth(int year, int month) { // 用 YearMonth 拼表名与边界,规避 1月/12月 跨年的日历计算坑 java.time.YearMonth ym = java.time.YearMonth.of(year, month); String name = "im_message_" + ym.format(MONTH_FMT); ZonedDateTime start = ym.atDay(1).atStartOfDay(ZONE); ZonedDateTime end = ym.plusMonths(1).atDay(1).atStartOfDay(ZONE); String sql = "CREATE TABLE IF NOT EXISTS " + name + " PARTITION OF im_message FOR VALUES FROM ('" + start.format(TS_FMT) + "') TO ('" + end.format(TS_FMT) + "')"; partitionMapper.execute(sql); log.debug("分区就绪 {}", name); } }