package com.tcm.training.admin.app.notice.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.tcm.training.admin.app.notice.entity.AppSystemNotice;
|
import com.tcm.training.admin.app.notice.mapper.AppSystemNoticeMapper;
|
import com.tcm.training.admin.app.settings.entity.AppUserNotificationSetting;
|
import com.tcm.training.admin.app.settings.mapper.AppUserNotificationSettingMapper;
|
import com.tcm.training.admin.common.Jsons;
|
import com.tcm.training.admin.system.entity.MessagePushConfig;
|
import com.tcm.training.admin.system.mapper.MessagePushConfigMapper;
|
import com.tcm.training.admin.utils.JGPushUtil;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.time.LocalDateTime;
|
import java.util.Collections;
|
import java.util.LinkedHashMap;
|
import java.util.Map;
|
|
/**
|
* APP 统一系统通知发放服务。
|
*/
|
@Service
|
public class AppSystemNoticeDispatchService {
|
|
private static final int DELETED_NO = 0;
|
private static final int ENABLED_YES = 1;
|
private static final String PUSH_STATUS_PENDING = "PENDING";
|
private static final String PUSH_STATUS_SUCCESS = "SUCCESS";
|
private static final String PUSH_STATUS_FAILED = "FAILED";
|
private static final String PUSH_STATUS_SKIPPED = "SKIPPED";
|
|
private final AppSystemNoticeMapper appSystemNoticeMapper;
|
private final MessagePushConfigMapper messagePushConfigMapper;
|
private final AppUserNotificationSettingMapper appUserNotificationSettingMapper;
|
private final JGPushUtil jgPushUtil;
|
|
/**
|
* 注入统一通知发放所需依赖。
|
*/
|
public AppSystemNoticeDispatchService(AppSystemNoticeMapper appSystemNoticeMapper,
|
MessagePushConfigMapper messagePushConfigMapper,
|
AppUserNotificationSettingMapper appUserNotificationSettingMapper,
|
JGPushUtil jgPushUtil) {
|
this.appSystemNoticeMapper = appSystemNoticeMapper;
|
this.messagePushConfigMapper = messagePushConfigMapper;
|
this.appUserNotificationSettingMapper = appUserNotificationSettingMapper;
|
this.jgPushUtil = jgPushUtil;
|
}
|
|
/**
|
* 为单个用户创建系统通知,并按双开关规则决定是否触发极光推送。
|
*/
|
@Transactional
|
public AppSystemNotice createAndPush(AppSystemNoticeDispatchCommand command) {
|
validateCommand(command);
|
AppSystemNotice existing = findExistingNotice(command);
|
if (existing != null) {
|
return existing;
|
}
|
AppSystemNotice notice = buildNotice(command);
|
appSystemNoticeMapper.insert(notice);
|
handlePush(notice, command);
|
return notice;
|
}
|
|
/**
|
* 校验发放命令完整性。
|
*/
|
private void validateCommand(AppSystemNoticeDispatchCommand command) {
|
if (command == null || command.getAppUserId() == null) {
|
throw new IllegalArgumentException("通知接收用户不能为空");
|
}
|
if (trimToEmpty(command.getNoticeTypeCode()).isEmpty()) {
|
throw new IllegalArgumentException("通知类型不能为空");
|
}
|
if (trimToEmpty(command.getTitle()).isEmpty()) {
|
throw new IllegalArgumentException("通知标题不能为空");
|
}
|
if (trimToEmpty(command.getContent()).isEmpty()) {
|
throw new IllegalArgumentException("通知内容不能为空");
|
}
|
}
|
|
/**
|
* 查询同一业务引用是否已生成过通知,保证通知发放幂等。
|
*/
|
private AppSystemNotice findExistingNotice(AppSystemNoticeDispatchCommand command) {
|
if (trimToEmpty(command.getReferenceType()).isEmpty() || command.getReferenceId() == null) {
|
return null;
|
}
|
return appSystemNoticeMapper.selectOne(new LambdaQueryWrapper<AppSystemNotice>()
|
.eq(AppSystemNotice::getAppUserId, command.getAppUserId())
|
.eq(AppSystemNotice::getNoticeTypeCode, command.getNoticeTypeCode())
|
.eq(AppSystemNotice::getReferenceType, command.getReferenceType())
|
.eq(AppSystemNotice::getReferenceId, command.getReferenceId())
|
.eq(AppSystemNotice::getDeleted, DELETED_NO)
|
.last("limit 1"));
|
}
|
|
/**
|
* 构造待落库的系统通知实体。
|
*/
|
private AppSystemNotice buildNotice(AppSystemNoticeDispatchCommand command) {
|
AppSystemNotice notice = new AppSystemNotice();
|
notice.setAppUserId(command.getAppUserId());
|
notice.setNoticeTypeCode(command.getNoticeTypeCode());
|
notice.setTitle(trimToEmpty(command.getTitle()));
|
notice.setContent(trimToEmpty(command.getContent()));
|
notice.setReferenceType(trimToNull(command.getReferenceType()));
|
notice.setReferenceId(command.getReferenceId());
|
notice.setPayloadJson(Jsons.toJsonString(command.getPayload() == null ? Collections.<String, String>emptyMap() : command.getPayload()));
|
notice.setPushStatus(PUSH_STATUS_PENDING);
|
notice.setDeleted(DELETED_NO);
|
return notice;
|
}
|
|
/**
|
* 根据后台配置和用户通知设置决定是否触发极光推送,并回写推送结果。
|
*/
|
private void handlePush(AppSystemNotice notice, AppSystemNoticeDispatchCommand command) {
|
MessagePushConfig config = resolveMessagePushConfig(command.getNoticeTypeCode());
|
if (!isPushEnabled(config)) {
|
markSkipped(notice, "后台推送开关关闭");
|
return;
|
}
|
if (Boolean.TRUE.equals(command.getRequireUserSetting()) && !isUserPushEnabled(command)) {
|
markSkipped(notice, "用户通知设置关闭");
|
return;
|
}
|
try {
|
Map<String, String> payload = new LinkedHashMap<String, String>();
|
if (command.getPayload() != null) {
|
payload.putAll(command.getPayload());
|
}
|
payload.put("noticeId", String.valueOf(notice.getId()));
|
payload.put("noticeTypeCode", notice.getNoticeTypeCode());
|
payload.put("referenceType", trimToEmpty(notice.getReferenceType()));
|
payload.put("referenceId", notice.getReferenceId() == null ? "" : String.valueOf(notice.getReferenceId()));
|
String alias = trimToEmpty(command.getPushAlias());
|
if (alias.isEmpty()) {
|
alias = String.valueOf(command.getAppUserId());
|
}
|
jgPushUtil.push(notice.getContent(), notice.getTitle(), payload, alias);
|
notice.setPushStatus(PUSH_STATUS_SUCCESS);
|
notice.setPushedAt(LocalDateTime.now());
|
notice.setPushErrorMessage(null);
|
appSystemNoticeMapper.updateById(notice);
|
} catch (Exception ex) {
|
notice.setPushStatus(PUSH_STATUS_FAILED);
|
notice.setPushErrorMessage(limitText(ex.getMessage(), 500));
|
appSystemNoticeMapper.updateById(notice);
|
}
|
}
|
|
/**
|
* 将未推送的通知标记为跳过。
|
*/
|
private void markSkipped(AppSystemNotice notice, String reason) {
|
notice.setPushStatus(PUSH_STATUS_SKIPPED);
|
notice.setPushErrorMessage(limitText(reason, 500));
|
appSystemNoticeMapper.updateById(notice);
|
}
|
|
/**
|
* 读取后台消息推送配置。
|
*/
|
private MessagePushConfig resolveMessagePushConfig(String noticeTypeCode) {
|
return messagePushConfigMapper.selectOne(new LambdaQueryWrapper<MessagePushConfig>()
|
.eq(MessagePushConfig::getNoticeTypeCode, noticeTypeCode)
|
.eq(MessagePushConfig::getDeleted, DELETED_NO)
|
.last("limit 1"));
|
}
|
|
/**
|
* 判断后台消息推送开关是否开启。
|
*/
|
private boolean isPushEnabled(MessagePushConfig config) {
|
return config != null && config.getPushEnabled() != null && config.getPushEnabled().intValue() == ENABLED_YES;
|
}
|
|
/**
|
* 判断当前用户对应的推送开关是否开启。
|
*/
|
private boolean isUserPushEnabled(AppSystemNoticeDispatchCommand command) {
|
AppUserNotificationSetting setting = appUserNotificationSettingMapper.selectOne(new LambdaQueryWrapper<AppUserNotificationSetting>()
|
.eq(AppUserNotificationSetting::getAppUserId, command.getAppUserId())
|
.eq(AppUserNotificationSetting::getDeleted, DELETED_NO)
|
.last("limit 1"));
|
if (setting == null) {
|
return true;
|
}
|
String noticeTypeCode = trimToEmpty(command.getNoticeTypeCode());
|
if (AppSystemNoticeType.LIVE_REMIND.equals(noticeTypeCode)) {
|
return isEnabled(setting.getLiveReservationEnabled());
|
}
|
if (AppSystemNoticeType.ACTIVITY_NOTICE.equals(noticeTypeCode)) {
|
return isEnabled(setting.getActivityNoticeEnabled());
|
}
|
if (AppSystemNoticeType.ORDER_STATUS_UPDATE.equals(noticeTypeCode)) {
|
return isEnabled(setting.getOrderStatusUpdateEnabled());
|
}
|
if (AppSystemNoticeType.COURSE_UPDATE_NOTICE.equals(noticeTypeCode)) {
|
return isEnabled(setting.getCourseUpdateEnabled());
|
}
|
return true;
|
}
|
|
/**
|
* 判断用户通知设置开关是否开启。
|
*/
|
private boolean isEnabled(Integer value) {
|
return value == null || value.intValue() == ENABLED_YES;
|
}
|
|
/**
|
* 将可选文本收敛为空字符串。
|
*/
|
private String trimToEmpty(String value) {
|
return value == null ? "" : value.trim();
|
}
|
|
/**
|
* 将可选文本收敛为空值。
|
*/
|
private String trimToNull(String value) {
|
String trimmed = trimToEmpty(value);
|
return trimmed.isEmpty() ? null : trimmed;
|
}
|
|
/**
|
* 截断错误文案,避免超出数据库字段长度。
|
*/
|
private String limitText(String value, int maxLength) {
|
String safeValue = trimToEmpty(value);
|
if (safeValue.length() <= maxLength) {
|
return safeValue;
|
}
|
return safeValue.substring(0, maxLength);
|
}
|
}
|