MessageCenterBizService.java 12.1 KB
package cn.fw.hestia.service.buz;

import cn.fw.common.data.mybatis.pagination.PageData;
import cn.fw.common.page.AppPage;
import cn.fw.hestia.common.constant.MessageConstant;
import cn.fw.hestia.common.utils.DateUtil;
import cn.fw.hestia.common.utils.StringUtils;
import cn.fw.hestia.component.SendMsgProducer;
import cn.fw.hestia.domain.db.MessageHistory;
import cn.fw.hestia.domain.db.SendLog;
import cn.fw.hestia.domain.enums.MessageStateEnum;
import cn.fw.hestia.domain.vo.HistoryQuery;
import cn.fw.hestia.domain.vo.MessageHistoryVO;
import cn.fw.hestia.rpc.passport.TemplateMessageService;
import cn.fw.hestia.rpc.passport.dto.TMParam;
import cn.fw.hestia.sdk.params.TemplateMessageParam;
import cn.fw.hestia.sdk.result.MessageSendMq;
import cn.fw.hestia.sdk.result.SendResult;
import cn.fw.hestia.service.data.MessageHistoryService;
import cn.fw.hestia.service.data.SendLogService;
import cn.fw.passport.sdk.api.param.WxMpTempMessageData;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static cn.fw.common.businessvalidator.Validator.BV;
import static cn.fw.hestia.common.constant.MessageConstant.MAX_FREQUENCY;

