diff --git a/front/src/views/admin/setting/WeCom/index.vue b/front/src/views/admin/setting/WeCom/index.vue index a580bab67bfc2ac2394d5d086db0148d087d0149..14318ce81f7f63bb9aef11110f81b40e3acf0824 100644 --- a/front/src/views/admin/setting/WeCom/index.vue +++ b/front/src/views/admin/setting/WeCom/index.vue @@ -15,6 +15,7 @@ {modifyType}修改类型,枚举值:修改,删除 {projectName}项目名称 {appName}应用名称 + {releaseNo}版本号 {docName}文档名称 {modifier}修改人 {modifyTime}修改时间 diff --git a/front/src/views/project/ProjectRelease/index.vue b/front/src/views/project/ProjectRelease/index.vue index 1914f172d6d979875d22f0127e37cd8d27e331c9..a78611de9e2775c625c2efca727ec1510a453e11 100644 --- a/front/src/views/project/ProjectRelease/index.vue +++ b/front/src/views/project/ProjectRelease/index.vue @@ -60,6 +60,11 @@ :label="$t('DingDingSetting.dingdingWebhookUrl')" width="400" /> + + + + { return new ArrayList<>(0); } List dingtalkInfoList = userDingtalkInfoMapper.list(UserDingtalkInfo::getUserInfoId, userIds); + if (CollectionUtils.isEmpty(dingtalkInfoList)) { + // 如果都未绑定钉钉,为了能发送消息,给一个默认值 + return Collections.singletonList(""); + } return dingtalkInfoList.stream() .map(UserDingtalkInfo::getUserid) .collect(Collectors.toList()); diff --git a/server/server-service/src/main/java/cn/torna/service/MessageService.java b/server/server-service/src/main/java/cn/torna/service/MessageService.java index 39f374fbac00c111e0378fc5aeba7871b710102d..605d7d01cdf5edb9af058ddab3be1e8a1e7ce37f 100644 --- a/server/server-service/src/main/java/cn/torna/service/MessageService.java +++ b/server/server-service/src/main/java/cn/torna/service/MessageService.java @@ -1,16 +1,18 @@ package cn.torna.service; -import cn.torna.common.bean.EnvironmentKeys; import cn.torna.common.enums.MessageNotifyTypeEnum; import cn.torna.common.enums.ModifyType; import cn.torna.common.util.DingTalkOrWeComWebHookUtil; -import cn.torna.common.util.IdUtil; import cn.torna.dao.entity.Module; import cn.torna.dao.entity.Project; import cn.torna.dao.mapper.ModuleMapper; import cn.torna.dao.mapper.ProjectMapper; +import cn.torna.service.builder.MessageBuilder; +import cn.torna.service.builder.MessageBuilder.UniversalMessage; import cn.torna.service.dto.DocInfoDTO; import cn.torna.service.event.ReleaseDocMessageEvent; +import java.util.List; +import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -18,13 +20,6 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import javax.annotation.Resource; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - /** * 负责发送消息,站内信、钉钉、企业微信 * @@ -33,7 +28,6 @@ import java.util.stream.Collectors; @Service @Slf4j public class MessageService { - private static final DateTimeFormatter YMDHMS_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Autowired @@ -59,27 +53,34 @@ public class MessageService { */ public void pushDocModifyMessage(DocInfoDTO docInfoDTO, ModifyType modifyType) { try { - if (modifyType == ModifyType.UPDATE || modifyType == ModifyType.DELETE) { + if (modifyType == ModifyType.UPDATE || modifyType == ModifyType.DELETE || modifyType == ModifyType.ADD) { // 钉钉机器人 webhook url String url = moduleConfigService.getDingDingRobotWebhookUrl(docInfoDTO.getModuleId()); if (StringUtils.hasText(url)) { List dingDingUserIds = docInfoService.listSubscribeDocDingDingUserIds(docInfoDTO.getId()); - String content = buildDingDingMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, docInfoDTO, modifyType, dingDingUserIds); + String content = buildMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, docInfoDTO, modifyType, dingDingUserIds); if (!StringUtils.hasText(content)) { return; } - DingTalkOrWeComWebHookUtil.pushRobotMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, url, content, dingDingUserIds); + + if (modifyType == ModifyType.ADD || !CollectionUtils.isEmpty(dingDingUserIds)) { + DingTalkOrWeComWebHookUtil.pushRobotMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, url, content, dingDingUserIds); + } + } + + if (modifyType != ModifyType.ADD) { + // 这里推送关联版本的webhook + applicationContext.publishEvent(new ReleaseDocMessageEvent(this, docInfoDTO, modifyType)); } - // 这里推送关联版本的钉钉机器人webhook - applicationContext.publishEvent(new ReleaseDocMessageEvent(this, docInfoDTO, modifyType)); + // 企业微信webhook url url = moduleConfigService.getWeComWebhookUrl(docInfoDTO.getModuleId()); if (StringUtils.hasText(url)) { // 关注的用户的 企业微信手机号码 List weComUserMobiles = docInfoService.listSubscribeDocWeComUserMobiles(docInfoDTO.getId()); - // 如果没有人关注 则跳过 - if (!CollectionUtils.isEmpty(weComUserMobiles)) { - String content = buildDingDingMessage(MessageNotifyTypeEnum.WECOM_WEBHOOK, docInfoDTO, modifyType, weComUserMobiles); + // 如果是新增或者有人关注才推送 + if (modifyType == ModifyType.ADD || !CollectionUtils.isEmpty(weComUserMobiles)) { + String content = buildMessage(MessageNotifyTypeEnum.WECOM_WEBHOOK, docInfoDTO, modifyType, weComUserMobiles); DingTalkOrWeComWebHookUtil.pushRobotMessage(MessageNotifyTypeEnum.WECOM_WEBHOOK, url, content, weComUserMobiles); } } @@ -90,41 +91,22 @@ public class MessageService { } - private String buildDingDingMessage(MessageNotifyTypeEnum notificationType, DocInfoDTO docInfoDTO, ModifyType modifyType, List userIds) { + private String buildMessage(MessageNotifyTypeEnum notificationType, DocInfoDTO docInfoDTO, ModifyType modifyType, List userIds) { Project project = projectMapper.getById(docInfoDTO.getProjectId()); Module module = moduleMapper.getById(docInfoDTO.getModuleId()); - Map replaceMap = new HashMap<>(16); - replaceMap.put("{projectName}", project.getName()); - replaceMap.put("{appName}", module.getName()); - replaceMap.put("{docName}", docInfoDTO.getName()); - replaceMap.put("{url}", docInfoDTO.getUrl()); - replaceMap.put("{modifier}", docInfoDTO.getModifierName()); - replaceMap.put("{modifyTime}", docInfoDTO.getGmtModified().format(YMDHMS_PATTERN)); - replaceMap.put("{modifyType}", modifyType.getDescription()); - String frontUrl = EnvironmentKeys.TORNA_FRONT_URL.getValue("http://localhost:7700"); - String docViewUrl = frontUrl + "/#/view/" + IdUtil.encode(docInfoDTO.getId()); - replaceMap.put("{docViewUrl}", docViewUrl); - String content = null; - // 钉钉 - if (notificationType.equals(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK)) { - String atUser = ""; - if (!CollectionUtils.isEmpty(userIds)) { - atUser = userIds.stream() - .map(userId -> "@" + userId) - .collect(Collectors.joining(" ")); - } - replaceMap.put("{@user}", atUser); - content = EnvironmentKeys.PUSH_DINGDING_WEBHOOK_CONTENT.getValue(); - } - if (notificationType.equals(MessageNotifyTypeEnum.WECOM_WEBHOOK)) { - content = EnvironmentKeys.PUSH_WECOM_WEBHOOK_CONTENT.getValue(); - } - if (!StringUtils.hasText(content)) { - return content; - } - for (Map.Entry entry : replaceMap.entrySet()) { - content = content.replace(entry.getKey(), entry.getValue()); - } - return content; + + return MessageBuilder.buildMessage(UniversalMessage.builder() + .notificationType(notificationType) + .projectName(project.getName()) + .appName(module.getName()) + .releaseNo(null) + .docId(docInfoDTO.getId()) + .docName(docInfoDTO.getName()) + .url(docInfoDTO.getUrl()) + .modifier(docInfoDTO.getModifierName()) + .modifyTime(docInfoDTO.getGmtModified()) + .modifyType(modifyType) + .userIdList(userIds) + .build()); } } diff --git a/server/server-service/src/main/java/cn/torna/service/ProjectReleaseService.java b/server/server-service/src/main/java/cn/torna/service/ProjectReleaseService.java index eac1a31456a243892dec7286edb827fb6f77809e..82221547ad6e097a952bb5e487104ca73d6811bc 100644 --- a/server/server-service/src/main/java/cn/torna/service/ProjectReleaseService.java +++ b/server/server-service/src/main/java/cn/torna/service/ProjectReleaseService.java @@ -136,7 +136,7 @@ public class ProjectReleaseService extends BaseLambdaService> moduleSourceIdMap) { + int status, String dingdingWebhook, String weComWebhook, Map> moduleSourceIdMap) { Query projectReleaseQuery = LambdaQuery.create(ProjectRelease.class) .eq(ProjectRelease::getProjectId, projectId) .eq(ProjectRelease::getReleaseNo, releaseNo); @@ -150,6 +150,7 @@ public class ProjectReleaseService extends BaseLambdaService> moduleSourceIdMap) { + String weComWebhook, Map> moduleSourceIdMap) { ProjectRelease projectRelease = projectReleaseMapper.getById(id); if (projectRelease == null) { throw new BizException("该版本号在此项目不存在"); @@ -176,6 +177,7 @@ public class ProjectReleaseService extends BaseLambdaService replaceMap = new HashMap<>(16); + replaceMap.put("{projectName}", universalMessage.getProjectName()); + replaceMap.put("{appName}", universalMessage.getAppName()); + replaceMap.put("{releaseNo}", universalMessage.getReleaseNo()); + replaceMap.put("{docName}", universalMessage.getDocName()); + replaceMap.put("{url}", universalMessage.getUrl()); + replaceMap.put("{modifier}", universalMessage.getModifier()); + replaceMap.put("{modifyTime}", universalMessage.getModifyTime().format(DATE_TIME_FORMATTER)); + replaceMap.put("{modifyType}", universalMessage.getModifyType().getDescription()); + String frontUrl = EnvironmentKeys.TORNA_FRONT_URL.getValue("http://localhost:7700"); + String docViewUrl = frontUrl + "/#/view/" + IdUtil.encode(universalMessage.getDocId()); + replaceMap.put("{docViewUrl}", docViewUrl); + String content = null; + // 钉钉 + MessageNotifyTypeEnum notificationType = universalMessage.getNotificationType(); + List userIdList = universalMessage.getUserIdList(); + if (notificationType.equals(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK)) { + String atUser = ""; + if (!CollectionUtils.isEmpty(userIdList)) { + atUser = userIdList.stream() + .filter(StringUtils::hasText) + .map(userId -> "@" + userId) + .collect(Collectors.joining(" ")); + } + replaceMap.put("{@user}", atUser); + content = EnvironmentKeys.PUSH_DINGDING_WEBHOOK_CONTENT.getValue(); + } + if (notificationType.equals(MessageNotifyTypeEnum.WECOM_WEBHOOK)) { + content = EnvironmentKeys.PUSH_WECOM_WEBHOOK_CONTENT.getValue(); + } + if (!StringUtils.hasText(content)) { + return content; + } + for (Map.Entry entry : replaceMap.entrySet()) { + String value = entry.getValue() == null ? "" : entry.getValue(); + content = content.replace(entry.getKey(), value); + } + return content; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class UniversalMessage { + + /** + * 消息通知类型 + */ + private MessageNotifyTypeEnum notificationType; + /** + * 项目名称 + */ + private String projectName; + /** + * 应用名称 + */ + private String appName; + /** + * 版本号 + */ + private String releaseNo; + /** + * 文档id + */ + private Long docId; + /** + * 文档名称 + */ + private String docName; + /** + * 链接 + */ + private String url; + /** + * 修改人 + */ + private String modifier; + /** + * 修改时间 + */ + private LocalDateTime modifyTime; + /** + * 修改类型 + */ + private ModifyType modifyType; + /** + * 用户ID列表 + */ + private List userIdList; + } +} diff --git a/server/server-service/src/main/java/cn/torna/service/dto/ProjectReleaseDTO.java b/server/server-service/src/main/java/cn/torna/service/dto/ProjectReleaseDTO.java index f0da62d5da2ee8445d9a0275c35a858272785088..94b4b0f223c2d97e229f8b709252d9e2815de74f 100644 --- a/server/server-service/src/main/java/cn/torna/service/dto/ProjectReleaseDTO.java +++ b/server/server-service/src/main/java/cn/torna/service/dto/ProjectReleaseDTO.java @@ -36,6 +36,9 @@ public class ProjectReleaseDTO { /** 钉钉机器人webhook */ private String dingdingWebhook; + /** 企业微信机器人webhook */ + private String weComWebhook; + /** 数据库字段:gmt_create */ private LocalDateTime gmtCreate; diff --git a/server/server-service/src/main/java/cn/torna/service/listener/ReleaseDocMessageListener.java b/server/server-service/src/main/java/cn/torna/service/listener/ReleaseDocMessageListener.java index 51a83147fbf82c112e117a240aa3bd8bbdeb40c6..d98ea9cea666751adaeb163cb8f47af84067a405 100644 --- a/server/server-service/src/main/java/cn/torna/service/listener/ReleaseDocMessageListener.java +++ b/server/server-service/src/main/java/cn/torna/service/listener/ReleaseDocMessageListener.java @@ -1,21 +1,21 @@ package cn.torna.service.listener; -import cn.torna.common.bean.EnvironmentKeys; import cn.torna.common.enums.MessageNotifyTypeEnum; import cn.torna.common.enums.ModifyType; import cn.torna.common.enums.UserSubscribeTypeEnum; import cn.torna.common.util.DingTalkOrWeComWebHookUtil; -import cn.torna.common.util.IdUtil; import cn.torna.dao.entity.Module; import cn.torna.dao.entity.Project; import cn.torna.dao.entity.UserDingtalkInfo; -import cn.torna.dao.entity.UserInfo; +import cn.torna.dao.entity.UserWeComInfo; import cn.torna.dao.mapper.ModuleMapper; import cn.torna.dao.mapper.ProjectMapper; import cn.torna.service.ProjectReleaseService; import cn.torna.service.UserDingtalkInfoService; -import cn.torna.service.UserInfoService; import cn.torna.service.UserSubscribeService; +import cn.torna.service.UserWeComInfoService; +import cn.torna.service.builder.MessageBuilder; +import cn.torna.service.builder.MessageBuilder.UniversalMessage; import cn.torna.service.dto.DocInfoDTO; import cn.torna.service.dto.ProjectReleaseDTO; import cn.torna.service.event.ReleaseDocMessageEvent; @@ -58,6 +58,9 @@ public class ReleaseDocMessageListener { @Resource private UserDingtalkInfoService userDingtalkInfoService; + @Resource + private UserWeComInfoService userWeComInfoService; + @Resource private Executor messageExecutor; @@ -80,31 +83,39 @@ public class ReleaseDocMessageListener { // 如果没有人关注 则跳过 if (!CollectionUtils.isEmpty(sourceId2UserIdsMap)) { for (ProjectReleaseDTO projectReleaseDTO : projectReleaseDTOS) { - String url = projectReleaseDTO.getDingdingWebhook(); + // 获取此版本的关注用户 + List userIds = sourceId2UserIdsMap.get(projectReleaseDTO.getId()); + if (CollectionUtils.isEmpty(userIds)) { + continue; + } + + String dingDingWebHookUrl = projectReleaseDTO.getDingdingWebhook(); // 未配置钉钉机器人URL 则跳过 - if (StringUtils.hasText(url) && isUrl(url)) { - // 获取此版本的关注用户 - List userIds = sourceId2UserIdsMap.get(projectReleaseDTO.getId()); - if (CollectionUtils.isEmpty(userIds)) { - continue; - } + if (StringUtils.hasText(dingDingWebHookUrl) && isUrl(dingDingWebHookUrl)) { // 获取此版本的关注用户钉钉userid List dingDingUserIds = userDingtalkInfoService.list(UserDingtalkInfo::getUserInfoId, userIds) .stream() .map(UserDingtalkInfo::getUserid) .collect(Collectors.toList()); - // 获取此版本的关注用户钉钉手机号码 - List dingDingUserMobiles = null; - // 关注人未绑定钉钉,则跳过此版本文档消息提醒 - if (CollectionUtils.isEmpty(dingDingUserIds)) { - continue; - } - String content = buildDingDingMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, docInfoDTO, projectReleaseDTO, - modifyType, dingDingUserIds, dingDingUserMobiles); + String content = buildMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, docInfoDTO, projectReleaseDTO, + modifyType, dingDingUserIds); if (!StringUtils.hasText(content)) { continue; } - DingTalkOrWeComWebHookUtil.pushRobotMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, url, content, dingDingUserIds, dingDingUserMobiles); + DingTalkOrWeComWebHookUtil.pushRobotMessage(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK, dingDingWebHookUrl, content, dingDingUserIds); + } + + String weComWebhookUrl = projectReleaseDTO.getWeComWebhook(); + if (StringUtils.hasText(weComWebhookUrl)) { + // 关注的用户的 企业微信手机号码 + List weComUserMobiles = userWeComInfoService.list(UserWeComInfo::getUserInfoId, userIds) + .stream() + .map(UserWeComInfo::getMobile) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + String content = buildMessage(MessageNotifyTypeEnum.WECOM_WEBHOOK, docInfoDTO, projectReleaseDTO, modifyType, weComUserMobiles); + DingTalkOrWeComWebHookUtil.pushRobotMessage(MessageNotifyTypeEnum.WECOM_WEBHOOK, weComWebhookUrl, content, weComUserMobiles); } } } @@ -121,48 +132,24 @@ public class ReleaseDocMessageListener { } } - - private String buildDingDingMessage(MessageNotifyTypeEnum notificationType, DocInfoDTO docInfoDTO, ProjectReleaseDTO projectReleaseDTO, - ModifyType modifyType, Collection userIds, Collection userMobiles) { + private String buildMessage(MessageNotifyTypeEnum notificationType, DocInfoDTO docInfoDTO, + ProjectReleaseDTO projectReleaseDTO, ModifyType modifyType, List userIds) { Project project = projectMapper.getById(docInfoDTO.getProjectId()); Module module = moduleMapper.getById(docInfoDTO.getModuleId()); - Map replaceMap = new HashMap<>(16); - replaceMap.put("{projectName}", project.getName()); - replaceMap.put("{appName}", module.getName()); - replaceMap.put("{releaseNo}", projectReleaseDTO.getReleaseNo()); - replaceMap.put("{docName}", docInfoDTO.getName()); - replaceMap.put("{url}", docInfoDTO.getUrl()); - replaceMap.put("{modifier}", docInfoDTO.getModifierName()); - replaceMap.put("{modifyTime}", docInfoDTO.getGmtModified().format(YMDHMS_PATTERN)); - replaceMap.put("{modifyType}", modifyType.getDescription()); - String frontUrl = EnvironmentKeys.TORNA_FRONT_URL.getValue("http://localhost:7700"); - String docViewUrl = frontUrl + "/#/view/" + IdUtil.encode(docInfoDTO.getId()); - replaceMap.put("{docViewUrl}", docViewUrl); - String content = null; - // 钉钉 - if (notificationType.equals(MessageNotifyTypeEnum.DING_TALK_WEB_HOOK)) { - String atUser = ""; - if (!CollectionUtils.isEmpty(userIds)) { - atUser = userIds.stream() - .map(userId -> "@" + userId) - .collect(Collectors.joining(" ")); - } - // 有钉钉手机号添加@手机号 - if (!CollectionUtils.isEmpty(userMobiles)) { - atUser = atUser + userMobiles.stream() - .map(userMobile -> "@" + userMobile) - .collect(Collectors.joining(" ")); - } - replaceMap.put("{@user}", atUser); - content = EnvironmentKeys.PUSH_DINGDING_WEBHOOK_CONTENT.getValue(); - } - if (!StringUtils.hasText(content)) { - return content; - } - for (Map.Entry entry : replaceMap.entrySet()) { - content = content.replace(entry.getKey(), entry.getValue()); - } - return content; + + return MessageBuilder.buildMessage(UniversalMessage.builder() + .notificationType(notificationType) + .projectName(project.getName()) + .appName(module.getName()) + .releaseNo(projectReleaseDTO.getReleaseNo()) + .docId(docInfoDTO.getId()) + .docName(docInfoDTO.getName()) + .url(docInfoDTO.getUrl()) + .modifier(docInfoDTO.getModifierName()) + .modifyTime(docInfoDTO.getGmtModified()) + .modifyType(modifyType) + .userIdList(userIds) + .build()); } } diff --git a/server/server-web/src/main/java/cn/torna/web/controller/project/ProjectReleaseController.java b/server/server-web/src/main/java/cn/torna/web/controller/project/ProjectReleaseController.java index a68ea6e29326a023a86d426127aa795583775313..e167d96f713c080efa11167430deb517b8e0c74b 100644 --- a/server/server-web/src/main/java/cn/torna/web/controller/project/ProjectReleaseController.java +++ b/server/server-web/src/main/java/cn/torna/web/controller/project/ProjectReleaseController.java @@ -69,6 +69,7 @@ public class ProjectReleaseController { param.getReleaseDesc(), param.getStatus(), param.getDingdingWebhook(), + param.getWeComWebhook(), param.getModuleSourceIdMap() ); return Result.ok(); @@ -88,6 +89,7 @@ public class ProjectReleaseController { param.getReleaseDesc(), param.getStatus(), param.getDingdingWebhook(), + param.getWeComWebhook(), param.getModuleSourceIdMap() ); return Result.ok(); diff --git a/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseAddParam.java b/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseAddParam.java index 1cf9a8f8957d1587d33bf130c5e38f8d77f4e561..f98ddf2a3c65cc5c10d929b8e6cf69aea84c943c 100644 --- a/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseAddParam.java +++ b/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseAddParam.java @@ -32,9 +32,13 @@ public class ProjectReleaseAddParam { private Integer status; /** 钉钉机器人webhook */ - @URL(message = "地址格式不正确") + @URL(message = "钉钉地址格式不正确") private String dingdingWebhook; + /** 企业微信机器人webhook */ + @URL(message = "企业微信地址格式不正确") + private String weComWebhook; + /** 关联文档Map (key:模块hashId value: 文档hashId集合) */ private Map> moduleSourceIdMap; } diff --git a/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseUpdateParam.java b/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseUpdateParam.java index 1467431f6d6fdc0542ba5cd144ad275d5f535068..b6ae935a662f326de069e82c9892a556d34f5bbd 100644 --- a/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseUpdateParam.java +++ b/server/server-web/src/main/java/cn/torna/web/controller/project/param/ProjectReleaseUpdateParam.java @@ -30,6 +30,10 @@ public class ProjectReleaseUpdateParam { @URL(message = "地址格式不正确") private String dingdingWebhook; + /** 企业微信机器人webhook */ + @URL(message = "企业微信地址格式不正确") + private String weComWebhook; + /** 关联文档Map (key:模块hashId value: 文档hashId集合) */ private Map> moduleSourceIdMap; }