liu
2026-09-30 91491239c5d4611b5b1404a7abeb2bb23d0edad5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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<WxCpNoticeDO> 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;
    }
 
}