/**
 * @author : kurisu
 * @className : MessageCenterBizService
 * @description : 消息中心处理类
 * @date: 2021-09-24 17:10
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class MessageCenterBizService {
    private final MessageHistoryService messageHistoryService;
    private final SendLogService sendLogService;
    private final TemplateMessageService templateMessageService;
    private final SendMsgProducer sendMsgProducer;
    private final StringRedisTemplate redisTemplate;

    @Value("${spring.cache.custom.global-prefix}:template:code")
    @Getter
    private String keyPrefix;

    /**
     * 发送
     *
     * @param templateMessageParam
     * @return token
     */
    @Transactional(rollbackFor = Exception.class)
    public SendResult saveMessage(TemplateMessageParam templateMessageParam) {
        MessageHistory messageHistory = createBeans(templateMessageParam);
        messageHistoryService.save(messageHistory);
        return new SendResult(messageHistory.getId(), messageHistory.getMemberId());
    }

    /**
     * 批量发送
     *
     * @param paramList
     * @return token
     */
    @Transactional(rollbackFor = Exception.class)
    public List<SendResult> saveBatchMessage(List<TemplateMessageParam> paramList) {
        List<MessageHistory> messageHistory = createBeans(paramList);
        messageHistoryService.saveBatch(messageHistory);
        List<SendResult> list = new ArrayList<>();
        for (MessageHistory history : messageHistory) {
            list.add(new SendResult(history.getId(), history.getMemberId()));
        }
        return list;
    }


    /**
     * 发送模板消息
     *
     * @param history
     */
    @Transactional(rollbackFor = Exception.class)
    public void sendMessage(MessageHistory history) {
        String result = saveLog(createTmParam(history), history.getId(), r -> r.setManual(Boolean.FALSE));
        boolean succeed = MessageConstant.SUCCEED_STR.equals(result);
        final int frequency = history.getFrequency() + 1;
        history.setFrequency(frequency);
        if (succeed) {
            history.setState(MessageStateEnum.SUMI);
            //发送成功发送mq
            sendMsgProducer.send(new MessageSendMq(history.getId(), history.getMemberId(), new Date()));
        } else {
            history.setSendTime(DateUtil.getExpired(history.getSendTime(), 1 << frequency, Calendar.MINUTE));
        }
        messageHistoryService.updateById(history);
    }

    /**
     * 撤回消息
     *
     * @param sceneToken
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean revokeMessage(Long sceneToken) {
        MessageHistory history = messageHistoryService.getById(sceneToken);
        if (Objects.isNull(history)) {
            return Boolean.TRUE;
        }
        history.setYn(Boolean.FALSE);
        return messageHistoryService.updateById(history);
    }

    /**
     * 查询未读数
     *
     * @param memberId
     * @return
     */
    public int unreadCount(Long memberId) {
        return messageHistoryService.count(Wrappers.<MessageHistory>lambdaQuery()
                .eq(MessageHistory::getMemberId, memberId)
                .eq(MessageHistory::getReadz, Boolean.FALSE)
                .eq(MessageHistory::getYn, Boolean.TRUE)
                .ge(MessageHistory::getSendTime, DateUtil.localDateTime2Date(LocalDateTime.now().minusMonths(3L)))
        );
    }

    /**
     * 查询页面所需参数
     *
     * @param sceneToken
     * @return
     */
    public Map<String, String> queryPageParams(Long sceneToken) {
        MessageHistory history = messageHistoryService.getById(sceneToken);
        BV.notNull(history, () -> "消息不存在或者已经被撤销");
        readMessage(history);
        String pageParams = history.getPageParams();
        if (StringUtils.isEmpty(pageParams)) {
            return new HashMap<>(0);
        }
        return JSON.<HashMap<String, String>>parseObject(pageParams, HashMap.class);
    }

    /**
     * 查询历史消息记录
     *
     * @param query
     * @return
     */
    public AppPage<MessageHistoryVO> queryHistory(HistoryQuery query) {
        PageData<MessageHistory> pageData = messageHistoryService.page(new PageData<>(query), Wrappers.<MessageHistory>lambdaQuery()
                .eq(MessageHistory::getMemberId, query.getMemberId())
                .eq(MessageHistory::getYn, Boolean.TRUE)
                .ge(MessageHistory::getSendTime, DateUtil.localDateTime2Date(LocalDateTime.now().minusMonths(3L)))
                .orderByDesc(MessageHistory::getCreateTime)
        );
        AppPage<MessageHistoryVO> page = AppPage.empty(query);
        page.setCurrent((int) pageData.getCurrent());
        page.setPageSize((int) pageData.getSize());
        page.setTotal(pageData.getTotal());
        page.setData(new ArrayList<>());
        if (!CollectionUtils.isEmpty(pageData.getRecords())) {
            List<MessageHistoryVO> voList = pageData.getRecords().stream().map(MessageHistoryVO::with).collect(Collectors.toList());
            page.setData(voList);
        }
        return page;
    }

    /**
     * 当用户关注了公众号
     *
     * @param memberId
     */
    public void onAttention(Long memberId) {
        if (Objects.isNull(memberId)) {
            return;
        }
        List<MessageHistory> list = messageHistoryService.list(Wrappers.<MessageHistory>lambdaQuery()
                .eq(MessageHistory::getMemberId, memberId)
                .eq(MessageHistory::getYn, Boolean.TRUE)
                .eq(MessageHistory::getReadz, Boolean.FALSE)
                .eq(MessageHistory::getState, MessageStateEnum.MADA)
        );
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (MessageHistory history : list) {
            history.setSendTime(new Date());
            if (history.getFrequency() >= MAX_FREQUENCY) {
                history.setFrequency(MAX_FREQUENCY - 1);
            }
        }
        messageHistoryService.updateBatchById(list);
    }

    @Transactional(rollbackFor = Exception.class)
    protected void readMessage(MessageHistory history) {
        if (Boolean.FALSE.equals(history.getReadz())) {
            history.setReadz(Boolean.TRUE);
            history.setState(MessageStateEnum.SUMI);
            messageHistoryService.updateById(history);
        }
    }

    public String manualSend(Long id) {
        MessageHistory messageHistory = messageHistoryService.getById(id);
        String result = saveLog(createTmParam(messageHistory), messageHistory.getId(), r -> r.setManual(Boolean.TRUE));
        boolean succeed = MessageConstant.SUCCEED_STR.equals(result);
        if (succeed) {
            messageHistory.setState(MessageStateEnum.SUMI);
            sendMsgProducer.send(new MessageSendMq(messageHistory.getId(), messageHistory.getMemberId(), new Date()));
        }
        messageHistoryService.updateById(messageHistory);
        return result;
    }

    public void changeRdsTemplateCode(String code) {
        BoundValueOperations<String, String> boundValueOps = redisTemplate.boundValueOps(getKeyPrefix());
        boundValueOps.set(code);
    }

    public String getRdsTemplateCode() {
        BoundValueOperations<String, String> boundValueOps = redisTemplate.boundValueOps(getKeyPrefix());
        return boundValueOps.get();
    }

    private String saveLog(TMParam tmParam, Long messageId, Consumer<SendLog> cons) {
        String result = templateMessageService.sendTemplateMessage(tmParam);
        SendLog sendLog = new SendLog();
        sendLog.setMessageId(messageId);
        sendLog.setSendTime(new Date());
        sendLog.setSucceed(MessageConstant.SUCCEED_STR.equals(result));
        sendLog.setResult(result);
        sendLog.setManual(Boolean.FALSE);
        cons.accept(sendLog);
        sendLogService.save(sendLog);
        return result;
    }

    private List<MessageHistory> createBeans(List<TemplateMessageParam> paramList) {
        List<MessageHistory> list = new ArrayList<>();
        for (TemplateMessageParam param : paramList) {
            list.add(createBeans(param));
        }
        return list;
    }

    private MessageHistory createBeans(TemplateMessageParam param) {
        MessageHistory messageHistory = new MessageHistory();
        messageHistory.setMemberId(param.getMemberId());
        messageHistory.setTemplateCode(getTemplateCode());
        messageHistory.setTitle(param.getTitle());
        StringBuilder content = new StringBuilder(param.getContent());
        if (!CollectionUtils.isEmpty(param.getExtraMap())) {
            content.append("\n");
            for (Map.Entry<String, String> mapEntry : param.getExtraMap().entrySet()) {
                content.append("\n")
                        .append(mapEntry.getKey())
                        .append(":")
                        .append(mapEntry.getValue());
            }
        }
        messageHistory.setContent(content.toString());
        messageHistory.setRemark(param.getRemark());
        messageHistory.setPagePath(param.getPath());
        messageHistory.setReadz(StringUtils.isEmpty(param.getPath()));
        messageHistory.setFrequency(0);
        messageHistory.setYn(Boolean.TRUE);
        messageHistory.setSendTime(new Date());
        messageHistory.setState(MessageStateEnum.MADA);
        List<WxMpTempMessageData> keywords = Arrays.asList(
                new WxMpTempMessageData(param.getTitle()),
                new WxMpTempMessageData("无")
        );
        messageHistory.setKeywords(JSONArray.toJSONString(keywords));
        if (!CollectionUtils.isEmpty(param.getParamMap())) {
            messageHistory.setPageParams(JSON.toJSONString(param.getParamMap()));
        }
        return messageHistory;
    }

    private TMParam createTmParam(MessageHistory history) {
        TMParam tmParam = new TMParam();
        tmParam.setSceneToken(history.getId());
        tmParam.setTitle(history.getContent());
        tmParam.setRemark(history.getRemark());
        tmParam.setPath(history.getPagePath());
        tmParam.setTemplateCode(history.getTemplateCode());
        tmParam.setMemberId(history.getMemberId());
        tmParam.setKeywords(history.getKeywords());
        return tmParam;
    }

    private String getTemplateCode() {
        BoundValueOperations<String, String> operations = redisTemplate.boundValueOps(getKeyPrefix());
        String templateCode = operations.get();
        if (StringUtils.isEmpty(templateCode)) {
            return "OPENTM412432053";
        }
        return templateCode;
    }
}