package cn.iocoder.yudao.module.wxcp.service.notice; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.wxcp.controller.admin.notice.vo.WxCpNoticePageReqVO; import cn.iocoder.yudao.module.wxcp.controller.admin.notice.vo.WxCpNoticeSaveReqVO; import cn.iocoder.yudao.module.wxcp.controller.admin.vo.WxCpPushReqVO; import cn.iocoder.yudao.module.wxcp.controller.admin.vo.WxCpPushResultVO; import cn.iocoder.yudao.module.wxcp.dal.dataobject.notice.WxCpNoticeDO; import cn.iocoder.yudao.module.wxcp.dal.mysql.notice.WxCpNoticeMapper; import cn.iocoder.yudao.module.wxcp.enums.activity.WxCpPushScopeEnum; import cn.iocoder.yudao.module.wxcp.enums.notice.WxCpNoticeStatusEnum; import cn.iocoder.yudao.module.wxcp.service.push.WxCpMessagePushService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.wxcp.enums.ErrorCodeConstants.*; /** * 企业微信通知 Service 实现类 * * @author 超级管理员 */ @Service @Validated @Slf4j public class WxCpNoticeServiceImpl implements WxCpNoticeService { /** * 企业微信文本消息上限约 2048 字节,中文按 3 字节算。 * 这里按字符数留出足够余量,超出部分截断并在末尾提示,避免整条推送失败。 */ private static final int MAX_PUSH_CONTENT_LENGTH = 600; @Resource private WxCpNoticeMapper wxcpNoticeMapper; @Resource private WxCpMessagePushService messagePushService; @Override public Long createNotice(WxCpNoticeSaveReqVO createReqVO) { WxCpNoticeDO notice = BeanUtils.toBean(createReqVO, WxCpNoticeDO.class); notice.setStatus(WxCpNoticeStatusEnum.DRAFT.getStatus()); notice.setPushStatus(0); // 推送范围选填,不传默认全员;推送时还能在弹窗里改 if (notice.getPushScope() == null) { notice.setPushScope(WxCpPushScopeEnum.ALL.getScope()); } wxcpNoticeMapper.insert(notice); return notice.getId(); } @Override public void updateNotice(WxCpNoticeSaveReqVO updateReqVO) { WxCpNoticeDO notice = validateNoticeExists(updateReqVO.getId()); if (Objects.equals(notice.getStatus(), WxCpNoticeStatusEnum.REVOKED.getStatus())) { throw exception(NOTICE_CANNOT_UPDATE); } WxCpNoticeDO updateObj = BeanUtils.toBean(updateReqVO, WxCpNoticeDO.class); // 状态与推送信息由系统维护,不允许前端覆盖 updateObj.setStatus(null); updateObj.setPushStatus(null); updateObj.setPushTime(null); wxcpNoticeMapper.updateById(updateObj); } @Override public void deleteNotice(Long id) { validateNoticeExists(id); wxcpNoticeMapper.deleteById(id); } @Override public WxCpNoticeDO getNotice(Long id) { return wxcpNoticeMapper.selectById(id); } @Override public PageResult getNoticePage(WxCpNoticePageReqVO pageReqVO) { return wxcpNoticeMapper.selectPage(pageReqVO); } @Override public void publishNotice(Long id) { WxCpNoticeDO notice = validateNoticeExists(id); if (Objects.equals(notice.getStatus(), WxCpNoticeStatusEnum.PUBLISHED.getStatus())) { return; // 已发布,幂等返回 } if (Objects.equals(notice.getStatus(), WxCpNoticeStatusEnum.REVOKED.getStatus())) { throw exception(NOTICE_STATUS_REVOKED); } wxcpNoticeMapper.updateById(WxCpNoticeDO.builder() .id(id).status(WxCpNoticeStatusEnum.PUBLISHED.getStatus()).build()); } @Override public void revokeNotice(Long id) { validateNoticeExists(id); wxcpNoticeMapper.updateById(WxCpNoticeDO.builder() .id(id).status(WxCpNoticeStatusEnum.REVOKED.getStatus()).build()); } @Override public WxCpPushResultVO pushNotice(WxCpPushReqVO pushReqVO) { WxCpNoticeDO notice = validateNoticeExists(pushReqVO.getId()); if (!Objects.equals(notice.getStatus(), WxCpNoticeStatusEnum.PUBLISHED.getStatus())) { throw exception(NOTICE_STATUS_NOT_PUBLISHED); } Integer scope = pushReqVO.getPushScope() != null ? pushReqVO.getPushScope() : notice.getPushScope(); String target = StrUtil.isNotBlank(pushReqVO.getPushTarget()) ? pushReqVO.getPushTarget() : notice.getPushTarget(); if (scope == null) { scope = WxCpPushScopeEnum.ALL.getScope(); } // 通知用纯文本推送:没有可跳转的详情页,而 textcard 类型强制要求 url WxCpPushResultVO result = messagePushService.pushText( buildPushContent(notice), scope, target); if (Boolean.TRUE.equals(result.getSuccess())) { wxcpNoticeMapper.updateById(WxCpNoticeDO.builder() .id(notice.getId()) .pushScope(scope) .pushTarget(target) .pushStatus(1) .pushTime(LocalDateTime.now()) .build()); } return result; } /** * 拼装推送正文:标题加粗样式用不了,改用【】包裹;过长则截断并提示 */ private String buildPushContent(WxCpNoticeDO notice) { String body = StrUtil.blankToDefault(notice.getContent(), ""); String content = "【" + notice.getTitle() + "】\n" + body; if (content.length() > MAX_PUSH_CONTENT_LENGTH) { content = content.substring(0, MAX_PUSH_CONTENT_LENGTH) + "\n……(内容过长已截断,请到系统内查看完整通知)"; } return content; } private WxCpNoticeDO validateNoticeExists(Long id) { if (id == null) { throw exception(NOTICE_NOT_EXISTS); } WxCpNoticeDO notice = wxcpNoticeMapper.selectById(id); if (notice == null) { throw exception(NOTICE_NOT_EXISTS); } return notice; } }