feat(notification): 添加微信订阅消息配置管理及幂等授权功能

- 新增微信订阅消息配置管理控制器,支持模板、场景、事件映射配置
- 实现用户通知授权服务的幂等控制,避免前端重试导致授权次数虚增
- 添加微信订阅消息发送日志记录,用于幂等与排障
- 新增视频生成完成时的订阅消息触发功能
- 实现场景模板查询接口,返回用户授权余额信息
- 添加模板V2相关数据表映射器和实体类
- 集成微信订阅消息触发服务到任务完成流程中
This commit is contained in:
2026-01-01 17:53:59 +08:00
parent 81dc2f1b86
commit f1a2958251
61 changed files with 3655 additions and 9 deletions

View File

@@ -0,0 +1,173 @@
package com.ycwl.basic.repository;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ycwl.basic.mapper.WechatSubscribeEventTemplateMapper;
import com.ycwl.basic.mapper.WechatSubscribeSceneTemplateMapper;
import com.ycwl.basic.mapper.WechatSubscribeTemplateConfigMapper;
import com.ycwl.basic.model.pc.notify.entity.WechatSubscribeEventTemplateEntity;
import com.ycwl.basic.model.pc.notify.entity.WechatSubscribeSceneTemplateEntity;
import com.ycwl.basic.model.pc.notify.entity.WechatSubscribeTemplateConfigEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 微信订阅消息配置仓库(scenic 覆盖:scenic_id=具体值 > scenic_id=0)
*
* @Author: System
* @Date: 2025/12/31
*/
@Component
public class WechatSubscribeNotifyConfigRepository {
private static final long DEFAULT_SCENIC_ID = 0L;
private final WechatSubscribeTemplateConfigMapper templateConfigMapper;
private final WechatSubscribeSceneTemplateMapper sceneTemplateMapper;
private final WechatSubscribeEventTemplateMapper eventTemplateMapper;
public WechatSubscribeNotifyConfigRepository(WechatSubscribeTemplateConfigMapper templateConfigMapper,
WechatSubscribeSceneTemplateMapper sceneTemplateMapper,
WechatSubscribeEventTemplateMapper eventTemplateMapper) {
this.templateConfigMapper = templateConfigMapper;
this.sceneTemplateMapper = sceneTemplateMapper;
this.eventTemplateMapper = eventTemplateMapper;
}
public List<WechatSubscribeSceneTemplateEntity> listEffectiveSceneTemplateMappings(Long scenicId, String sceneKey) {
Objects.requireNonNull(scenicId, "scenicId is null");
if (StringUtils.isBlank(sceneKey)) {
throw new IllegalArgumentException("sceneKey is blank");
}
List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
QueryWrapper<WechatSubscribeSceneTemplateEntity> wrapper = new QueryWrapper<>();
wrapper.eq("scene_key", sceneKey)
.in("scenic_id", scenicIds);
List<WechatSubscribeSceneTemplateEntity> rows = sceneTemplateMapper.selectList(wrapper);
return pickEffectiveByTemplateKey(rows, scenicId).values().stream()
.filter(this::isEnabled)
.sorted((a, b) -> {
int cmp = Integer.compare(safeInt(a.getSortOrder()), safeInt(b.getSortOrder()));
if (cmp != 0) {
return cmp;
}
return Objects.toString(a.getTemplateKey(), "").compareTo(Objects.toString(b.getTemplateKey(), ""));
})
.collect(Collectors.toList());
}
public List<WechatSubscribeEventTemplateEntity> listEffectiveEventTemplateMappings(Long scenicId, String eventKey) {
Objects.requireNonNull(scenicId, "scenicId is null");
if (StringUtils.isBlank(eventKey)) {
throw new IllegalArgumentException("eventKey is blank");
}
List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
QueryWrapper<WechatSubscribeEventTemplateEntity> wrapper = new QueryWrapper<>();
wrapper.eq("event_key", eventKey)
.in("scenic_id", scenicIds);
List<WechatSubscribeEventTemplateEntity> rows = eventTemplateMapper.selectList(wrapper);
return pickEffectiveByTemplateKey(rows, scenicId).values().stream()
.filter(this::isEnabled)
.sorted((a, b) -> {
int cmp = Integer.compare(safeInt(a.getSortOrder()), safeInt(b.getSortOrder()));
if (cmp != 0) {
return cmp;
}
return Objects.toString(a.getTemplateKey(), "").compareTo(Objects.toString(b.getTemplateKey(), ""));
})
.collect(Collectors.toList());
}
public Map<String, WechatSubscribeTemplateConfigEntity> getEffectiveTemplateConfigs(Long scenicId,
Collection<String> templateKeys) {
Objects.requireNonNull(scenicId, "scenicId is null");
if (CollectionUtils.isEmpty(templateKeys)) {
return new HashMap<>();
}
List<Long> scenicIds = List.of(DEFAULT_SCENIC_ID, scenicId);
QueryWrapper<WechatSubscribeTemplateConfigEntity> wrapper = new QueryWrapper<>();
wrapper.in("template_key", templateKeys)
.in("scenic_id", scenicIds);
List<WechatSubscribeTemplateConfigEntity> rows = templateConfigMapper.selectList(wrapper);
Map<String, WechatSubscribeTemplateConfigEntity> effective = new HashMap<>();
for (WechatSubscribeTemplateConfigEntity row : rows) {
if (row == null || row.getTemplateKey() == null) {
continue;
}
WechatSubscribeTemplateConfigEntity existing = effective.get(row.getTemplateKey());
if (existing == null) {
effective.put(row.getTemplateKey(), row);
continue;
}
if (Objects.equals(row.getScenicId(), scenicId)) {
effective.put(row.getTemplateKey(), row);
}
}
return effective;
}
private boolean isEnabled(WechatSubscribeSceneTemplateEntity entity) {
return entity != null && Objects.equals(entity.getEnabled(), 1);
}
private boolean isEnabled(WechatSubscribeEventTemplateEntity entity) {
return entity != null && Objects.equals(entity.getEnabled(), 1);
}
private static int safeInt(Integer value) {
return value != null ? value : 0;
}
private static <T> Map<String, T> pickEffectiveByTemplateKey(List<T> rows, Long scenicId) {
Map<String, T> effective = new HashMap<>();
if (rows == null || rows.isEmpty()) {
return effective;
}
for (T row : rows) {
String templateKey = getTemplateKey(row);
if (templateKey == null) {
continue;
}
T existing = effective.get(templateKey);
if (existing == null) {
effective.put(templateKey, row);
continue;
}
Long rowScenicId = getScenicId(row);
if (Objects.equals(rowScenicId, scenicId)) {
effective.put(templateKey, row);
}
}
return effective;
}
private static String getTemplateKey(Object row) {
if (row instanceof WechatSubscribeSceneTemplateEntity entity) {
return entity.getTemplateKey();
}
if (row instanceof WechatSubscribeEventTemplateEntity entity) {
return entity.getTemplateKey();
}
return null;
}
private static Long getScenicId(Object row) {
if (row instanceof WechatSubscribeSceneTemplateEntity entity) {
return entity.getScenicId();
}
if (row instanceof WechatSubscribeEventTemplateEntity entity) {
return entity.getScenicId();
}
return null;
}
}