From b2514267bcdd5e889dcee134222c7e951496ae94 Mon Sep 17 00:00:00 2001 From: easonzhu Date: Wed, 5 Feb 2025 12:08:59 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=A8=E4=BA=A4=E4=BA=92=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../advisor/query/ListAdvisorInfoQuery.java | 11 + .../advisor/service/AdvisorInfoService.java | 2 + .../common/config/WebSocketConfig.java | 2 + .../upchina/common/config/cache/CacheKey.java | 9 +- .../common/interceptor/SessionInfo.java | 19 ++ .../interceptor/WebSocketAuthHandler.java | 26 +- .../interceptor/WebSocketSessionHandler.java | 70 ++++- .../com/upchina/common/model/SessionInfo.java | 18 -- .../admin/AdminGroupInfoController.java | 10 +- .../admin/AdminGroupMessageController.java | 26 ++ .../com/upchina/group/entity/GroupInfo.java | 35 ++- .../upchina/group/entity/GroupUserFlow.java | 108 +++++++ .../upchina/group/mapper/GroupInfoMapper.java | 2 +- .../group/mapper/GroupMessageMapper.java | 2 +- .../group/mapper/GroupUserFlowMapper.java | 27 ++ .../upchina/group/query/SaveGroupQuery.java | 51 ++- .../message/GroupMessageStatusQuery.java | 16 + .../admin/AdminGroupMessageService.java | 59 +++- .../service/app/AppGroupMessageService.java | 74 ++--- .../service/common/GroupCacheService.java | 128 +++++++- .../service/common/GroupCommonService.java | 7 - .../service/common/GroupMessageService.java | 27 +- .../java/com/upchina/group/vo/GroupVO.java | 290 +++++++++++++++++- .../com/upchina/video/entity/OnlineUser.java | 52 ++-- .../admin/AdminVideoInteractionService.java | 5 +- src/main/resources/conf/advisorServer.yaml | 2 +- .../app/AppGroupMessageServiceTest.java | 3 - 27 files changed, 919 insertions(+), 162 deletions(-) create mode 100644 src/main/java/com/upchina/common/interceptor/SessionInfo.java delete mode 100644 src/main/java/com/upchina/common/model/SessionInfo.java create mode 100644 src/main/java/com/upchina/group/entity/GroupUserFlow.java create mode 100644 src/main/java/com/upchina/group/mapper/GroupUserFlowMapper.java diff --git a/src/main/java/com/upchina/advisor/query/ListAdvisorInfoQuery.java b/src/main/java/com/upchina/advisor/query/ListAdvisorInfoQuery.java index 92c9cbf..ca6eb74 100644 --- a/src/main/java/com/upchina/advisor/query/ListAdvisorInfoQuery.java +++ b/src/main/java/com/upchina/advisor/query/ListAdvisorInfoQuery.java @@ -20,6 +20,9 @@ public class ListAdvisorInfoQuery extends PageQuery { @ApiModelProperty("营业部id") private String deptId; + @ApiModelProperty("指定营业部") + private String selectDeptId; + @ApiModelProperty("是否过滤掉自己 1:过滤 2:不过滤") private Integer filterSelf; @@ -63,6 +66,14 @@ public class ListAdvisorInfoQuery extends PageQuery { this.deptId = deptId; } + public String getSelectDeptId() { + return selectDeptId; + } + + public void setSelectDeptId(String selectDeptId) { + this.selectDeptId = selectDeptId; + } + public Integer getFilterSelf() { return filterSelf; } diff --git a/src/main/java/com/upchina/advisor/service/AdvisorInfoService.java b/src/main/java/com/upchina/advisor/service/AdvisorInfoService.java index 63253a9..32bf827 100644 --- a/src/main/java/com/upchina/advisor/service/AdvisorInfoService.java +++ b/src/main/java/com/upchina/advisor/service/AdvisorInfoService.java @@ -142,6 +142,7 @@ public class AdvisorInfoService { String showName = query.getShowName(); Integer status = query.getStatus(); String deptId = query.getDeptId(); + String selectDeptId = query.getSelectDeptId(); Integer filterSelf = query.getFilterSelf(); Map deptMap = deptService.getDeptMap(); Dept dept = deptMap.get(deptId); @@ -157,6 +158,7 @@ public class AdvisorInfoService { .like(StrUtil.isNotEmpty(showName), "show_name", showName) .eq(status != null, "status", status) .eq(StrUtil.isNotBlank(deptId), "dept_id", deptId) + .eq(StrUtil.isNotBlank(selectDeptId), "dept_id", deptId) .ne(IsOrNot.IS.value.equals(filterSelf) && backendUserVO.getAdvisorId() != null, "id", backendUserVO.getAdvisorId()); Page page = advisorInfoMapper.selectPage(query.toPage(), wrapper); List list = page.getRecords().stream().map(advisorInfo -> { diff --git a/src/main/java/com/upchina/common/config/WebSocketConfig.java b/src/main/java/com/upchina/common/config/WebSocketConfig.java index 82be585..526de93 100644 --- a/src/main/java/com/upchina/common/config/WebSocketConfig.java +++ b/src/main/java/com/upchina/common/config/WebSocketConfig.java @@ -3,6 +3,7 @@ package com.upchina.common.config; import com.upchina.common.handler.WebSocketErrorHandler; import com.upchina.common.interceptor.WebSocketAuthChannelInterceptor; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @@ -33,6 +34,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { private WebSocketErrorHandler webSocketErrorHandler; @Resource + @Lazy private WebSocketAuthChannelInterceptor clientInChannelInterceptor; /** 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 bbb7ee5..a05fe2a 100644 --- a/src/main/java/com/upchina/common/config/cache/CacheKey.java +++ b/src/main/java/com/upchina/common/config/cache/CacheKey.java @@ -414,10 +414,11 @@ public class CacheKey { } 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|"; + 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|"; + public static final String USER_TOTAL_ONLINE = "user_total_online|"; } } diff --git a/src/main/java/com/upchina/common/interceptor/SessionInfo.java b/src/main/java/com/upchina/common/interceptor/SessionInfo.java new file mode 100644 index 0000000..78b7fd8 --- /dev/null +++ b/src/main/java/com/upchina/common/interceptor/SessionInfo.java @@ -0,0 +1,19 @@ +package com.upchina.common.interceptor; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class SessionInfo { + private String userId; + private Integer productType; + private Integer productId; + private String sessionId; + private String sessionKey; + + public boolean isValid() { + return userId != null && productType != null && productId != null + && sessionId != null && sessionKey != null; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/common/interceptor/WebSocketAuthHandler.java b/src/main/java/com/upchina/common/interceptor/WebSocketAuthHandler.java index 936fc4e..d0541b9 100644 --- a/src/main/java/com/upchina/common/interceptor/WebSocketAuthHandler.java +++ b/src/main/java/com/upchina/common/interceptor/WebSocketAuthHandler.java @@ -1,6 +1,8 @@ package com.upchina.common.interceptor; import cn.hutool.core.util.StrUtil; +import com.google.common.collect.ImmutableSet; +import com.upchina.common.constant.ProductType; import com.upchina.common.filter.AuthFilter; import com.upchina.common.handler.BizException; import com.upchina.common.result.ResponseStatus; @@ -13,6 +15,7 @@ import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Map; +import java.util.Set; @Slf4j @Component @@ -21,6 +24,8 @@ public class WebSocketAuthHandler { @Resource private AuthFilter authFilter; + private Set VALID_PRODUCT_TYPES = ImmutableSet.of(ProductType.VIDEO_SINGLE.value, ProductType.GROUP.value); + public Message handleConnect(Message message, StompHeaderAccessor header) { validateHeaders(header); Map attributes = header.getSessionAttributes(); @@ -31,9 +36,12 @@ public class WebSocketAuthHandler { } String sessionId = header.getFirstNativeHeader("sessionId"); - Integer videoId = getVideoId(header); - - populateAttributes(attributes, userId, sessionId, videoId); + Integer productType = getInteger(header, "productType"); + if (productType == null || !VALID_PRODUCT_TYPES.contains(productType)) { + throw new BizException(ResponseStatus.PARM_ERROR, "产品类型错误" + productType); + } + Integer productId = getInteger(header, "productId"); + populateAttributes(attributes, userId, sessionId, productType, productId); return message; } @@ -71,16 +79,16 @@ public class WebSocketAuthHandler { return null; } - private Integer getVideoId(StompHeaderAccessor header) { - String groupId = header.getFirstNativeHeader("GroupId"); - return StrUtil.isNotEmpty(groupId) ? Integer.parseInt(groupId) : null; + private Integer getInteger(StompHeaderAccessor header, String headerName) { + String value = header.getFirstNativeHeader(headerName); + return StrUtil.isNotEmpty(value) ? Integer.parseInt(value) : null; } - private void populateAttributes(Map attributes, String userId, - String sessionId, Integer videoId) { + private void populateAttributes(Map attributes, String userId, String sessionId, Integer productType, Integer productId) { attributes.put("userId", userId); attributes.put("sessionId", sessionId); - attributes.put("videoId", videoId); + attributes.put("productType", productType); + attributes.put("productId", productId); attributes.put("sessionKey", userId + "-" + sessionId); } } \ No newline at end of file diff --git a/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java b/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java index bb5e453..bb07304 100644 --- a/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java +++ b/src/main/java/com/upchina/common/interceptor/WebSocketSessionHandler.java @@ -2,8 +2,10 @@ package com.upchina.common.interceptor; import com.hazelcast.map.IMap; import com.upchina.common.constant.IsOrNot; -import com.upchina.common.model.SessionInfo; +import com.upchina.common.constant.ProductType; import com.upchina.common.vo.FrontUserVO; +import com.upchina.group.service.common.GroupCacheService; +import com.upchina.group.service.common.GroupMessageService; import com.upchina.video.entity.OnlineUser; import com.upchina.video.service.common.VideoCacheService; import com.upchina.video.service.common.VideoMessageService; @@ -23,6 +25,12 @@ public class WebSocketSessionHandler { @Resource private VideoMessageService videoMessageService; + @Resource + private GroupCacheService groupCacheService; + + @Resource + private GroupMessageService groupMessageService; + public void handleConnect(StompHeaderAccessor header) { Map attributes = header.getSessionAttributes(); FrontUserVO frontUser = (FrontUserVO) attributes.get("frontUser"); @@ -36,8 +44,8 @@ public class WebSocketSessionHandler { } OnlineUser onlineUser = createOnlineUser(sessionInfo, frontUser); - updateOnlineStatus(sessionInfo.getVideoId(), sessionInfo.getSessionKey(), onlineUser); - notifyUserConnect(sessionInfo.getVideoId(), frontUser, onlineUser); + updateOnlineStatus(sessionInfo.getProductType(), sessionInfo.getProductId(), sessionInfo.getSessionKey(), onlineUser); + notifyUserConnect(sessionInfo.getProductType(), sessionInfo.getProductId(), frontUser, onlineUser); } public void handleDisconnect(StompHeaderAccessor header) { @@ -58,7 +66,8 @@ public class WebSocketSessionHandler { private SessionInfo extractSessionInfo(Map attributes) { return SessionInfo.builder() .userId((String) attributes.get("userId")) - .videoId((Integer) attributes.get("videoId")) + .productType((Integer) attributes.get("productType")) + .productId((Integer)attributes.get("productId")) .sessionId((String) attributes.get("sessionId")) .sessionKey((String) attributes.get("sessionKey")) .build(); @@ -66,7 +75,8 @@ public class WebSocketSessionHandler { private OnlineUser createOnlineUser(SessionInfo sessionInfo, FrontUserVO frontUser) { return new OnlineUser( - sessionInfo.getVideoId(), + sessionInfo.getProductType(), + sessionInfo.getProductId(), sessionInfo.getUserId(), frontUser.getUserName(), frontUser.getImgUrl(), @@ -77,23 +87,28 @@ public class WebSocketSessionHandler { ); } - private void updateOnlineStatus(Integer videoId, String sessionKey, OnlineUser onlineUser) { - IMap totalOnlineMap = videoCacheService.getTotalOnlineMap(videoId); - totalOnlineMap.put(sessionKey, onlineUser); + private void updateOnlineStatus(Integer productType, Integer productId, String sessionKey, OnlineUser onlineUser) { + IMap totalOnlineMap = getTotalOnlineMap(productType, productId); + if (totalOnlineMap != null) { + totalOnlineMap.put(sessionKey, onlineUser); + } } - private void notifyUserConnect(Integer videoId, FrontUserVO frontUser, OnlineUser onlineUser) { - videoMessageService.memberNotify(videoId, onlineUser); - videoMessageService.publishEnterMessage(videoId, frontUser); + private void notifyUserConnect(Integer productType, Integer productId, FrontUserVO frontUser, OnlineUser onlineUser) { + memberNotify(productType, productId, onlineUser); + publishEnterMessage(productType, productId, frontUser); } private void handleUserDisconnect(SessionInfo sessionInfo) { - IMap totalOnlineMap = videoCacheService.getTotalOnlineMap(sessionInfo.getVideoId()); - OnlineUser onlineUser = totalOnlineMap.get(sessionInfo.getSessionKey()); + Integer productType = sessionInfo.getProductType(); + Integer productId = sessionInfo.getProductId(); + String sessionKey = sessionInfo.getSessionKey(); + IMap totalOnlineMap = getTotalOnlineMap(productType, productId); + OnlineUser onlineUser = totalOnlineMap.get(sessionKey); if (onlineUser != null) { updateOfflineStatus(onlineUser); - totalOnlineMap.put(sessionInfo.getSessionKey(), onlineUser); - videoMessageService.memberNotify(sessionInfo.getVideoId(), onlineUser); + totalOnlineMap.put(sessionKey, onlineUser); + memberNotify(productType, productId, onlineUser); } } @@ -102,4 +117,29 @@ public class WebSocketSessionHandler { onlineUser.setIsPlay(IsOrNot.NOT.value); onlineUser.setExitTime(LocalDateTime.now()); } + + private IMap getTotalOnlineMap(Integer productType, Integer productId) { + if (ProductType.VIDEO_SINGLE.value.equals(productType)) { + return videoCacheService.getTotalOnlineMap(productId); + } else if (ProductType.GROUP.value.equals(productType)) { + return groupCacheService.getTotalOnlineMap(productId); + } + return null; + } + + private void memberNotify(Integer productType, Integer productId, OnlineUser onlineUser) { + if (ProductType.VIDEO_SINGLE.value.equals(productType)) { + videoMessageService.memberNotify(productId, onlineUser); + } else if (ProductType.GROUP.value.equals(productType)) { + groupMessageService.memberNotify(productId, onlineUser); + } + } + + private void publishEnterMessage(Integer productType, Integer productId, FrontUserVO frontUser) { + if (ProductType.VIDEO_SINGLE.value.equals(productType)) { + videoMessageService.publishEnterMessage(productId, frontUser); + } else if (ProductType.GROUP.value.equals(productType)) { + groupMessageService.publishEnterMessage(productId, frontUser); + } + } } \ No newline at end of file diff --git a/src/main/java/com/upchina/common/model/SessionInfo.java b/src/main/java/com/upchina/common/model/SessionInfo.java deleted file mode 100644 index 45b8901..0000000 --- a/src/main/java/com/upchina/common/model/SessionInfo.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.upchina.common.model; - -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class SessionInfo { - private String userId; - private Integer videoId; - private String sessionId; - private String sessionKey; - - public boolean isValid() { - return userId != null && videoId != null && - sessionId != null && sessionKey != null; - } -} \ No newline at end of file diff --git a/src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java b/src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java index 4b3ebb6..9082b98 100644 --- a/src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java +++ b/src/main/java/com/upchina/group/controller/admin/AdminGroupInfoController.java @@ -35,7 +35,7 @@ public class AdminGroupInfoController { @PostMapping("/admin/group/info/save") @Operation(module = ProductType.GROUP) public CommonResult save(@Validated @RequestBody @ApiParam(required = true) SaveGroupQuery query, - @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { InsertIdVO vo = groupInfoService.save(query, backendUserVO); return CommonResult.success(vo); } @@ -44,7 +44,7 @@ public class AdminGroupInfoController { @PostMapping("/admin/group/info/update") @Operation(module = ProductType.GROUP, statusKey = "event") public CommonResult update(@Validated @RequestBody @ApiParam(required = true) UpdateGroupQuery query, - @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { groupInfoService.update(query, backendUserVO); return CommonResult.success(); } @@ -53,7 +53,7 @@ public class AdminGroupInfoController { @PostMapping("/admin/group/info/updateStatus") @Operation(module = ProductType.GROUP, statusKey = "event") public CommonResult updateStatus(@Validated @RequestBody @ApiParam(required = true) UpdateGroupStatusQuery query, - @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { groupInfoService.updateStatus(query, backendUserVO); return CommonResult.success(); } @@ -61,7 +61,7 @@ public class AdminGroupInfoController { @ApiOperation("后台查询交易圈列表") @PostMapping("/admin/group/info/list") public CommonResult> list(@Validated @RequestBody @ApiParam(required = true) ListGroupQuery query, - @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { Pager page = groupInfoService.list(query, backendUserVO); return CommonResult.success(page); } @@ -69,7 +69,7 @@ public class AdminGroupInfoController { @ApiOperation("后台查询交易圈详情") @PostMapping("/admin/group/info/get") public CommonResult get(@Validated @RequestBody @ApiParam(required = true) OnlyIdQuery query, - @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) { GroupVO vo = groupInfoService.get(query, backendUserVO); return CommonResult.success(vo); } diff --git a/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java b/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java index 939fa54..1917b01 100644 --- a/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java +++ b/src/main/java/com/upchina/group/controller/admin/AdminGroupMessageController.java @@ -99,4 +99,30 @@ public class AdminGroupMessageController { adminGroupMessageService.setShowMemberCount(query, backendUser); return CommonResult.success(); } + + @ApiOperation("设置显示昵称") + @PostMapping("/admin/group/message/setShowNickName") + public CommonResult setShowNickName( + @Validated @RequestBody @ApiParam(required = true) GroupMessageStatusQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUser) { + if (backendUser == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + adminGroupMessageService.setShowNickName(query, backendUser); + return CommonResult.success(); + } + + @ApiOperation("设置先审后发") + @PostMapping("/admin/group/message/setFirstAudit") + public CommonResult setFirstAudit( + @Validated @RequestBody @ApiParam(required = true) GroupMessageStatusQuery query, + @RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUser) { + + if (backendUser == null) { + throw new BizException(ResponseStatus.SESSION_EXPIRY); + } + adminGroupMessageService.setFirstAudit(query, backendUser); + return CommonResult.success(); + + } } \ 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 d916c13..0c7059e 100644 --- a/src/main/java/com/upchina/group/entity/GroupInfo.java +++ b/src/main/java/com/upchina/group/entity/GroupInfo.java @@ -14,7 +14,7 @@ import java.time.LocalDateTime; *

* * @author easonzhu - * @since 2025-01-29 + * @since 2025-02-05 */ public class GroupInfo implements Serializable { @@ -71,11 +71,23 @@ public class GroupInfo implements Serializable { private Integer privateChatStatus; /** - * 显示成员人数 1:开启 2:关闭 + * 显示人数 1:开启 2:关闭 */ @TableField("show_member_count") private Integer showMemberCount; + /** + * 显示昵称 1:开启 2:关闭 + */ + @TableField("show_nick_name") + private Integer showNickName; + + /** + * 先审后发 1:开启 2:关闭 + */ + @TableField("first_audit") + private Integer firstAudit; + /** * 落地页ID */ @@ -244,7 +256,6 @@ public class GroupInfo implements Serializable { public void setInteractiveStatus(Integer interactiveStatus) { this.interactiveStatus = interactiveStatus; } - public Integer getPrivateChatStatus() { return privateChatStatus; } @@ -259,6 +270,20 @@ public class GroupInfo implements Serializable { public void setShowMemberCount(Integer showMemberCount) { this.showMemberCount = showMemberCount; } + public Integer getShowNickName() { + return showNickName; + } + + public void setShowNickName(Integer showNickName) { + this.showNickName = showNickName; + } + public Integer getFirstAudit() { + return firstAudit; + } + + public void setFirstAudit(Integer firstAudit) { + this.firstAudit = firstAudit; + } public Integer getPageId() { return pageId; } @@ -403,7 +428,11 @@ public class GroupInfo implements Serializable { ", applicableUser=" + applicableUser + ", detail=" + detail + ", welcomeMessage=" + welcomeMessage + + ", interactiveStatus=" + interactiveStatus + ", privateChatStatus=" + privateChatStatus + + ", showMemberCount=" + showMemberCount + + ", showNickName=" + showNickName + + ", firstAudit=" + firstAudit + ", pageId=" + pageId + ", originalPrice=" + originalPrice + ", activityPrice=" + activityPrice + diff --git a/src/main/java/com/upchina/group/entity/GroupUserFlow.java b/src/main/java/com/upchina/group/entity/GroupUserFlow.java new file mode 100644 index 0000000..843f5ed --- /dev/null +++ b/src/main/java/com/upchina/group/entity/GroupUserFlow.java @@ -0,0 +1,108 @@ +package com.upchina.group.entity; + +import com.baomidou.mybatisplus.annotation.TableField; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + *

+ * 交易圈在线流水表 + *

+ * + * @author easonzhu + * @since 2025-02-04 + */ +public class GroupUserFlow implements Serializable { + + + /** + * 交易圈id + */ + @TableField("group_id") + private Integer groupId; + + /** + * 用户userId + */ + @TableField("user_id") + private String userId; + + /** + * session所属设备 + */ + @TableField("session_id") + private String sessionId; + + /** + * 时间yyyy-MM-dd HH:ss + */ + private LocalDateTime time; + + /** + * 进入时间 + */ + @TableField("enter_time") + private LocalDateTime enterTime; + + /** + * 退出时间 + */ + @TableField("exit_time") + private LocalDateTime exitTime; + + 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 LocalDateTime getTime() { + return time; + } + + public void setTime(LocalDateTime time) { + this.time = time; + } + public LocalDateTime getEnterTime() { + return enterTime; + } + + public void setEnterTime(LocalDateTime enterTime) { + this.enterTime = enterTime; + } + public LocalDateTime getExitTime() { + return exitTime; + } + + public void setExitTime(LocalDateTime exitTime) { + this.exitTime = exitTime; + } + + @Override + public String toString() { + return "GroupUserFlow{" + + "groupId=" + groupId + + ", userId=" + userId + + ", sessionId=" + sessionId + + ", time=" + time + + ", enterTime=" + enterTime + + ", exitTime=" + exitTime + + "}"; + } +} diff --git a/src/main/java/com/upchina/group/mapper/GroupInfoMapper.java b/src/main/java/com/upchina/group/mapper/GroupInfoMapper.java index 8af6d7e..e7807b0 100644 --- a/src/main/java/com/upchina/group/mapper/GroupInfoMapper.java +++ b/src/main/java/com/upchina/group/mapper/GroupInfoMapper.java @@ -9,7 +9,7 @@ import com.upchina.group.entity.GroupInfo; *

* * @author easonzhu - * @since 2025-01-29 + * @since 2025-02-05 */ public interface GroupInfoMapper extends BaseMapper { diff --git a/src/main/java/com/upchina/group/mapper/GroupMessageMapper.java b/src/main/java/com/upchina/group/mapper/GroupMessageMapper.java index 82f115a..cc4c870 100644 --- a/src/main/java/com/upchina/group/mapper/GroupMessageMapper.java +++ b/src/main/java/com/upchina/group/mapper/GroupMessageMapper.java @@ -1,7 +1,7 @@ package com.upchina.group.mapper; -import com.upchina.group.entity.GroupMessage; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.upchina.group.entity.GroupMessage; /** *

diff --git a/src/main/java/com/upchina/group/mapper/GroupUserFlowMapper.java b/src/main/java/com/upchina/group/mapper/GroupUserFlowMapper.java new file mode 100644 index 0000000..4f5eb57 --- /dev/null +++ b/src/main/java/com/upchina/group/mapper/GroupUserFlowMapper.java @@ -0,0 +1,27 @@ +package com.upchina.group.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.upchina.group.entity.GroupUserFlow; +import com.upchina.video.entity.OnlineUser; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + *

+ * 交易圈在线流水表 Mapper 接口 + *

+ * + * @author easonzhu + * @since 2025-02-04 + */ +public interface GroupUserFlowMapper extends BaseMapper { + + @Select("SELECT h.group_id, h.user_id, h.session_id, 2 as is_online " + + "FROM group_user_flow h " + + "${ew.customSqlSegment}") + List loadHis(@Param(Constants.WRAPPER) LambdaQueryWrapper wrapper); +} diff --git a/src/main/java/com/upchina/group/query/SaveGroupQuery.java b/src/main/java/com/upchina/group/query/SaveGroupQuery.java index cd90b3c..83945c6 100644 --- a/src/main/java/com/upchina/group/query/SaveGroupQuery.java +++ b/src/main/java/com/upchina/group/query/SaveGroupQuery.java @@ -34,10 +34,21 @@ public class SaveGroupQuery { @ApiModelProperty("欢迎语") private String welcomeMessage; + @ApiModelProperty("互动状态 1:开启 2:关闭") + private Integer interactiveStatus; + @ApiModelProperty("私聊状态 1:开启 2:关闭") - @NotNull private Integer privateChatStatus; + @ApiModelProperty("显示人数 1:开启 2:关闭") + private Integer showMemberCount; + + @ApiModelProperty("显示昵称 1:开启 2:关闭") + private Integer showNickName; + + @ApiModelProperty("先审后发 1:开启 2:关闭") + private Integer firstAudit; + @ApiModelProperty("落地页ID") private Integer pageId; @@ -76,7 +87,11 @@ public class SaveGroupQuery { groupInfo.setApplicableUser(applicableUser); groupInfo.setDetail(detail); groupInfo.setWelcomeMessage(welcomeMessage); + groupInfo.setInteractiveStatus(interactiveStatus); groupInfo.setPrivateChatStatus(privateChatStatus); + groupInfo.setShowMemberCount(showMemberCount); + groupInfo.setShowNickName(showNickName); + groupInfo.setFirstAudit(firstAudit); groupInfo.setPageId(pageId); groupInfo.setOriginalPrice(originalPrice); groupInfo.setActivityPrice(activityPrice); @@ -141,6 +156,14 @@ public class SaveGroupQuery { this.welcomeMessage = welcomeMessage; } + public Integer getInteractiveStatus() { + return interactiveStatus; + } + + public void setInteractiveStatus(Integer interactiveStatus) { + this.interactiveStatus = interactiveStatus; + } + public Integer getPrivateChatStatus() { return privateChatStatus; } @@ -149,6 +172,30 @@ public class SaveGroupQuery { this.privateChatStatus = privateChatStatus; } + public Integer getShowMemberCount() { + return showMemberCount; + } + + public void setShowMemberCount(Integer showMemberCount) { + this.showMemberCount = showMemberCount; + } + + public Integer getShowNickName() { + return showNickName; + } + + public void setShowNickName(Integer showNickName) { + this.showNickName = showNickName; + } + + public Integer getFirstAudit() { + return firstAudit; + } + + public void setFirstAudit(Integer firstAudit) { + this.firstAudit = firstAudit; + } + public Integer getPageId() { return pageId; } @@ -220,4 +267,6 @@ public class SaveGroupQuery { public void setWechatWorkId(Integer wechatWorkId) { this.wechatWorkId = wechatWorkId; } + + } \ 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 index 20c7773..3da6c8d 100644 --- a/src/main/java/com/upchina/group/query/message/GroupMessageStatusQuery.java +++ b/src/main/java/com/upchina/group/query/message/GroupMessageStatusQuery.java @@ -41,6 +41,22 @@ public class GroupMessageStatusQuery { return groupInfo; } + public GroupInfo toShowNickNamePO() { + GroupInfo groupInfo = new GroupInfo(); + groupInfo.setId(groupId); + groupInfo.setShowNickName(status); + groupInfo.setUpdateTime(LocalDateTime.now()); + return groupInfo; + } + + public GroupInfo toFirstAuditPO() { + GroupInfo groupInfo = new GroupInfo(); + groupInfo.setId(groupId); + groupInfo.setFirstAudit(status); + groupInfo.setUpdateTime(LocalDateTime.now()); + return groupInfo; + } + public Integer getGroupId() { return groupId; } diff --git a/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java b/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java index 0a9a512..76fdb3c 100644 --- a/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java +++ b/src/main/java/com/upchina/group/service/admin/AdminGroupMessageService.java @@ -47,9 +47,16 @@ public class AdminGroupMessageService { @Transactional(rollbackFor = Exception.class) public OnlyIdVO sendAdvisorMessage(SendGroupMessageAdminQuery query, BackendUserVO backendUser) { + GroupInfo group = groupInfoMapper.selectById(query.getGroupId()); + if (group == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "交易圈不存在"); + } GroupMessage message = query.toPO(backendUser); groupMessageMapper.insert(message); - groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.RECOMMEND_PRODUCT, query.getGroupId(), message.getContent()); + + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.NORMAL, query.getGroupId(), message.getContent()); + groupCacheService.addMessage(message); + return new OnlyIdVO(message.getId()); } @@ -59,6 +66,11 @@ public class AdminGroupMessageService { if (groupMessageInDB == null) { throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR); } + Integer groupId = groupMessageInDB.getGroupId(); + GroupInfo group = groupInfoMapper.selectById(groupId); + if (group == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "交易圈不存在"); + } // 状态机扭转 GroupMessageStatus dbStatus = GroupMessageStatus.fromValue(groupMessageInDB.getStatus()); @@ -66,7 +78,15 @@ public class AdminGroupMessageService { GroupMessage groupMessage = query.toPO(targetStatus, backendUserVO); groupMessageMapper.updateById(groupMessage); - groupCacheService.clearMessageCache(query.getId(), groupMessage.getGroupId()); + + if (GroupMessageStatus.DELETED.equals(targetStatus)) { + groupCacheService.removeMessage(groupMessageInDB); + } else if (GroupMessageStatus.AUDITED.equals(targetStatus)) { + if (!IsOrNot.IS.value.equals(group.getFirstAudit())) { + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.NORMAL, groupId, groupMessageInDB.getContent()); + groupCacheService.addMessage(groupMessageInDB); + } + } } public AppPager getMessageList(ListGroupMessageQuery query, BackendUserVO backendUser) { @@ -134,7 +154,7 @@ public class AdminGroupMessageService { GroupInfo group = query.toInteractivePO(); groupInfoMapper.updateById(group); - groupMessageService.publishGroupMessage(GroupMessageChannel.APP, GroupMessageType.OPEN_INTERACTIVE, groupId, query.getStatus()); + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.OPEN_INTERACTIVE, groupId, query.getStatus()); } /** @@ -150,7 +170,7 @@ public class AdminGroupMessageService { GroupInfo group = query.toPrivatePO(); groupInfoMapper.updateById(group); - groupMessageService.publishGroupMessage(GroupMessageChannel.APP, GroupMessageType.OPEN_PRIVATE_CHAT, groupId, query.getStatus()); + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.OPEN_PRIVATE_CHAT, groupId, query.getStatus()); } /** @@ -163,10 +183,37 @@ public class AdminGroupMessageService { throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR); } - GroupInfo group = query.toPrivatePO(); + GroupInfo group = query.toShowMemberCountPO(); groupInfoMapper.updateById(group); - groupMessageService.publishGroupMessage(GroupMessageChannel.APP, GroupMessageType.SHOW_GROUP_MEMBER_COUNT, groupId, query.getStatus()); + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.SHOW_GROUP_MEMBER_COUNT, groupId, query.getStatus()); + } + + public void setShowNickName(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.toShowNickNamePO(); + groupInfoMapper.updateById(group); + + + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.SHOW_FULL_NICKNAME, groupId, query.getStatus()); + } + + public void setFirstAudit(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.toFirstAuditPO(); + groupInfoMapper.updateById(group); + + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.PRE_CHECK_SEND, 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 index 49ac381..f3f6cfa 100644 --- a/src/main/java/com/upchina/group/service/app/AppGroupMessageService.java +++ b/src/main/java/com/upchina/group/service/app/AppGroupMessageService.java @@ -1,28 +1,21 @@ 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; @@ -32,7 +25,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NavigableSet; @Service public class AppGroupMessageService { @@ -55,15 +51,6 @@ public class AppGroupMessageService { @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(); @@ -79,7 +66,7 @@ public class AppGroupMessageService { // TODO 验证权限 groupCommonService.validateUserPermission(userId, groupVO); QueryGroupMessageType msgType = QueryGroupMessageType.fromValue(type); - NavigableSet sortedSet = this.getMsgIdSet(groupId, userId, msgType); + NavigableSet sortedSet = groupCacheService.getMessageIdSet(groupId, userId, msgType); if (lastId != null && lastId != 0) { sortedSet = sortedSet.tailSet(lastId, false); } @@ -96,30 +83,6 @@ public class AppGroupMessageService { 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(); @@ -128,23 +91,30 @@ public class AppGroupMessageService { sensitiveWordService.check(content); // 检查交易圈状态 - if (!groupCommonService.checkGroupStatus(groupId)) { - throw new BizException(ResponseStatus.STATUS_ERROR, "交易圈未开启"); + GroupVO groupVO = groupInfoService.getForApp(new OnlyIdQuery(groupId), null); + if (groupVO == null) { + throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "交易圈不存在"); + } + if (!IsOrNot.IS.value.equals(groupVO.getInteractiveStatus())) { + throw new BizException(ResponseStatus.MESSAGE_PERMISSION_ERROR, "互动已关闭"); } - // 检查用户是否被禁言 - if (groupCommonService.checkUserForbidden(frontUser.getUserId())) { - throw new BizException(ResponseStatus.COMMENT_BLACK_USER_ERROR, "您已被禁言"); - } + // TODO 禁言check +// 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); - + + if (!IsOrNot.IS.value.equals(groupVO.getFirstAudit())) { + groupMessageService.publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.NORMAL, groupId, vo); + groupCacheService.addMessage(message); + } + return vo; } diff --git a/src/main/java/com/upchina/group/service/common/GroupCacheService.java b/src/main/java/com/upchina/group/service/common/GroupCacheService.java index 0a87a63..ea55b16 100644 --- a/src/main/java/com/upchina/group/service/common/GroupCacheService.java +++ b/src/main/java/com/upchina/group/service/common/GroupCacheService.java @@ -1,14 +1,31 @@ package com.upchina.group.service.common; +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.result.ResponseStatus; import com.upchina.common.service.CacheService; +import com.upchina.group.constant.GroupInteractiveType; +import com.upchina.group.constant.GroupMessageUserType; +import com.upchina.group.constant.QueryGroupMessageType; import com.upchina.group.entity.GroupMessage; +import com.upchina.group.entity.GroupUserFlow; import com.upchina.group.mapper.GroupMessageMapper; +import com.upchina.group.mapper.GroupUserFlowMapper; import com.upchina.group.vo.message.GroupMessageVO; +import com.upchina.video.entity.OnlineUser; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.util.Comparator; +import java.util.List; +import java.util.NavigableSet; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collectors; @Service public class GroupCacheService { @@ -19,18 +36,99 @@ public class GroupCacheService { @Resource private GroupMessageMapper groupMessageMapper; + @Resource + private GroupUserFlowMapper groupUserFlowMapper; + @Resource private IMap groupCache; - public void clearMessageCache(Integer msgId, Integer groupId) { - if (msgId != null) { - groupCache.remove(CacheKey.GroupKey.GROUP_MESSAGE_DETAIL + msgId); +// 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 NavigableSet getMessageIdSet(Integer groupId, String userId, QueryGroupMessageType type) { + if (QueryGroupMessageType.CUSTOMER.equals(type)) { + throw new BizException(ResponseStatus.PARM_ERROR, "查询类型错误"); } - if (groupId != null) { - groupCache.remove(CacheKey.GroupKey.GROUP_MESSAGE_LIST + groupId); + String cacheKey = buildMessageIdSetKey(userId, type); + return cacheService.get(groupCache, cacheKey, () -> { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .select(GroupMessage::getId) + .eq(GroupMessage::getGroupId, groupId) + .in(QueryGroupMessageType.ADVISOR.equals(type), GroupMessage::getUserType, GroupMessageUserType.ADVISOR.value, GroupMessageUserType.ASSISTANT.value) + .eq(QueryGroupMessageType.CUSTOMER.equals(type), GroupMessage::getUserType, GroupMessageUserType.CUSTOMER.value) + .eq(QueryGroupMessageType.SELECTED.equals(type), GroupMessage::getIsRecommend, IsOrNot.IS.value) + .eq(QueryGroupMessageType.PRIVATE.equals(type), 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; + }); + } + + public void removeMessage(GroupMessage message) { + String privateUserId = message.getPrivateUserId(); + Integer groupId = message.getGroupId(); + Integer messageId = message.getId(); + Integer interactiveType = message.getInteractiveType(); + Integer userType = message.getUserType(); + Integer isRecommend = message.getIsRecommend(); + if (GroupInteractiveType.GROUP.value.equals(interactiveType)) { + removeMessage(groupId, null, QueryGroupMessageType.ALL, messageId); + if (GroupMessageUserType.ADVISOR.value.equals(userType)) { + removeMessage(groupId, null, QueryGroupMessageType.ADVISOR, messageId); + } else if (GroupMessageUserType.CUSTOMER.value.equals(userType)) { + removeMessage(groupId, null, QueryGroupMessageType.CUSTOMER, messageId); + } + if (IsOrNot.IS.value.equals(isRecommend)) { + removeMessage(groupId, null, QueryGroupMessageType.SELECTED, messageId); + } + } else if (GroupInteractiveType.PRIVATE.value.equals(interactiveType)) { + removeMessage(groupId, privateUserId, QueryGroupMessageType.PRIVATE, messageId); } } + private void removeMessage(Integer groupId, String userId, QueryGroupMessageType type, Integer messageId) { + NavigableSet set = getMessageIdSet(groupId, userId, type); + set.remove(messageId); + String cacheKey = buildMessageIdSetKey(userId, type); + groupCache.put(cacheKey, set); + } + + public void addMessage(GroupMessage message) { + String privateUserId = message.getPrivateUserId(); + Integer groupId = message.getGroupId(); + Integer messageId = message.getId(); + Integer interactiveType = message.getInteractiveType(); + Integer userType = message.getUserType(); + Integer isRecommend = message.getIsRecommend(); + if (GroupInteractiveType.GROUP.value.equals(interactiveType)) { + addMessage(groupId, null, QueryGroupMessageType.ALL, messageId); + if (GroupMessageUserType.ADVISOR.value.equals(userType)) { + addMessage(groupId, null, QueryGroupMessageType.ADVISOR, messageId); + } else if (GroupMessageUserType.CUSTOMER.value.equals(userType)) { + addMessage(groupId, null, QueryGroupMessageType.CUSTOMER, messageId); + } + if (IsOrNot.IS.value.equals(isRecommend)) { + addMessage(groupId, null, QueryGroupMessageType.SELECTED, messageId); + } + } else if (GroupInteractiveType.PRIVATE.value.equals(interactiveType)) { + addMessage(groupId, privateUserId, QueryGroupMessageType.PRIVATE, messageId); + } + } + + private void addMessage(Integer groupId, String userId, QueryGroupMessageType type, Integer messageId) { + NavigableSet set = getMessageIdSet(groupId, userId, type); + set.add(messageId); + String cacheKey = buildMessageIdSetKey(userId, type); + groupCache.put(cacheKey, set); + } + public GroupMessageVO getMessage(Integer messageId) { return cacheService.get(groupCache, CacheKey.GroupKey.GROUP_MESSAGE_DETAIL + messageId, () -> { @@ -41,4 +139,24 @@ public class GroupCacheService { return new GroupMessageVO(message); }); } + + public IMap getTotalOnlineMap(Integer groupId) { + return cacheService.getMap(CacheKey.GroupKey.USER_TOTAL_ONLINE + groupId, () -> { + synchronized (GroupCacheService.class) { + List hisList = groupUserFlowMapper.loadHis(Wrappers.lambdaQuery() + .eq(GroupUserFlow::getGroupId, groupId) + .groupBy(GroupUserFlow::getUserId, GroupUserFlow::getSessionId)); + return hisList.stream() + .collect(Collectors.toMap(h -> h.getUserId() + "-" + h.getSessionId(), Function.identity())); + } + }); + } + + private static String buildMessageIdSetKey(String userId, QueryGroupMessageType type) { + String cacheKey = CacheKey.GroupKey.GROUP_MESSAGE_LIST + type.value; + if (QueryGroupMessageType.PRIVATE.equals(type)) { + cacheKey += "|" + userId; + } + return cacheKey; + } } \ 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 index fb7a44f..8ac0447 100644 --- a/src/main/java/com/upchina/group/service/common/GroupCommonService.java +++ b/src/main/java/com/upchina/group/service/common/GroupCommonService.java @@ -1,7 +1,5 @@ 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; @@ -15,11 +13,6 @@ 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; diff --git a/src/main/java/com/upchina/group/service/common/GroupMessageService.java b/src/main/java/com/upchina/group/service/common/GroupMessageService.java index d6d7966..033a670 100644 --- a/src/main/java/com/upchina/group/service/common/GroupMessageService.java +++ b/src/main/java/com/upchina/group/service/common/GroupMessageService.java @@ -3,28 +3,33 @@ 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.common.vo.FrontUserVO; 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 com.upchina.video.entity.OnlineUser; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Map; @Service public class GroupMessageService { - @Resource - private GroupMessageMapper groupMessageMapper; - @Resource private SimpMessagingTemplate simpMessagingTemplate; @Resource private HazelcastInstance hazelcastInstance; + private final Map lastEnterMessageTimeMap = new HashMap<>(); + + // 进入消息频率控制(本节点) + private final long enterMessageInterval = 2000; + public ITopic> adminGroupTopic = null; public ITopic> adminPrivateTopic = null; @@ -124,4 +129,18 @@ public class GroupMessageService { } } + public void memberNotify(Integer groupId, OnlineUser onlineUser) { + publishGroupMessage(GroupMessageChannel.ADMIN, GroupMessageType.USER_ONLINE_OFFLINE, groupId, onlineUser); + } + + public void publishEnterMessage(Integer groupId, FrontUserVO frontUser) { + Long lastEnterMessageTime = lastEnterMessageTimeMap.get(groupId); + long now = System.currentTimeMillis(); + if (lastEnterMessageTime != null && now - lastEnterMessageTime < enterMessageInterval) { + // 频率控制,如果周期内推送过就不推送该次消息 + return; + } + publishGroupMessage(GroupMessageChannel.ALL, GroupMessageType.ENTER_GROUP, groupId, frontUser.getUserName()); + lastEnterMessageTimeMap.put(groupId, now); + } } \ No newline at end of file diff --git a/src/main/java/com/upchina/group/vo/GroupVO.java b/src/main/java/com/upchina/group/vo/GroupVO.java index fde0388..3c2f6bb 100644 --- a/src/main/java/com/upchina/group/vo/GroupVO.java +++ b/src/main/java/com/upchina/group/vo/GroupVO.java @@ -6,14 +6,10 @@ import com.upchina.course.vo.PageVO; import com.upchina.group.entity.GroupInfo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import lombok.Data; -import lombok.NoArgsConstructor; import java.math.BigDecimal; import java.time.LocalDateTime; -@Data -@NoArgsConstructor @ApiModel("交易圈详情") public class GroupVO { @@ -41,9 +37,21 @@ public class GroupVO { @ApiModelProperty("欢迎语") private String welcomeMessage; - @ApiModelProperty("私聊状态") + @ApiModelProperty("互动状态 1:开启 2:关闭") + private Integer interactiveStatus; + + @ApiModelProperty("私聊状态 1:开启 2:关闭") private Integer privateChatStatus; + @ApiModelProperty("显示人数 1:开启 2:关闭") + private Integer showMemberCount; + + @ApiModelProperty("显示昵称 1:开启 2:关闭") + private Integer showNickName; + + @ApiModelProperty("先审后发 1:开启 2:关闭") + private Integer firstAudit; + @ApiModelProperty("落地页") private PageVO page; @@ -104,6 +112,8 @@ public class GroupVO { @ApiModelProperty("权限结果") private AuthResultVO authResultVo; + public GroupVO() {} + public GroupVO(GroupInfo groupInfo, AdvisorBasic advisor, String createUserName, String auditUserName, PageVO page) { this.id = groupInfo.getId(); this.advisorId = groupInfo.getAdvisorId(); @@ -114,6 +124,10 @@ public class GroupVO { this.detail = groupInfo.getDetail(); this.welcomeMessage = groupInfo.getWelcomeMessage(); this.privateChatStatus = groupInfo.getPrivateChatStatus(); + this.interactiveStatus = groupInfo.getInteractiveStatus(); + this.showMemberCount = groupInfo.getShowMemberCount(); + this.showNickName = groupInfo.getShowNickName(); + this.firstAudit = groupInfo.getFirstAudit(); this.page = page; this.originalPrice = groupInfo.getOriginalPrice(); this.activityPrice = groupInfo.getActivityPrice(); @@ -134,4 +148,268 @@ public class GroupVO { this.mainPageText = groupInfo.getMainPageText(); this.wechatWorkId = groupInfo.getWechatWorkId(); } -} \ No newline at end of file + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getAdvisorId() { + return advisorId; + } + + public void setAdvisorId(Integer advisorId) { + this.advisorId = advisorId; + } + + public AdvisorBasic getAdvisor() { + return advisor; + } + + public void setAdvisor(AdvisorBasic advisor) { + this.advisor = advisor; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getApplicableUser() { + return applicableUser; + } + + public void setApplicableUser(String applicableUser) { + this.applicableUser = applicableUser; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + + public String getWelcomeMessage() { + return welcomeMessage; + } + + 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; + } + + public void setPrivateChatStatus(Integer privateChatStatus) { + this.privateChatStatus = privateChatStatus; + } + + public Integer getShowMemberCount() { + return showMemberCount; + } + + public void setShowMemberCount(Integer showMemberCount) { + this.showMemberCount = showMemberCount; + } + + public Integer getShowNickName() { + return showNickName; + } + + public void setShowNickName(Integer showNickName) { + this.showNickName = showNickName; + } + + public Integer getFirstAudit() { + return firstAudit; + } + + public void setFirstAudit(Integer firstAudit) { + this.firstAudit = firstAudit; + } + + public PageVO getPage() { + return page; + } + + public void setPage(PageVO page) { + this.page = page; + } + + public BigDecimal getOriginalPrice() { + return originalPrice; + } + + public void setOriginalPrice(BigDecimal originalPrice) { + this.originalPrice = originalPrice; + } + + public BigDecimal getActivityPrice() { + return activityPrice; + } + + public void setActivityPrice(BigDecimal activityPrice) { + this.activityPrice = activityPrice; + } + + public String getPaymentUrl() { + return paymentUrl; + } + + public void setPaymentUrl(String paymentUrl) { + this.paymentUrl = paymentUrl; + } + + public String getAuthorityId() { + return authorityId; + } + + public void setAuthorityId(String authorityId) { + this.authorityId = authorityId; + } + + public Integer getMemberLimit() { + return memberLimit; + } + + public void setMemberLimit(Integer memberLimit) { + this.memberLimit = memberLimit; + } + + public String getCoverImage() { + return coverImage; + } + + public void setCoverImage(String coverImage) { + this.coverImage = coverImage; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public Integer getRiskLevel() { + return riskLevel; + } + + public void setRiskLevel(Integer riskLevel) { + this.riskLevel = riskLevel; + } + + public LocalDateTime getCreateTime() { + return createTime; + } + + public void setCreateTime(LocalDateTime createTime) { + this.createTime = createTime; + } + + public LocalDateTime getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(LocalDateTime updateTime) { + this.updateTime = updateTime; + } + + public LocalDateTime getAuditTime() { + return auditTime; + } + + public void setAuditTime(LocalDateTime auditTime) { + this.auditTime = auditTime; + } + + public String getCreateUserName() { + return createUserName; + } + + public void setCreateUserName(String createUserName) { + this.createUserName = createUserName; + } + + public String getAuditUserName() { + return auditUserName; + } + + public void setAuditUserName(String auditUserName) { + this.auditUserName = auditUserName; + } + + public Integer getIsRecommend() { + return isRecommend; + } + + public void setIsRecommend(Integer isRecommend) { + this.isRecommend = isRecommend; + } + + public Integer getIsDisplay() { + return isDisplay; + } + + public void setIsDisplay(Integer isDisplay) { + this.isDisplay = isDisplay; + } + + public String getMainPageText() { + return mainPageText; + } + + public void setMainPageText(String mainPageText) { + this.mainPageText = mainPageText; + } + + public Integer getWechatWorkId() { + return wechatWorkId; + } + + public void setWechatWorkId(Integer wechatWorkId) { + this.wechatWorkId = wechatWorkId; + } + + public AuthResultVO getAuthResultVo() { + return authResultVo; + } + + public void setAuthResultVo(AuthResultVO authResultVo) { + this.authResultVo = authResultVo; + } +} \ No newline at end of file diff --git a/src/main/java/com/upchina/video/entity/OnlineUser.java b/src/main/java/com/upchina/video/entity/OnlineUser.java index 3dda72e..cb9e39b 100644 --- a/src/main/java/com/upchina/video/entity/OnlineUser.java +++ b/src/main/java/com/upchina/video/entity/OnlineUser.java @@ -9,8 +9,11 @@ public class OnlineUser implements Serializable { private static final long serialVersionUID = 1L; - @ApiModelProperty("视频id") - private Integer videoId; + @ApiModelProperty("产品类型 3:直播 41:交易圈") + private Integer productType; + + @ApiModelProperty("产品ID") + private Integer productId; @ApiModelProperty("用户id") private String userId; @@ -39,8 +42,9 @@ public class OnlineUser implements Serializable { public OnlineUser() { } - public OnlineUser(Integer videoId, String userId, String userName, String img, String sessionId, Integer isOnline, Integer isPlay, LocalDateTime createTime) { - this.videoId = videoId; + public OnlineUser(Integer productType, Integer productId, String userId, String userName, String img, String sessionId, Integer isOnline, Integer isPlay, LocalDateTime createTime) { + this.productType = productType; + this.productId = productId; this.userId = userId; this.userName = userName; this.img = img; @@ -50,6 +54,30 @@ public class OnlineUser implements Serializable { this.createTime = createTime; } + public Integer getProductType() { + return productType; + } + + public void setProductType(Integer productType) { + this.productType = productType; + } + + public Integer getProductId() { + return productId; + } + + public void setProductId(Integer productId) { + this.productId = productId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + public String getUserName() { return userName; } @@ -66,22 +94,6 @@ public class OnlineUser implements Serializable { this.img = img; } - public Integer getVideoId() { - return videoId; - } - - public void setVideoId(Integer videoId) { - this.videoId = videoId; - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } - public String getSessionId() { return sessionId; } diff --git a/src/main/java/com/upchina/video/service/admin/AdminVideoInteractionService.java b/src/main/java/com/upchina/video/service/admin/AdminVideoInteractionService.java index a3d071f..91ca0bb 100644 --- a/src/main/java/com/upchina/video/service/admin/AdminVideoInteractionService.java +++ b/src/main/java/com/upchina/video/service/admin/AdminVideoInteractionService.java @@ -563,7 +563,10 @@ public class AdminVideoInteractionService { @Transactional(rollbackFor = Exception.class) public void saveVideoUserDataToDB() { LocalDateTime now = LocalDateTime.now(); - List videoLives = videoLiveMapper.selectList(Wrappers.emptyWrapper()); + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .select(VideoLive::getId, VideoLive::getLiveStatus) + .ge(VideoLive::getStartTime, now.plusDays(-7)); + List videoLives = videoLiveMapper.selectList(wrapper); if (CollUtil.isEmpty(videoLives)) { return; } diff --git a/src/main/resources/conf/advisorServer.yaml b/src/main/resources/conf/advisorServer.yaml index 43b5970..707a857 100644 --- a/src/main/resources/conf/advisorServer.yaml +++ b/src/main/resources/conf/advisorServer.yaml @@ -13,7 +13,7 @@ file: hazelcast: members: 127.0.0.1:5709 #缓存集群的ip端口号 serverPort: 5709 #自己作为缓存服务器监听的端口号 -scheduledEnable: false +scheduledEnable: true cron: saveVideoCount: "10 * * * * ?" #从cache刷新视频播放量到DB 每分钟的第10s执行 refreshTranscodeStatus: "2 3/5 * * * ?" #从腾讯云拉取录播上传视频信息更新到DB diff --git a/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java b/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java index 74f0eaa..5c377cc 100644 --- a/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java +++ b/src/test/java/com/upchina/group/service/app/AppGroupMessageServiceTest.java @@ -145,7 +145,6 @@ class AppGroupMessageServiceTest { // 设置模拟行为 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); @@ -171,7 +170,6 @@ class AppGroupMessageServiceTest { // 设置模拟行为 doNothing().when(sensitiveWordService).check(anyString()); - when(groupCommonService.checkGroupStatus(anyInt())).thenReturn(false); // 执行测试并验证异常 assertThrows(BizException.class, () -> @@ -188,7 +186,6 @@ class AppGroupMessageServiceTest { // 设置模拟行为 doNothing().when(sensitiveWordService).check(anyString()); - when(groupCommonService.checkGroupStatus(anyInt())).thenReturn(true); when(groupCommonService.checkUserForbidden(anyString())).thenReturn(true); // 执行测试并验证异常