From 47dd1ecaf0aafa66b9c39fff9c91abc1c611b02c Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期二, 27 十月 2026 17:59:46 +0800
Subject: [PATCH] feat(wxcp): 通知支持图片/视频/Markdown/多图文模板与成员预览
---
yudao-module-wxcp/src/main/java/cn/iocoder/yudao/module/wxcp/service/notice/WxCpNoticeServiceImpl.java | 572 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 538 insertions(+), 34 deletions(-)
diff --git a/yudao-module-wxcp/src/main/java/cn/iocoder/yudao/module/wxcp/service/notice/WxCpNoticeServiceImpl.java b/yudao-module-wxcp/src/main/java/cn/iocoder/yudao/module/wxcp/service/notice/WxCpNoticeServiceImpl.java
index 25fcabf..bf61273 100644
--- a/yudao-module-wxcp/src/main/java/cn/iocoder/yudao/module/wxcp/service/notice/WxCpNoticeServiceImpl.java
+++ b/yudao-module-wxcp/src/main/java/cn/iocoder/yudao/module/wxcp/service/notice/WxCpNoticeServiceImpl.java
@@ -1,28 +1,44 @@
package cn.iocoder.yudao.module.wxcp.service.notice;
+import cn.hutool.core.collection.CollUtil;
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.system.controller.admin.storage.vo.SystemStorageAttachmentListReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentSaveReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageBlobRespVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO;
+import cn.iocoder.yudao.module.system.service.storage.SystemStorageAttachmentService;
+import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService;
+import cn.iocoder.yudao.module.wxcp.controller.admin.notice.vo.WxCpNoticeArticleSaveReqVO;
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.WxCpNoticeArticleDO;
import cn.iocoder.yudao.module.wxcp.dal.dataobject.notice.WxCpNoticeDO;
+import cn.iocoder.yudao.module.wxcp.dal.mysql.notice.WxCpNoticeArticleMapper;
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.enums.notice.WxCpNoticeTypeEnum;
+import cn.iocoder.yudao.module.wxcp.framework.config.WxCpProperties;
import cn.iocoder.yudao.module.wxcp.service.push.WxCpMessagePushService;
-import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO;
-import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.cp.bean.article.NewArticle;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.io.File;
+import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.wxcp.enums.ErrorCodeConstants.*;
@@ -43,15 +59,78 @@
*/
private static final int MAX_PUSH_CONTENT_LENGTH = 600;
+ /**
+ * 鍥剧墖閫氱煡鐨勫浘鐗囧叧鑱旇褰曠被鍨嬶紝瀵瑰簲 system_storage_attachment.record_type
+ */
+ public static final String NOTICE_IMAGE_RECORD_TYPE = "wxcp_notice_image";
+
+ /**
+ * 闄勪欢閫氱煡鐨勯檮浠跺叧鑱旇褰曠被鍨嬶紝瀵瑰簲 system_storage_attachment.record_type
+ */
+ public static final String NOTICE_FILE_RECORD_TYPE = "wxcp_notice_file";
+
+ /**
+ * 瑙嗛閫氱煡鐨勮棰戝叧鑱旇褰曠被鍨嬶紝瀵瑰簲 system_storage_attachment.record_type
+ */
+ public static final String NOTICE_VIDEO_RECORD_TYPE = "wxcp_notice_video";
+
+ /**
+ * 澶氬浘鏂囧皝闈㈠浘鐨勫叧鑱旇褰曠被鍨嬶紝record_id 涓哄浘鏂囨潯鐩紪鍙�
+ */
+ public static final String NOTICE_ARTICLE_COVER_RECORD_TYPE = "wxcp_notice_article_cover";
+
+ /**
+ * 闄勪欢鐢ㄩ�旓紝涓庡叾瀹冩ā鍧椾繚鎸佷竴鑷�
+ */
+ private static final String ATTACHMENT_APPLICATION = "file";
+
+ /**
+ * 浼佷笟寰俊鏂囦欢娑堟伅涓婇檺 20MB
+ */
+ private static final long MAX_FILE_SIZE = 20L * 1024L * 1024L;
+
+ /**
+ * 浼佷笟寰俊鍥剧墖娑堟伅涓婇檺 10MB
+ */
+ private static final long MAX_IMAGE_SIZE = 10L * 1024L * 1024L;
+
+ /**
+ * 浼佷笟寰俊瑙嗛娑堟伅涓婇檺 10MB
+ */
+ private static final long MAX_VIDEO_SIZE = 10L * 1024L * 1024L;
+
+ /**
+ * 浼佷笟寰俊 Markdown 娑堟伅涓婇檺 2048 瀛楄妭
+ */
+ private static final int MAX_MARKDOWN_BYTES = 2048;
+
+ /**
+ * 涓�娆℃帹閫佺殑鍥剧墖鏁伴噺涓婇檺
+ */
+ private static final int MAX_IMAGE_COUNT = 9;
+
+ /**
+ * 涓�娆℃帹閫佺殑闄勪欢鏁伴噺涓婇檺
+ */
+ private static final int MAX_FILE_COUNT = 5;
+
+ /**
+ * 澶氬浘鏂囨秷鎭渶澶氱瘒鏁�
+ */
+ private static final int MAX_ARTICLE_COUNT = 8;
+
@Resource
private WxCpNoticeMapper wxcpNoticeMapper;
+ @Resource
+ private WxCpNoticeArticleMapper wxcpNoticeArticleMapper;
@Resource
private WxCpMessagePushService messagePushService;
@Resource
private SystemStorageBlobService storageBlobService;
-
- /** 浼佷笟寰俊鏂囦欢娑堟伅涓婇檺 20MB */
- private static final long MAX_FILE_SIZE = 20L * 1024L * 1024L;
+ @Resource
+ private SystemStorageAttachmentService storageAttachmentService;
+ @Resource
+ private WxCpProperties wxCpProperties;
@Override
public Long createNotice(WxCpNoticeSaveReqVO createReqVO) {
@@ -66,6 +145,14 @@
notice.setPushScope(WxCpPushScopeEnum.ALL.getScope());
}
wxcpNoticeMapper.insert(notice);
+
+ // 绱犳潗缁熶竴璧伴檮浠朵腑闂磋〃锛涙柊寤烘椂 null 涓庣┖闆嗗悎閮借〃绀烘病鏈夌礌鏉�
+ bindAttachments(NOTICE_IMAGE_RECORD_TYPE, notice.getId(), createReqVO.getImageBlobIds());
+ List<Long> fileBlobIds = resolveFileBlobIds(createReqVO);
+ bindAttachments(NOTICE_FILE_RECORD_TYPE, notice.getId(), fileBlobIds);
+ bindAttachments(NOTICE_VIDEO_RECORD_TYPE, notice.getId(), createReqVO.getVideoBlobIds());
+ syncFileSnapshot(notice.getId(), fileBlobIds, createReqVO.getFileName());
+ saveArticles(notice.getId(), createReqVO.getArticleList());
return notice.getId();
}
@@ -81,12 +168,37 @@
updateObj.setPushStatus(null);
updateObj.setPushTime(null);
wxcpNoticeMapper.updateById(updateObj);
+
+ // 绱犳潗鍒楄〃 null = 涓嶆敼鍔紝[] = 娓呯┖锛涜繖鏍峰彧鏀规爣棰樻椂涓嶄細璇垹宸蹭笂浼犵殑绱犳潗
+ if (updateReqVO.getImageBlobIds() != null) {
+ bindAttachments(NOTICE_IMAGE_RECORD_TYPE, notice.getId(), updateReqVO.getImageBlobIds());
+ }
+ if (updateReqVO.getVideoBlobIds() != null) {
+ bindAttachments(NOTICE_VIDEO_RECORD_TYPE, notice.getId(), updateReqVO.getVideoBlobIds());
+ }
+ List<Long> fileBlobIds = resolveFileBlobIds(updateReqVO);
+ if (fileBlobIds != null) {
+ bindAttachments(NOTICE_FILE_RECORD_TYPE, notice.getId(), fileBlobIds);
+ syncFileSnapshot(notice.getId(), fileBlobIds, updateReqVO.getFileName());
+ }
+ if (updateReqVO.getArticleList() != null) {
+ saveArticles(notice.getId(), updateReqVO.getArticleList());
+ }
}
@Override
public void deleteNotice(Long id) {
validateNoticeExists(id);
wxcpNoticeMapper.deleteById(id);
+ storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(NOTICE_IMAGE_RECORD_TYPE, id);
+ storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(NOTICE_FILE_RECORD_TYPE, id);
+ storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(NOTICE_VIDEO_RECORD_TYPE, id);
+ // 鍥炬枃鏉$洰鍙婂叾灏侀潰闄勪欢涓�璧锋竻鐞�
+ for (WxCpNoticeArticleDO article : wxcpNoticeArticleMapper.selectListByNoticeId(id)) {
+ storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(
+ NOTICE_ARTICLE_COVER_RECORD_TYPE, article.getId());
+ }
+ wxcpNoticeArticleMapper.deleteByNoticeId(id);
}
@Override
@@ -97,6 +209,31 @@
@Override
public PageResult<WxCpNoticeDO> getNoticePage(WxCpNoticePageReqVO pageReqVO) {
return wxcpNoticeMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public List<SystemStorageBlobRespVO> getNoticeImageAttachments(Long noticeId) {
+ return listAttachments(NOTICE_IMAGE_RECORD_TYPE, noticeId);
+ }
+
+ @Override
+ public List<SystemStorageBlobRespVO> getNoticeFileAttachments(Long noticeId) {
+ return listAttachments(NOTICE_FILE_RECORD_TYPE, noticeId);
+ }
+
+ @Override
+ public List<SystemStorageBlobRespVO> getNoticeVideoAttachments(Long noticeId) {
+ return listAttachments(NOTICE_VIDEO_RECORD_TYPE, noticeId);
+ }
+
+ @Override
+ public List<WxCpNoticeArticleDO> getNoticeArticles(Long noticeId) {
+ return wxcpNoticeArticleMapper.selectListByNoticeId(noticeId);
+ }
+
+ @Override
+ public List<SystemStorageBlobRespVO> getNoticeArticleCoverAttachments(Long articleId) {
+ return listAttachments(NOTICE_ARTICLE_COVER_RECORD_TYPE, articleId);
}
@Override
@@ -141,8 +278,20 @@
String linkUrl = StrUtil.blankToDefault(pushReqVO.getLinkUrl(), notice.getLinkUrl());
String linkBtnText = StrUtil.blankToDefault(pushReqVO.getLinkBtnText(), notice.getLinkBtnText());
String coverUrl = StrUtil.blankToDefault(pushReqVO.getCoverUrl(), notice.getCoverUrl());
- Long fileBlobId = pushReqVO.getFileBlobId() != null ? pushReqVO.getFileBlobId() : notice.getFileBlobId();
- String fileName = StrUtil.blankToDefault(pushReqVO.getFileName(), notice.getFileName());
+
+ // 鏈鎺ㄩ�佺殑绱犳潗锛氳姹傞噷浼犱簡灏辫鐩栵紝娌′紶鍒欐部鐢ㄩ�氱煡涓婂凡淇濆瓨鐨�
+ List<Long> imageBlobIds = pushReqVO.getImageBlobIds() != null
+ ? pushReqVO.getImageBlobIds() : toBlobIds(getNoticeImageAttachments(notice.getId()));
+ List<Long> fileBlobIds = pushReqVO.getFileBlobIds() != null
+ ? pushReqVO.getFileBlobIds() : toBlobIds(getNoticeFileAttachments(notice.getId()));
+ List<Long> videoBlobIds = pushReqVO.getVideoBlobIds() != null
+ ? pushReqVO.getVideoBlobIds() : toBlobIds(getNoticeVideoAttachments(notice.getId()));
+ List<WxCpNoticeArticleSaveReqVO> articles = pushReqVO.getArticleList() != null
+ ? pushReqVO.getArticleList() : toArticleReqList(getNoticeArticles(notice.getId()));
+ if (CollUtil.isEmpty(fileBlobIds) && notice.getFileBlobId() != null && notice.getFileBlobId() > 0) {
+ // 鍏煎鏃ф暟鎹細鍘嗗彶涓婃枃浠堕�氱煡鍙瓨浜嗕竴涓� fileBlobId
+ fileBlobIds = List.of(notice.getFileBlobId());
+ }
// 鎸夋湰娆¢�夋嫨鐨勬ā鏉跨被鍨嬫牎楠屽繀濉」
if (Objects.equals(noticeType, WxCpNoticeTypeEnum.TEXT.getType())
@@ -154,8 +303,29 @@
&& StrUtil.isBlank(linkUrl)) {
throw exception(NOTICE_LINK_URL_REQUIRED);
}
- if (Objects.equals(noticeType, WxCpNoticeTypeEnum.FILE.getType()) && fileBlobId == null) {
+ if (Objects.equals(noticeType, WxCpNoticeTypeEnum.FILE.getType())
+ && CollUtil.isEmpty(fileBlobIds)) {
throw exception(NOTICE_FILE_REQUIRED);
+ }
+ if (Objects.equals(noticeType, WxCpNoticeTypeEnum.IMAGE.getType())
+ && CollUtil.isEmpty(imageBlobIds)) {
+ throw exception(NOTICE_IMAGE_REQUIRED);
+ }
+ if (Objects.equals(noticeType, WxCpNoticeTypeEnum.VIDEO.getType())
+ && CollUtil.isEmpty(videoBlobIds)) {
+ throw exception(NOTICE_VIDEO_REQUIRED);
+ }
+ if (Objects.equals(noticeType, WxCpNoticeTypeEnum.MARKDOWN.getType())) {
+ validateMarkdownContent(buildMarkdownContent(notice));
+ }
+ if (Objects.equals(noticeType, WxCpNoticeTypeEnum.MULTI_NEWS.getType())) {
+ validateArticles(articles);
+ }
+ if (imageBlobIds.size() > MAX_IMAGE_COUNT) {
+ throw exception(NOTICE_IMAGE_COUNT_EXCEEDED);
+ }
+ if (fileBlobIds.size() > MAX_FILE_COUNT) {
+ throw exception(NOTICE_FILE_COUNT_EXCEEDED);
}
WxCpPushResultVO result;
@@ -164,11 +334,19 @@
StrUtil.blankToDefault(summary, notice.getContent()),
linkUrl, StrUtil.blankToDefault(linkBtnText, "鍘诲~鎶�"), scope, target);
} else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.FILE.getType())) {
- result = pushFileNotice(fileBlobId, fileName, notice, scope, target);
+ result = pushFileNotice(fileBlobIds, notice, scope, target);
} else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.NEWS.getType())) {
result = messagePushService.pushNews(notice.getTitle(),
StrUtil.blankToDefault(summary, notice.getContent()),
linkUrl, coverUrl, scope, target);
+ } else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.IMAGE.getType())) {
+ result = pushImageNotice(imageBlobIds, notice, scope, target);
+ } else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.VIDEO.getType())) {
+ result = pushVideoNotice(videoBlobIds.get(0), notice, scope, target);
+ } else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.MARKDOWN.getType())) {
+ result = messagePushService.pushMarkdown(buildMarkdownContent(notice), scope, target);
+ } else if (Objects.equals(noticeType, WxCpNoticeTypeEnum.MULTI_NEWS.getType())) {
+ result = pushMultiNewsNotice(articles, scope, target);
} else {
result = messagePushService.pushText(buildPushContent(notice), scope, target);
}
@@ -186,47 +364,150 @@
}
/**
- * 鏂囦欢閫氱煡锛氫粠绯荤粺闄勪欢涓績鍙栨枃浠讹紝浜ょ粰鎺ㄩ�佹湇鍔′笂浼犱紒涓氬井淇″苟鍙戦��
+ * 鍥剧墖閫氱煡锛氬厛鍙戜竴鏉℃枃瀛楄鏄庯紙鏈夋鏂囨椂锛夛紝鍐嶉�愬紶鍙戦�佸浘鐗�
*/
- private WxCpPushResultVO pushFileNotice(Long fileBlobId, String fileName, WxCpNoticeDO notice,
- Integer scope, String target) {
- if (fileBlobId == null) {
- throw exception(NOTICE_FILE_REQUIRED);
+ private WxCpPushResultVO pushImageNotice(List<Long> imageBlobIds, WxCpNoticeDO notice,
+ Integer scope, String target) {
+ List<WxCpPushResultVO> results = new ArrayList<>();
+ if (StrUtil.isNotBlank(notice.getContent())) {
+ // 鏂囧瓧澶辫触涓嶅奖鍝嶅悗缁浘鐗囧彂閫侊紝鏈�缁堢敱 aggregatePushResults 姹囨��
+ results.add(messagePushService.pushText(buildPushContent(notice), scope, target));
}
- SystemStorageBlobDO blob = storageBlobService.getStorageBlob(fileBlobId);
+ for (Long blobId : imageBlobIds) {
+ SystemStorageBlobDO blob = storageBlobService.getStorageBlob(blobId);
+ if (blob == null) {
+ throw exception(NOTICE_FILE_NOT_EXISTS);
+ }
+ if (StrUtil.isNotBlank(blob.getContentType())
+ && !StrUtil.startWithIgnoreCase(blob.getContentType(), "image/")) {
+ throw exception(NOTICE_IMAGE_ONLY);
+ }
+ if (blob.getByteSize() != null && blob.getByteSize() > MAX_IMAGE_SIZE) {
+ throw exception(NOTICE_IMAGE_TOO_LARGE);
+ }
+ File file = storageBlobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey());
+ if (file == null || !file.exists()) {
+ throw exception(NOTICE_FILE_NOT_EXISTS);
+ }
+ results.add(messagePushService.pushImage(file, scope, target));
+ }
+ // 鍥剧墖娑堟伅鏈韩涓嶅彲鐐瑰嚮锛屽啀琛ヤ竴寮犲崱鐗囨寚鍚戦瑙堥〉锛屾垚鍛樺彲鍦ㄩ〉鍐呴�愬紶鏀惧ぇ鏌ョ湅
+ appendPreviewCard(results, notice, "鏌ョ湅鍏ㄩ儴鍥剧墖", scope, target);
+ return aggregatePushResults(results, "鍥剧墖");
+ }
+
+ /**
+ * 闄勪欢閫氱煡锛氬厛鍙戜竴鏉℃枃瀛楄鏄庯紙鏈夋鏂囨椂锛夛紝鍐嶉�愪釜鍙戦�佹枃浠�
+ */
+ private WxCpPushResultVO pushFileNotice(List<Long> fileBlobIds, WxCpNoticeDO notice,
+ Integer scope, String target) {
+ List<WxCpPushResultVO> results = new ArrayList<>();
+ if (StrUtil.isNotBlank(notice.getContent())) {
+ results.add(messagePushService.pushText(buildPushContent(notice), scope, target));
+ }
+ for (Long blobId : fileBlobIds) {
+ SystemStorageBlobDO blob = storageBlobService.getStorageBlob(blobId);
+ if (blob == null) {
+ throw exception(NOTICE_FILE_NOT_EXISTS);
+ }
+ if (blob.getByteSize() != null && blob.getByteSize() > MAX_FILE_SIZE) {
+ throw exception(NOTICE_FILE_TOO_LARGE);
+ }
+ File file = storageBlobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey());
+ if (file == null || !file.exists()) {
+ throw exception(NOTICE_FILE_NOT_EXISTS);
+ }
+ String actualFileName = StrUtil.blankToDefault(blob.getOriginalFilename(), blob.getUidFilename());
+ results.add(messagePushService.pushFile(file, actualFileName, null, scope, target));
+ }
+ // 鏂囦欢娑堟伅鍙兘涓嬭浇鍗曚釜闄勪欢锛屽啀琛ヤ竴寮犲崱鐗囨寚鍚戦瑙堥〉锛屾垚鍛樺彲鍦ㄩ〉鍐呯湅鍒板叏閮ㄩ檮浠�
+ appendPreviewCard(results, notice, "鏌ョ湅鍏ㄩ儴闄勪欢", scope, target);
+ return aggregatePushResults(results, "闄勪欢");
+ }
+
+ /**
+ * 瑙嗛閫氱煡锛氬彂閫佽棰戞秷鎭紝骞惰ˉ涓�寮犲崱鐗囨寚鍚戦瑙堥〉
+ */
+ private WxCpPushResultVO pushVideoNotice(Long videoBlobId, WxCpNoticeDO notice,
+ Integer scope, String target) {
+ SystemStorageBlobDO blob = storageBlobService.getStorageBlob(videoBlobId);
if (blob == null) {
throw exception(NOTICE_FILE_NOT_EXISTS);
}
- if (blob.getByteSize() != null && blob.getByteSize() > MAX_FILE_SIZE) {
- throw exception(NOTICE_FILE_TOO_LARGE);
+ if (StrUtil.isNotBlank(blob.getContentType())
+ && !StrUtil.startWithIgnoreCase(blob.getContentType(), "video/")) {
+ throw exception(NOTICE_VIDEO_ONLY);
+ }
+ if (blob.getByteSize() != null && blob.getByteSize() > MAX_VIDEO_SIZE) {
+ throw exception(NOTICE_VIDEO_TOO_LARGE);
}
File file = storageBlobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey());
if (file == null || !file.exists()) {
throw exception(NOTICE_FILE_NOT_EXISTS);
}
- String actualFileName = StrUtil.blankToDefault(fileName, blob.getOriginalFilename());
- return messagePushService.pushFile(file, actualFileName, buildPushContent(notice), scope, target);
+ List<WxCpPushResultVO> results = new ArrayList<>();
+ String description = StrUtil.blankToDefault(notice.getSummary(), notice.getContent());
+ if (description.length() > 300) {
+ description = description.substring(0, 300) + "鈥︹��";
+ }
+ results.add(messagePushService.pushVideo(file, notice.getTitle(), description, scope, target));
+ appendPreviewCard(results, notice, "鏌ョ湅閫氱煡璇︽儏", scope, target);
+ return aggregatePushResults(results, "瑙嗛");
}
/**
- * 鎸夋ā鏉跨被鍨嬫牎楠屽繀濉」
+ * 澶氬浘鏂囨秷鎭細涓�娆℃秷鎭彂閫佸绡囧浘鏂�
*/
- private void validateNoticeByType(WxCpNoticeSaveReqVO reqVO) {
- Integer type = reqVO.getNoticeType() != null
- ? reqVO.getNoticeType() : WxCpNoticeTypeEnum.TEXT.getType();
- if (Objects.equals(type, WxCpNoticeTypeEnum.TEXT.getType())
- && StrUtil.isBlank(reqVO.getContent())) {
- throw exception(NOTICE_CONTENT_REQUIRED);
+ private WxCpPushResultVO pushMultiNewsNotice(List<WxCpNoticeArticleSaveReqVO> articles,
+ Integer scope, String target) {
+ List<NewArticle> newsList = new ArrayList<>(articles.size());
+ for (WxCpNoticeArticleSaveReqVO article : articles) {
+ newsList.add(NewArticle.builder()
+ .title(StrUtil.trim(article.getTitle()))
+ .description(StrUtil.blankToDefault(article.getDescription(), ""))
+ .url(StrUtil.trim(article.getLinkUrl()))
+ .picUrl(resolveArticleCoverUrl(article))
+ .build());
}
- if ((Objects.equals(type, WxCpNoticeTypeEnum.CARD.getType())
- || Objects.equals(type, WxCpNoticeTypeEnum.NEWS.getType()))
- && StrUtil.isBlank(reqVO.getLinkUrl())) {
- throw exception(NOTICE_LINK_URL_REQUIRED);
+ return messagePushService.pushMultiNews(newsList, scope, target);
+ }
+
+ /**
+ * 澶氬浘鏂囧皝闈㈠湴鍧�锛氬彇鍥炬枃鏉$洰缁戝畾鐨勫皝闈㈤檮浠讹紙閫夊~锛�
+ */
+ private String resolveArticleCoverUrl(WxCpNoticeArticleSaveReqVO article) {
+ Long articleId = article.getId();
+ if (articleId == null) {
+ // 鎺ㄩ�佹椂涓存椂娣诲姞銆佹湭钀藉簱鐨勫浘鏂囨病鏈夐檮浠跺叧绯伙紝鐩存帴蹇界暐灏侀潰
+ return null;
}
- if (Objects.equals(type, WxCpNoticeTypeEnum.FILE.getType())
- && reqVO.getFileBlobId() == null) {
- throw exception(NOTICE_FILE_REQUIRED);
+ List<SystemStorageBlobRespVO> covers = listAttachments(NOTICE_ARTICLE_COVER_RECORD_TYPE, articleId);
+ if (CollUtil.isEmpty(covers)) {
+ return null;
}
+ return StrUtil.blankToDefault(covers.get(0).getPreviewURL(), covers.get(0).getUrl());
+ }
+
+ /**
+ * 澶氭潯娑堟伅鍚堝苟缁撴灉锛氫换涓�鏉″け璐ュ嵆瑙嗕负鏁翠綋澶辫触锛屽苟鎶婂け璐ュ師鍥犳眹鎬荤粰鍓嶇
+ */
+ private WxCpPushResultVO aggregatePushResults(List<WxCpPushResultVO> results, String label) {
+ if (CollUtil.isEmpty(results)) {
+ return new WxCpPushResultVO(false, null, label + "鎺ㄩ�佸唴瀹逛负绌�", null, null);
+ }
+ List<WxCpPushResultVO> failures = results.stream()
+ .filter(r -> !Boolean.TRUE.equals(r.getSuccess()))
+ .toList();
+ if (failures.isEmpty()) {
+ return new WxCpPushResultVO(true, 0, "鎺ㄩ�佹垚鍔�", null, null);
+ }
+ String message = failures.stream()
+ .map(WxCpPushResultVO::getMessage)
+ .filter(StrUtil::isNotBlank)
+ .distinct()
+ .collect(Collectors.joining("锛�"));
+ return new WxCpPushResultVO(false, failures.get(0).getErrCode(),
+ StrUtil.blankToDefault(message, label + "鎺ㄩ�佸け璐�"), null, null);
}
/**
@@ -242,6 +523,229 @@
return content;
}
+ /**
+ * Markdown 姝f枃锛氭妸閫氱煡鏍囬浣滀负涓�绾ф爣棰樻嫾鍦ㄦ鏂囧墠锛屼究浜庢垚鍛樼涓�鐪肩湅鍒颁富棰�
+ */
+ private String buildMarkdownContent(WxCpNoticeDO notice) {
+ String body = StrUtil.blankToDefault(notice.getContent(), "");
+ if (StrUtil.isBlank(body)) {
+ return "## " + notice.getTitle();
+ }
+ if (body.startsWith("#")) {
+ return body;
+ }
+ return "## " + notice.getTitle() + "\n" + body;
+ }
+
+ /**
+ * 鏍¢獙 Markdown 鍐呭鏄惁瓒呭嚭浼佷笟寰俊 2048 瀛楄妭闄愬埗
+ */
+ private void validateMarkdownContent(String content) {
+ if (StrUtil.isBlank(content)) {
+ throw exception(NOTICE_MARKDOWN_CONTENT_REQUIRED);
+ }
+ int bytes = content.getBytes(StandardCharsets.UTF_8).length;
+ if (bytes > MAX_MARKDOWN_BYTES) {
+ throw exception(NOTICE_MARKDOWN_TOO_LONG, bytes);
+ }
+ }
+
+ /**
+ * 鏍¢獙澶氬浘鏂囨秷鎭殑鏉$洰
+ */
+ private void validateArticles(List<WxCpNoticeArticleSaveReqVO> articles) {
+ if (CollUtil.isEmpty(articles)) {
+ throw exception(NOTICE_ARTICLE_REQUIRED);
+ }
+ if (articles.size() > MAX_ARTICLE_COUNT) {
+ throw exception(NOTICE_ARTICLE_COUNT_EXCEEDED);
+ }
+ int index = 1;
+ for (WxCpNoticeArticleSaveReqVO article : articles) {
+ if (article == null || StrUtil.isBlank(article.getTitle())) {
+ throw exception(NOTICE_ARTICLE_TITLE_REQUIRED, index);
+ }
+ if (StrUtil.isBlank(article.getLinkUrl())) {
+ throw exception(NOTICE_ARTICLE_URL_REQUIRED, index);
+ }
+ index++;
+ }
+ }
+
+ // ==================== 闄勪欢涓庡浘鏂� ====================
+
+ /**
+ * 缁戝畾鍥剧墖/闄勪欢/瑙嗛鍒伴�氱煡
+ *
+ * 璧� system 妯″潡鐨勯檮浠朵腑闂磋〃锛屼笟鍔¤〃涓嶅姞鏂囦欢鍦板潃鍒椼�佷笉鑷缓 upload 鎺ュ彛銆�
+ * 浼犵┖闆嗗悎琛ㄧず娓呯┖鍏宠仈锛堝彧鍒犱腑闂磋〃璁板綍锛屼笉鍒犳枃浠舵湰韬級銆�
+ */
+ private void bindAttachments(String recordType, Long recordId, List<Long> blobIds) {
+ SystemStorageAttachmentSaveReqVO bindReqVO = new SystemStorageAttachmentSaveReqVO();
+ bindReqVO.setApplication(ATTACHMENT_APPLICATION);
+ bindReqVO.setRecordType(recordType);
+ bindReqVO.setRecordId(recordId);
+ bindReqVO.setBlobItems(toBlobItems(blobIds));
+ storageAttachmentService.bindAttachments(bindReqVO);
+ }
+
+ private List<SystemStorageAttachmentSaveReqVO.BlobItem> toBlobItems(List<Long> blobIds) {
+ if (CollUtil.isEmpty(blobIds)) {
+ return Collections.emptyList();
+ }
+ return blobIds.stream()
+ .filter(Objects::nonNull)
+ .distinct()
+ .map(blobId -> {
+ SystemStorageAttachmentSaveReqVO.BlobItem item = new SystemStorageAttachmentSaveReqVO.BlobItem();
+ item.setBlobId(blobId);
+ return item;
+ })
+ .toList();
+ }
+
+ private List<SystemStorageBlobRespVO> listAttachments(String recordType, Long recordId) {
+ SystemStorageAttachmentListReqVO listReqVO = new SystemStorageAttachmentListReqVO();
+ listReqVO.setApplication(ATTACHMENT_APPLICATION);
+ listReqVO.setRecordType(recordType);
+ listReqVO.setRecordId(recordId);
+ return storageAttachmentService.listAttachments(listReqVO);
+ }
+
+ private List<Long> toBlobIds(List<SystemStorageBlobRespVO> attachments) {
+ if (CollUtil.isEmpty(attachments)) {
+ return Collections.emptyList();
+ }
+ // 搴曞眰鏄� selectByIds锛屼笉鎵胯杩斿洖椤哄簭锛岃繖閲屾寜 blobId 鍗囧簭锛堝嵆涓婁紶鍏堝悗锛変繚璇佹帹閫侀『搴忕ǔ瀹�
+ return attachments.stream()
+ .map(SystemStorageBlobRespVO::getId)
+ .filter(Objects::nonNull)
+ .sorted()
+ .toList();
+ }
+
+ /**
+ * 淇濆瓨澶氬浘鏂囨潯鐩細鍏堟竻绌哄啀鎻掑叆锛屼繚璇佹彁浜ら『搴忓氨鏄睍绀洪『搴�
+ */
+ private void saveArticles(Long noticeId, List<WxCpNoticeArticleSaveReqVO> articles) {
+ if (articles == null) {
+ return;
+ }
+ // 娓呮帀鏃ф潯鐩強鍏跺皝闈㈤檮浠跺叧绯�
+ for (WxCpNoticeArticleDO article : wxcpNoticeArticleMapper.selectListByNoticeId(noticeId)) {
+ storageAttachmentService.deleteAttachmentsByRecordTypeAndRecordId(
+ NOTICE_ARTICLE_COVER_RECORD_TYPE, article.getId());
+ }
+ wxcpNoticeArticleMapper.deleteByNoticeId(noticeId);
+
+ int index = 1;
+ for (WxCpNoticeArticleSaveReqVO item : articles) {
+ if (item == null) {
+ continue;
+ }
+ WxCpNoticeArticleDO article = WxCpNoticeArticleDO.builder()
+ .noticeId(noticeId)
+ .title(StrUtil.trim(item.getTitle()))
+ .description(StrUtil.trim(item.getDescription()))
+ .linkUrl(StrUtil.trim(item.getLinkUrl()))
+ .coverBlobId(item.getCoverBlobId())
+ .sortNo(item.getSortNo() != null ? item.getSortNo() : index)
+ .build();
+ wxcpNoticeArticleMapper.insert(article);
+ if (item.getCoverBlobId() != null) {
+ bindAttachments(NOTICE_ARTICLE_COVER_RECORD_TYPE, article.getId(),
+ List.of(item.getCoverBlobId()));
+ }
+ index++;
+ }
+ }
+
+ private List<WxCpNoticeArticleSaveReqVO> toArticleReqList(List<WxCpNoticeArticleDO> articles) {
+ if (CollUtil.isEmpty(articles)) {
+ return Collections.emptyList();
+ }
+ return articles.stream().map(article -> {
+ WxCpNoticeArticleSaveReqVO item = new WxCpNoticeArticleSaveReqVO();
+ item.setId(article.getId());
+ item.setTitle(article.getTitle());
+ item.setDescription(article.getDescription());
+ item.setLinkUrl(article.getLinkUrl());
+ item.setCoverBlobId(article.getCoverBlobId());
+ item.setSortNo(article.getSortNo());
+ return item;
+ }).toList();
+ }
+
+ /**
+ * 鍏煎鏃х増鍗曢檮浠跺瓧娈� fileBlobId锛氭湭浼� fileBlobIds 鏃跺綋浣滃彧鏈変竴涓檮浠�
+ */
+ private List<Long> resolveFileBlobIds(WxCpNoticeSaveReqVO reqVO) {
+ if (reqVO.getFileBlobIds() != null) {
+ return reqVO.getFileBlobIds();
+ }
+ if (reqVO.getFileBlobId() != null) {
+ return List.of(reqVO.getFileBlobId());
+ }
+ return null;
+ }
+
+ /**
+ * 鎶婇涓檮浠跺洖鍐欏埌鏃х増 fileBlobId/fileName 瀛楁锛屼究浜庤�佸墠绔垪琛ㄧ洿鎺ュ睍绀�
+ */
+ private void syncFileSnapshot(Long noticeId, List<Long> fileBlobIds, String fileName) {
+ if (fileBlobIds == null) {
+ return;
+ }
+ if (CollUtil.isEmpty(fileBlobIds)) {
+ // 闄勪欢娓呯┖鏃舵樉寮忕疆绌猴細updateById 浼氬拷鐣� null 瀛楁锛屽繀椤荤敤 UpdateWrapper
+ wxcpNoticeMapper.update(null, new LambdaUpdateWrapper<WxCpNoticeDO>()
+ .eq(WxCpNoticeDO::getId, noticeId)
+ .set(WxCpNoticeDO::getFileBlobId, null)
+ .set(WxCpNoticeDO::getFileName, null));
+ return;
+ }
+ Long firstBlobId = fileBlobIds.get(0);
+ String actualFileName = fileName;
+ if (StrUtil.isBlank(actualFileName)) {
+ SystemStorageBlobDO blob = storageBlobService.getStorageBlob(firstBlobId);
+ actualFileName = blob != null ? blob.getOriginalFilename() : null;
+ }
+ wxcpNoticeMapper.updateById(WxCpNoticeDO.builder()
+ .id(noticeId).fileBlobId(firstBlobId).fileName(actualFileName).build());
+ }
+
+ /**
+ * 鍥剧墖/闄勪欢/瑙嗛鎺ㄩ�佸悗杩藉姞棰勮椤靛崱鐗�
+ *
+ * 鏈厤缃缃戝湴鍧�鍓嶇紑鏃朵笉杩藉姞锛岄伩鍏嶆帹閫佷竴涓墦涓嶅紑鐨勯摼鎺ャ��
+ */
+ private void appendPreviewCard(List<WxCpPushResultVO> results, WxCpNoticeDO notice,
+ String btnText, Integer scope, String target) {
+ String previewUrl = buildPreviewUrl(notice.getId());
+ if (StrUtil.isBlank(previewUrl)) {
+ log.warn("[appendPreviewCard][鏈厤缃� wx.cp.signup-url-prefix锛岃烦杩囬瑙堝崱鐗� noticeId={}]", notice.getId());
+ return;
+ }
+ String description = StrUtil.isNotBlank(notice.getContent())
+ ? StrUtil.blankToDefault(notice.getSummary(), notice.getContent())
+ : "鐐瑰嚮涓嬫柟鎸夐挳鏌ョ湅閫氱煡鍐呭";
+ if (description.length() > 300) {
+ description = description.substring(0, 300) + "鈥︹��";
+ }
+ results.add(messagePushService.pushCard(notice.getTitle(), description, previewUrl, btnText, scope, target));
+ }
+
+ @Override
+ public String buildPreviewUrl(Long id) {
+ String prefix = wxCpProperties.getSignupUrlPrefix();
+ if (StrUtil.isBlank(prefix)) {
+ return null;
+ }
+ // 鍘绘帀缁撳熬鏂滄潬锛岄伩鍏嶆嫾鍑� //admin-api
+ String base = StrUtil.removeSuffix(prefix.trim(), "/");
+ return base + "/admin-api/wxcp/notice/preview-page?id=" + id;
+ }
+
private WxCpNoticeDO validateNoticeExists(Long id) {
if (id == null) {
throw exception(NOTICE_NOT_EXISTS);
@@ -253,4 +757,4 @@
return notice;
}
-}
+}
\ No newline at end of file
--
Gitblit v1.9.3