diff --git a/pom.xml b/pom.xml index e9d0da2..27b49f0 100644 --- a/pom.xml +++ b/pom.xml @@ -151,5 +151,38 @@ bcprov-jdk15on 1.66 + + + org.junit.jupiter + junit-jupiter + 5.8.2 + test + + + org.mockito + mockito-junit-jupiter + 4.5.1 + test + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + true + true + + + + org.springframework.boot + spring-boot-maven-plugin + + com.upchina.startup.Main + + + + diff --git a/src/main/java/com/upchina/advisor/entity/AdvisorBasic.java b/src/main/java/com/upchina/advisor/entity/AdvisorBasic.java index 95bde84..84ad367 100644 --- a/src/main/java/com/upchina/advisor/entity/AdvisorBasic.java +++ b/src/main/java/com/upchina/advisor/entity/AdvisorBasic.java @@ -17,6 +17,9 @@ public class AdvisorBasic implements Serializable { public String profile; public String phone; + public AdvisorBasic() { + } + public AdvisorBasic(AdvisorInfo advisorInfo, String deptName) { this.id = advisorInfo.getId(); this.userId = advisorInfo.getUserId(); diff --git a/src/main/java/com/upchina/common/config/cache/CacheKey.java b/src/main/java/com/upchina/common/config/cache/CacheKey.java index 8c45e73..bbb7ee5 100644 --- a/src/main/java/com/upchina/common/config/cache/CacheKey.java +++ b/src/main/java/com/upchina/common/config/cache/CacheKey.java @@ -403,9 +403,21 @@ public class CacheKey { public static final String GROUP = "group"; + // 消息主题 + public static class GroupMessageTopicKey { + public static final String ADMIN_GROUP_TOPIC = "admin_group_topic"; + public static final String ADMIN_PRIVATE_TOPIC = "admin_private_topic"; + public static final String ADMIN_SESSION_TOPIC = "admin_session_topic"; + public static final String APP_GROUP_TOPIC = "app_group_topic"; + public static final String APP_PRIVATE_TOPIC = "app_private_topic"; + public static final String APP_SESSION_TOPIC = "app_session_topic"; + } + public static class GroupKey { public static final String GROUP_INFO = "group|info|"; public static final String MAIN_GROUP_LIST = "group|main|list|"; + public static final String GROUP_MESSAGE_LIST = "group|message|list|"; + public static final String GROUP_MESSAGE_DETAIL = "group|message|detail|"; } } diff --git a/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java b/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java index 9031fb8..bb5e453 100644 --- a/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java +++ b/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java @@ -7,7 +7,6 @@ import com.upchina.common.vo.FrontUserVO; import com.upchina.video.entity.OnlineUser; import com.upchina.video.service.common.VideoCacheService; import com.upchina.video.service.common.VideoMessageService; -import lombok.extern.slf4j.Slf4j; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/upchina/common/result/ResponseStatus.java b/src/main/java/com/upchina/common/result/ResponseStatus.java index 9e1482c..b2ab6d5 100644 --- a/src/main/java/com/upchina/common/result/ResponseStatus.java +++ b/src/main/java/com/upchina/common/result/ResponseStatus.java @@ -127,6 +127,7 @@ public enum ResponseStatus { DECRYPT_ERROR(4059, "AES解密失败"), SECRET_ACQUIRE_FAIL(4060, "获取加密推送地址失败"), + MESSAGE_PERMISSION_ERROR(4061, "消息权限错误"), /*********************内部系统***********************/ SYS_BUSY(5000, "系统忙,请稍后重试"), diff --git a/src/main/java/com/upchina/common/state/GroupMessageStateMachine.java b/src/main/java/com/upchina/common/state/GroupMessageStateMachine.java new file mode 100644 index 0000000..e1e0104 --- /dev/null +++ b/src/main/java/com/upchina/common/state/GroupMessageStateMachine.java @@ -0,0 +1,23 @@ +package com.upchina.common.state; + +import com.upchina.group.constant.GroupMessageStatus; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class GroupMessageStateMachine { + + @Bean + public StateMachine groupMessageSM() { + StateMachine groupMessageSM = new StateMachine<>(); + + // 初始 -> 审核 -> 已审核 + groupMessageSM.add(GroupMessageStatus.INITIAL, GroupMessageStatus.AUDITED); + // 初始 -> 删除 -> 已删除 + groupMessageSM.add(GroupMessageStatus.INITIAL, GroupMessageStatus.DELETED); + // 已审核 -> 删除 -> 已删除 + groupMessageSM.add(GroupMessageStatus.AUDITED, GroupMessageStatus.DELETED); + + return groupMessageSM; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/GroupInteractiveType.java b/src/main/java/com/upchina/group/constant/GroupInteractiveType.java new file mode 100644 index 0000000..20aeeef --- /dev/null +++ b/src/main/java/com/upchina/group/constant/GroupInteractiveType.java @@ -0,0 +1,16 @@ +package com.upchina.group.constant; + +public enum GroupInteractiveType { + GROUP(1, "群聊"), + PRIVATE(2, "私聊"), + SESSION(3, "会话消息"), + ; + + public final Integer value; + public final String desc; + + GroupInteractiveType(Integer value, String desc) { + this.value = value; + this.desc = desc; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/GroupMessageChannel.java b/src/main/java/com/upchina/group/constant/GroupMessageChannel.java new file mode 100644 index 0000000..6b2d465 --- /dev/null +++ b/src/main/java/com/upchina/group/constant/GroupMessageChannel.java @@ -0,0 +1,17 @@ +package com.upchina.group.constant; + +// 消息类型:1APP;2ADMIN +public enum GroupMessageChannel { + APP(1, "APP"), + ADMIN(2, "ADMIN"), + ALL(3, "ALL"); + ; + + public final Integer value; + public final String name; + + GroupMessageChannel(Integer value, String name) { + this.value = value; + this.name = name; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/GroupMessageContentType.java b/src/main/java/com/upchina/group/constant/GroupMessageContentType.java new file mode 100644 index 0000000..36c1b2b --- /dev/null +++ b/src/main/java/com/upchina/group/constant/GroupMessageContentType.java @@ -0,0 +1,17 @@ +package com.upchina.group.constant; + +public enum GroupMessageContentType { + TEXT(1, "文本"), + IMAGE(2, "图片"), + FILE(3, "文件"), + PRODUCT(4, "产品"), + QUESTIONNAIRE(5, "问卷"); + + public final Integer value; + public final String desc; + + GroupMessageContentType(Integer value, String desc) { + this.value = value; + this.desc = desc; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/GroupMessageStatus.java b/src/main/java/com/upchina/group/constant/GroupMessageStatus.java new file mode 100644 index 0000000..1bdc287 --- /dev/null +++ b/src/main/java/com/upchina/group/constant/GroupMessageStatus.java @@ -0,0 +1,27 @@ +package com.upchina.group.constant; + +public enum GroupMessageStatus { + INITIAL(1, "初始"), + AUDITED(2, "已审核"), + DELETED(3, "已删除"); + + public final Integer value; + public final String desc; + + GroupMessageStatus(Integer value, String desc) { + this.value = value; + this.desc = desc; + } + + public static GroupMessageStatus fromValue(Integer value) { + if (value == null) { + return null; + } + for (GroupMessageStatus status : values()) { + if (status.value.equals(value)) { + return status; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/GroupMessageType.java b/src/main/java/com/upchina/group/constant/GroupMessageType.java new file mode 100644 index 0000000..257fc19 --- /dev/null +++ b/src/main/java/com/upchina/group/constant/GroupMessageType.java @@ -0,0 +1,32 @@ +package com.upchina.group.constant; + +// 消息类型:1普通消息;2推荐产品消息;3进入交易圈消息;4用户关注投顾消息;5分享交易圈消息;6开启互动消息;7关闭互动消息; 8下单产品; 9购物车产品数据量修改; 10购物车产品上架; 11问卷;12用户上下线;13开启、关闭企微二维码;14购物车产品推送;15取消推送产品;16显示完整昵称;17先审后发;18开启私聊;19显示圈子人数; +public enum GroupMessageType { + NORMAL(1, "普通消息"), + RECOMMEND_PRODUCT(2, "推荐产品消息"), + ENTER_GROUP(3, "进入交易圈消息"), + USER_FOLLOW_ADVISOR(4, "用户关注投顾消息"), + SHARE_GROUP(5, "分享交易圈消息"), + OPEN_INTERACTIVE(6, "开启互动消息"), + CLOSE_INTERACTIVE(7, "关闭互动消息"), + ORDER_PRODUCT(8, "下单产品"), + CART_PRODUCT_COUNT_CHANGE(9, "购物车产品数据量修改"), + CART_PRODUCT_ON_SHELF(10, "购物车产品上架"), + QUESTION(11, "问卷"), + USER_ONLINE_OFFLINE(12, "用户上下线"), + OPEN_CLOSE_WECHAT_QRCODE(13, "开启、关闭企微二维码"), + CART_PRODUCT_PUSH(14, "购物车产品推送"), + CANCEL_PUSH_PRODUCT(15, "取消推送产品"), + SHOW_FULL_NICKNAME(16, "显示完整昵称"), + PRE_CHECK_SEND(17, "先审后发"), + OPEN_PRIVATE_CHAT(18, "开启私聊"), + SHOW_GROUP_MEMBER_COUNT(19, "显示圈子人数"); + + public final Integer value; + public final String desc; + + GroupMessageType(Integer value, String desc) { + this.value = value; + this.desc = desc; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/GroupMessageUserType.java b/src/main/java/com/upchina/group/constant/GroupMessageUserType.java new file mode 100644 index 0000000..5aee882 --- /dev/null +++ b/src/main/java/com/upchina/group/constant/GroupMessageUserType.java @@ -0,0 +1,20 @@ +package com.upchina.group.constant; + +// 用户类型:1投顾;2用户;3助教;4运营人员 +public enum GroupMessageUserType { + + ADVISOR(1, "投顾"), + CUSTOMER(2, "用户"), + ASSISTANT(3, "助教"), + MANAGER(4, "运营"), + ; + + + public final Integer value; + public final String name; + + GroupMessageUserType(Integer value, String name) { + this.value = value; + this.name = name; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/constant/QueryGroupMessageType.java b/src/main/java/com/upchina/group/constant/QueryGroupMessageType.java new file mode 100644 index 0000000..48223c7 --- /dev/null +++ b/src/main/java/com/upchina/group/constant/QueryGroupMessageType.java @@ -0,0 +1,32 @@ +package com.upchina.group.constant; + +//查询类型:1全部;2投顾;3用户;4精选 +public enum QueryGroupMessageType { + ALL(1, "全部"), + ADVISOR(2, "投顾"), + CUSTOMER(3, "用户"), + SELECTED(4, "精选"), + PRIVATE(5, "私聊"), + ; + + public final Integer value; + public final String name; + + QueryGroupMessageType(Integer value, String name) { + this.value = value; + this.name = name; + } + + public static QueryGroupMessageType fromValue(Integer value) { + if (value == null) { + return null; + } + for (QueryGroupMessageType type : values()) { + if (type.value.equals(value)) { + return type; + } + } + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/controller/admin/AdminGroupController.java b/src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java similarity index 98% rename from src/main/java/com/upchina/group/controller/admin/AdminGroupController.java rename to src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java index e9d5118..4b3ebb6 100644 --- a/src/main/java/com/upchina/group/controller/admin/AdminGroupController.java +++ b/src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java @@ -26,7 +26,7 @@ import javax.annotation.Resource; @Api(tags = "交易圈admin接口") @RestController -public class AdminGroupController { +public class AdminGroupInfoController { @Resource private GroupInfoService groupInfoService; diff --git a/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java b/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java new file mode 100644 index 0000000..939fa54 --- /dev/null +++ b/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java @@ -0,0 +1,102 @@ +package com.upchina.group.controller.admin; + +import com.upchina.common.handler.BizException; +import com.upchina.common.result.AppPager; +import com.upchina.common.result.CommonResult; +import com.upchina.common.result.ResponseStatus; +import com.upchina.common.vo.BackendUserVO; +import com.upchina.common.vo.OnlyIdVO; +import com.upchina.group.query.message.*; +import com.upchina.group.service.admin.AdminGroupMessageService; +import com.upchina.group.vo.message.GroupMessageVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Api(tags = "交易圈admin消息接口") +@RestController +public class AdminGroupMessageController { + + @Resource + private AdminGroupMessageService adminGroupMessageService; + + @ApiOperation("后台发送互动消息") + @PostMapping("/admin/group/message/sendAdvisorMessage") + public CommonResult sendAdvisorMessage(@Validated @RequestBody @ApiParam(required = true) SendGroupMessageAdminQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + if (backendUserVO == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + OnlyIdVO vo = adminGroupMessageService.sendAdvisorMessage(query, backendUserVO); + return CommonResult.success(vo); + } + + @ApiOperation("后台更新互动消息状态") + @PostMapping("/admin/group/message/updateStatus") + public CommonResult updateStatus(@Validated @RequestBody @ApiParam(required = true) UpdateGroupMessageStatusQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + adminGroupMessageService.updateStatus(query, backendUserVO); + return CommonResult.success(); + } + @ApiOperation("后台获取互动消息") + @PostMapping("/admin/group/message/getMessageList") + public CommonResult> getMessageList(@Validated @RequestBody @ApiParam(required = true) ListGroupMessageQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + AppPager list = adminGroupMessageService.getMessageList(query, backendUserVO); + return CommonResult.success(list); + } + + @ApiOperation("后台推荐产品消息") + @PostMapping("/admin/group/message/sendProductMessage") + public CommonResult sendProductMessage(@Validated @RequestBody @ApiParam(required = true) GroupMessageProductQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + if (backendUserVO == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + OnlyIdVO vo = adminGroupMessageService.saveProductMessage(query, backendUserVO); + return CommonResult.success(vo); + } + + @ApiOperation("设置互动状态") + @PostMapping("/admin/group/message/setInteractiveStatus") + public CommonResult setInteractiveStatus( + @Validated @RequestBody @ApiParam(required = true) GroupMessageStatusQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUser) { + if (backendUser == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + adminGroupMessageService.sendInteractiveStatusMessage(query, backendUser); + return CommonResult.success(); + } + + @ApiOperation("设置私聊状态") + @PostMapping("/admin/group/message/setPrivateStatus") + public CommonResult openPrivateChat( + @Validated @RequestBody @ApiParam(required = true) GroupMessageStatusQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUser) { + if (backendUser == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + adminGroupMessageService.sendOpenPrivateChatMessage(query, backendUser); + return CommonResult.success(); + } + + @ApiOperation("设置显示圈子人数") + @PostMapping("/admin/group/message/setShowMemberCount") + public CommonResult showMemberCount( + @Validated @RequestBody @ApiParam(required = true) GroupMessageStatusQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUser) { + if (backendUser == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + adminGroupMessageService.setShowMemberCount(query, backendUser); + return CommonResult.success(); + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/controller/app/AppGroupController.java b/src/main/java/com/upchina/group/controller/app/AppGroupInfoController.java similarity index 97% rename from src/main/java/com/upchina/group/controller/app/AppGroupController.java rename to src/main/java/com/upchina/group/controller/app/AppGroupInfoController.java index 4927060..44da56b 100644 --- a/src/main/java/com/upchina/group/controller/app/AppGroupController.java +++ b/src/main/java/com/upchina/group/controller/app/AppGroupInfoController.java @@ -20,7 +20,7 @@ import javax.annotation.Resource; @Api(tags = "交易圈app接口") @RestController -public class AppGroupController { +public class AppGroupInfoController { @Resource private GroupInfoService groupInfoService; diff --git a/src/main/java/com/upchina/group/controller/app/AppGroupMessageController.java b/src/main/java/com/upchina/group/controller/app/AppGroupMessageController.java new file mode 100644 index 0000000..62969f4 --- /dev/null +++ b/src/main/java/com/upchina/group/controller/app/AppGroupMessageController.java @@ -0,0 +1,51 @@ +package com.upchina.group.controller.app; + +import com.upchina.common.handler.BizException; +import com.upchina.common.result.AppPager; +import com.upchina.common.result.CommonResult; +import com.upchina.common.result.ResponseStatus; +import com.upchina.common.vo.FrontUserVO; +import com.upchina.group.query.message.ListGroupMessageAppQuery; +import com.upchina.group.query.message.SendGroupMessageAppQuery; +import com.upchina.group.service.app.AppGroupMessageService; +import com.upchina.group.vo.message.GroupMessageVO; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@Api(tags = "交易圈app消息接口") +@RestController +public class AppGroupMessageController { + + @Resource + private AppGroupMessageService appGroupMessageService; + + @ApiOperation("APP获取交易圈互动消息") + @PostMapping("/app/group/message/getMessageList") + public CommonResult> getMessageList( + @Validated @RequestBody @ApiParam(required = true) ListGroupMessageAppQuery query, + @RequestAttribute(value = "frontUser", required = false) FrontUserVO frontUserVO) { + AppPager list = appGroupMessageService.getMessageList(query, frontUserVO); + return CommonResult.success(list); + } + + @ApiOperation("APP发送交易圈互动消息") + @PostMapping("/app/group/message/sendMessage") + public CommonResult sendMessage( + @Validated @RequestBody @ApiParam(required = true) SendGroupMessageAppQuery query, + @RequestAttribute(value = "frontUser", required = false) FrontUserVO frontUser) { + if (frontUser == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + GroupMessageVO vo = appGroupMessageService.sendMessage(query, frontUser); + return CommonResult.success(vo); + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/entity/GroupInfo.java b/src/main/java/com/upchina/group/entity/GroupInfo.java index d0e67e3..d916c13 100644 --- a/src/main/java/com/upchina/group/entity/GroupInfo.java +++ b/src/main/java/com/upchina/group/entity/GroupInfo.java @@ -58,12 +58,24 @@ public class GroupInfo implements Serializable { @TableField("welcome_message") private String welcomeMessage; + /** + * 互动状态 1:开启 2:关闭 + */ + @TableField("interactive_status") + private Integer interactiveStatus; + /** * 私聊状态 1:开启 2:关闭 */ @TableField("private_chat_status") private Integer privateChatStatus; + /** + * 显示成员人数 1:开启 2:关闭 + */ + @TableField("show_member_count") + private Integer showMemberCount; + /** * 落地页ID */ @@ -225,6 +237,14 @@ public class GroupInfo implements Serializable { public void setWelcomeMessage(String welcomeMessage) { this.welcomeMessage = welcomeMessage; } + public Integer getInteractiveStatus() { + return interactiveStatus; + } + + public void setInteractiveStatus(Integer interactiveStatus) { + this.interactiveStatus = interactiveStatus; + } + public Integer getPrivateChatStatus() { return privateChatStatus; } @@ -232,6 +252,13 @@ public class GroupInfo implements Serializable { public void setPrivateChatStatus(Integer privateChatStatus) { this.privateChatStatus = privateChatStatus; } + public Integer getShowMemberCount() { + return showMemberCount; + } + + public void setShowMemberCount(Integer showMemberCount) { + this.showMemberCount = showMemberCount; + } public Integer getPageId() { return pageId; } diff --git a/src/main/java/com/upchina/group/entity/GroupMessage.java b/src/main/java/com/upchina/group/entity/GroupMessage.java index afb9edd..90aea1e 100644 --- a/src/main/java/com/upchina/group/entity/GroupMessage.java +++ b/src/main/java/com/upchina/group/entity/GroupMessage.java @@ -3,6 +3,7 @@ package com.upchina.group.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; + import java.io.Serializable; import java.time.LocalDateTime; @@ -36,7 +37,7 @@ public class GroupMessage implements Serializable { private Integer groupId; /** - * 交互类型:1群聊;2私聊 + * 交互类型:1群聊;2私聊 3:会话消息 */ @TableField("interactive_type") private Integer interactiveType; @@ -71,6 +72,12 @@ public class GroupMessage implements Serializable { @TableField("user_name") private String userName; + /** + * 私聊用户ID + */ + @TableField("private_user_id") + private String privateUserId; + /** * 消息内容 */ @@ -222,6 +229,13 @@ public class GroupMessage implements Serializable { public void setUserName(String userName) { this.userName = userName; } + public String getPrivateUserId() { + return privateUserId; + } + + public void setPrivateUserId(String privateUserId) { + this.privateUserId = privateUserId; + } public String getContent() { return content; } diff --git a/src/main/java/com/upchina/group/query/message/DeleteGroupMessageQuery.java b/src/main/java/com/upchina/group/query/message/DeleteGroupMessageQuery.java new file mode 100644 index 0000000..fff88d9 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/DeleteGroupMessageQuery.java @@ -0,0 +1,48 @@ +package com.upchina.group.query.message; + +import com.upchina.common.vo.BackendUserVO; +import com.upchina.group.constant.GroupMessageStatus; +import com.upchina.group.entity.GroupMessage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.List; + +@ApiModel("删除交易圈消息请求") +public class DeleteGroupMessageQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty(value = "消息ID列表", required = true) + @NotEmpty(message = "消息ID列表不能为空") + private List ids; + + public GroupMessage toPO(BackendUserVO userVO) { + GroupMessage message = new GroupMessage(); + message.setStatus(GroupMessageStatus.DELETED.value); + message.setDeleteUserId(userVO.getUserId()); + message.setDeleteTime(LocalDateTime.now()); + return message; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public List getIds() { + return ids; + } + + public void setIds(List ids) { + this.ids = ids; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/FollowAdvisorMessageQuery.java b/src/main/java/com/upchina/group/query/message/FollowAdvisorMessageQuery.java new file mode 100644 index 0000000..db614e5 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/FollowAdvisorMessageQuery.java @@ -0,0 +1,19 @@ +package com.upchina.group.query.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +@ApiModel("关注投顾消息请求") +public class FollowAdvisorMessageQuery { + @NotNull(message = "交易圈ID不能为空") + @ApiModelProperty(value = "交易圈ID", required = true) + private Integer groupId; + + @NotNull(message = "投顾ID不能为空") + @ApiModelProperty(value = "投顾ID", required = true) + private Integer advisorId; +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/GroupMessageProductQuery.java b/src/main/java/com/upchina/group/query/message/GroupMessageProductQuery.java new file mode 100644 index 0000000..9f0f685 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/GroupMessageProductQuery.java @@ -0,0 +1,69 @@ +package com.upchina.group.query.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@ApiModel("推荐产品消息请求") +public class GroupMessageProductQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty(value = "产品ID", required = true) + @NotNull(message = "产品ID不能为空") + private Integer productId; + + @ApiModelProperty(value = "产品名称", required = true) + @NotBlank(message = "产品名称不能为空") + private String productName; + + @ApiModelProperty(value = "产品描述") + private String productDesc; + + @ApiModelProperty(value = "产品链接") + private String productUrl; + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public Integer getProductId() { + return productId; + } + + public void setProductId(Integer productId) { + this.productId = productId; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public String getProductDesc() { + return productDesc; + } + + public void setProductDesc(String productDesc) { + this.productDesc = productDesc; + } + + public String getProductUrl() { + return productUrl; + } + + public void setProductUrl(String productUrl) { + this.productUrl = productUrl; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/GroupMessageStatusQuery.java b/src/main/java/com/upchina/group/query/message/GroupMessageStatusQuery.java new file mode 100644 index 0000000..20c7773 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/GroupMessageStatusQuery.java @@ -0,0 +1,60 @@ +package com.upchina.group.query.message; + +import com.upchina.group.entity.GroupInfo; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@ApiModel("交易圈互动状态请求") +public class GroupMessageStatusQuery { + @NotNull(message = "交易圈ID不能为空") + @ApiModelProperty(value = "交易圈ID", required = true) + private Integer groupId; + + @NotNull(message = "状态不能为空") + @ApiModelProperty(value = "状态 1:开启 2:关闭", required = true) + private Integer status; + + public GroupInfo toInteractivePO() { + GroupInfo groupInfo = new GroupInfo(); + groupInfo.setId(groupId); + groupInfo.setInteractiveStatus(status); + groupInfo.setUpdateTime(LocalDateTime.now()); + return groupInfo; + } + + public GroupInfo toPrivatePO() { + GroupInfo groupInfo = new GroupInfo(); + groupInfo.setId(groupId); + groupInfo.setPrivateChatStatus(status); + groupInfo.setUpdateTime(LocalDateTime.now()); + return groupInfo; + } + + public GroupInfo toShowMemberCountPO() { + GroupInfo groupInfo = new GroupInfo(); + groupInfo.setId(groupId); + groupInfo.setShowMemberCount(status); + groupInfo.setUpdateTime(LocalDateTime.now()); + return groupInfo; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/ListGroupMessageAppQuery.java b/src/main/java/com/upchina/group/query/message/ListGroupMessageAppQuery.java new file mode 100644 index 0000000..5f1c372 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/ListGroupMessageAppQuery.java @@ -0,0 +1,55 @@ +package com.upchina.group.query.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +@ApiModel("APP查询交易圈消息请求") +public class ListGroupMessageAppQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty("最后一条消息ID") + private Integer lastId; + + @ApiModelProperty("查询类型:1全部;2投顾;3用户;4精选;5私聊") + private Integer type; + + @ApiModelProperty("每页大小") + private Integer size = 20; + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public Integer getLastId() { + return lastId; + } + + public void setLastId(Integer lastId) { + this.lastId = lastId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/ListGroupMessageQuery.java b/src/main/java/com/upchina/group/query/message/ListGroupMessageQuery.java new file mode 100644 index 0000000..2903ec5 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/ListGroupMessageQuery.java @@ -0,0 +1,66 @@ +package com.upchina.group.query.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +@ApiModel("查询交易圈消息请求") +public class ListGroupMessageQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty("消息状态:1初始;2已审核;3已删除") + private Integer status; + + @ApiModelProperty("查询类型:1全部;2投顾;3用户;4精选") + private Integer type; + + @ApiModelProperty("最后一条消息ID") + private Integer lastId; + + @ApiModelProperty("消息数量") + private Integer size; + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Integer getLastId() { + return lastId; + } + + public void setLastId(Integer lastId) { + this.lastId = lastId; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/ListPrivateMessageQuery.java b/src/main/java/com/upchina/group/query/message/ListPrivateMessageQuery.java new file mode 100644 index 0000000..69b91a3 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/ListPrivateMessageQuery.java @@ -0,0 +1,58 @@ +package com.upchina.group.query.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@ApiModel("私聊消息列表查询请求") +public class ListPrivateMessageQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty(value = "对方用户ID", required = true) + @NotBlank(message = "对方用户ID不能为空") + private String toUserId; + + @ApiModelProperty("最后一条消息ID") + private Integer lastId; + + @ApiModelProperty("每页大小") + private Integer size = 20; + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public String getToUserId() { + return toUserId; + } + + public void setToUserId(String toUserId) { + this.toUserId = toUserId; + } + + public Integer getLastId() { + return lastId; + } + + public void setLastId(Integer lastId) { + this.lastId = lastId; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/MessageAuditQuery.java b/src/main/java/com/upchina/group/query/message/MessageAuditQuery.java new file mode 100644 index 0000000..1ccb69b --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/MessageAuditQuery.java @@ -0,0 +1,50 @@ +package com.upchina.group.query.message; + +import com.upchina.common.vo.BackendUserVO; +import com.upchina.group.entity.GroupMessage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@ApiModel("消息审核请求") +public class MessageAuditQuery { + + @ApiModelProperty(value = "消息ID", required = true) + @NotNull(message = "消息ID不能为空") + private Integer messageId; + + @ApiModelProperty(value = "审核状态:2已审核;", required = true) + @NotNull(message = "审核状态不能为空") + @Min(2) + @Max(2) + private Integer status; + + public GroupMessage toPO(BackendUserVO backendUser) { + GroupMessage po = new GroupMessage(); + po.setId(messageId); + po.setStatus(status); + po.setAuditUserId(backendUser.getUserId()); + po.setAuditTime(LocalDateTime.now()); + return po; + } + + public Integer getMessageId() { + return messageId; + } + + public void setMessageId(Integer messageId) { + this.messageId = messageId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/SendGroupMessageAdminQuery.java b/src/main/java/com/upchina/group/query/message/SendGroupMessageAdminQuery.java new file mode 100644 index 0000000..499ec12 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/SendGroupMessageAdminQuery.java @@ -0,0 +1,117 @@ +package com.upchina.group.query.message; + +import com.upchina.common.vo.BackendUserVO; +import com.upchina.group.constant.GroupInteractiveType; +import com.upchina.group.constant.GroupMessageContentType; +import com.upchina.group.constant.GroupMessageStatus; +import com.upchina.group.entity.GroupMessage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@ApiModel("发送交易圈消息请求") +public class SendGroupMessageAdminQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty(value = "消息内容", required = true) + @NotBlank(message = "消息内容不能为空") + private String content; + + @ApiModelProperty("回复消息ID") + private Integer replyId; + + @ApiModelProperty("引用消息ID") + private Integer quoteId; + + @ApiModelProperty("消息类型:1文本;2图片;3文件;4产品;5问卷") + @NotNull(message = "消息类型不能为空") + private Integer contentType = GroupMessageContentType.TEXT.value; + + @ApiModelProperty("交互类型:1群聊;2私聊;3会话消息") + private Integer interactiveType; + + @ApiModelProperty("接收用户ID(私聊)") + private String toUserId; + + public GroupMessage toPO(BackendUserVO userVO) { + GroupMessage message = new GroupMessage(); + message.setGroupId(groupId); + message.setContent(content); + message.setUserId(userVO.getUserId().toString()); + message.setAdvisorId(userVO.getAdvisorId()); + message.setUserName(userVO.getUserName()); + message.setContentType(contentType); + message.setReplyId(replyId); + message.setQuoteId(quoteId); + message.setInteractiveType(interactiveType); + if (GroupInteractiveType.PRIVATE.value.equals(interactiveType)) { + message.setToUserId(toUserId); + message.setPrivateUserId(toUserId); + } + message.setStatus(GroupMessageStatus.INITIAL.value); + message.setCreateTime(LocalDateTime.now()); + return message; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getReplyId() { + return replyId; + } + + public void setReplyId(Integer replyId) { + this.replyId = replyId; + } + + public Integer getQuoteId() { + return quoteId; + } + + public void setQuoteId(Integer quoteId) { + this.quoteId = quoteId; + } + + public Integer getContentType() { + return contentType; + } + + public void setContentType(Integer contentType) { + this.contentType = contentType; + } + + public Integer getInteractiveType() { + return interactiveType; + } + + public void setInteractiveType(Integer interactiveType) { + this.interactiveType = interactiveType; + } + + public String getToUserId() { + return toUserId; + } + + public void setToUserId(String toUserId) { + this.toUserId = toUserId; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/SendGroupMessageAppQuery.java b/src/main/java/com/upchina/group/query/message/SendGroupMessageAppQuery.java new file mode 100644 index 0000000..7c0bc75 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/SendGroupMessageAppQuery.java @@ -0,0 +1,70 @@ +package com.upchina.group.query.message; + +import com.upchina.common.vo.FrontUserVO; +import com.upchina.group.constant.GroupMessageContentType; +import com.upchina.group.constant.GroupMessageStatus; +import com.upchina.group.entity.GroupMessage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@ApiModel("发送交易圈消息请求") +public class SendGroupMessageAppQuery { + + @ApiModelProperty(value = "交易圈ID", required = true) + @NotNull(message = "交易圈ID不能为空") + private Integer groupId; + + @ApiModelProperty(value = "消息内容", required = true) + @NotBlank(message = "消息内容不能为空") + private String content; + + @ApiModelProperty("交互类型:1群聊;2私聊") + @Min(1) + @Max(2) + private Integer interactiveType; + + public GroupMessage toPO(FrontUserVO userVO) { + GroupMessage message = new GroupMessage(); + message.setGroupId(groupId); + message.setContent(content); + message.setUserId(userVO.getUserId()); + message.setUserName(userVO.getUserName()); + message.setContentType(GroupMessageContentType.TEXT.value); + message.setInteractiveType(interactiveType); + message.setPrivateUserId(userVO.getUserId()); + message.setStatus(GroupMessageStatus.INITIAL.value); + message.setCreateTime(LocalDateTime.now()); + return message; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getInteractiveType() { + return interactiveType; + } + + public void setInteractiveType(Integer interactiveType) { + this.interactiveType = interactiveType; + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/query/message/UpdateGroupMessageStatusQuery.java b/src/main/java/com/upchina/group/query/message/UpdateGroupMessageStatusQuery.java new file mode 100644 index 0000000..7fac0db --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/UpdateGroupMessageStatusQuery.java @@ -0,0 +1,51 @@ +package com.upchina.group.query.message; + +import com.upchina.common.vo.BackendUserVO; +import com.upchina.group.constant.GroupMessageStatus; +import com.upchina.group.entity.GroupMessage; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + +public class UpdateGroupMessageStatusQuery { + + @ApiModelProperty(value = "消息ID", required = true) + @NotNull(message = "消息ID不能为空") + private Integer id; + + @ApiModelProperty(value = "状态 1:初始 2:已审核 3:已删除", required = true) + @NotNull(message = "状态不能为空") + private Integer status; + + public GroupMessage toPO(GroupMessageStatus targetStatus, BackendUserVO backendUserVO) { + GroupMessage groupMessage = new GroupMessage(); + groupMessage.setId(id); + groupMessage.setStatus(targetStatus.value); + if (GroupMessageStatus.AUDITED.equals(targetStatus)) { + groupMessage.setAuditUserId(backendUserVO.getUserId()); + groupMessage.setAuditTime(LocalDateTime.now()); + } else if (GroupMessageStatus.DELETED.equals(targetStatus)) { + groupMessage.setDeleteUserId(backendUserVO.getUserId()); + groupMessage.setDeleteTime(LocalDateTime.now()); + } + return groupMessage; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + +} diff --git a/src/main/java/com/upchina/group/query/message/UserOnlineStatusMessageQuery.java b/src/main/java/com/upchina/group/query/message/UserOnlineStatusMessageQuery.java new file mode 100644 index 0000000..4152121 --- /dev/null +++ b/src/main/java/com/upchina/group/query/message/UserOnlineStatusMessageQuery.java @@ -0,0 +1,19 @@ +package com.upchina.group.query.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +@Data +@ApiModel("用户在线状态消息请求") +public class UserOnlineStatusMessageQuery { + @NotNull(message = "交易圈ID不能为空") + @ApiModelProperty(value = "交易圈ID", required = true) + private Integer groupId; + + @NotNull(message = "在线状态不能为空") + @ApiModelProperty(value = "是否在线", required = true) + private Boolean online; +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java b/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java new file mode 100644 index 0000000..0a9a512 --- /dev/null +++ b/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java @@ -0,0 +1,172 @@ +package com.upchina.group.service.admin; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.upchina.common.constant.IsOrNot; +import com.upchina.common.handler.BizException; +import com.upchina.common.result.AppPager; +import com.upchina.common.result.ResponseStatus; +import com.upchina.common.state.StateMachine; +import com.upchina.common.vo.BackendUserVO; +import com.upchina.common.vo.OnlyIdVO; +import com.upchina.group.constant.*; +import com.upchina.group.entity.GroupInfo; +import com.upchina.group.entity.GroupMessage; +import com.upchina.group.mapper.GroupInfoMapper; +import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.query.message.*; +import com.upchina.group.service.common.GroupCacheService; +import com.upchina.group.service.common.GroupMessageService; +import com.upchina.group.vo.message.GroupMessageVO; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Service +public class AdminGroupMessageService { + + @Resource + private GroupMessageMapper groupMessageMapper; + + @Resource + private GroupInfoMapper groupInfoMapper; + + @Resource + private GroupMessageService groupMessageService; + + @Resource + private GroupCacheService groupCacheService; + + @Resource + private StateMachine groupMessageSM; + + @Transactional(rollbackFor = Exception.class) + public OnlyIdVO sendAdvisorMessage(SendGroupMessageAdminQuery query, BackendUserVO backendUser) { + GroupMessage message = query.toPO(backendUser); + groupMessageMapper.insert(message); + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.RECOMMEND_PRODUCT, query.getGroupId(), message.getContent()); + return new OnlyIdVO(message.getId()); + } + + @Transactional(rollbackFor = Exception.class) + public void updateStatus(UpdateGroupMessageStatusQuery query, BackendUserVO backendUserVO) { + GroupMessage groupMessageInDB = groupMessageMapper.selectById(query.getId()); + if (groupMessageInDB == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR); + } + + // 状态机扭转 + GroupMessageStatus dbStatus = GroupMessageStatus.fromValue(groupMessageInDB.getStatus()); + GroupMessageStatus targetStatus = groupMessageSM.send(dbStatus, GroupMessageStatus.fromValue(query.getStatus())); + + GroupMessage groupMessage = query.toPO(targetStatus, backendUserVO); + groupMessageMapper.updateById(groupMessage); + groupCacheService.clearMessageCache(query.getId(), groupMessage.getGroupId()); + } + + public AppPager getMessageList(ListGroupMessageQuery query, BackendUserVO backendUser) { + Integer groupId = query.getGroupId(); + Integer status = query.getStatus(); + Integer type = query.getType(); + Integer lastId = query.getLastId(); + Integer size = query.getSize(); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .select(GroupMessage::getId) + .eq(GroupMessage::getGroupId, groupId) + .in(QueryGroupMessageType.ADVISOR.value.equals(type), GroupMessage::getUserType, GroupMessageUserType.ADVISOR.value, GroupMessageUserType.ASSISTANT.value) + .eq(QueryGroupMessageType.CUSTOMER.value.equals(type), GroupMessage::getUserType, GroupMessageUserType.CUSTOMER.value) + .eq(QueryGroupMessageType.SELECTED.value.equals(type), GroupMessage::getIsRecommend, IsOrNot.IS.value) + .eq(status != null, GroupMessage::getStatus, query.getStatus()) + .lt(lastId != null, GroupMessage::getId, lastId) + .orderByDesc(GroupMessage::getId) + .last("limit " + (size + 1)); + + List list = groupMessageMapper.selectList(wrapper); + boolean hasNext = list.size() > size; + if (hasNext) { + list = list.subList(0, size); + } + + List voList = list.stream().map(GroupMessage::getId).map(groupCacheService::getMessage).collect(Collectors.toList()); + return new AppPager<>(voList, hasNext); + } + + @Transactional(rollbackFor = Exception.class) + public OnlyIdVO saveProductMessage(GroupMessageProductQuery query, BackendUserVO backendUser) { + GroupMessage message = new GroupMessage(); + message.setGroupId(query.getGroupId()); + message.setContentType(GroupMessageContentType.PRODUCT.value); + message.setStatus(GroupMessageStatus.AUDITED.value); + message.setCreateUserId(backendUser.getUserId()); + message.setCreateTime(LocalDateTime.now()); + message.setAdvisorId(backendUser.getAdvisorId()); + + // 构建产品消息内容 + JSONObject content = new JSONObject(); + content.put("productId", query.getProductId()); + content.put("productName", query.getProductName()); + content.put("productDesc", query.getProductDesc()); + content.put("productUrl", query.getProductUrl()); + message.setContent(content.toJSONString()); + + groupMessageMapper.insert(message); + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.RECOMMEND_PRODUCT, query.getGroupId(), content); + + return new OnlyIdVO(message.getId()); + } + + /** + * 发送开启/关闭互动消息 + */ + public void sendInteractiveStatusMessage(GroupMessageStatusQuery query, BackendUserVO backendUser) { + Integer groupId = query.getGroupId(); + GroupInfo groupInfo = groupInfoMapper.selectById(groupId); + if (groupInfo == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR); + } + + GroupInfo group = query.toInteractivePO(); + groupInfoMapper.updateById(group); + + groupMessageService.publishGroupMessage(GroupMessageChannel.APP, GroupMessageType.OPEN_INTERACTIVE, groupId, query.getStatus()); + } + + /** + * 发送开启私聊消息 + */ + public void sendOpenPrivateChatMessage(GroupMessageStatusQuery query, BackendUserVO backendUser) { + Integer groupId = query.getGroupId(); + GroupInfo groupInfo = groupInfoMapper.selectById(groupId); + if (groupInfo == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR); + } + + GroupInfo group = query.toPrivatePO(); + groupInfoMapper.updateById(group); + + groupMessageService.publishGroupMessage(GroupMessageChannel.APP, GroupMessageType.OPEN_PRIVATE_CHAT, groupId, query.getStatus()); + } + + /** + * 发送显示圈子人数消息 + */ + public void setShowMemberCount(GroupMessageStatusQuery query, BackendUserVO backendUser) { + Integer groupId = query.getGroupId(); + GroupInfo groupInfo = groupInfoMapper.selectById(groupId); + if (groupInfo == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR); + } + + GroupInfo group = query.toPrivatePO(); + groupInfoMapper.updateById(group); + + groupMessageService.publishGroupMessage(GroupMessageChannel.APP, GroupMessageType.SHOW_GROUP_MEMBER_COUNT, groupId, query.getStatus()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/service/app/AppGroupMessageService.java b/src/main/java/com/upchina/group/service/app/AppGroupMessageService.java new file mode 100644 index 0000000..49ac381 --- /dev/null +++ b/src/main/java/com/upchina/group/service/app/AppGroupMessageService.java @@ -0,0 +1,151 @@ +package com.upchina.group.service.app; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.hazelcast.map.IMap; +import com.upchina.common.config.cache.CacheKey; +import com.upchina.common.constant.IsOrNot; +import com.upchina.common.handler.BizException; +import com.upchina.common.query.OnlyIdQuery; +import com.upchina.common.result.AppPager; +import com.upchina.common.result.ResponseStatus; +import com.upchina.common.service.CacheService; +import com.upchina.common.service.SensitiveWordService; +import com.upchina.common.util.TextUtil; +import com.upchina.common.vo.FrontUserVO; +import com.upchina.group.constant.GroupMessageChannel; +import com.upchina.group.constant.GroupMessageType; +import com.upchina.group.constant.GroupMessageUserType; +import com.upchina.group.constant.QueryGroupMessageType; +import com.upchina.group.entity.GroupMessage; +import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.query.message.ListGroupMessageAppQuery; +import com.upchina.group.query.message.SendGroupMessageAppQuery; +import com.upchina.group.service.GroupInfoService; +import com.upchina.group.service.admin.AdminGroupMessageService; +import com.upchina.group.service.common.GroupCacheService; +import com.upchina.group.service.common.GroupCommonService; +import com.upchina.group.service.common.GroupMessageService; +import com.upchina.group.vo.GroupVO; +import com.upchina.group.vo.message.GroupMessageVO; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.*; + +@Service +public class AppGroupMessageService { + + @Resource + private GroupMessageMapper groupMessageMapper; + + @Resource + private GroupMessageService groupMessageService; + + @Resource + private GroupInfoService groupInfoService; + + @Resource + private GroupCacheService groupCacheService; + + @Resource + private GroupCommonService groupCommonService; + + @Resource + private SensitiveWordService sensitiveWordService; + + @Resource + private AdminGroupMessageService adminGroupMessageService; + + @Resource + private CacheService cacheService; + + @Resource + private IMap groupCache; + + public AppPager getMessageList(ListGroupMessageAppQuery query, FrontUserVO frontUser) { + Integer groupId = query.getGroupId(); + Integer lastId = query.getLastId(); + Integer type = query.getType(); + Integer size = query.getSize(); + GroupVO groupVO = groupInfoService.getForApp(new OnlyIdQuery(groupId), null); + if (groupVO == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "交易圈不存在"); + } else if (QueryGroupMessageType.PRIVATE.value.equals(type) && !IsOrNot.IS.value.equals(groupVO.getPrivateChatStatus())) { + throw new BizException(ResponseStatus.MESSAGE_PERMISSION_ERROR, "交易圈私聊未开启"); + } + String userId = frontUser.getUserId(); + // TODO 验证权限 + groupCommonService.validateUserPermission(userId, groupVO); + QueryGroupMessageType msgType = QueryGroupMessageType.fromValue(type); + NavigableSet sortedSet = this.getMsgIdSet(groupId, userId, msgType); + if (lastId != null && lastId != 0) { + sortedSet = sortedSet.tailSet(lastId, false); + } + List list = new ArrayList<>(size); + Iterator it = sortedSet.iterator(); + while (it.hasNext()) { + Integer msgId = it.next(); + GroupMessageVO msg = groupCacheService.getMessage(msgId); + if (msg != null) { + list.add(msg); + if (--size == 0) break; + } + } + return new AppPager<>(list, it.hasNext()); + } + + private NavigableSet getMsgIdSet(Integer groupId, String userId, QueryGroupMessageType msgType) { + if (QueryGroupMessageType.CUSTOMER.equals(msgType)) { + throw new BizException(ResponseStatus.PARM_ERROR, "查询类型错误"); + } + String cacheKey = CacheKey.GroupKey.GROUP_MESSAGE_LIST + msgType.value; + if (QueryGroupMessageType.PRIVATE.equals(msgType)) { + cacheKey += "|" + userId; + } + + return cacheService.get(groupCache, cacheKey, () -> { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .select(GroupMessage::getId) + .eq(GroupMessage::getGroupId, groupId) + .in(QueryGroupMessageType.ADVISOR.equals(msgType), GroupMessage::getUserType, GroupMessageUserType.ADVISOR.value, GroupMessageUserType.ASSISTANT.value) + .eq(QueryGroupMessageType.CUSTOMER.equals(msgType), GroupMessage::getUserType, GroupMessageUserType.CUSTOMER.value) + .eq(QueryGroupMessageType.SELECTED.equals(msgType), GroupMessage::getIsRecommend, IsOrNot.IS.value) + .eq(QueryGroupMessageType.PRIVATE.equals(msgType), GroupMessage::getPrivateUserId, userId); + List objList = groupMessageMapper.selectObjs(wrapper); + NavigableSet set = new TreeSet<>(Comparator.reverseOrder()); + objList.stream().map(obj -> (Integer) obj).forEach(set::add); + return set; + }); + } + + @Transactional(rollbackFor = Exception.class) + public GroupMessageVO sendMessage(SendGroupMessageAppQuery query, FrontUserVO frontUser) { + Integer groupId = query.getGroupId(); + String content = query.getContent(); + // 敏感词检查 + sensitiveWordService.check(content); + + // 检查交易圈状态 + if (!groupCommonService.checkGroupStatus(groupId)) { + throw new BizException(ResponseStatus.STATUS_ERROR, "交易圈未开启"); + } + + // 检查用户是否被禁言 + if (groupCommonService.checkUserForbidden(frontUser.getUserId())) { + throw new BizException(ResponseStatus.COMMENT_BLACK_USER_ERROR, "您已被禁言"); + } + + GroupMessage message = query.toPO(frontUser); + message.setContent(TextUtil.cleanUnsafeHtml(content)); + + groupMessageMapper.insert(message); + + GroupMessageVO vo = new GroupMessageVO(message); + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.NORMAL, groupId, vo); + + return vo; + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/service/common/GroupCacheService.java b/src/main/java/com/upchina/group/service/common/GroupCacheService.java new file mode 100644 index 0000000..0a87a63 --- /dev/null +++ b/src/main/java/com/upchina/group/service/common/GroupCacheService.java @@ -0,0 +1,44 @@ +package com.upchina.group.service.common; + +import com.hazelcast.map.IMap; +import com.upchina.common.config.cache.CacheKey; +import com.upchina.common.service.CacheService; +import com.upchina.group.entity.GroupMessage; +import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.vo.message.GroupMessageVO; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +@Service +public class GroupCacheService { + + @Resource + private CacheService cacheService; + + @Resource + private GroupMessageMapper groupMessageMapper; + + @Resource + private IMap groupCache; + + public void clearMessageCache(Integer msgId, Integer groupId) { + if (msgId != null) { + groupCache.remove(CacheKey.GroupKey.GROUP_MESSAGE_DETAIL + msgId); + } + if (groupId != null) { + groupCache.remove(CacheKey.GroupKey.GROUP_MESSAGE_LIST + groupId); + } + } + + public GroupMessageVO getMessage(Integer messageId) { + return cacheService.get(groupCache, + CacheKey.GroupKey.GROUP_MESSAGE_DETAIL + messageId, () -> { + GroupMessage message = groupMessageMapper.selectById(messageId); + if (message == null) { + return null; + } + return new GroupMessageVO(message); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/service/common/GroupCommonService.java b/src/main/java/com/upchina/group/service/common/GroupCommonService.java new file mode 100644 index 0000000..fb7a44f --- /dev/null +++ b/src/main/java/com/upchina/group/service/common/GroupCommonService.java @@ -0,0 +1,31 @@ +package com.upchina.group.service.common; + +import com.upchina.group.constant.GroupInfoStatus; +import com.upchina.group.entity.GroupInfo; +import com.upchina.group.mapper.GroupInfoMapper; +import com.upchina.group.vo.GroupVO; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +@Service +public class GroupCommonService { + + + @Resource + private GroupInfoMapper groupInfoMapper; + + public boolean checkGroupStatus(Integer groupId) { + GroupInfo group = groupInfoMapper.selectById(groupId); + return GroupInfoStatus.AUDITED.value.equals(group.getStatus()); + } + + public boolean checkUserForbidden(String userId) { + // 实现用户禁言检查逻辑 + return false; + } + + public boolean validateUserPermission(String userId, GroupVO groupVO) { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/service/common/GroupMessageService.java b/src/main/java/com/upchina/group/service/common/GroupMessageService.java new file mode 100644 index 0000000..d6d7966 --- /dev/null +++ b/src/main/java/com/upchina/group/service/common/GroupMessageService.java @@ -0,0 +1,127 @@ +package com.upchina.group.service.common; + +import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.topic.ITopic; +import com.upchina.common.config.cache.CacheKey; +import com.upchina.group.constant.GroupMessageChannel; +import com.upchina.group.constant.GroupMessageType; +import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.vo.message.GroupWsMessageVO; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; + +@Service +public class GroupMessageService { + + @Resource + private GroupMessageMapper groupMessageMapper; + + @Resource + private SimpMessagingTemplate simpMessagingTemplate; + + @Resource + private HazelcastInstance hazelcastInstance; + + public ITopic> adminGroupTopic = null; + + public ITopic> adminPrivateTopic = null; + + public ITopic> adminSessionTopic = null; + + public ITopic> appGroupTopic = null; + + public ITopic> appPrivateTopic = null; + + public ITopic> appSessionTopic = null; + + // 后端群组消息 + public static final String ADMIN_GROUP_TOPIC = "/admin/group/topic"; + + // 后端私聊消息 + public static final String ADMIN_PRIVATE_TOPIC = "/admin/private/topic"; + + // 后端用户消息 + public static final String ADMIN_SESSION_TOPIC = "/admin/session/topic"; + + // APP端群组消息 + public static final String APP_GROUP_TOPIC = "/app/group/topic"; + + // APP端私聊消息 + public static final String APP_PRIVATE_TOPIC = "/app/private/topic"; + + // APP端个人消息 + public static final String APP_SESSION_TOPIC = "/app/session/topic"; + + /** + * 启动时初始化Topic用于跨服务节点消息通信 + */ + @PostConstruct + private void initVideoTopic() { + adminGroupTopic = initTopic(CacheKey.GroupMessageTopicKey.ADMIN_GROUP_TOPIC, ADMIN_GROUP_TOPIC, true, false, false); + adminPrivateTopic = initTopic(CacheKey.GroupMessageTopicKey.ADMIN_PRIVATE_TOPIC, ADMIN_PRIVATE_TOPIC, true, true, false); + adminSessionTopic = initTopic(CacheKey.GroupMessageTopicKey.ADMIN_SESSION_TOPIC, ADMIN_SESSION_TOPIC, false, true, true); + appGroupTopic = initTopic(CacheKey.GroupMessageTopicKey.APP_GROUP_TOPIC, APP_GROUP_TOPIC, true, false, false); + appPrivateTopic = initTopic(CacheKey.GroupMessageTopicKey.APP_PRIVATE_TOPIC, APP_PRIVATE_TOPIC, true, true, false); + appSessionTopic = initTopic(CacheKey.GroupMessageTopicKey.APP_SESSION_TOPIC, APP_SESSION_TOPIC, false, true, true); + } + + /** + * 初始化Topic + * + * @param topicKey 主题Key + * @param subAddress 订阅地址 + * @return 主题 + */ + private ITopic> initTopic(String topicKey, String subAddress, boolean containGroupId, boolean containUserId, boolean containSessionId) { + ITopic> topic = hazelcastInstance.getTopic(topicKey); + topic.addMessageListener(message -> { + GroupWsMessageVO payload = message.getMessageObject(); + String destination = subAddress; + if (containGroupId) { + destination += "/" + payload.getGroupId(); + } + if (containUserId) { + destination += "/" + payload.getUserId(); + } + if (containSessionId) { + destination += "/" + payload.getSessionId(); + } + simpMessagingTemplate.convertAndSend(destination, payload); + }); + return topic; + } + + public void publishGroupMessage(GroupMessageChannel channel, GroupMessageType messageType, Integer groupId, Object payload) { + GroupWsMessageVO vo = GroupWsMessageVO.successGroupMsg(payload, groupId, messageType); + if (GroupMessageChannel.APP.equals(channel) || GroupMessageChannel.ALL.equals(channel)) { + appGroupTopic.publish(vo); + } + if (GroupMessageChannel.ADMIN.equals(channel) || GroupMessageChannel.ALL.equals(channel)) { + adminGroupTopic.publish(vo); + } + } + + public void publishPrivateMessage(GroupMessageChannel channel, GroupMessageType messageType, Integer groupId, String userId, Object payload) { + GroupWsMessageVO vo = GroupWsMessageVO.successPrivateMsg(payload, groupId, userId, messageType); + if (GroupMessageChannel.APP.equals(channel) || GroupMessageChannel.ALL.equals(channel)) { + appPrivateTopic.publish(vo); + } + if (GroupMessageChannel.ADMIN.equals(channel) || GroupMessageChannel.ALL.equals(channel)) { + adminPrivateTopic.publish(vo); + } + } + + public void publishSessionMessage(GroupMessageChannel channel, GroupMessageType messageType, String userId, String sessionId, Object payload) { + GroupWsMessageVO vo = GroupWsMessageVO.successSessionMsg(payload, userId, sessionId, messageType); + if (GroupMessageChannel.APP.equals(channel) || GroupMessageChannel.ALL.equals(channel)) { + appSessionTopic.publish(vo); + } + if (GroupMessageChannel.ADMIN.equals(channel) || GroupMessageChannel.ALL.equals(channel)) { + adminSessionTopic.publish(vo); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/vo/message/GroupMessageVO.java b/src/main/java/com/upchina/group/vo/message/GroupMessageVO.java new file mode 100644 index 0000000..13d28a4 --- /dev/null +++ b/src/main/java/com/upchina/group/vo/message/GroupMessageVO.java @@ -0,0 +1,220 @@ +package com.upchina.group.vo.message; + +import com.upchina.advisor.vo.AdvisorBasicVO; +import com.upchina.group.entity.GroupMessage; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.time.LocalDateTime; + +@ApiModel("交易圈消息VO") +public class GroupMessageVO { + + @ApiModelProperty("消息ID") + private Integer id; + + @ApiModelProperty("消息类型") + private Integer msgType; + + @ApiModelProperty("交易圈ID") + private Integer groupId; + + @ApiModelProperty("交互类型:1群聊;2私聊;3会话消息") + private Integer interactiveType; + + @ApiModelProperty("用户类型") + private Integer userType; + + @ApiModelProperty("用户ID") + private String userId; + + @ApiModelProperty("用户名称") + private String userName; + + @ApiModelProperty("消息内容") + private String content; + + @ApiModelProperty("发布类型") + private Integer contentType; + + @ApiModelProperty("回复消息ID") + private Integer replyId; + + @ApiModelProperty("回复消息") + private GroupMessageVO replyMessage; + + @ApiModelProperty("引用消息ID") + private Integer quoteId; + + @ApiModelProperty("引用消息") + private GroupMessageVO quoteMessage; + + @ApiModelProperty("状态") + private Integer status; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("投顾ID") + private Integer advisorId; + + @ApiModelProperty("投顾") + private AdvisorBasicVO advisor; + + public GroupMessageVO() { + } + + public GroupMessageVO(GroupMessage message) { + this.id = message.getId(); + this.msgType = message.getMsgType(); + this.groupId = message.getGroupId(); + this.interactiveType = message.getInteractiveType(); + this.userType = message.getUserType(); + this.userId = message.getUserId(); + this.userName = message.getUserName(); + this.content = message.getContent(); + this.contentType = message.getContentType(); + this.replyId = message.getReplyId(); + this.quoteId = message.getQuoteId(); + this.status = message.getStatus(); + this.createTime = message.getCreateTime(); + this.advisorId = message.getAdvisorId(); + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getMsgType() { + return msgType; + } + + public void setMsgType(Integer msgType) { + this.msgType = msgType; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public Integer getInteractiveType() { + return interactiveType; + } + + public void setInteractiveType(Integer interactiveType) { + this.interactiveType = interactiveType; + } + + public Integer getUserType() { + return userType; + } + + public void setUserType(Integer userType) { + this.userType = userType; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public Integer getContentType() { + return contentType; + } + + public void setContentType(Integer contentType) { + this.contentType = contentType; + } + + public Integer getReplyId() { + return replyId; + } + + public void setReplyId(Integer replyId) { + this.replyId = replyId; + } + + public GroupMessageVO getReplyMessage() { + return replyMessage; + } + + public void setReplyMessage(GroupMessageVO replyMessage) { + this.replyMessage = replyMessage; + } + + public Integer getQuoteId() { + return quoteId; + } + + public void setQuoteId(Integer quoteId) { + this.quoteId = quoteId; + } + + public GroupMessageVO getQuoteMessage() { + return quoteMessage; + } + + public void setQuoteMessage(GroupMessageVO quoteMessage) { + this.quoteMessage = quoteMessage; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public LocalDateTime getCreateTime() { + return createTime; + } + + public void setCreateTime(LocalDateTime createTime) { + this.createTime = createTime; + } + + public Integer getAdvisorId() { + return advisorId; + } + + public void setAdvisorId(Integer advisorId) { + this.advisorId = advisorId; + } + + public AdvisorBasicVO getAdvisor() { + return advisor; + } + + public void setAdvisor(AdvisorBasicVO advisor) { + this.advisor = advisor; + } + +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/vo/message/GroupNotificationVO.java b/src/main/java/com/upchina/group/vo/message/GroupNotificationVO.java new file mode 100644 index 0000000..db44681 --- /dev/null +++ b/src/main/java/com/upchina/group/vo/message/GroupNotificationVO.java @@ -0,0 +1,13 @@ +package com.upchina.group.vo.message; + +public class GroupNotificationVO { + private Integer type; + private String content; + + public GroupNotificationVO(Integer type, String content) { + this.type = type; + this.content = content; + } + + // getters and setters +} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/vo/message/GroupWsMessageVO.java b/src/main/java/com/upchina/group/vo/message/GroupWsMessageVO.java new file mode 100644 index 0000000..2d94d6e --- /dev/null +++ b/src/main/java/com/upchina/group/vo/message/GroupWsMessageVO.java @@ -0,0 +1,154 @@ +package com.upchina.group.vo.message; + +import com.upchina.common.handler.BizException; +import com.upchina.common.result.ResponseStatus; +import com.upchina.common.util.RequestIdUtil; +import com.upchina.group.constant.GroupMessageType; +import io.swagger.annotations.ApiModelProperty; + +import java.io.Serializable; + +public class GroupWsMessageVO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty("状态码") + private Integer code; + + @ApiModelProperty("状态详情") + private String message; + + @ApiModelProperty("圈子ID") + private Integer groupId; + + @ApiModelProperty("用户ID 属于私聊或个人消息时,该字段有值") + private String userId; + + @ApiModelProperty("会话ID 属于个人消息时,该字段有值") + private String sessionId; + + @ApiModelProperty("类型 1:查询在线人数 2:查询成员列表 3:查询历史消息 4:发送消息结果通知") + private Integer type; + + @ApiModelProperty("数据体") + private T data; + + @ApiModelProperty("序列号") + private String requestId; + + public GroupWsMessageVO(Integer code, String message, Integer groupId, String userId, String sessionId, GroupMessageType type, T data) { + this.code = code; + this.message = message; + this.groupId = groupId; + this.userId = userId; + this.sessionId = sessionId; + this.type = type.value; + this.data = data; + this.requestId = RequestIdUtil.getValue(); + } + + public static GroupWsMessageVO successGroupMsg(T data, Integer groupId, GroupMessageType type) { + return new GroupWsMessageVO<>(ResponseStatus.OK.code, ResponseStatus.OK.message, groupId, null, null, type, data); + } + + public static GroupWsMessageVO successPrivateMsg(T data, Integer groupId, String userId, GroupMessageType type) { + return new GroupWsMessageVO<>(ResponseStatus.OK.code, ResponseStatus.OK.message, groupId, userId, null, type, data); + } + + public static GroupWsMessageVO successSessionMsg(T data, String userId, String sessionId, GroupMessageType type) { + return new GroupWsMessageVO<>(ResponseStatus.OK.code, ResponseStatus.OK.message, null, userId, sessionId, type, data); + } + + public static GroupWsMessageVO errorSessionMsg(ResponseStatus status, String userId, String sessionId, GroupMessageType type) { + return new GroupWsMessageVO<>(status.code, status.message, null, userId, sessionId, type,null); + } + + public static GroupWsMessageVO errorSessionMsg(BizException ex, String userId, String sessionId, GroupMessageType type) { + return new GroupWsMessageVO<>(ex.getErrorCode(), ex.getMessage(), null, userId, sessionId, type, null); + } + + public static GroupWsMessageVO errorSessionMsg(Integer code, String message, String userId, String sessionId, GroupMessageType type) { + return new GroupWsMessageVO<>(code, message, null, userId, sessionId, type, null); + } + + public boolean isSuccess() { + return ResponseStatus.OK.code.equals(this.code); + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Integer getGroupId() { + return groupId; + } + + public void setGroupId(Integer groupId) { + this.groupId = groupId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getRequestId() { + return requestId; + } + + public void setRequestId(String requestId) { + this.requestId = requestId; + } + + @Override + public String toString() { + return "GroupWsMessageVO{" + + "code=" + code + + ", message='" + message + '\'' + + ", groupId=" + groupId + + ", userId='" + userId + '\'' + + ", sessionId='" + sessionId + '\'' + + ", type=" + type + + ", data=" + data + + ", requestId='" + requestId + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/com/taf/server/startup/Main.java b/src/main/java/com/upchina/startup/Main.java similarity index 95% rename from src/main/java/com/taf/server/startup/Main.java rename to src/main/java/com/upchina/startup/Main.java index a61ebca..a175dce 100644 --- a/src/main/java/com/taf/server/startup/Main.java +++ b/src/main/java/com/upchina/startup/Main.java @@ -1,4 +1,4 @@ -package com.taf.server.startup; +package com.upchina.startup; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/resources/conf/advisorServer.yaml b/src/main/resources/conf/advisorServer.yaml index 1c161e7..6add770 100644 --- a/src/main/resources/conf/advisorServer.yaml +++ b/src/main/resources/conf/advisorServer.yaml @@ -16,7 +16,7 @@ hazelcast: taf: servant: advisorServant: DemoVideoDev.AdvisorServer.AdvisorObj@tcp -h 127.0.0.1 -t 60000 -p 45600 -scheduledEnable: true +scheduledEnable: false cron: saveVideoCount: "10 * * * * ?" #从cache刷新视频播放量到DB 每分钟的第10s执行 refreshTranscodeStatus: "2 3/5 * * * ?" #从腾讯云拉取录播上传视频信息更新到DB diff --git a/src/main/resources/conf/application.yaml b/src/main/resources/conf/application.yaml index 6bfca20..2659f59 100644 --- a/src/main/resources/conf/application.yaml +++ b/src/main/resources/conf/application.yaml @@ -33,7 +33,7 @@ spring: datasource: master: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://47.96.178.171:3306/advisor_video?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8 + url: jdbc:mysql://47.96.178.171:3306/advisor_video?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8 username: eason password: mysql2025easonzhu mvc: diff --git a/src/test/java/com/upchina/group/service/GroupInfoServiceTest.java b/src/test/java/com/upchina/group/service/GroupInfoServiceTest.java new file mode 100644 index 0000000..e19a72f --- /dev/null +++ b/src/test/java/com/upchina/group/service/GroupInfoServiceTest.java @@ -0,0 +1,192 @@ +package com.upchina.group.service; + +import com.hazelcast.map.IMap; +import com.upchina.advisor.entity.AdvisorBasic; +import com.upchina.advisor.service.AdvisorInfoService; +import com.upchina.common.query.OnlyIdQuery; +import com.upchina.common.service.AppUserService; +import com.upchina.common.service.CacheService; +import com.upchina.common.service.SensitiveWordService; +import com.upchina.common.state.StateMachine; +import com.upchina.common.vo.BackendUserVO; +import com.upchina.common.vo.InsertIdVO; +import com.upchina.course.service.PageService; +import com.upchina.group.constant.GroupInfoStatus; +import com.upchina.group.entity.GroupInfo; +import com.upchina.group.mapper.GroupInfoMapper; +import com.upchina.group.query.SaveGroupQuery; +import com.upchina.group.query.UpdateGroupQuery; +import com.upchina.group.query.UpdateGroupStatusQuery; +import com.upchina.rbac.entity.UserDept; +import com.upchina.rbac.service.AuthService; +import com.upchina.rbac.service.UserService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class GroupInfoServiceTest { + + @Mock + private GroupInfoMapper groupInfoMapper; + + @Mock + private SensitiveWordService sensitiveWordService; + + @Mock + private StateMachine groupSM; + + @Mock + private AdvisorInfoService advisorInfoService; + + @Mock + private UserService userService; + + @Mock + private AuthService authService; + + @Mock + private PageService pageService; + + @Mock + private CacheService cacheService; + + @Mock + private AppUserService appUserService; + + @Mock + private IMap groupCache; + + @InjectMocks + private GroupInfoService groupInfoService; + + private BackendUserVO mockBackendUser; + + @BeforeEach + void setUp() { + mockBackendUser = new BackendUserVO(); + mockBackendUser.setUserId(1); + mockBackendUser.setAdvisorId(100); + } + + @Test + void save_ShouldSucceed() { + // 准备测试数据 + SaveGroupQuery query = new SaveGroupQuery(); + query.setName("测试群组"); + query.setRemark("测试备注"); + query.setDetail("测试详情"); + query.setAdvisorId(100); + + // 设置模拟行为 + doNothing().when(sensitiveWordService).check(anyString(), anyString(), anyString()); + when(groupInfoMapper.insert(any(GroupInfo.class))).thenReturn(1); + + // 执行测试 + InsertIdVO result = groupInfoService.save(query, mockBackendUser); + + // 验证结果 + assertNotNull(result); + verify(groupInfoMapper).insert(any(GroupInfo.class)); + verify(sensitiveWordService).check(anyString(), anyString(), anyString()); + } + + @Test + void update_ShouldSucceed() { + // 准备测试数据 + UpdateGroupQuery query = new UpdateGroupQuery(); + query.setId(1); + query.setName("更新的群组名"); + + GroupInfo existingGroup = new GroupInfo(); + existingGroup.setId(1); + existingGroup.setStatus(GroupInfoStatus.TO_COMMIT.value); + existingGroup.setAdvisorId(100); + + // 设置模拟行为 - 修改这里的 mock 设置 + when(groupInfoMapper.selectById(1)).thenReturn(existingGroup); + when(groupInfoMapper.updateById(any(GroupInfo.class))).thenReturn(1); + + // 执行测试 + groupInfoService.update(query, mockBackendUser); + + // 验证结果 + verify(groupInfoMapper).updateById(any(GroupInfo.class)); + verify(groupCache).delete(anyString()); + // 验证 sensitiveWordService.check 被调用,使用参数捕获器 + verify(sensitiveWordService).check( + eq("更新的群组名"), // 名称 + isNull(), // 备注 + isNull() // 详情 + ); + } + + @Test + void updateStatus_ShouldSucceed() { + // 准备测试数据 + UpdateGroupStatusQuery query = new UpdateGroupStatusQuery(); + query.setId(1); + query.setEvent(GroupInfoStatus.EVENT_SUBMIT.value); + + GroupInfo existingGroup = new GroupInfo(); + existingGroup.setId(1); + existingGroup.setStatus(GroupInfoStatus.TO_AUDIT.value); + existingGroup.setAdvisorId(100); + + // 设置模拟行为 + when(groupInfoMapper.selectById(1)).thenReturn(existingGroup); + when(groupInfoMapper.updateById(any(GroupInfo.class))).thenReturn(1); + when(groupSM.send(any(), any())).thenReturn(GroupInfoStatus.TO_AUDIT); + + // 执行测试 + groupInfoService.updateStatus(query, mockBackendUser); + + // 验证结果 + verify(groupInfoMapper).updateById(any(GroupInfo.class)); + verify(groupCache).delete(anyString()); + } + + @Test + void get_ShouldReturnGroupVO() { + // 准备测试数据 + OnlyIdQuery query = new OnlyIdQuery(1); + + GroupInfo groupInfo = new GroupInfo(); + groupInfo.setId(1); + groupInfo.setAdvisorId(100); + + Map advisorMap = new HashMap<>(); + AdvisorBasic advisor = new AdvisorBasic(); + advisor.setId(100); + advisorMap.put(100, advisor); + + Map userMap = new HashMap<>(); + UserDept userDept = new UserDept(); + userDept.setUserId(1); + userDept.setName("测试用户"); + userMap.put(1, userDept); + + // 设置模拟行为 + when(groupInfoMapper.selectById(1)).thenReturn(groupInfo); + when(advisorInfoService.getAdvisorMap()).thenReturn(advisorMap); + when(userService.getUserMap()).thenReturn(userMap); + + // 执行测试 + var result = groupInfoService.get(query, mockBackendUser); + + // 验证结果 + assertNotNull(result); + assertEquals(1, result.getId()); + } +} \ No newline at end of file diff --git a/src/test/java/com/upchina/group/service/admin/AdminGroupMessageServiceTest.java b/src/test/java/com/upchina/group/service/admin/AdminGroupMessageServiceTest.java new file mode 100644 index 0000000..d35390b --- /dev/null +++ b/src/test/java/com/upchina/group/service/admin/AdminGroupMessageServiceTest.java @@ -0,0 +1,134 @@ +package com.upchina.group.service.admin; + +import com.upchina.common.state.StateMachine; +import com.upchina.common.vo.BackendUserVO; +import com.upchina.common.vo.OnlyIdVO; +import com.upchina.group.constant.GroupMessageChannel; +import com.upchina.group.constant.GroupMessageStatus; +import com.upchina.group.constant.GroupMessageType; +import com.upchina.group.entity.GroupInfo; +import com.upchina.group.entity.GroupMessage; +import com.upchina.group.mapper.GroupInfoMapper; +import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.query.message.GroupMessageProductQuery; +import com.upchina.group.query.message.GroupMessageStatusQuery; +import com.upchina.group.query.message.SendGroupMessageAdminQuery; +import com.upchina.group.service.common.GroupCacheService; +import com.upchina.group.service.common.GroupMessageService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class AdminGroupMessageServiceTest { + + @Mock + private GroupMessageMapper groupMessageMapper; + + @Mock + private GroupInfoMapper groupInfoMapper; + + @Mock + private GroupMessageService groupMessageService; + + @Mock + private GroupCacheService groupCacheService; + + @Mock + private StateMachine groupMessageSM; + + @InjectMocks + private AdminGroupMessageService adminGroupMessageService; + + private BackendUserVO mockBackendUser; + + @BeforeEach + void setUp() { + mockBackendUser = new BackendUserVO(); + mockBackendUser.setUserId(1); + mockBackendUser.setAdvisorId(100); + } + + @Test + void sendAdvisorMessage_ShouldSucceed() { + // 准备测试数据 + SendGroupMessageAdminQuery query = new SendGroupMessageAdminQuery(); + query.setGroupId(1); + query.setContent("测试消息"); + + when(groupMessageMapper.insert(any(GroupMessage.class))).thenReturn(1); + + // 执行测试 + OnlyIdVO result = adminGroupMessageService.sendAdvisorMessage(query, mockBackendUser); + + // 验证结果 + assertNotNull(result); + verify(groupMessageMapper).insert(any(GroupMessage.class)); + verify(groupMessageService).publishGroupMessage( + eq(GroupMessageChannel.ALL), + eq(GroupMessageType.RECOMMEND_PRODUCT), + eq(query.getGroupId()), + any() + ); + } + + @Test + void sendInteractiveStatusMessage_ShouldSucceed() { + // 准备测试数据 + GroupMessageStatusQuery query = new GroupMessageStatusQuery(); + query.setGroupId(1); + query.setStatus(1); + + GroupInfo mockGroupInfo = new GroupInfo(); + mockGroupInfo.setId(1); + + when(groupInfoMapper.selectById(query.getGroupId())).thenReturn(mockGroupInfo); + when(groupInfoMapper.updateById(any(GroupInfo.class))).thenReturn(1); + + // 执行测试 + adminGroupMessageService.sendInteractiveStatusMessage(query, mockBackendUser); + + // 验证结果 + verify(groupInfoMapper).selectById(query.getGroupId()); + verify(groupInfoMapper).updateById(any(GroupInfo.class)); + verify(groupMessageService).publishGroupMessage( + eq(GroupMessageChannel.APP), + eq(GroupMessageType.OPEN_INTERACTIVE), + eq(query.getGroupId()), + eq(query.getStatus()) + ); + } + + @Test + void saveProductMessage_ShouldSucceed() { + // 准备测试数据 + GroupMessageProductQuery query = new GroupMessageProductQuery(); + query.setGroupId(1); + query.setProductId(100); + query.setProductName("测试产品"); + query.setProductDesc("产品描述"); + query.setProductUrl("http://example.com"); + + when(groupMessageMapper.insert(any(GroupMessage.class))).thenReturn(1); + + // 执行测试 + OnlyIdVO result = adminGroupMessageService.saveProductMessage(query, mockBackendUser); + + // 验证结果 + assertNotNull(result); + verify(groupMessageMapper).insert(any(GroupMessage.class)); + verify(groupMessageService).publishGroupMessage( + eq(GroupMessageChannel.ALL), + eq(GroupMessageType.RECOMMEND_PRODUCT), + eq(query.getGroupId()), + any() + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java b/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java new file mode 100644 index 0000000..74f0eaa --- /dev/null +++ b/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java @@ -0,0 +1,215 @@ +package com.upchina.group.service.app; + +import com.hazelcast.map.IMap; +import com.upchina.common.constant.IsOrNot; +import com.upchina.common.handler.BizException; +import com.upchina.common.query.OnlyIdQuery; +import com.upchina.common.result.AppPager; +import com.upchina.common.service.CacheService; +import com.upchina.common.service.SensitiveWordService; +import com.upchina.common.vo.FrontUserVO; +import com.upchina.group.constant.GroupMessageChannel; +import com.upchina.group.constant.GroupMessageType; +import com.upchina.group.constant.QueryGroupMessageType; +import com.upchina.group.entity.GroupMessage; +import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.query.message.ListGroupMessageAppQuery; +import com.upchina.group.query.message.SendGroupMessageAppQuery; +import com.upchina.group.service.GroupInfoService; +import com.upchina.group.service.common.GroupCacheService; +import com.upchina.group.service.common.GroupCommonService; +import com.upchina.group.service.common.GroupMessageService; +import com.upchina.group.vo.GroupVO; +import com.upchina.group.vo.message.GroupMessageVO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.NavigableSet; +import java.util.TreeSet; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class AppGroupMessageServiceTest { + + @Mock + private GroupMessageMapper groupMessageMapper; + + @Mock + private GroupMessageService groupMessageService; + + @Mock + private GroupInfoService groupInfoService; + + @Mock + private GroupCacheService groupCacheService; + + @Mock + private GroupCommonService groupCommonService; + + @Mock + private SensitiveWordService sensitiveWordService; + + @Mock + private CacheService cacheService; + + @Mock + private IMap groupCache; + + @InjectMocks + private AppGroupMessageService appGroupMessageService; + + private FrontUserVO mockFrontUser; + private GroupVO mockGroupVO; + + @BeforeEach + void setUp() { + mockFrontUser = new FrontUserVO(); + mockFrontUser.setUserId("123"); + + mockGroupVO = new GroupVO(); + mockGroupVO.setId(1); + mockGroupVO.setPrivateChatStatus(IsOrNot.IS.value); + } + + @Test + void getMessageList_ShouldSucceed() { + // 准备测试数据 + ListGroupMessageAppQuery query = new ListGroupMessageAppQuery(); + query.setGroupId(1); + query.setType(QueryGroupMessageType.ADVISOR.value); + query.setSize(10); + + NavigableSet mockSet = new TreeSet<>(); + mockSet.add(1); + mockSet.add(2); + + GroupMessageVO mockMessageVO = new GroupMessageVO(); + mockMessageVO.setId(1); + + // 设置模拟行为 + when(groupInfoService.getForApp(any(OnlyIdQuery.class), any())).thenReturn(mockGroupVO); + when(groupCommonService.validateUserPermission(anyString(), any(GroupVO.class))).thenReturn(true); + when(cacheService.get(eq(groupCache), anyString(), any())).thenReturn(mockSet); + when(groupCacheService.getMessage(anyInt())).thenReturn(mockMessageVO); + + // 执行测试 + AppPager result = appGroupMessageService.getMessageList(query, mockFrontUser); + + // 验证结果 + assertNotNull(result); + assertFalse(result.getList().isEmpty()); + assertEquals(1, result.getList().get(0).getId()); + } + + @Test + void getMessageList_WithPrivateType_ShouldSucceed() { + // 准备测试数据 + ListGroupMessageAppQuery query = new ListGroupMessageAppQuery(); + query.setGroupId(1); + query.setType(QueryGroupMessageType.PRIVATE.value); + query.setSize(10); + + NavigableSet mockSet = new TreeSet<>(); + mockSet.add(1); + + GroupMessageVO mockMessageVO = new GroupMessageVO(); + mockMessageVO.setId(1); + + // 设置模拟行为 + when(groupInfoService.getForApp(any(OnlyIdQuery.class), any())).thenReturn(mockGroupVO); + when(groupCommonService.validateUserPermission(anyString(), any(GroupVO.class))).thenReturn(true); + when(cacheService.get(eq(groupCache), anyString(), any())).thenReturn(mockSet); + when(groupCacheService.getMessage(anyInt())).thenReturn(mockMessageVO); + + // 执行测试 + AppPager result = appGroupMessageService.getMessageList(query, mockFrontUser); + + // 验证结果 + assertNotNull(result); + assertFalse(result.getList().isEmpty()); + } + + @Test + void sendMessage_ShouldSucceed() { + // 准备测试数据 + SendGroupMessageAppQuery query = new SendGroupMessageAppQuery(); + query.setGroupId(1); + query.setContent("测试消息"); + + // 设置模拟行为 + doNothing().when(sensitiveWordService).check(anyString()); + when(groupCommonService.checkGroupStatus(anyInt())).thenReturn(true); + when(groupCommonService.checkUserForbidden(anyString())).thenReturn(false); + when(groupMessageMapper.insert(any(GroupMessage.class))).thenReturn(1); + + // 执行测试 + GroupMessageVO result = appGroupMessageService.sendMessage(query, mockFrontUser); + + // 验证结果 + assertNotNull(result); + verify(groupMessageService).publishGroupMessage( + eq(GroupMessageChannel.ALL), + eq(GroupMessageType.NORMAL), + eq(query.getGroupId()), + any(GroupMessageVO.class) + ); + } + + @Test + void sendMessage_WhenGroupClosed_ShouldThrowException() { + // 准备测试数据 + SendGroupMessageAppQuery query = new SendGroupMessageAppQuery(); + query.setGroupId(1); + query.setContent("测试消息"); + + // 设置模拟行为 + doNothing().when(sensitiveWordService).check(anyString()); + when(groupCommonService.checkGroupStatus(anyInt())).thenReturn(false); + + // 执行测试并验证异常 + assertThrows(BizException.class, () -> + appGroupMessageService.sendMessage(query, mockFrontUser) + ); + } + + @Test + void sendMessage_WhenUserForbidden_ShouldThrowException() { + // 准备测试数据 + SendGroupMessageAppQuery query = new SendGroupMessageAppQuery(); + query.setGroupId(1); + query.setContent("测试消息"); + + // 设置模拟行为 + doNothing().when(sensitiveWordService).check(anyString()); + when(groupCommonService.checkGroupStatus(anyInt())).thenReturn(true); + when(groupCommonService.checkUserForbidden(anyString())).thenReturn(true); + + // 执行测试并验证异常 + assertThrows(BizException.class, () -> + appGroupMessageService.sendMessage(query, mockFrontUser) + ); + } + + @Test + void getMessageList_WithInvalidGroup_ShouldThrowException() { + // 准备测试数据 + ListGroupMessageAppQuery query = new ListGroupMessageAppQuery(); + query.setGroupId(1); + query.setType(QueryGroupMessageType.ADVISOR.value); + + // 设置模拟行为 + when(groupInfoService.getForApp(any(OnlyIdQuery.class), any())).thenReturn(null); + + // 执行测试并验证异常 + assertThrows(BizException.class, () -> + appGroupMessageService.getMessageList(query, mockFrontUser) + ); + } +} \ No newline at end of file