Compare commits
2 Commits
53a6dbea2d
...
f1b0cba4bd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1b0cba4bd | ||
|
|
fdc4a4aeb9 |
22
pom.xml
22
pom.xml
@ -180,31 +180,9 @@
|
||||
<version>1.7.36</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.8.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>4.5.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
<configuration>
|
||||
<skipTests>true</skipTests>
|
||||
<testFailureIgnore>true</testFailureIgnore>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
package com.upchina.app.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.hazelcast.org.apache.calcite.util.Holder;
|
||||
import com.upchina.common.util.LoggerUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class CouponService {
|
||||
|
||||
private final int pageSize = 100;
|
||||
|
||||
/**
|
||||
* 查询视频优惠券
|
||||
*
|
||||
* @param videoId 视频ID
|
||||
* @return 优惠券Map key: 用户ID value: 优惠券列表
|
||||
*/
|
||||
public Map<String, List<Object>> queryVideoCoupon(Integer videoId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户优惠券
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 优惠券Map key: 视频ID value: 优惠券列表
|
||||
*/
|
||||
public Map<Integer, List<Object>> queryUserCoupon(String userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int queryCouponCount(Integer videoId) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private List<UserCoupon> queryCoupon(Integer videoId, String userId) {
|
||||
List<UserCoupon> results = new ArrayList<>();
|
||||
// 先查第一页,获取总数
|
||||
LiveCouponReq req = new LiveCouponReq();
|
||||
if (videoId != null && videoId > 0) {
|
||||
req.setLiveId(videoId);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(userId)) {
|
||||
req.setFundId(userId);
|
||||
}
|
||||
req.setCurrent(1);
|
||||
req.setSize(pageSize);
|
||||
Holder<LiveCouponRsp> holder = new Holder<>();
|
||||
orderSystemPrx.getLiveCoupons(req, holder);
|
||||
LiveCouponRsp firstPage = holder.getValue();
|
||||
results.addAll(firstPage.getUserCoupon());
|
||||
// 计算总页数,如果总页数超过1,再查剩下的页
|
||||
long total = firstPage.getTotal();
|
||||
int totalPage = total % pageSize == 0 ? (int) (total / pageSize) : (int) (total / pageSize) + 1;
|
||||
LoggerUtil.info("优惠券总数:" + total, "页数:" + totalPage);
|
||||
for (int i = 2; i <= totalPage; i++) {
|
||||
req.setCurrent(i);
|
||||
holder = new Holder<>();
|
||||
orderSystemPrx.getLiveCoupons(req, holder);
|
||||
LiveCouponRsp page = holder.getValue();
|
||||
results.addAll(page.getUserCoupon());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
package com.upchina.common.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.auth0.jwt.exceptions.TokenExpiredException;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.util.LoggerUtil;
|
||||
import com.upchina.common.util.RequestIdUtil;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.common.vo.FrontUserVO;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
public class WebSocketAspect {
|
||||
|
||||
@Pointcut("@annotation(org.springframework.messaging.handler.annotation.MessageMapping)")
|
||||
private void pointcut() {
|
||||
}
|
||||
|
||||
@Before("pointcut()")
|
||||
public void before(JoinPoint joinPoint) {
|
||||
RequestIdUtil.setValue();
|
||||
Object[] args = joinPoint.getArgs();
|
||||
List<Object> arguments = new ArrayList<>();
|
||||
String destination = "";
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof SimpMessageHeaderAccessor) {
|
||||
SimpMessageHeaderAccessor accessor = (SimpMessageHeaderAccessor) arg;
|
||||
destination = accessor.getDestination();
|
||||
Map<String, Object> attributes = accessor.getSessionAttributes();
|
||||
BackendUserVO backendUser = (BackendUserVO) attributes.get("backendUser");
|
||||
FrontUserVO frontUser = (FrontUserVO) attributes.get("frontUser");
|
||||
if (backendUser != null) {
|
||||
arguments.add(backendUser);
|
||||
}
|
||||
if (frontUser != null) {
|
||||
arguments.add(frontUser);
|
||||
}
|
||||
} else {
|
||||
arguments.add(arg);
|
||||
}
|
||||
}
|
||||
LoggerUtil.info(String.format("%s:param:%s", destination, JSONObject.toJSONString(arguments)));
|
||||
}
|
||||
|
||||
@AfterThrowing(value = "pointcut()", throwing = "ex")
|
||||
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
|
||||
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
|
||||
Class<?> clazz = methodSignature.getDeclaringType();
|
||||
Object[] args = joinPoint.getArgs();
|
||||
Optional<Object> accessorOptional = Arrays.stream(args).filter(obj -> obj instanceof SimpMessageHeaderAccessor).findFirst();
|
||||
if (!accessorOptional.isPresent()) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "WebSocket异常拦截未检查到accessor,请检查:" + clazz.getSimpleName() + "." + methodSignature.getMethod().getName() + "的入参");
|
||||
}
|
||||
SimpMessageHeaderAccessor accessor = (SimpMessageHeaderAccessor) accessorOptional.get();
|
||||
Map<String, Object> attributes = accessor.getSessionAttributes();
|
||||
// String sessionId = (String) attributes.get("sessionId");
|
||||
// if (StringUtils.isEmpty(sessionId)) {
|
||||
// throw new BizException(ResponseStatus.PARM_ERROR, "header里没有包含sessionId");
|
||||
// }
|
||||
// BackendUserVO backendUser = (BackendUserVO) attributes.get("backendUser");
|
||||
// FrontUserVO frontUser = (FrontUserVO) attributes.get("frontUser");
|
||||
if (ex instanceof TokenExpiredException) {
|
||||
throw new BizException(ResponseStatus.SESSION_USER_LOGOUT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,12 +20,8 @@ public class CacheKey {
|
||||
public static final String DISTRIBUTED_LOCK = "distributed_lock";
|
||||
|
||||
public static class LockKey {
|
||||
// 从cache刷新观点互动数量到DB 分布式锁(字符串常量)
|
||||
public static final String SAVE_VIEW_COUNT_TO_DB_LOCK = "save_view_count_to_db_lock";
|
||||
// 清除多余定时器日志
|
||||
public static final String CLEAR_HISTORY_SCHEDULE_LOG = "clearHistoryScheduleLog";
|
||||
// 自动解除锁定用户
|
||||
public static final String UN_LOCK_USER = "un_lock_user";
|
||||
// 结束直播中/暂停中的直播 分布式锁(字符串常量)
|
||||
public static final String STOP_LIVING_VIDEO_LOCK = "stop_living_video_lock";
|
||||
// 刷新视频直播状态 分布式锁(字符串常量)
|
||||
@ -47,6 +43,9 @@ public class CacheKey {
|
||||
|
||||
public static final String SYNC_APP_ORDER = "sync_app_order";
|
||||
public static final String LOAD_USER_BLACK_LIST = "load_user_black_list";
|
||||
public static final String SAVE_MESSAGE_READ = "save_message_read";
|
||||
public static final String SAVE_GROUP_USER = "save_group_user";
|
||||
public static final String COLLECT_GROUP_DATA = "collect_group_data";
|
||||
}
|
||||
|
||||
// 消息主题
|
||||
@ -135,71 +134,6 @@ public class CacheKey {
|
||||
public static final String USER_ADVISOR_DEPT_MAP = "user_advisor_dept_map";
|
||||
}
|
||||
|
||||
// 观点栏目
|
||||
public static final String VIEW_COLUMN = "view_column";
|
||||
|
||||
public static final class ViewColumnKey {
|
||||
// columnId -> columnName
|
||||
public static final String VIEW_COLUMN_MAP = "column_map";
|
||||
// List<ViewColumnAppVO>
|
||||
public static final String APP_COLUMN_LIST = "app_column_list|";
|
||||
}
|
||||
|
||||
// 观点
|
||||
public static final String VIEW_INFO = "view_info";
|
||||
|
||||
public static class ViewInfoKey {
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_PUBLISH_TIME_LIST = "app_publish_time_list";
|
||||
// SortedSet<ViewSortEntity, Integer> (readCount, viewId)
|
||||
public static final String APP_READ_COUNT_LIST = "app_read_count_list";
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_KEYWORD_LIST = "app_keyword_all|";
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_COLUMN_LIST = "app_column_list|";
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_ADVISOR_LIST = "app_advisor_list|";
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_PACKAGE_LIST = "app_package_list|";
|
||||
// ViewInfoAppVO
|
||||
public static final String APP_OBJ = "app_obj|";
|
||||
// ViewDetailAppVO
|
||||
public static final String APP_DETAIL_OBJ = "app_detail_obj|";
|
||||
// userId -> Set<ViewId>
|
||||
public static final String USER_FAVOR_VIEW_IDS = "user_favor_view_ids|";
|
||||
// viewId -> PN Counter<favorCount>
|
||||
public static final String APP_FAVOR_COUNT = "app_favor_count|";
|
||||
// viewId -> PN Counter<readCount>
|
||||
public static final String APP_READ_COUNT = "app_read_count|";
|
||||
// Set<ViewRead>
|
||||
public static final String TEMP_READ_COUNT_SET = "temp_read_count_set";
|
||||
}
|
||||
|
||||
// 观点包
|
||||
public static final String VIEW_PACKAGE = "view_package";
|
||||
|
||||
public static class ViewPackageKey {
|
||||
// id -> ViewPackageAppVO
|
||||
public static final String APP_OBJ = "app_obj|";
|
||||
// SortedSet<ViewSortEntity> (weight, subCount, packageId)
|
||||
public static final String APP_LIST = "app_list";
|
||||
// SortedSet<ViewSortEntity> (inPackageWeight, subCount, packageId)
|
||||
public static final String APP_KEYWORD_LIST = "app_keyword_list|";
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_COLUMN_LIST = "app_column_list|";
|
||||
// SortedSet<ViewSortEntity, Integer> (publishTime, viewId)
|
||||
public static final String APP_ADVISOR_LIST = "app_advisor_list|";
|
||||
public static final String APP_ADVISOR_ORDER_BY_SUBCOUNT_LIST = "app_advisor_order_by_subcount_list|";
|
||||
}
|
||||
|
||||
//观点卡片
|
||||
public static final String VIEW_CARD = "view_card";
|
||||
|
||||
public static class ViewCardKey {
|
||||
//viewId --> List<ViewCard>
|
||||
public static final String VIEW_CARD_LIST = "view_card_list|";
|
||||
}
|
||||
|
||||
public static final String ADVERT = "advert";
|
||||
|
||||
public static class AdvertKey {
|
||||
@ -420,6 +354,7 @@ public class CacheKey {
|
||||
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|";
|
||||
public static final String TEMP_READ_LIST = "temp_read_list";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -60,11 +60,7 @@ public class HazelcastConfiguration {
|
||||
|
||||
configMap.put(DEPT, new LocalMapConfig(10000, 3600));
|
||||
configMap.put(TAG, new LocalMapConfig(10000, 3600));
|
||||
configMap.put(VIEW_COLUMN, new LocalMapConfig(10000, 3600));
|
||||
configMap.put(ADVISOR_INFO, new LocalMapConfig(10000, 300));
|
||||
configMap.put(VIEW_INFO, new LocalMapConfig(10000, 300));
|
||||
configMap.put(VIEW_PACKAGE, new LocalMapConfig(10000, 300));
|
||||
configMap.put(VIEW_CARD, new LocalMapConfig(10000, 300));
|
||||
|
||||
configMap.put(RECOMMEND, new LocalMapConfig(10000, 300));
|
||||
configMap.put(USER, new LocalMapConfig(10000, 3600));
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package com.upchina.video.entity;
|
||||
package com.upchina.common.entity;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
package com.upchina.common.generator;
|
||||
|
||||
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
|
||||
import com.baomidou.mybatisplus.generator.AutoGenerator;
|
||||
import com.baomidou.mybatisplus.generator.config.*;
|
||||
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
|
||||
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
// 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中
|
||||
public class CodeGenerator {
|
||||
|
||||
public static String scanner(String tip) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String help = "请输入" + tip + ":";
|
||||
System.out.println(help);
|
||||
if (scanner.hasNext()) {
|
||||
String ipt = scanner.nextLine();
|
||||
if (StrUtil.isNotBlank(ipt)) {
|
||||
return ipt;
|
||||
}
|
||||
}
|
||||
throw new MybatisPlusException("请输入正确的" + tip + "!");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
DataSourceConfig dsc = new DataSourceConfig
|
||||
.Builder("jdbc:mysql://172.16.8.64/video_demo_wx?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8",
|
||||
"root",
|
||||
"123456")
|
||||
// .Builder("jdbc:mysql://172.16.9.44:3306/db_crm_dyqh?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false",
|
||||
// "taf",
|
||||
// "taf2015")
|
||||
.build();
|
||||
// 代码生成器
|
||||
AutoGenerator mpg = new AutoGenerator(dsc);
|
||||
|
||||
String moduleName = scanner("模块名");
|
||||
String projectPath = System.getProperty("user.dir") + "/AdvisorServer";
|
||||
|
||||
// 全局配置
|
||||
GlobalConfig globalConfig = new GlobalConfig
|
||||
.Builder()
|
||||
.outputDir(projectPath + "/src/main/java")
|
||||
.author("easonzhu")
|
||||
.openDir(false)
|
||||
.fileOverride()
|
||||
.build();
|
||||
|
||||
// 包配置
|
||||
PackageConfig packageConfig = new PackageConfig
|
||||
.Builder()
|
||||
.parent("com.upchina")
|
||||
.moduleName(moduleName)
|
||||
.build();
|
||||
|
||||
// 配置模板
|
||||
TemplateConfig templateConfig = new TemplateConfig
|
||||
.Builder()
|
||||
.mapperXml(null)
|
||||
.service(null, null)
|
||||
.controller(null)
|
||||
.build();
|
||||
|
||||
// 策略配置
|
||||
StrategyConfig strategyConfig = new StrategyConfig
|
||||
.Builder()
|
||||
.addInclude(scanner("表名,多个英文逗号分割").split(","))
|
||||
.entityBuilder()
|
||||
.naming(NamingStrategy.underline_to_camel)
|
||||
.controllerBuilder()
|
||||
.enableRestStyle()
|
||||
.build();
|
||||
|
||||
mpg.global(globalConfig);
|
||||
mpg.packageInfo(packageConfig);
|
||||
mpg.template(templateConfig);
|
||||
mpg.strategy(strategyConfig);
|
||||
mpg.execute(new FreemarkerTemplateEngine());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
package com.upchina.common.handler;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.vo.WsVO;
|
||||
import com.upchina.video.constant.VideoWsMessageType;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* WebSocket处理ERROR指令错误
|
||||
* </p>
|
||||
*
|
||||
* @author fangliangbao
|
||||
* @since 2023-02-24
|
||||
*/
|
||||
@Component
|
||||
public class WebSocketErrorHandler extends StompSubProtocolErrorHandler {
|
||||
|
||||
public static String exceptionToJson(Throwable throwable) {
|
||||
BizException exception;
|
||||
if (throwable == null) {
|
||||
exception = new BizException(ResponseStatus.SYS_BUSY);
|
||||
} else if (throwable instanceof BizException) {
|
||||
exception = (BizException) throwable;
|
||||
} else {
|
||||
exception = new BizException(ResponseStatus.SYS_BUSY, throwable.getLocalizedMessage());
|
||||
}
|
||||
return JsonUtil.toJson(WsVO.errorUserMsg(exception, null, null, VideoWsMessageType.ERROR_MSG));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
protected Message<byte[]> handleInternal(@Nonnull StompHeaderAccessor accessor, @Nonnull byte[] ignored1, Throwable cause, StompHeaderAccessor ignored2) {
|
||||
String errMessage = accessor.getFirstNativeHeader("message");
|
||||
if (StrUtil.isEmpty(errMessage)) {
|
||||
errMessage = WebSocketErrorHandler.exceptionToJson(cause == null ? null : cause.getCause());
|
||||
}
|
||||
accessor.setMessage(null);
|
||||
return MessageBuilder.createMessage(errMessage.getBytes(StandardCharsets.UTF_8), accessor.getMessageHeaders());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,160 +0,0 @@
|
||||
package com.upchina.common.interceptor;
|
||||
|
||||
import com.hazelcast.map.IMap;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.filter.AuthFilter;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
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 org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.simp.stomp.StompCommand;
|
||||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
|
||||
import org.springframework.messaging.support.ChannelInterceptor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* WebSocket连结token鉴权
|
||||
* </p>
|
||||
*
|
||||
* @author fangliangbao
|
||||
* @since 2022-09-25
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
|
||||
public class WebSocketAuthChannelInterceptor implements ChannelInterceptor {
|
||||
|
||||
@Resource
|
||||
private AuthFilter authFilter;
|
||||
|
||||
@Resource
|
||||
private VideoCacheService videoCacheService;
|
||||
|
||||
@Resource
|
||||
private VideoMessageService videoMessageService;
|
||||
|
||||
/**
|
||||
* 发送前监听是否为登录用户
|
||||
*/
|
||||
@Override
|
||||
public Message<?> preSend(@NonNull Message<?> message, @NonNull MessageChannel channel) {
|
||||
StompHeaderAccessor header = StompHeaderAccessor.wrap(message);
|
||||
if (header == null || header.getCommand() == null) {
|
||||
return message;
|
||||
}
|
||||
// 只处理连接消息
|
||||
if (!header.getCommand().equals(StompCommand.CONNECT)) {
|
||||
return message;
|
||||
}
|
||||
String sessionId = header.getFirstNativeHeader("sessionId");
|
||||
if (StringUtils.isEmpty(sessionId)) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "header里没有包含sessionId");
|
||||
}
|
||||
String groupId = header.getFirstNativeHeader("GroupId");
|
||||
Integer videoId = StringUtils.isNotEmpty(groupId) ? Integer.parseInt(groupId) : null;
|
||||
if (videoId == null) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "header里没有包含videoId");
|
||||
}
|
||||
Map<String, Object> attributes = header.getSessionAttributes();
|
||||
if (attributes == null) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "header里没有包含attributes");
|
||||
}
|
||||
String userId = null;
|
||||
String authorization = header.getFirstNativeHeader("Authorization");
|
||||
if (StringUtils.isNotEmpty(authorization)) {
|
||||
BackendUserVO backendUser = authFilter.parseBackendUser(authorization);
|
||||
attributes.put("backendUser", backendUser);
|
||||
userId = backendUser.getUserId().toString();
|
||||
} else {
|
||||
String token = header.getFirstNativeHeader("token");
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
FrontUserVO frontUser = authFilter.parseFrontUser(token);
|
||||
attributes.put("frontUser", frontUser);
|
||||
userId = frontUser.getUserId();
|
||||
}
|
||||
}
|
||||
if (userId == null) {
|
||||
throw new BizException(ResponseStatus.SESSION_EXPIRY);
|
||||
}
|
||||
attributes.put("userId", userId);
|
||||
attributes.put("sessionId", sessionId);
|
||||
attributes.put("videoId", videoId);
|
||||
attributes.put("sessionKey", userId + "-" + sessionId);
|
||||
return message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
StompHeaderAccessor header = StompHeaderAccessor.wrap(message);
|
||||
if (header == null || header.getCommand() == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> attributes = header.getSessionAttributes();
|
||||
FrontUserVO frontUser = (FrontUserVO) attributes.get("frontUser");
|
||||
if (frontUser == null) {
|
||||
return;
|
||||
}
|
||||
String userId = (String) attributes.get("userId");
|
||||
Integer videoId = (Integer) attributes.get("videoId");
|
||||
String sessionId = (String) attributes.get("sessionId");
|
||||
String sessionKey = (String) attributes.get("sessionKey");
|
||||
if (userId == null || videoId == null || sessionId == null || sessionKey == null) {
|
||||
return;
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
switch (header.getCommand()) {
|
||||
case CONNECT: {
|
||||
IMap<String, OnlineUser> totalOnlineMap = videoCacheService.getTotalOnlineMap(videoId);
|
||||
OnlineUser onlineUser = new OnlineUser(
|
||||
videoId,
|
||||
userId,
|
||||
frontUser.getUserName(),
|
||||
frontUser.getImgUrl(),
|
||||
sessionId,
|
||||
IsOrNot.IS.value,
|
||||
IsOrNot.NOT.value,
|
||||
now
|
||||
);
|
||||
totalOnlineMap.put(sessionKey, onlineUser);
|
||||
//上线通知
|
||||
// LoggerUtil.websocket.info("上线通知:" + JSONObject.toJSONString(onlineUser));
|
||||
videoMessageService.memberNotify(videoId, onlineUser);
|
||||
videoMessageService.publishEnterMessage(videoId, frontUser);
|
||||
break;
|
||||
}
|
||||
case DISCONNECT: {
|
||||
IMap<String, OnlineUser> totalOnlineMap = videoCacheService.getTotalOnlineMap(videoId);
|
||||
OnlineUser onlineUser = totalOnlineMap.get(sessionKey);
|
||||
if (onlineUser != null) {
|
||||
onlineUser.setIsOnline(IsOrNot.NOT.value);
|
||||
onlineUser.setIsPlay(IsOrNot.NOT.value);
|
||||
onlineUser.setExitTime(now);
|
||||
totalOnlineMap.put(sessionKey, onlineUser);
|
||||
//下线通知
|
||||
// LoggerUtil.websocket.info("下线通知:" + JSONObject.toJSONString(onlineUser));
|
||||
videoMessageService.memberNotify(videoId, onlineUser);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,10 +3,10 @@ package com.upchina.common.interceptor;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
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;
|
||||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
|
||||
|
||||
@ -1,203 +0,0 @@
|
||||
package com.upchina.common.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Table;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.AdvertPosition;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.entity.Advert;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.mapper.AdvertMapper;
|
||||
import com.upchina.common.query.ListAdvertQuery;
|
||||
import com.upchina.common.query.OnlyIdQuery;
|
||||
import com.upchina.common.query.SaveAdvertQuery;
|
||||
import com.upchina.common.query.UpdateAdvertQuery;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.vo.AdvertAppVO;
|
||||
import com.upchina.common.vo.AdvertVO;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.common.vo.MergeProductInfoVO;
|
||||
import com.upchina.rbac.entity.UserDept;
|
||||
import com.upchina.rbac.service.UserService;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AdvertService {
|
||||
|
||||
@Resource
|
||||
private AdvertMapper advertMapper;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Resource
|
||||
private MergeProductService mergeProductService;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Resource
|
||||
private RecommendService recommendService;
|
||||
|
||||
/**
|
||||
* 推荐位置和对应类型
|
||||
* 首页(投顾社区首页):所有类型,包括H5
|
||||
*/
|
||||
// 推荐位置和产品类型关系
|
||||
private static final Multimap<Integer, Integer> positionProductTypeMap = HashMultimap.create();
|
||||
// 产品类型和推荐位置关系
|
||||
private static final Multimap<Integer, Integer> productTypePositionMap = HashMultimap.create();
|
||||
|
||||
private static void putRelation(Integer advertPosition, Collection<Integer> productTypes) {
|
||||
productTypes.forEach(productType -> {
|
||||
positionProductTypeMap.put(advertPosition, productType);
|
||||
productTypePositionMap.put(productType, advertPosition);
|
||||
});
|
||||
}
|
||||
|
||||
static {
|
||||
putRelation(AdvertPosition.HOME_PAGE.value, Arrays.stream(ProductType.values()).map(ProductType::getValue).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
public Pager<AdvertVO> list(ListAdvertQuery query) {
|
||||
Integer productType = query.getProductType();
|
||||
Integer productId = query.getProductId();
|
||||
String name = query.getName();
|
||||
Integer position = query.getPosition();
|
||||
Integer createUserId = query.getCreateUserId();
|
||||
QueryWrapper<Advert> wrapper = Wrappers.query();
|
||||
wrapper.eq(productType != null && productType != 0, "product_type", productType)
|
||||
.eq(productId != null && productId != 0, "product_id", productId)
|
||||
.like(StrUtil.isNotEmpty(name), "name", name)
|
||||
.eq(position != null && position != 0, "position", position)
|
||||
.eq(createUserId != null && createUserId != 0, "create_user_id", createUserId)
|
||||
.last("order by weight desc,create_time desc");
|
||||
Page<Advert> page = advertMapper.selectPage(query.toPage(), wrapper);
|
||||
Map<Integer, UserDept> userMap = userService.getUserMap();
|
||||
List<AdvertVO> list = page.getRecords().stream().map(advert -> new AdvertVO(advert, userMap.get(advert.getCreateUserId()))).collect(Collectors.toList());
|
||||
// 填充非H5类型的产品名称
|
||||
List<AdvertVO> filterH5List = list.stream().filter(vo -> !ProductType.H5.value.equals(vo.getProductType())).collect(Collectors.toList());
|
||||
Table<Integer, Integer, MergeProductInfoVO> productTable = mergeProductService.queryMergeProductInfo(filterH5List);
|
||||
filterH5List.forEach(vo -> {
|
||||
MergeProductInfoVO productVO = productTable.get(vo.getProductType(), vo.getProductId());
|
||||
if (productVO != null) {
|
||||
vo.setName(productVO.getProductName());
|
||||
vo.setSummary(productVO.getSummary());
|
||||
}
|
||||
});
|
||||
return new Pager<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(SaveAdvertQuery query, BackendUserVO backendUserVO) {
|
||||
validateProduct(query);
|
||||
ProductType productType = ProductType.fromValue(query.getProductType());
|
||||
Advert advert = query.toPO(backendUserVO.getUserId());
|
||||
try {
|
||||
advertMapper.insert(advert);
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw new BizException(ResponseStatus.ADVERT_DUPLICATE);
|
||||
}
|
||||
this.clearCache(AdvertPosition.fromValue(query.getPosition()));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(UpdateAdvertQuery query, BackendUserVO backendUserVO) {
|
||||
validateProduct(query);
|
||||
ProductType productType = ProductType.fromValue(query.getProductType());
|
||||
Advert advert = query.toPO();
|
||||
int rows;
|
||||
try {
|
||||
rows = advertMapper.updateById(advert);
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw new BizException(ResponseStatus.ADVERT_DUPLICATE);
|
||||
}
|
||||
if (rows == 0) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
this.clearCache(AdvertPosition.fromValue(query.getPosition()));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
Advert advert = advertMapper.selectById(query.getId());
|
||||
if (advert == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
advertMapper.deleteById(query.getId());
|
||||
this.clearCache(AdvertPosition.fromValue(advert.getPosition()));
|
||||
ProductType productType = ProductType.fromValue(advert.getProductType());
|
||||
}
|
||||
|
||||
public List<AdvertAppVO> listForApp(Integer position) {
|
||||
return cacheService.get(CacheKey.ADVERT, CacheKey.AdvertKey.APP_ADVERT_LIST + position, () -> {
|
||||
QueryWrapper<Advert> wrapper = Wrappers.query();
|
||||
wrapper.eq("position", position).last("order by weight desc,create_time desc");
|
||||
List<Advert> list = advertMapper.selectList(wrapper);
|
||||
return list.stream().map(AdvertAppVO::new).collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
||||
public void clearCache(AdvertPosition position) {
|
||||
Map<String, Object> cacheMap = hazelcastInstance.getMap(CacheKey.ADVERT);
|
||||
cacheMap.remove(CacheKey.AdvertKey.APP_ADVERT_LIST + position.value);
|
||||
}
|
||||
|
||||
public void clearCache(ProductType productType) {
|
||||
Map<String, Object> cacheMap = hazelcastInstance.getMap(CacheKey.ADVERT);
|
||||
Collection<Integer> positionSet = productTypePositionMap.get(productType.value);
|
||||
positionSet.forEach(position -> cacheMap.remove(CacheKey.AdvertKey.APP_ADVERT_LIST + position));
|
||||
}
|
||||
|
||||
private void validateProduct(SaveAdvertQuery query) {
|
||||
Integer position = query.getPosition();
|
||||
Integer productType = query.getProductType();
|
||||
Collection<Integer> productTypeSet = positionProductTypeMap.get(position);
|
||||
if (!productTypeSet.contains(productType)) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "产品类型不符合推荐位置要求");
|
||||
}
|
||||
if (ProductType.H5.value.equals(productType)) {
|
||||
if (StrUtil.isEmpty(query.getName())) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "H5链接名称不能为空");
|
||||
}
|
||||
if (StrUtil.hasText(query.getUrl())) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "H5链接URL不能为空");
|
||||
}
|
||||
} else {
|
||||
if (query.getProductId() == null) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "非H5类型,产品ID不能为空");
|
||||
}
|
||||
}
|
||||
recommendService.validateProduct(query);
|
||||
}
|
||||
|
||||
public void validateRecommendExist(ProductType productType, Integer productId) {
|
||||
// TODO 校验关联数据(投顾\观点\观点包\锦囊\三方产品\套餐产品)
|
||||
QueryWrapper<Advert> advertWrapper = Wrappers.query();
|
||||
advertWrapper.eq("product_type", productType.value)
|
||||
.eq("product_id", productId);
|
||||
Long advertCount = advertMapper.selectCount(advertWrapper);
|
||||
if (advertCount > 0) throw new BizException(ResponseStatus.ADVERT_CAN_NOT_SOLD_OUT);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,272 +0,0 @@
|
||||
package com.upchina.common.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.google.common.collect.Table;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.upchina.common.constant.CommentBlackStatus;
|
||||
import com.upchina.common.constant.CommentBlackType;
|
||||
import com.upchina.common.entity.CommentBlack;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.mapper.CommentBlackMapper;
|
||||
import com.upchina.common.mapper.CommentMapper;
|
||||
import com.upchina.common.query.AddCommentBlackQuery;
|
||||
import com.upchina.common.query.BaseProductQuery;
|
||||
import com.upchina.common.query.CommentBlackQuery;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.util.HideUtils;
|
||||
import com.upchina.common.util.LoggerUtil;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.common.vo.CommentBlackVO;
|
||||
import com.upchina.common.vo.MergeProductInfoVO;
|
||||
import com.upchina.rbac.entity.Dept;
|
||||
import com.upchina.rbac.entity.UserDept;
|
||||
import com.upchina.rbac.service.AuthService;
|
||||
import com.upchina.rbac.service.DeptService;
|
||||
import com.upchina.rbac.service.UserService;
|
||||
import ma.glasnost.orika.MapperFacade;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.upchina.common.config.cache.CacheKey.COMMENT_BLACK;
|
||||
import static com.upchina.common.config.cache.CacheKey.CommentBlackKey.ALL_BLACK_USER;
|
||||
|
||||
@Service
|
||||
public class CommentBlackService {
|
||||
|
||||
@Value("${rsa.priKey}")
|
||||
private String ypPriKey;
|
||||
|
||||
@Value("${rsa.pubKey}")
|
||||
private String ypPubKey;
|
||||
|
||||
@Resource
|
||||
private MapperFacade mapperFacade;
|
||||
|
||||
@Resource
|
||||
private CommentBlackMapper commentBlackMapper;
|
||||
|
||||
@Resource
|
||||
private CommentMapper commentMapper;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Resource
|
||||
private MergeProductService mergeProductService;
|
||||
|
||||
@Resource
|
||||
private HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
|
||||
@Resource
|
||||
private DeptService deptService;
|
||||
|
||||
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
public Set<String> getAllBlackUser() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<CommentBlack> list = cacheService.get(COMMENT_BLACK, ALL_BLACK_USER, () -> {
|
||||
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
|
||||
wrapper.in("status", Arrays.asList(CommentBlackStatus.EFFECT.value, CommentBlackStatus.EXPIRED.value));
|
||||
List<CommentBlack> commentBlackList = commentBlackMapper.selectList(wrapper);
|
||||
LoggerUtil.info("db当前黑名单用户:" + JSONObject.toJSONString(commentBlackList));
|
||||
if (CollectionUtils.isEmpty(commentBlackList)) {
|
||||
return null;
|
||||
}
|
||||
return commentBlackList;
|
||||
});
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
Set<String> set = list.stream()
|
||||
.filter(black -> now.isAfter(black.getStartTime()) && now.isBefore(black.getEndTime()))
|
||||
.map(CommentBlack::getPhone).collect(Collectors.toSet());
|
||||
LoggerUtil.info("当前黑名单用户:" + JSONObject.toJSONString(set));
|
||||
return set;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Integer addCommentBlack(BackendUserVO backendUserVO, AddCommentBlackQuery addCommentBlackQuery) {
|
||||
// String userPhone = RsaUtil.priKeyDecryption(ypPriKey, addCommentBlackQuery.getUserPhone());
|
||||
String userPhone = addCommentBlackQuery.getUserPhone();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
//禁言开始时间-1s
|
||||
now = now.minusSeconds(1L);
|
||||
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
|
||||
wrapper.eq("phone", userPhone)
|
||||
.in("status", Arrays.asList(CommentBlackStatus.EFFECT.value, CommentBlackStatus.EXPIRED.value))
|
||||
.lt("start_time", now)
|
||||
.ge("end_time", now);
|
||||
long validSize = commentBlackMapper.selectCount(wrapper);
|
||||
if (validSize > 0) {
|
||||
throw new BizException(ResponseStatus.REPETITIVE_ERROR);
|
||||
}
|
||||
CommentBlack commentBlack = mapperFacade.map(addCommentBlackQuery, CommentBlack.class);
|
||||
commentBlack.setUserName(addCommentBlackQuery.getUserName());
|
||||
commentBlack.setPhone(userPhone);
|
||||
commentBlack.setStartTime(now);
|
||||
commentBlack.setStatus(CommentBlackStatus.EFFECT.value);
|
||||
commentBlack.setOperatorId(backendUserVO.getUserId());
|
||||
commentBlack.setUpdateTime(now);
|
||||
commentBlack.setAdvisorId(backendUserVO.getAdvisorId());
|
||||
if (CommentBlackType.DAY.value.equals(commentBlack.getType())) {
|
||||
commentBlack.setEndTime(now.plusDays(1));
|
||||
} else if (CommentBlackType.MONTH.value.equals(commentBlack.getType())) {
|
||||
commentBlack.setEndTime(now.plusMonths(1));
|
||||
} else {
|
||||
commentBlack.setEndTime(now.plusYears(100));
|
||||
}
|
||||
int count = commentBlackMapper.insert(commentBlack);
|
||||
if (count < 1) {
|
||||
throw new BizException(ResponseStatus.DB_SAVE_ERROR);
|
||||
}
|
||||
this.clearCache(Collections.singletonList(ALL_BLACK_USER));
|
||||
return commentBlack.getId();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeCommentBlack(String phone) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
|
||||
wrapper.eq("phone", phone)
|
||||
.in("status", Arrays.asList(CommentBlackStatus.EFFECT.value, CommentBlackStatus.EXPIRED.value))
|
||||
.lt("start_time", now)
|
||||
.ge("end_time", now);
|
||||
List<CommentBlack> commentBlackList = commentBlackMapper.selectList(wrapper);
|
||||
if (CollectionUtils.isEmpty(commentBlackList)) {
|
||||
throw new BizException(ResponseStatus.REMOVE_BLACK_USER_ERROR);
|
||||
}
|
||||
for (CommentBlack commentBlack : commentBlackList) {
|
||||
CommentBlack updateObj = new CommentBlack();
|
||||
updateObj.setId(commentBlack.getId());
|
||||
updateObj.setStatus(CommentBlackStatus.REMOVED.value);
|
||||
updateObj.setUpdateTime(now);
|
||||
updateObj.setEndTime(now);
|
||||
int count = commentBlackMapper.updateById(updateObj);
|
||||
if (count < 1) {
|
||||
throw new BizException(ResponseStatus.DB_SAVE_ERROR);
|
||||
}
|
||||
}
|
||||
this.clearCache(Collections.singletonList(ALL_BLACK_USER));
|
||||
}
|
||||
|
||||
public Pager<CommentBlackVO> queryCommentBlackList(BackendUserVO backendUserVO, CommentBlackQuery commentBlackQuery) {
|
||||
LocalDateTime startTime = null;
|
||||
LocalDateTime endTime = null;
|
||||
LocalDateTime startOpTime = null;
|
||||
LocalDateTime endOpTime = null;
|
||||
if (StrUtil.isNotEmpty(commentBlackQuery.getStartTime())) {
|
||||
startTime = LocalDateTime.parse(commentBlackQuery.getStartTime(), formatter);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(commentBlackQuery.getEndTime())) {
|
||||
endTime = LocalDateTime.parse(commentBlackQuery.getEndTime(), formatter);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(commentBlackQuery.getStartOpTime())) {
|
||||
startOpTime = LocalDateTime.parse(commentBlackQuery.getStartOpTime(), formatter);
|
||||
}
|
||||
if (StrUtil.isNotEmpty(commentBlackQuery.getEndOpTime())) {
|
||||
endOpTime = LocalDateTime.parse(commentBlackQuery.getEndOpTime(), formatter);
|
||||
}
|
||||
/*Set<Integer> advisorIdSet = authService.getAccessibleAdviserSet(null, backendUserVO, UserType.fromValue(commentBlackQuery.getUserType()));
|
||||
if (advisorIdSet != null && advisorIdSet.isEmpty()) {
|
||||
return Pager.emptyPager();
|
||||
}*/
|
||||
QueryWrapper<CommentBlack> wrapper = Wrappers.query();
|
||||
wrapper.like(StrUtil.isNotEmpty(commentBlackQuery.getUserName()), "user_name", commentBlackQuery.getUserName())
|
||||
.eq(StrUtil.isNotEmpty(commentBlackQuery.getPhone()), "phone", commentBlackQuery.getPhone())
|
||||
.eq(commentBlackQuery.getType() != null, "type", commentBlackQuery.getType())
|
||||
//.in(!CollectionUtils.isEmpty(advisorIdSet), "advisor_id", advisorIdSet)
|
||||
.eq(commentBlackQuery.getProductType() != null, "product_type", commentBlackQuery.getProductType())
|
||||
.eq(commentBlackQuery.getProductType() != null && commentBlackQuery.getProductId() != null, "product_id", commentBlackQuery.getProductId())
|
||||
.like(StrUtil.isNotBlank(commentBlackQuery.getContent()), "content", commentBlackQuery.getContent())
|
||||
.like(StrUtil.isNotEmpty(commentBlackQuery.getReason()), "reason", commentBlackQuery.getReason())
|
||||
.eq(commentBlackQuery.getOperatorId() != null, "operator_id", commentBlackQuery.getOperatorId())
|
||||
.ge(startTime != null, "start_time", startTime)
|
||||
.lt(endTime != null, "start_time", endTime)
|
||||
.ge(startOpTime != null, "end_time", startOpTime)
|
||||
.lt(endOpTime != null, "end_time", endOpTime);
|
||||
wrapper.orderByDesc("start_time");
|
||||
Page<CommentBlack> page = commentBlackMapper.selectPage(commentBlackQuery.toPage(), wrapper);
|
||||
List<CommentBlack> list = page.getRecords();
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return Pager.emptyPager();
|
||||
}
|
||||
List<BaseProductQuery> baseProductQueryList = list.stream().map(commentBlack -> {
|
||||
if (commentBlack.getProductId() != null && commentBlack.getProductType() != null) {
|
||||
return new BaseProductQuery(commentBlack.getProductId(), commentBlack.getProductType());
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
Table<Integer, Integer, MergeProductInfoVO> productTable = mergeProductService.queryMergeProductInfo(baseProductQueryList);
|
||||
Map<Integer, UserDept> userMap = userService.getUserMap();
|
||||
Map<String, Dept> deptMap = deptService.getDeptMap();
|
||||
List<CommentBlackVO> voList = list.stream().map(commentBlack -> mapperFacade.map(commentBlack, CommentBlackVO.class)).collect(Collectors.toList());
|
||||
for (CommentBlackVO commentBlackVO : voList) {
|
||||
// 查询产品信息
|
||||
if (commentBlackVO.getProductId() != null && commentBlackVO.getProductType() != null) {
|
||||
MergeProductInfoVO mergeProductInfoVO = productTable.get(commentBlackVO.getProductType(), commentBlackVO.getProductId());
|
||||
if (mergeProductInfoVO != null) {
|
||||
commentBlackVO.setProductName(mergeProductInfoVO.getProductName());
|
||||
}
|
||||
}
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (CommentBlackStatus.EFFECT.value.equals(commentBlackVO.getStatus()) && now.isAfter(commentBlackVO.getEndTime())) {
|
||||
commentBlackVO.setStatus(CommentBlackStatus.EXPIRED.value);
|
||||
}
|
||||
UserDept user = userMap.get(commentBlackVO.getOperatorId());
|
||||
if (user != null) {
|
||||
commentBlackVO.setOperatorName(user.getName());
|
||||
}
|
||||
if (StrUtil.hasText(commentBlackVO.getUserOrgNo())) {
|
||||
Dept dept = deptMap.get(commentBlackVO.getUserOrgNo());
|
||||
if (dept != null) {
|
||||
commentBlackVO.setUserOrgName(dept.getName());
|
||||
}
|
||||
}
|
||||
commentBlackVO.setPhone(HideUtils.hidePhoneNo(commentBlackVO.getPhone()));
|
||||
commentBlackVO.setHidePhone(commentBlackVO.getPhone());
|
||||
commentBlackVO.setUserName(HideUtils.maskLastName(commentBlackVO.getUserName()));
|
||||
}
|
||||
return new Pager<>(voList, page.getTotal());
|
||||
}
|
||||
|
||||
private void clearCache(List<String> cacheKeys) {
|
||||
IMap<String, Object> cacheMap = hazelcastInstance.getMap(COMMENT_BLACK);
|
||||
for (String key : cacheKeys) {
|
||||
cacheMap.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否禁言
|
||||
*/
|
||||
public void check(String phone) {
|
||||
// 判断是否禁言用户
|
||||
Set<String> blackUsers = getAllBlackUser();
|
||||
if (CollUtil.isNotEmpty(blackUsers) && blackUsers.contains(phone)) {
|
||||
throw new BizException("禁言用户,禁止发言");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,130 +0,0 @@
|
||||
package com.upchina.common.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.upchina.common.entity.SensitiveWord;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.mapper.SensitiveWordMapper;
|
||||
import com.upchina.common.query.KeywordPageQuery;
|
||||
import com.upchina.common.query.OnlyIdQuery;
|
||||
import com.upchina.common.query.SaveSensitiveQuery;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.common.vo.SensitiveWordVO;
|
||||
import org.ahocorasick.trie.Emit;
|
||||
import org.ahocorasick.trie.Trie;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author easonzhu
|
||||
* @since 2022-08-30
|
||||
*/
|
||||
|
||||
@Service
|
||||
public class SensitiveWordService {
|
||||
|
||||
private static final long trieExpireTime = 300000;
|
||||
private static Trie trie;
|
||||
private static long trieBuildTime = 0L;
|
||||
@Resource
|
||||
private SensitiveWordMapper sensitiveWordMapper;
|
||||
|
||||
public Pager<SensitiveWordVO> list(KeywordPageQuery query) {
|
||||
String keyword = query.getKeyword();
|
||||
QueryWrapper<SensitiveWord> wrapper = Wrappers.query();
|
||||
wrapper.like(StrUtil.isNotEmpty(keyword), "word", keyword).orderByDesc("create_time");
|
||||
Page<SensitiveWord> page = sensitiveWordMapper.selectPage(query.toPage(), wrapper);
|
||||
List<SensitiveWordVO> list = page.getRecords().stream().map(SensitiveWordVO::new).collect(Collectors.toList());
|
||||
return new Pager<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(SaveSensitiveQuery query, BackendUserVO backendUserVO) {
|
||||
QueryWrapper<SensitiveWord> wrapper = Wrappers.query();
|
||||
query.setWords(query.getWords().stream().distinct().collect(Collectors.toList()));
|
||||
wrapper.in("word", query.getWords());
|
||||
sensitiveWordMapper.delete(wrapper);
|
||||
List<SensitiveWord> list = query.toPO();
|
||||
list.forEach(sensitiveWordMapper::insert);
|
||||
getTrie(false);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
SensitiveWord sensitiveWord = sensitiveWordMapper.selectById(query.getId());
|
||||
int rows = sensitiveWordMapper.deleteById(query.getId());
|
||||
if (rows == 0) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
getTrie(false);
|
||||
}
|
||||
|
||||
public String[] upload(MultipartFile file) throws IOException {
|
||||
String content = new String(file.getBytes());
|
||||
String[] lines = content.split("\r\n");
|
||||
if (lines.length == 0) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "数据为空");
|
||||
}
|
||||
int lineNum = 0;
|
||||
for (String line : lines) {
|
||||
lineNum++;
|
||||
if (StrUtil.isEmpty(line)) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "第" + lineNum + "行为空");
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
public void check(List<String> textList) {
|
||||
if (textList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Trie trie = this.getTrie(true);
|
||||
for (String text : textList) {
|
||||
Collection<Emit> emits = trie.parseText(text);
|
||||
if (!emits.isEmpty()) {
|
||||
Set<String> collect = emits.stream().map(Emit::getKeyword).collect(Collectors.toSet());
|
||||
String emittedSensitiveWords = String.join(",", collect);
|
||||
// String emittedSensitiveWords = emits.stream().map(Emit::getKeyword).collect(Collectors.joining(","));
|
||||
throw new BizException(ResponseStatus.SENSITIVE_WORD_ERROR.code, ResponseStatus.SENSITIVE_WORD_ERROR.message + ":" + emittedSensitiveWords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void check(String... texts) {
|
||||
this.check(Arrays.stream(texts).filter(StringUtils::isNotEmpty).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private synchronized Trie getTrie(boolean flag) {
|
||||
if (flag && (trie != null && System.currentTimeMillis() - trieBuildTime < trieExpireTime)) {
|
||||
return trie;
|
||||
}
|
||||
Trie.TrieBuilder trieBuilder = Trie.builder();
|
||||
List<SensitiveWord> list = sensitiveWordMapper.selectList(Wrappers.emptyWrapper());
|
||||
for (SensitiveWord word : list) {
|
||||
trieBuilder.addKeyword(word.getWord());
|
||||
}
|
||||
trieBuildTime = System.currentTimeMillis();
|
||||
trie = trieBuilder.build();
|
||||
return trie;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package com.upchina.common.util;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class HideUtils {
|
||||
|
||||
public static String hidePhoneNo(String phoneNo) {
|
||||
if (StringUtils.isBlank(phoneNo)) {
|
||||
return phoneNo;
|
||||
}
|
||||
if (phoneNo.length() >= 7) {
|
||||
//前三后四
|
||||
return phoneNo.substring(0, 3) + "****" +
|
||||
phoneNo.substring(phoneNo.length() - 4);
|
||||
} else {
|
||||
return phoneNo;
|
||||
}
|
||||
}
|
||||
|
||||
public static String maskLastName(String name) {
|
||||
if (StrUtil.isBlank(name)) {
|
||||
return null;
|
||||
}
|
||||
int length = name.length();
|
||||
return name.substring(0, length - 1) + "*";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
package com.upchina.common.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LoggerUtil {
|
||||
|
||||
public static class Logger {
|
||||
public static void info(String message) {
|
||||
// TODO
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public static void info(Object... message) {
|
||||
// TODO
|
||||
System.out.println(Arrays.toString(message));
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
// TODO
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
public static void error(Object... message) {
|
||||
// TODO
|
||||
System.err.println(Arrays.toString(message));
|
||||
}
|
||||
|
||||
public static void warn(String message) {
|
||||
// TODO
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
public static void error(Object... message) {
|
||||
// TODO
|
||||
System.err.println(Arrays.toString(message));
|
||||
}
|
||||
}
|
||||
|
||||
public static final Logger video = new Logger();
|
||||
|
||||
public static final Logger data = new Logger();
|
||||
|
||||
public static final Logger error = new Logger();
|
||||
|
||||
public static final Logger websocket = new Logger();
|
||||
|
||||
public static final Logger auth = new Logger();
|
||||
|
||||
public static void info(String message) {
|
||||
// TODO
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public static void info(Object... message) {
|
||||
// TODO
|
||||
System.out.println(Arrays.toString(message));
|
||||
}
|
||||
|
||||
public static void error(String message) {
|
||||
// TODO
|
||||
System.err.println(message);
|
||||
}
|
||||
|
||||
public static void error(Object... message) {
|
||||
// TODO
|
||||
System.err.println(Arrays.toString(message));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,143 +0,0 @@
|
||||
package com.upchina.common.util;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RsaUtil {
|
||||
|
||||
/**
|
||||
* RSA最大加密大小
|
||||
*/
|
||||
private final static int MAX_ENCRYPT_BLOCK = 117;
|
||||
|
||||
/**
|
||||
* **
|
||||
* RSA最大解密大小
|
||||
*/
|
||||
private final static int MAX_DECRYPT_BLOCK = 128;
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param pubKey 公钥字符串
|
||||
* @param str 需要加密操作的字符串
|
||||
* @return 结果再Base64加密后的字符串
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String pubKeyEncryption(String pubKey, String str) {
|
||||
try {
|
||||
// String 转 公钥
|
||||
PublicKey rsaPublicKey = getPublicKey(pubKey);
|
||||
//公钥加密
|
||||
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
byte[] inputArray = str.getBytes();
|
||||
int inputLength = inputArray.length;
|
||||
// 分段加密
|
||||
int offSet = 0;
|
||||
byte[] resultBytes = {};
|
||||
byte[] cache;
|
||||
while (inputLength - offSet > 0) {
|
||||
if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
|
||||
offSet += MAX_ENCRYPT_BLOCK;
|
||||
} else {
|
||||
cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
|
||||
offSet = inputLength;
|
||||
}
|
||||
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
|
||||
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
|
||||
}
|
||||
return Base64.encode(resultBytes);
|
||||
} catch (Exception exception) {
|
||||
LoggerUtil.info("RsaUtil.pubKeyEncryption异常:" + ExceptionUtils.getStackTrace(exception));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用私钥 解密
|
||||
*
|
||||
* @param priKey 私钥字符串
|
||||
* @param encryptionBase64Str RSA加密后又base64编码后的字符串
|
||||
* @return 解密结果
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String priKeyDecryption(String priKey, String encryptionBase64Str) {
|
||||
try {
|
||||
// base64 解码
|
||||
byte[] base64 = Base64.decode(encryptionBase64Str);
|
||||
// String 转 私钥
|
||||
PrivateKey rsaPrivateKey = getPrivateKey(priKey);
|
||||
//私钥解密
|
||||
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(rsaPrivateKey.getEncoded());
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
// 返回UTF-8编码的解密信息
|
||||
int inputLen = base64.length;
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
int offSet = 0;
|
||||
byte[] cache;
|
||||
int i = 0;
|
||||
// 对数据分段解密
|
||||
while (inputLen - offSet > 0) {
|
||||
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
|
||||
cache = cipher.doFinal(base64, offSet, MAX_DECRYPT_BLOCK);
|
||||
} else {
|
||||
cache = cipher.doFinal(base64, offSet, inputLen - offSet);
|
||||
}
|
||||
out.write(cache, 0, cache.length);
|
||||
i++;
|
||||
offSet = i * MAX_DECRYPT_BLOCK;
|
||||
}
|
||||
out.close();
|
||||
return out.toString("UTF-8");
|
||||
} catch (Exception exception) {
|
||||
LoggerUtil.info("RsaUtil.priKeyDecryption异常:" + ExceptionUtils.getStackTrace(exception));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* String转公钥PublicKey
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static PublicKey getPublicKey(String key) throws Exception {
|
||||
byte[] keyBytes = Base64.decode(key);
|
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
return keyFactory.generatePublic(keySpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* String转私钥PrivateKey
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static PrivateKey getPrivateKey(String key) throws Exception {
|
||||
byte[] keyBytes = Base64.decodeBase64(key);
|
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
return keyFactory.generatePrivate(keySpec);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package com.upchina.common.util;
|
||||
|
||||
import org.springframework.boot.web.context.WebServerApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@Component
|
||||
public class WebServerInfo {
|
||||
|
||||
@Resource
|
||||
private WebServerApplicationContext webServerApplicationContext;
|
||||
|
||||
public String getServerIp() {
|
||||
// 获取本地的 IP 地址,Spring Boot 默认会绑定到 0.0.0.0(所有网络接口)
|
||||
try {
|
||||
InetAddress inetAddress = InetAddress.getLocalHost();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return inetAddress.getHostAddress();
|
||||
}
|
||||
|
||||
public int getServerPort() {
|
||||
return webServerApplicationContext.getWebServer().getPort();
|
||||
}
|
||||
}
|
||||
@ -1,522 +0,0 @@
|
||||
package com.upchina.course.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.upchina.advisor.entity.AdvisorBasic;
|
||||
import com.upchina.advisor.service.AdvisorInfoService;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.IsRecommend;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.query.OnlyIdPageQuery;
|
||||
import com.upchina.common.query.OnlyIdQuery;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
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.AuthResultVO;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.common.vo.FrontUserVO;
|
||||
import com.upchina.common.vo.InsertIdVO;
|
||||
import com.upchina.course.constant.*;
|
||||
import com.upchina.course.entity.CourseContent;
|
||||
import com.upchina.course.entity.Serial;
|
||||
import com.upchina.course.entity.SerialContent;
|
||||
import com.upchina.course.mapper.CourseContentMapper;
|
||||
import com.upchina.course.mapper.SerialContentMapper;
|
||||
import com.upchina.course.mapper.SerialMapper;
|
||||
import com.upchina.course.query.*;
|
||||
import com.upchina.course.vo.SerialContentVO;
|
||||
import com.upchina.course.vo.SerialVO;
|
||||
import com.upchina.course.vo.ShortVideoVO;
|
||||
import com.upchina.rbac.entity.UserDept;
|
||||
import com.upchina.rbac.service.AuthService;
|
||||
import com.upchina.rbac.service.UserService;
|
||||
import com.upchina.video.constant.VideoStatus;
|
||||
import com.upchina.video.constant.VideoUserType;
|
||||
import com.upchina.video.service.app.AppVideoInfoService;
|
||||
import com.upchina.video.vo.info.VideoDetailAppVO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// 合集Service
|
||||
@Service
|
||||
public class SerialService {
|
||||
|
||||
@Resource
|
||||
private SerialMapper serialMapper;
|
||||
|
||||
@Resource
|
||||
private SensitiveWordService sensitiveWordService;
|
||||
|
||||
@Resource
|
||||
private StateMachine<SerialStatus> serialSM;
|
||||
|
||||
@Resource
|
||||
private AdvisorInfoService advisorInfoService;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Resource
|
||||
private SerialContentMapper serialContentMapper;
|
||||
|
||||
@Resource
|
||||
private AppVideoInfoService appVideoInfoService;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private CourseContentMapper courseContentMapper;
|
||||
|
||||
@Resource
|
||||
private CourseService courseService;
|
||||
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
|
||||
@Resource
|
||||
private ShortVideoService shortVideoService;
|
||||
|
||||
@Resource
|
||||
private CourseCommonService courseCommonService;
|
||||
|
||||
@Resource
|
||||
private AppUserService appUserService;
|
||||
|
||||
@Resource
|
||||
private IMap<String, Object> courseCache;
|
||||
|
||||
/**
|
||||
* 保存合集
|
||||
*
|
||||
* @param query 保存合集参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 合集ID
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public InsertIdVO save(SaveSerialQuery query, BackendUserVO backendUserVO) {
|
||||
sensitiveWordService.check(query.getName(), query.getRemark());
|
||||
Integer advisorId = courseCommonService.getAdvisorId(query.getAdvisorId(), backendUserVO);
|
||||
Serial serial = query.toPO(backendUserVO, advisorId);
|
||||
serialMapper.insert(serial);
|
||||
return new InsertIdVO(serial.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新合集
|
||||
*
|
||||
* @param query 更新合集参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(UpdateSerialQuery query, BackendUserVO backendUserVO) {
|
||||
sensitiveWordService.check(query.getName(), query.getRemark());
|
||||
Serial serialInDB = serialMapper.selectById(query.getId());
|
||||
if (serialInDB == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
|
||||
// 状态机扭转,判断是否能够编辑
|
||||
SerialStatus dbStatus = SerialStatus.fromValue(serialInDB.getStatus());
|
||||
serialSM.send(dbStatus, SerialStatus.EVENT_UPDATE);
|
||||
|
||||
Serial serial = query.toPO(backendUserVO);
|
||||
serialMapper.updateById(serial);
|
||||
clearCache(query.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新合集状态
|
||||
*
|
||||
* @param query 更新合集状态参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStatus(UpdateSerialStatusQuery query, BackendUserVO backendUserVO) {
|
||||
Serial serialInDB = serialMapper.selectById(query.getId());
|
||||
if (serialInDB == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
|
||||
// 状态机扭转
|
||||
SerialStatus dbStatus = SerialStatus.fromValue(serialInDB.getStatus());
|
||||
SerialStatus targetStatus = serialSM.send(dbStatus, SerialStatus.fromValue(query.getEvent()));
|
||||
|
||||
Serial serial = query.toPO(targetStatus, backendUserVO);
|
||||
serialMapper.updateById(serial);
|
||||
clearCache(query.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询合集列表
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 合集列表
|
||||
*/
|
||||
public Pager<SerialVO> list(ListSerialQuery query, BackendUserVO backendUserVO) {
|
||||
String name = query.getName();
|
||||
Integer status = query.getStatus();
|
||||
Integer type = query.getType();
|
||||
Integer userType = query.getUserType();
|
||||
Integer contentType = query.getContentType();
|
||||
Integer contentId = query.getContentId();
|
||||
Integer excludeCourseId = query.getExcludeCourseId();
|
||||
|
||||
Set<Integer> authSet = null;
|
||||
if (backendUserVO != null) {
|
||||
authSet = authService.getAuthByUserType(VideoUserType.format(userType), backendUserVO, true);
|
||||
if (authSet != null && authSet.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<Serial> wrapper = Wrappers.<Serial>lambdaQuery()
|
||||
.in(CollUtil.isNotEmpty(authSet), Serial::getAdvisorId, authSet)
|
||||
.like(StrUtil.isNotEmpty(name), Serial::getName, name)
|
||||
.eq(status != null && status != 0, Serial::getStatus, status)
|
||||
.eq(type != null && type != 0, Serial::getType, type)
|
||||
.exists(contentType != null && contentId != null,
|
||||
"select 0 from serial_content where serial_content.serial_id = serial.id and serial_content.type = {0} and serial_content.content_id = {1}",
|
||||
contentType, contentId)
|
||||
.notExists("select 0 from course_content where course_content.content_id = serial.id and course_content.type = {0} and course_content.course_id = {1}",
|
||||
CourseContentType.SERIAL.value, excludeCourseId)
|
||||
.ne(!VideoUserType.MANAGER_USER.value.equals(userType), Serial::getStatus, SerialStatus.DELETED.value)
|
||||
.orderByDesc(Serial::getCreateTime);
|
||||
Page<Serial> page = serialMapper.selectPage(query.toPage(), wrapper);
|
||||
Map<Integer, String> advisorNameMap = advisorInfoService.getAdvisorMap().values().stream()
|
||||
.collect(Collectors.toMap(AdvisorBasic::getId, AdvisorBasic::getName));
|
||||
Map<Integer, String> userNameMap = userService.getUserMap().values().stream()
|
||||
.collect(Collectors.toMap(UserDept::getUserId, UserDept::getName));
|
||||
List<SerialVO> list = page.getRecords().stream().map(serial -> new SerialVO(
|
||||
serial,
|
||||
advisorNameMap.get(serial.getAdvisorId()),
|
||||
userNameMap.get(serial.getCreateUserId()),
|
||||
userNameMap.get(serial.getAuditUserId()),
|
||||
getContentCount(serial.getId())
|
||||
)).collect(Collectors.toList());
|
||||
return new Pager<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询合集详情
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 合集详情
|
||||
*/
|
||||
public SerialVO get(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
Integer id = query.getId();
|
||||
Serial serial = getEntity(id, backendUserVO);
|
||||
if (serial == null) {
|
||||
return null;
|
||||
}
|
||||
Map<Integer, String> advisorNameMap = advisorInfoService.getAdvisorMap().values().stream()
|
||||
.collect(Collectors.toMap(AdvisorBasic::getId, AdvisorBasic::getName));
|
||||
Map<Integer, String> userNameMap = userService.getUserMap().values().stream()
|
||||
.collect(Collectors.toMap(UserDept::getUserId, UserDept::getName));
|
||||
return new SerialVO(
|
||||
serial,
|
||||
advisorNameMap.get(serial.getAdvisorId()),
|
||||
userNameMap.get(serial.getCreateUserId()),
|
||||
userNameMap.get(serial.getAuditUserId()),
|
||||
getContentCount(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询合集内容列表
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 合集内容列表
|
||||
*/
|
||||
public Pager<SerialContentVO> listContent(OnlyIdPageQuery query, BackendUserVO backendUserVO) {
|
||||
// 校验合集是否存在以及数据权限
|
||||
Serial serial = getEntity(query.getId(), backendUserVO);
|
||||
if (serial == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
Integer type = serial.getType();
|
||||
LambdaQueryWrapper<SerialContent> wrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.eq(SerialContent::getSerialId, query.getId())
|
||||
.eq(SerialContent::getType, type)
|
||||
.orderByDesc(SerialContent::getIsRecommend, SerialContent::getCreateTime);
|
||||
Page<SerialContent> page = serialContentMapper.selectPage(query.toPage(), wrapper);
|
||||
List<SerialContentVO> list = page.getRecords().stream().map(content -> {
|
||||
if (SerialType.SHORT_VIDEO.value.equals(type)) {
|
||||
ShortVideoVO shortVideo = shortVideoService.get(new OnlyIdQuery(content.getContentId()), null);
|
||||
return new SerialContentVO(content, shortVideo);
|
||||
} else if (SerialType.VIDEO.value.equals(type)) {
|
||||
VideoDetailAppVO video = appVideoInfoService.getVideoDetail(null, content.getContentId(), IsOrNot.NOT.value, null, null);
|
||||
return new SerialContentVO(content, video);
|
||||
}
|
||||
return null;
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
return new Pager<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑合集内容
|
||||
*
|
||||
* @param query 编辑合集内容参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateContent(UpdateSerialContentQuery query, BackendUserVO backendUserVO) {
|
||||
// 校验合集是否存在以及数据权限
|
||||
Serial serial = getEntity(query.getSerialId(), backendUserVO);
|
||||
if (serial == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
// 构造查询条件
|
||||
LambdaQueryWrapper<SerialContent> wrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.eq(SerialContent::getSerialId, query.getSerialId())
|
||||
.eq(SerialContent::getType, serial.getType())
|
||||
.eq(SerialContent::getContentId, query.getContentId());
|
||||
UpdateContentEvent event = UpdateContentEvent.fromValue(query.getEvent());
|
||||
switch (event) {
|
||||
case SAVE:
|
||||
saveContentList(query.getSerialId(), serial.getType(), query.toContentListPO(serial.getType()), event);
|
||||
break;
|
||||
case RECOMMEND:
|
||||
if (query.getIsRecommend() == null || IsRecommend.NO.value.equals(query.getIsRecommend())) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR, "权重值错误");
|
||||
}
|
||||
SerialContent recommendContent = new SerialContent();
|
||||
recommendContent.setIsRecommend(query.getIsRecommend());
|
||||
serialContentMapper.update(recommendContent, wrapper);
|
||||
break;
|
||||
case UN_RECOMMEND:
|
||||
SerialContent unRecommendContent = new SerialContent();
|
||||
unRecommendContent.setIsRecommend(IsRecommend.NO.value);
|
||||
serialContentMapper.update(unRecommendContent, wrapper);
|
||||
break;
|
||||
case ADD:
|
||||
saveContentList(query.getSerialId(), serial.getType(), query.toContentListPO(serial.getType()), event);
|
||||
break;
|
||||
case DELETE:
|
||||
saveContentList(query.getSerialId(), serial.getType(), Collections.singletonList(query.toContentPO(serial.getType())), event);
|
||||
break;
|
||||
}
|
||||
clearCache(query.getSerialId());
|
||||
}
|
||||
|
||||
/**
|
||||
* APP端查询合集详情
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @return 合集详情
|
||||
*/
|
||||
public SerialVO getForApp(OnlyIdQuery query, FrontUserVO frontUserVO) {
|
||||
SerialVO vo = cacheService.get(CacheKey.COURSE, CacheKey.CourseKey.SERIAL_INFO + query.getId(), () -> {
|
||||
SerialVO serial = get(query, null);
|
||||
if (serial == null) {
|
||||
return null;
|
||||
}
|
||||
// 如果合集本来就没有权限,不计算父级权限,允许免费观看
|
||||
if (!StringUtils.isBlank(serial.getAuthorityId())) {
|
||||
serial.setAuthorityId(getAuthorityId(query.getId()));
|
||||
}
|
||||
return serial;
|
||||
});
|
||||
if (vo == null) {
|
||||
return null;
|
||||
}
|
||||
AuthResultVO authResultVo = appUserService.checkAuth(vo.getAuthorityId(), frontUserVO);
|
||||
vo.setAuthResultVo(authResultVo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* APP端查询合集内容列表
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @return 合集内容列表
|
||||
*/
|
||||
public List<SerialContentVO> listContentForApp(OnlyIdQuery query, FrontUserVO frontUserVO) {
|
||||
if (frontUserVO != null) {
|
||||
SerialVO vo = getForApp(query, frontUserVO);
|
||||
if (vo == null) {
|
||||
return null;
|
||||
}
|
||||
if (!vo.getAuthResultVo().getAuth()) {
|
||||
throw new BizException(ResponseStatus.NOT_PRODUCT_AUTH);
|
||||
}
|
||||
}
|
||||
return cacheService.get(CacheKey.COURSE, CacheKey.CourseKey.SERIAL_CONTENT + query.getId(), () -> {
|
||||
OnlyIdPageQuery listQuery = new OnlyIdPageQuery(query.getId());
|
||||
listQuery.setCurrent(1);
|
||||
listQuery.setSize(Integer.MAX_VALUE);
|
||||
Pager<SerialContentVO> page = listContent(listQuery, null);
|
||||
// 过滤内容状态
|
||||
return page.getList().stream().filter(c ->
|
||||
SerialType.VIDEO.value.equals(c.getType()) && VideoStatus.PASS.value.equals(c.getVideo().getStatus())
|
||||
|| SerialType.SHORT_VIDEO.value.equals(c.getType()) && ShortVideoStatus.AUDITED.value.equals(c.getShortVideo().getStatus())
|
||||
).collect(Collectors.toList());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询合集内容数量
|
||||
*
|
||||
* @param serialId 合集ID
|
||||
* @return 合集内容数量
|
||||
*/
|
||||
public int getContentCount(Integer serialId) {
|
||||
LambdaQueryWrapper<SerialContent> wrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.eq(SerialContent::getSerialId, serialId);
|
||||
return serialContentMapper.selectCount(wrapper).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存内容列表
|
||||
*
|
||||
* @param contentId 内容ID
|
||||
* @param contentType 内容类型
|
||||
* @param serialIds 集合ID列表
|
||||
*/
|
||||
public void saveContentList(Integer contentId, SerialType contentType, List<Integer> serialIds) {
|
||||
LambdaQueryWrapper<SerialContent> wrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.select(SerialContent::getSerialId)
|
||||
.eq(SerialContent::getContentId, contentId)
|
||||
.eq(SerialContent::getType, contentType.value);
|
||||
List<SerialContent> existSerialList = serialContentMapper.selectList(wrapper);
|
||||
Set<Integer> existSerialIdSet = existSerialList.stream().map(SerialContent::getSerialId).collect(Collectors.toSet());
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (CollUtil.isNotEmpty(serialIds)) {
|
||||
// 添加新增的
|
||||
List<SerialContent> contentList = serialIds.stream().filter(serialId -> !existSerialIdSet.contains(serialId)).map(serialId -> {
|
||||
SerialContent content = new SerialContent();
|
||||
content.setSerialId(serialId);
|
||||
content.setContentId(contentId);
|
||||
content.setType(contentType.value);
|
||||
content.setCreateTime(now);
|
||||
content.setIsRecommend(IsRecommend.NO.value);
|
||||
return content;
|
||||
}).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(contentList)) {
|
||||
serialContentMapper.insertBatchSomeColumn(contentList);
|
||||
}
|
||||
}
|
||||
// 删除多余的
|
||||
existSerialList.stream().filter(content -> !serialIds.contains(content.getSerialId())).forEach(content -> {
|
||||
LambdaQueryWrapper<SerialContent> deleteWrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.eq(SerialContent::getSerialId, content.getSerialId())
|
||||
.eq(SerialContent::getContentId, contentId)
|
||||
.eq(SerialContent::getType, contentType.value);
|
||||
serialContentMapper.delete(deleteWrapper);
|
||||
});
|
||||
if (CollUtil.isNotEmpty(serialIds)) {
|
||||
serialIds.forEach(this::refreshUpdateTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询包含此内容且最后更新的合集
|
||||
*
|
||||
* @param contentId 内容ID
|
||||
* @param serialType 合集类型
|
||||
* @return 合集
|
||||
*/
|
||||
public SerialVO getLastUpdateSerial(Integer contentId, SerialType serialType) {
|
||||
LambdaQueryWrapper<SerialContent> wrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.select(SerialContent::getSerialId)
|
||||
.eq(SerialContent::getContentId, contentId)
|
||||
.eq(SerialContent::getType, serialType.value)
|
||||
.orderByDesc(SerialContent::getCreateTime)
|
||||
.last("limit 1");
|
||||
SerialContent serialContent = serialContentMapper.selectOne(wrapper);
|
||||
if (serialContent == null) {
|
||||
return null;
|
||||
}
|
||||
return get(new OnlyIdQuery(serialContent.getSerialId()), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*
|
||||
* @param id 合集ID
|
||||
*/
|
||||
private void clearCache(Integer id) {
|
||||
courseCache.delete(CacheKey.CourseKey.SERIAL_INFO + id);
|
||||
courseCache.delete(CacheKey.CourseKey.SERIAL_CONTENT + id);
|
||||
LambdaQueryWrapper<CourseContent> wrapper = Wrappers.<CourseContent>lambdaQuery()
|
||||
.select(CourseContent::getCourseId)
|
||||
.eq(CourseContent::getContentId, id)
|
||||
.eq(CourseContent::getType, CourseContentType.SERIAL.value);
|
||||
List<CourseContent> list = courseContentMapper.selectList(wrapper);
|
||||
list.stream().map(CourseContent::getCourseId).forEach(courseId -> courseService.clearCache(courseId, null));
|
||||
}
|
||||
|
||||
private Serial getEntity(Integer id, BackendUserVO backendUserVO) {
|
||||
Serial serial = serialMapper.selectById(id);
|
||||
if (serial == null) {
|
||||
return null;
|
||||
}
|
||||
// 数据权限检查
|
||||
courseCommonService.checkAdvisorId(serial.getAdvisorId(), backendUserVO);
|
||||
return serial;
|
||||
}
|
||||
|
||||
private void saveContentList(Integer serialId, Integer contentType, List<SerialContent> contentListPO, UpdateContentEvent event) {
|
||||
LambdaQueryWrapper<SerialContent> wrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.select(SerialContent::getContentId)
|
||||
.eq(SerialContent::getSerialId, serialId)
|
||||
.eq(SerialContent::getType, contentType);
|
||||
List<SerialContent> existContentList = serialContentMapper.selectList(wrapper);
|
||||
Set<Integer> existContentIdSet = existContentList.stream().map(SerialContent::getContentId).collect(Collectors.toSet());
|
||||
if (UpdateContentEvent.SAVE.equals(event) || UpdateContentEvent.ADD.equals(event)) {
|
||||
// 添加新增的
|
||||
List<SerialContent> contentList = contentListPO.stream().filter(content -> !existContentIdSet.contains(content.getContentId())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(contentList)) {
|
||||
serialContentMapper.insertBatchSomeColumn(contentList);
|
||||
}
|
||||
}
|
||||
if (UpdateContentEvent.SAVE.equals(event) || UpdateContentEvent.DELETE.equals(event)) {
|
||||
Stream<SerialContent> contentStream = Stream.empty();
|
||||
if (UpdateContentEvent.SAVE.equals(event)) {
|
||||
// 删除多余的
|
||||
Set<Integer> contentIdSet = contentListPO.stream().map(SerialContent::getContentId).collect(Collectors.toSet());
|
||||
contentStream = existContentList.stream().filter(content -> !contentIdSet.contains(content.getContentId()));
|
||||
} else if (UpdateContentEvent.DELETE.equals(event)) {
|
||||
// 删除传入的
|
||||
contentStream = contentListPO.stream();
|
||||
}
|
||||
contentStream.forEach(content -> {
|
||||
LambdaQueryWrapper<SerialContent> deleteWrapper = Wrappers.<SerialContent>lambdaQuery()
|
||||
.eq(SerialContent::getSerialId, serialId)
|
||||
.eq(SerialContent::getContentId, content.getContentId())
|
||||
.eq(SerialContent::getType, contentType);
|
||||
serialContentMapper.delete(deleteWrapper);
|
||||
});
|
||||
}
|
||||
refreshUpdateTime(serialId);
|
||||
}
|
||||
|
||||
private void refreshUpdateTime(Integer serialId) {
|
||||
Serial serial = new Serial();
|
||||
serial.setId(serialId);
|
||||
serial.setUpdateTime(LocalDateTime.now());
|
||||
serialMapper.updateById(serial);
|
||||
}
|
||||
|
||||
private String getAuthorityId(Integer id) {
|
||||
String authorityId = serialMapper.getAuthorityId(id, CourseContentType.SERIAL.value);
|
||||
return authorityId == null ? null : Arrays.stream(authorityId.split(",|,")).distinct().collect(Collectors.joining(","));
|
||||
}
|
||||
}
|
||||
@ -1,879 +0,0 @@
|
||||
package com.upchina.course.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.hazelcast.collection.ISet;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.upchina.advisor.service.AdvisorInfoService;
|
||||
import com.upchina.advisor.vo.AdvisorBasicVO;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsDisplay;
|
||||
import com.upchina.common.constant.IsLike;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.entity.VideoTransFlow;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.mapper.VideoTransFlowMapper;
|
||||
import com.upchina.common.query.OnlyIdQuery;
|
||||
import com.upchina.common.result.AppPager;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.service.*;
|
||||
import com.upchina.common.state.StateMachine;
|
||||
import com.upchina.common.util.LoggerUtil;
|
||||
import com.upchina.common.vo.*;
|
||||
import com.upchina.course.constant.*;
|
||||
import com.upchina.course.entity.*;
|
||||
import com.upchina.course.mapper.*;
|
||||
import com.upchina.course.query.*;
|
||||
import com.upchina.course.vo.*;
|
||||
import com.upchina.rbac.entity.UserDept;
|
||||
import com.upchina.rbac.entity.WxUser;
|
||||
import com.upchina.rbac.mapper.WxUserMapper;
|
||||
import com.upchina.rbac.service.AuthService;
|
||||
import com.upchina.rbac.service.UserService;
|
||||
import com.upchina.video.constant.VideoLimitType;
|
||||
import com.upchina.video.constant.VideoTransStatus;
|
||||
import com.upchina.video.constant.VideoUserType;
|
||||
import com.upchina.video.entity.CloudMediaEntity;
|
||||
import com.upchina.video.query.info.AppLimitQuery;
|
||||
import com.upchina.video.service.app.AppVideoInfoService;
|
||||
import com.upchina.video.service.common.VideoCloudService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// 短视频Service
|
||||
@Service
|
||||
public class ShortVideoService {
|
||||
|
||||
@Resource
|
||||
private ShortVideoMapper shortVideoMapper;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private SensitiveWordService sensitiveWordService;
|
||||
|
||||
@Resource
|
||||
private AuthService authService;
|
||||
|
||||
@Resource
|
||||
private AdvisorInfoService advisorInfoService;
|
||||
|
||||
@Resource
|
||||
private StateMachine<ShortVideoStatus> shortVideoSM;
|
||||
|
||||
@Resource
|
||||
private ShortVideoCartMapper shortVideoCartMapper;
|
||||
|
||||
@Resource
|
||||
private ShortVideoFavorMapper shortVideoFavorMapper;
|
||||
|
||||
@Resource
|
||||
private CommentService commentService;
|
||||
|
||||
@Resource
|
||||
private SerialService serialService;
|
||||
|
||||
@Resource
|
||||
private VideoCloudService videoCloudService;
|
||||
|
||||
@Resource
|
||||
private CourseCommonService courseCommonService;
|
||||
|
||||
@Resource
|
||||
private ShortVideoWatchMapper shortVideoWatchMapper;
|
||||
|
||||
@Resource
|
||||
private ShortVideoShareMapper shortVideoShareMapper;
|
||||
|
||||
@Resource
|
||||
private HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Resource
|
||||
private AppUserService appUserService;
|
||||
|
||||
@Resource
|
||||
private AppVideoInfoService appVideoInfoService;
|
||||
|
||||
@Resource
|
||||
private ShortVideoCartClickMapper shortVideoCartClickMapper;
|
||||
|
||||
@Resource
|
||||
private WxUserMapper wxUserMapper;
|
||||
|
||||
@Resource
|
||||
private UrlService urlService;
|
||||
|
||||
@Resource
|
||||
private ShortVideoSaleMapper shortVideoSaleMapper;
|
||||
|
||||
@Resource
|
||||
private IMap<String, Object> courseCache;
|
||||
|
||||
@Resource
|
||||
private VideoTransFlowMapper videoTransFlowMapper;
|
||||
|
||||
@Value("${video.finishReadRatio}")
|
||||
private Double finishReadRatio;
|
||||
|
||||
@Value("${resizeUrl.shortVideoUrl}")
|
||||
private String shortVideoUrl;
|
||||
|
||||
/**
|
||||
* 保存短视频
|
||||
*
|
||||
* @param query 保存短视频参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 短视频ID
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public InsertIdVO save(SaveShortVideoQuery query, BackendUserVO backendUserVO) {
|
||||
sensitiveWordService.check(query.getTitle(), query.getViewPoint());
|
||||
Integer advisorId = courseCommonService.getAdvisorId(query.getAdvisorId(), backendUserVO);
|
||||
// 检测短视频回调情况
|
||||
boolean hasTrans = checkShortVideoHasTrans(query.getFileId());
|
||||
ShortVideo shortVideo = query.toPO(backendUserVO, advisorId, hasTrans);
|
||||
shortVideoMapper.insert(shortVideo);
|
||||
Integer id = shortVideo.getId();
|
||||
|
||||
// 保存购物车
|
||||
if (IsOrNot.IS.value.equals(query.getIsCart())) {
|
||||
List<ShortVideoCart> cartList = query.toCartListPO(id);
|
||||
saveCartList(id, cartList);
|
||||
} else {
|
||||
removeCartList(id);
|
||||
}
|
||||
|
||||
// 保存合集
|
||||
if (CollUtil.isNotEmpty(query.getSerialIds())) {
|
||||
serialService.saveContentList(id, SerialType.SHORT_VIDEO, query.getSerialIds());
|
||||
}
|
||||
|
||||
// 检查转码状态,防止先转码成功,数据还没有保存的问题
|
||||
List<CloudMediaEntity> cloudMediaEntities = videoCloudService.describeMediaInfos(Collections.singleton(shortVideo.getFileId()));
|
||||
if (CollUtil.isNotEmpty(cloudMediaEntities)) {
|
||||
CloudMediaEntity media = cloudMediaEntities.get(0);
|
||||
ShortVideo updateVideo = new ShortVideo();
|
||||
updateVideo.setId(id);
|
||||
updateVideo.setFileName(media.getName());
|
||||
updateVideo.setDuration(media.getDuration().intValue());
|
||||
updateVideo.setSize(media.getSize().intValue());
|
||||
updateVideo.setTransStatus(VideoTransStatus.HAS_TRANSCODE.value);
|
||||
shortVideoMapper.updateById(updateVideo);
|
||||
}
|
||||
|
||||
|
||||
return new InsertIdVO(id);
|
||||
}
|
||||
|
||||
/*
|
||||
检测是否上传视频已回调 true已转码 false未转码
|
||||
*/
|
||||
public boolean checkShortVideoHasTrans(String fileId) {
|
||||
VideoTransFlow videoTransFlow = videoTransFlowMapper.selectOne(new QueryWrapper<VideoTransFlow>()
|
||||
.eq("file_id", fileId)
|
||||
.last("limit 1")
|
||||
);
|
||||
return videoTransFlow != null && Objects.equals(videoTransFlow.getErrCode(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新短视频
|
||||
*
|
||||
* @param query 更新短视频参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(UpdateShortVideoQuery query, BackendUserVO backendUserVO) {
|
||||
sensitiveWordService.check(query.getTitle(), query.getViewPoint());
|
||||
|
||||
Integer id = query.getId();
|
||||
ShortVideo videoInDB = shortVideoMapper.selectById(id);
|
||||
if (videoInDB == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
|
||||
// 状态机扭转,判断是否能够编辑
|
||||
ShortVideoStatus dbStatus = ShortVideoStatus.fromValue(videoInDB.getStatus());
|
||||
shortVideoSM.send(dbStatus, ShortVideoStatus.EVENT_UPDATE);
|
||||
|
||||
ShortVideo shortVideo = query.toPO(backendUserVO, checkShortVideoHasTrans(query.getFileId()));
|
||||
shortVideoMapper.updateById(shortVideo);
|
||||
|
||||
// 保存购物车
|
||||
if (IsOrNot.IS.value.equals(query.getIsCart())) {
|
||||
List<ShortVideoCart> cartList = query.toCartListPO(id);
|
||||
saveCartList(id, cartList);
|
||||
} else {
|
||||
removeCartList(id);
|
||||
}
|
||||
|
||||
// 保存合集
|
||||
if (CollUtil.isNotEmpty(query.getSerialIds())) {
|
||||
serialService.saveContentList(id, SerialType.SHORT_VIDEO, query.getSerialIds());
|
||||
}
|
||||
|
||||
clearCache(id, videoInDB.getAdvisorId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新短视频状态
|
||||
*
|
||||
* @param query 更新短视频状态参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateStatus(UpdateShortVideoStatusQuery query, BackendUserVO backendUserVO) {
|
||||
ShortVideo videoInDB = shortVideoMapper.selectById(query.getId());
|
||||
if (videoInDB == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
// 状态机扭转
|
||||
ShortVideoStatus dbStatus = ShortVideoStatus.fromValue(videoInDB.getStatus());
|
||||
ShortVideoStatus targetStatus = shortVideoSM.send(dbStatus, ShortVideoStatus.fromValue(query.getEvent()));
|
||||
|
||||
ShortVideo shortVideo = query.toPO(targetStatus, backendUserVO);
|
||||
shortVideoMapper.updateById(shortVideo);
|
||||
clearCache(query.getId(), videoInDB.getAdvisorId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询短视频列表
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 短视频列表
|
||||
*/
|
||||
public Pager<ShortVideoVO> list(ListShortVideoQuery query, BackendUserVO backendUserVO) {
|
||||
String title = query.getTitle();
|
||||
Integer status = query.getStatus();
|
||||
Integer userType = query.getUserType();
|
||||
Integer advisorId = query.getAdvisorId();
|
||||
Integer excludeSerialId = query.getExcludeSerialId();
|
||||
Integer isDisplay = query.getIsDisplay();
|
||||
|
||||
Set<Integer> authSet = authService.getAuthByUserType(VideoUserType.format(userType), backendUserVO, true);
|
||||
if (authSet != null && authSet.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LambdaQueryWrapper<ShortVideo> wrapper = Wrappers.<ShortVideo>lambdaQuery()
|
||||
.in(CollUtil.isNotEmpty(authSet), ShortVideo::getAdvisorId, authSet)
|
||||
.eq(advisorId != null && advisorId != 0, ShortVideo::getAdvisorId, advisorId)
|
||||
.like(StrUtil.isNotEmpty(title), ShortVideo::getTitle, title)
|
||||
.eq(status != null && status != 0, ShortVideo::getStatus, status)
|
||||
.eq(isDisplay != null && isDisplay != 0, ShortVideo::getIsDisplay, isDisplay)
|
||||
.notExists(excludeSerialId != null && excludeSerialId != 0, "select 0 from serial_content where serial_content.content_id = short_video.id and serial_content.serial_id = {0} and type = {1}", excludeSerialId, SerialType.SHORT_VIDEO.value)
|
||||
.ne(!VideoUserType.MANAGER_USER.value.equals(userType), ShortVideo::getStatus, ShortVideoStatus.DELETED.value)
|
||||
.orderByDesc(!IsOrNot.IS.value.equals(isDisplay), ShortVideo::getCreateTime)
|
||||
.orderByDesc(IsOrNot.IS.value.equals(isDisplay), ShortVideo::getIsRecommend);
|
||||
Page<ShortVideo> page = shortVideoMapper.selectPage(query.toPage(), wrapper);
|
||||
Map<Integer, AdvisorBasicVO> advisorMap = advisorInfoService.getAdvisorVoMap();
|
||||
Map<Integer, String> userNameMap = userService.getUserMap().values().stream()
|
||||
.collect(Collectors.toMap(UserDept::getUserId, UserDept::getName));
|
||||
List<ShortVideoVO> list = page.getRecords().stream().map(video -> new ShortVideoVO(
|
||||
video,
|
||||
advisorMap.get(video.getAdvisorId()),
|
||||
userNameMap.get(video.getCreateUserId()),
|
||||
userNameMap.get(video.getAuditUserId()),
|
||||
getFavorCount(video.getId()),
|
||||
commentService.queryCommentCount(video.getId(), ProductType.SHORT_VIDEO.value, null, true),
|
||||
shortVideoWatchMapper.selectWatchCount(video.getId())
|
||||
)).collect(Collectors.toList());
|
||||
return new Pager<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询短视频详情
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后台用户
|
||||
* @return 短视频详情
|
||||
*/
|
||||
public ShortVideoVO get(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
Integer id = query.getId();
|
||||
ShortVideo video = getEntity(id, backendUserVO);
|
||||
if (video == null) {
|
||||
return null;
|
||||
}
|
||||
Map<Integer, AdvisorBasicVO> advisorMap = advisorInfoService.getAdvisorVoMap();
|
||||
Map<Integer, String> userNameMap = userService.getUserMap().values().stream()
|
||||
.collect(Collectors.toMap(UserDept::getUserId, UserDept::getName));
|
||||
ShortVideoVO vo = new ShortVideoVO(
|
||||
video,
|
||||
advisorMap.get(video.getAdvisorId()),
|
||||
userNameMap.get(video.getCreateUserId()),
|
||||
userNameMap.get(video.getAuditUserId()),
|
||||
getFavorCount(id),
|
||||
commentService.queryCommentCount(id, ProductType.SHORT_VIDEO.value, null, true),
|
||||
shortVideoWatchMapper.selectWatchCount(id)
|
||||
);
|
||||
List<ShortVideoCart> cartList = shortVideoCartMapper.selectList(Wrappers.<ShortVideoCart>lambdaQuery()
|
||||
.eq(ShortVideoCart::getVideoId, id)
|
||||
.orderByDesc(ShortVideoCart::getWeight));
|
||||
vo.setCartList(cartList.stream().map(ShortVideoCartVO::new).collect(Collectors.toList()));
|
||||
vo.setSerialList(listSerial(id, backendUserVO));
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置首页参数
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
public void setMainPageParam(SetMainPageQuery query, BackendUserVO backendUserVO) {
|
||||
// 不使用返回值,仅校验课程是否存在以及数据权限
|
||||
ShortVideo videoInDB = getEntity(query.getId(), backendUserVO);
|
||||
if (videoInDB == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
ShortVideo video = query.toShortVideoPO();
|
||||
shortVideoMapper.updateById(video);
|
||||
this.clearCache(query.getId(), videoInDB.getAdvisorId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新购物车商品上下架
|
||||
*
|
||||
* @param query 更新购物车商品上下架参数
|
||||
* @param backendUserVO 后台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateCartStatus(UpdateCartStatusQuery query, BackendUserVO backendUserVO) {
|
||||
Integer id = query.getVideoId();
|
||||
ShortVideo video = shortVideoMapper.selectById(id);
|
||||
if (video == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
// 数据权限检查
|
||||
courseCommonService.checkAdvisorId(video.getAdvisorId(), backendUserVO);
|
||||
LambdaQueryWrapper<ShortVideoCart> wrapper = Wrappers.<ShortVideoCart>lambdaQuery()
|
||||
.eq(ShortVideoCart::getVideoId, id)
|
||||
.eq(ShortVideoCart::getProductId, query.getProductId())
|
||||
.eq(ShortVideoCart::getProductType, query.getProductType());
|
||||
ShortVideoCart cart = new ShortVideoCart();
|
||||
cart.setStatus(query.getStatus());
|
||||
int rows = shortVideoCartMapper.update(cart, wrapper);
|
||||
if (rows == 0) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
clearCache(id, video.getAdvisorId());
|
||||
}
|
||||
|
||||
/**
|
||||
* APP端查询短视频详情
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param frontUserVO 前台用户
|
||||
* @return 短视频详情
|
||||
*/
|
||||
public ShortVideoVO getForApp(IdAndSaleUserQuery query, FrontUserVO frontUserVO, boolean removeLimitField) {
|
||||
Integer id = query.getId();
|
||||
Integer saleUserId = query.getSaleUserId();
|
||||
ShortVideoVO vo = cacheService.get(courseCache, CacheKey.CourseKey.SHORT_VIDEO + id, () -> {
|
||||
ShortVideoVO video = get(query.toOnlyIdQuery(), null);
|
||||
if (video == null) {
|
||||
return null;
|
||||
}
|
||||
// 如果视频本来就没有权限,不计算父级权限,允许免费观看
|
||||
if (!StringUtils.isBlank(video.getAuthorityId())) {
|
||||
video.setAuthorityId(getAuthorityId(id));
|
||||
}
|
||||
return video;
|
||||
});
|
||||
if (vo == null) {
|
||||
return null;
|
||||
}
|
||||
vo.setFavorCount(getFavorCount(id));
|
||||
vo.setCommentCount(commentService.queryCommentCount(id, ProductType.SHORT_VIDEO.value, frontUserVO, false));
|
||||
vo.setWatchCount(shortVideoWatchMapper.selectWatchCount(id));
|
||||
if (frontUserVO != null) {
|
||||
vo.setIsFavor(isFavor(query.getId(), frontUserVO.getUserId()));
|
||||
}
|
||||
vo.setAuthResultVo(appUserService.checkAuth(vo.getAuthorityId(), frontUserVO));
|
||||
vo.setIsLiving(appVideoInfoService.livingByAdvisorId(vo.getAdvisorId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
|
||||
// app端不返回合集列表,只返回最近更新的一个
|
||||
vo.setSerialList(null);
|
||||
vo.setSerial(serialService.getLastUpdateSerial(id, SerialType.SHORT_VIDEO));
|
||||
vo.setResizeUrl(urlService.getResizeUrlByApp(shortVideoUrl, CacheKey.URL_KEY.URL_KEY_SHORT_VIDEO_ID, id, saleUserId));
|
||||
vo.setUpcomingLive(appVideoInfoService.getNotPlayVideoByAdvisorId(vo.getAdvisorId(), frontUserVO));
|
||||
if (removeLimitField) {
|
||||
vo.setAuthorityId(null);
|
||||
vo.setInviteCode(null);
|
||||
}
|
||||
// 插入营销关系
|
||||
if (frontUserVO != null && saleUserId != null) {
|
||||
LambdaQueryWrapper<ShortVideoSale> saleWrapper = Wrappers.<ShortVideoSale>lambdaQuery()
|
||||
.eq(ShortVideoSale::getUserId, frontUserVO.getUserId())
|
||||
.eq(ShortVideoSale::getVideoId, id);
|
||||
ShortVideoSale sale = shortVideoSaleMapper.selectOne(saleWrapper);
|
||||
if (sale == null) {
|
||||
sale = new ShortVideoSale();
|
||||
sale.setUserId(frontUserVO.getUserId());
|
||||
sale.setVideoId(id);
|
||||
sale.setSaleUserId(saleUserId);
|
||||
shortVideoSaleMapper.insert(sale);
|
||||
} else {
|
||||
sale = new ShortVideoSale();
|
||||
sale.setUserId(frontUserVO.getUserId());
|
||||
sale.setVideoId(id);
|
||||
sale.setSaleUserId(saleUserId);
|
||||
shortVideoSaleMapper.update(sale, saleWrapper);
|
||||
}
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* APP端点赞短视频
|
||||
*
|
||||
* @param query 点赞短视频参数
|
||||
* @param frontUserVO 前台用户
|
||||
* @return 点赞数
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CountVO favor(FavorVideoQuery query, FrontUserVO frontUserVO) {
|
||||
Integer videoId = query.getVideoId();
|
||||
Integer option = query.getOption();
|
||||
String userId = frontUserVO.getUserId();
|
||||
if (IsLike.YES.value.equals(option)) {
|
||||
try {
|
||||
shortVideoFavorMapper.insert(query.toPO(userId));
|
||||
} catch (DuplicateKeyException e) {
|
||||
throw new BizException(ResponseStatus.REPETITIVE_ERROR);
|
||||
}
|
||||
getFavorSet(videoId).add(userId);
|
||||
} else if (IsLike.NO.value.equals(option)) {
|
||||
LambdaQueryWrapper<ShortVideoFavor> wrapper = Wrappers.<ShortVideoFavor>lambdaQuery()
|
||||
.eq(ShortVideoFavor::getUserId, userId)
|
||||
.eq(ShortVideoFavor::getVideoId, videoId);
|
||||
shortVideoFavorMapper.delete(wrapper);
|
||||
getFavorSet(videoId).remove(userId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return new CountVO(getFavorCount(videoId));
|
||||
}
|
||||
|
||||
/**
|
||||
* APP端查询短视频列表
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param frontUserVO 前台用户
|
||||
* @return 短视频列表
|
||||
*/
|
||||
public AppPager<ShortVideoVO> listForApp(ListCourseAppQuery query, FrontUserVO frontUserVO) {
|
||||
Integer id = query.getId();
|
||||
Integer lastId = query.getLastId();
|
||||
LocalDateTime lastPublishTime = query.getLastPublishTime();
|
||||
Integer lastWeight = query.getLastWeight();
|
||||
Integer size = query.getSize();
|
||||
NavigableSet<CourseSortEntity> sortedSet = cacheService.get(courseCache, CacheKey.CourseKey.MAIN_SHORT_VIDEO_LIST + id, () -> {
|
||||
LambdaQueryWrapper<ShortVideo> wrapper = Wrappers.<ShortVideo>lambdaQuery()
|
||||
.eq(ShortVideo::getAdvisorId, id)
|
||||
.eq(ShortVideo::getStatus, CourseStatus.AUDITED.value)
|
||||
.eq(ShortVideo::getIsDisplay, IsDisplay.YES.value)
|
||||
.orderByDesc(ShortVideo::getIsRecommend, ShortVideo::getAuditTime);
|
||||
List<ShortVideo> list = shortVideoMapper.selectList(wrapper);
|
||||
NavigableSet<CourseSortEntity> set = new TreeSet<>();
|
||||
list.stream().map(CourseSortEntity::new).forEach(set::add);
|
||||
return set;
|
||||
});
|
||||
CourseSortEntity lastEntity = lastId == null || lastWeight == null || lastPublishTime == null ? null : new CourseSortEntity(lastId, lastWeight, lastPublishTime);
|
||||
if (lastEntity != null) {
|
||||
sortedSet = sortedSet.tailSet(lastEntity, false);
|
||||
}
|
||||
List<ShortVideoVO> voList = new ArrayList<>(size);
|
||||
Iterator<CourseSortEntity> iterator = sortedSet.iterator();
|
||||
while (iterator.hasNext() && voList.size() < size) {
|
||||
CourseSortEntity entity = iterator.next();
|
||||
ShortVideoVO vo = getForApp(new IdAndSaleUserQuery(entity.getId()), null, true);
|
||||
if (vo != null) {
|
||||
voList.add(vo);
|
||||
}
|
||||
}
|
||||
return new AppPager<>(voList, iterator.hasNext());
|
||||
}
|
||||
|
||||
public OnlyBoolVO checkLimit(AppLimitQuery query, FrontUserVO frontUserVO) {
|
||||
Integer id = query.getVideoId();
|
||||
String code = query.getCode();
|
||||
ShortVideoVO vo = getForApp(new IdAndSaleUserQuery(id), frontUserVO, false);
|
||||
if (vo == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
if (!VideoLimitType.CODE.value.equals(vo.getLimitType()) || StrUtil.isBlank(vo.getInviteCode())) {
|
||||
return new OnlyBoolVO(true);
|
||||
}
|
||||
return new OnlyBoolVO(vo.getInviteCode().equals(code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存短视频观看记录
|
||||
*
|
||||
* @param query 保存短视频观看记录参数
|
||||
* @param frontUserVO 前台用户
|
||||
*/
|
||||
public void saveWatch(SaveShortVideoWatchListQuery query, FrontUserVO frontUserVO) {
|
||||
List<ShortVideoWatchVO> cacheList = hazelcastInstance.getList(CacheKey.CourseKey.SHORT_VIDEO_WATCH_LIST);
|
||||
List<ShortVideoWatchVO> voList = query.getList().stream()
|
||||
.filter(watch -> watch.getVideoId() != null)
|
||||
.map(watch -> new ShortVideoWatchVO(watch, frontUserVO))
|
||||
.collect(Collectors.toList());
|
||||
cacheList.addAll(voList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存短视频分享记录
|
||||
*
|
||||
* @param query 查询对象,包含视频ID
|
||||
* @param frontUserVO 前台用户
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveShare(OnlyIdQuery query, FrontUserVO frontUserVO) {
|
||||
LambdaQueryWrapper<ShortVideoShare> wrapper = Wrappers.<ShortVideoShare>lambdaQuery()
|
||||
.eq(ShortVideoShare::getVideoId, query.getId())
|
||||
.eq(ShortVideoShare::getUserId, frontUserVO.getUserId());
|
||||
ShortVideoShare share = shortVideoShareMapper.selectOne(wrapper);
|
||||
if (share == null) {
|
||||
share = new ShortVideoShare();
|
||||
share.setUserId(frontUserVO.getUserId());
|
||||
share.setVideoId(query.getId());
|
||||
share.setCount(1);
|
||||
share.setCreateTime(LocalDateTime.now());
|
||||
shortVideoShareMapper.insert(share);
|
||||
} else {
|
||||
share.setCount(share.getCount() + 1);
|
||||
share.setUpdateTime(LocalDateTime.now());
|
||||
shortVideoShareMapper.update(share, wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新短视频的转码状态
|
||||
*
|
||||
* @param fileIds 要刷新转码状态的短视频文件ID集合
|
||||
*/
|
||||
public void refreshTranscodeStatus(Set<String> fileIds) {
|
||||
try {
|
||||
if (CollUtil.isEmpty(fileIds)) {
|
||||
LambdaQueryWrapper<ShortVideo> wrapper = Wrappers.<ShortVideo>lambdaQuery()
|
||||
.select(ShortVideo::getFileId)
|
||||
.eq(ShortVideo::getTransStatus, TransStatus.TRANSCODING.value)
|
||||
.isNotNull(ShortVideo::getFileId);
|
||||
List<ShortVideo> transcodingVideoList = shortVideoMapper.selectList(wrapper);
|
||||
fileIds = transcodingVideoList.stream().map(ShortVideo::getFileId).collect(Collectors.toSet());
|
||||
}
|
||||
if (CollUtil.isEmpty(fileIds)) {
|
||||
return;
|
||||
}
|
||||
List<CloudMediaEntity> list = videoCloudService.describeMediaInfos(fileIds);
|
||||
for (CloudMediaEntity media : list) {
|
||||
try {
|
||||
if (Objects.equals(media.getTransStatus(), VideoTransStatus.HAS_TRANSCODE.value)) {
|
||||
LambdaQueryWrapper<ShortVideo> updateWrapper = Wrappers.<ShortVideo>lambdaQuery()
|
||||
.eq(ShortVideo::getFileId, media.getFileId());
|
||||
shortVideoMapper.update(media.toShortVideoPO(), updateWrapper);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LoggerUtil.error("更新视频资源失败:" + ExceptionUtils.getStackTrace(ex));
|
||||
}
|
||||
}
|
||||
} catch (BizException e) {
|
||||
String targets = fileIds == null ? "" : String.join(",", fileIds);
|
||||
LoggerUtil.error("查询云点播失败|" + targets + "|" + e.getErrorMsg());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存短视频观看记录
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveWatchToDB() {
|
||||
List<ShortVideoWatchVO> cacheList = hazelcastInstance.getList(CacheKey.CourseKey.SHORT_VIDEO_WATCH_LIST);
|
||||
// 合并重复项
|
||||
Set<Integer> videoIdSet = new HashSet<>();
|
||||
Map<ShortVideoWatchVO, ShortVideoWatch> map = new HashMap<>(cacheList.size());
|
||||
for (ShortVideoWatchVO vo : cacheList) {
|
||||
if (vo != null) {
|
||||
videoIdSet.add(vo.getVideoId());
|
||||
ShortVideoWatch watch = vo.toPO();
|
||||
ShortVideoWatch existWatch = map.get(vo);
|
||||
if (existWatch == null) {
|
||||
map.put(vo, watch);
|
||||
} else {
|
||||
existWatch.setCount(existWatch.getCount() + watch.getCount());
|
||||
existWatch.setSeconds(existWatch.getSeconds() + watch.getSeconds());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (videoIdSet.isEmpty() || map.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<ShortVideo> videoList = shortVideoMapper.selectBatchIds(videoIdSet);
|
||||
Map<Integer, ShortVideo> videoMap = videoList.stream().collect(Collectors.toMap(ShortVideo::getId, Function.identity()));
|
||||
// 保存
|
||||
map.forEach((k, v) -> {
|
||||
ShortVideo video = videoMap.get(v.getVideoId());
|
||||
if (video == null || video.getDuration() == null) {
|
||||
return;
|
||||
}
|
||||
LambdaQueryWrapper<ShortVideoWatch> wrapper = Wrappers.<ShortVideoWatch>lambdaQuery()
|
||||
.eq(ShortVideoWatch::getUserId, v.getUserId())
|
||||
.eq(ShortVideoWatch::getVideoId, v.getVideoId());
|
||||
ShortVideoWatch watch = shortVideoWatchMapper.selectOne(wrapper);
|
||||
if (watch == null) {
|
||||
v.setCreateTime(LocalDateTime.now());
|
||||
shortVideoWatchMapper.insert(v);
|
||||
} else {
|
||||
// 不能超过总视频时长的2倍
|
||||
v.setSeconds(Math.min(video.getDuration() * 2, watch.getSeconds() + v.getSeconds()));
|
||||
v.setCount(watch.getCount() + v.getCount());
|
||||
v.setUpdateTime(LocalDateTime.now());
|
||||
shortVideoWatchMapper.update(v, wrapper);
|
||||
}
|
||||
});
|
||||
cacheList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存购物车点击
|
||||
*
|
||||
* @param query
|
||||
* @param frontUserVO
|
||||
*/
|
||||
public void saveCartClick(SaveShortVideoCartClickQuery query, FrontUserVO frontUserVO) {
|
||||
if (frontUserVO == null || StrUtil.isBlank(frontUserVO.getUserId())) {
|
||||
throw new BizException(ResponseStatus.SESSION_USER_LOGOUT);
|
||||
}
|
||||
Integer videoId = query.getVideoId();
|
||||
Integer productId = query.getProductId();
|
||||
Integer productType = query.getProductType();
|
||||
String productName = query.getProductName();
|
||||
ShortVideoCartClick click = new ShortVideoCartClick();
|
||||
click.setUserId(frontUserVO.getUserId());
|
||||
click.setVideoId(videoId);
|
||||
click.setProductType(productType);
|
||||
click.setProductId(productId);
|
||||
click.setProductName(productName);
|
||||
try {
|
||||
// 忽略重复异常
|
||||
shortVideoCartClickMapper.insert(click);
|
||||
} catch (DuplicateKeyException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台统计短视频数据明细
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后端用户
|
||||
* @return 统计明细列表
|
||||
*/
|
||||
public Pager<ShortVideoCollectVO> listCollect(ListShortVideoCollectQuery query, BackendUserVO backendUserVO) {
|
||||
Integer id = query.getId();
|
||||
BigDecimal completeRate = query.getCompleteRate();
|
||||
ShortVideo video = getEntity(id, backendUserVO);
|
||||
if (video == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
Integer finishReadSecond = null;
|
||||
if (completeRate != null && completeRate.compareTo(BigDecimal.ZERO) > 0 && video.getDuration() != null && video.getDuration() > 0) {
|
||||
finishReadSecond = completeRate.multiply(BigDecimal.valueOf(video.getDuration())).intValue();
|
||||
}
|
||||
LambdaQueryWrapper<ShortVideoWatch> wrapper = Wrappers.<ShortVideoWatch>lambdaQuery()
|
||||
.eq(ShortVideoWatch::getVideoId, id)
|
||||
.gt(finishReadSecond != null && finishReadSecond < video.getDuration(), ShortVideoWatch::getSeconds, finishReadSecond)
|
||||
.ge(finishReadSecond != null && finishReadSecond >= video.getDuration(), ShortVideoWatch::getSeconds, finishReadSecond)
|
||||
.orderByDesc(ShortVideoWatch::getSeconds);
|
||||
Page<ShortVideoWatch> page = shortVideoWatchMapper.selectPage(query.toPage(), wrapper);
|
||||
Set<String> userIds = page.getRecords().stream().map(ShortVideoWatch::getUserId).filter(StrUtil::isNotBlank).collect(Collectors.toSet());
|
||||
Map<String, Integer> userSaleIdMap = new HashMap<>();
|
||||
Map<Integer, String> userNameMap = userService.getUserMap().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getName()));
|
||||
if (CollUtil.isNotEmpty(userIds)) {
|
||||
LambdaQueryWrapper<ShortVideoSale> saleWrapper = Wrappers.<ShortVideoSale>lambdaQuery()
|
||||
.eq(ShortVideoSale::getVideoId, id)
|
||||
.in(ShortVideoSale::getUserId, userIds);
|
||||
shortVideoSaleMapper.selectList(saleWrapper).forEach(sale -> userSaleIdMap.put(sale.getUserId(), sale.getSaleUserId()));
|
||||
}
|
||||
List<ShortVideoCollectVO> voList = page.getRecords().stream().map(watch -> {
|
||||
String userId = watch.getUserId();
|
||||
WxUser user = wxUserMapper.selectById(userId);
|
||||
Integer commentCount = commentService.getUserProductCommentCount(userId, ProductType.SHORT_VIDEO, id);
|
||||
Integer isFavor = isFavor(id, userId);
|
||||
Integer shareCount = getShareCount(id, userId);
|
||||
ShortVideoCollectVO vo = new ShortVideoCollectVO(user, video, watch, commentCount, isFavor, shareCount);
|
||||
Integer saleUserId = userSaleIdMap.get(userId);
|
||||
vo.setSaleUserId(saleUserId);
|
||||
if (saleUserId != null) {
|
||||
vo.setSaleUserName(userNameMap.get(saleUserId));
|
||||
}
|
||||
LambdaQueryWrapper<ShortVideoCartClick> cartClickWrapper = Wrappers.<ShortVideoCartClick>lambdaQuery()
|
||||
.select(ShortVideoCartClick::getProductName)
|
||||
.eq(ShortVideoCartClick::getUserId, userId)
|
||||
.eq(ShortVideoCartClick::getVideoId, id);
|
||||
List<ShortVideoCartClick> clickList = shortVideoCartClickMapper.selectList(cartClickWrapper);
|
||||
List<String> productNames = clickList.stream().map(ShortVideoCartClick::getProductName).collect(Collectors.toList());
|
||||
vo.setClickCartNames(productNames);
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
return new Pager<>(voList, page.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台统计短视频数据汇总
|
||||
*
|
||||
* @param query 查询参数
|
||||
* @param backendUserVO 后端用户
|
||||
* @return 统计汇总
|
||||
*/
|
||||
public ShortVideoCollectSummaryVO getCollectSummary(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
Integer id = query.getId();
|
||||
ShortVideo video = getEntity(id, backendUserVO);
|
||||
if (video == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
// 查询观看次数、观看人数、总播放时长
|
||||
ShortVideoCollectSummaryVO summary = shortVideoWatchMapper.selectCollectSummary(id);
|
||||
// 计算观看平均播放时长
|
||||
if (summary.getWatchSeconds() != 0 && summary.getWatchUserCount() != 0) {
|
||||
summary.setWatchSeconds(summary.getWatchSeconds() / summary.getWatchUserCount());
|
||||
} else {
|
||||
summary.setWatchSeconds(0);
|
||||
}
|
||||
// 查询完整观看人数
|
||||
if (video.getDuration() == null || video.getDuration() == 0) {
|
||||
summary.setWatchFullUserCount(0);
|
||||
} else {
|
||||
Integer finishReadSecond = (int) (video.getDuration() * finishReadRatio);
|
||||
LambdaQueryWrapper<ShortVideoWatch> wrapper = Wrappers.<ShortVideoWatch>lambdaQuery()
|
||||
.eq(ShortVideoWatch::getVideoId, id)
|
||||
.ge(ShortVideoWatch::getSeconds, finishReadSecond);
|
||||
Integer watchFullUserCount = shortVideoWatchMapper.selectCount(wrapper).intValue();
|
||||
summary.setWatchFullUserCount(watchFullUserCount);
|
||||
}
|
||||
// 评论数
|
||||
Integer commentCount = commentService.getUserProductCommentCount(null, ProductType.SHORT_VIDEO, id);
|
||||
summary.setCommentCount(commentCount);
|
||||
// 点赞数
|
||||
summary.setFavorCount(getFavorCount(id));
|
||||
// 分享数
|
||||
summary.setShareCount(getShareCount(id, null));
|
||||
// 互动率 = (评论人去重 + 分享数按人去重 + 点赞数) / 总观看人数
|
||||
BigDecimal commentRate = BigDecimal.ZERO;
|
||||
if (summary.getWatchUserCount() != 0) {
|
||||
// 评论数去重
|
||||
Integer distinctCommentCount = commentService.getDistinctUserCount(ProductType.SHORT_VIDEO, id);
|
||||
// 分享数去重
|
||||
QueryWrapper<ShortVideoShare> shareWrapper = Wrappers.<ShortVideoShare>query()
|
||||
.select("count(distinct user_id) as count")
|
||||
.eq("video_id", id);
|
||||
ShortVideoShare share = shortVideoShareMapper.selectOne(shareWrapper);
|
||||
Integer distinctShareCount = share == null ? 0 : share.getCount();
|
||||
commentRate = BigDecimal.valueOf(distinctCommentCount + distinctShareCount + summary.getFavorCount())
|
||||
.divide(BigDecimal.valueOf(summary.getWatchUserCount()), 4, RoundingMode.HALF_UP);
|
||||
}
|
||||
summary.setCommentRate(commentRate);
|
||||
return summary;
|
||||
}
|
||||
|
||||
private Integer isFavor(Integer videoId, String userId) {
|
||||
if (userId == null) {
|
||||
return IsLike.NO.value;
|
||||
}
|
||||
ISet<String> favorUserIds = getFavorSet(videoId);
|
||||
return favorUserIds.contains(userId) ? IsLike.YES.value : IsLike.NO.value;
|
||||
}
|
||||
|
||||
private ISet<String> getFavorSet(Integer videoId) {
|
||||
return cacheService.getSet(CacheKey.CourseKey.SHORT_VIDEO_FAVOR_USER_IDS + videoId, () -> {
|
||||
LambdaQueryWrapper<ShortVideoFavor> wrapper = Wrappers.<ShortVideoFavor>lambdaQuery()
|
||||
.select(ShortVideoFavor::getUserId)
|
||||
.eq(ShortVideoFavor::getVideoId, videoId);
|
||||
return shortVideoFavorMapper.selectList(wrapper).stream().map(ShortVideoFavor::getUserId).collect(Collectors.toSet());
|
||||
});
|
||||
}
|
||||
|
||||
private Integer getFavorCount(Integer videoId) {
|
||||
return getFavorSet(videoId).size();
|
||||
}
|
||||
|
||||
private void clearCache(Integer id, Integer advisorId) {
|
||||
courseCache.delete(CacheKey.CourseKey.SHORT_VIDEO + id);
|
||||
courseCache.delete(CacheKey.CourseKey.MAIN_SHORT_VIDEO_LIST + advisorId);
|
||||
}
|
||||
|
||||
private ShortVideo getEntity(Integer id, BackendUserVO backendUserVO) {
|
||||
ShortVideo video = shortVideoMapper.selectById(id);
|
||||
if (video == null) {
|
||||
return null;
|
||||
}
|
||||
// 数据权限检查
|
||||
courseCommonService.checkAdvisorId(video.getAdvisorId(), backendUserVO);
|
||||
return video;
|
||||
}
|
||||
|
||||
private void saveCartList(Integer id, List<ShortVideoCart> cartList) {
|
||||
removeCartList(id);
|
||||
if (CollUtil.isNotEmpty(cartList)) {
|
||||
shortVideoCartMapper.insertBatchSomeColumn(cartList);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeCartList(Integer id) {
|
||||
LambdaQueryWrapper<ShortVideoCart> wrapper = Wrappers.<ShortVideoCart>lambdaQuery()
|
||||
.eq(ShortVideoCart::getVideoId, id);
|
||||
shortVideoCartMapper.delete(wrapper);
|
||||
}
|
||||
|
||||
private String getAuthorityId(Integer id) {
|
||||
String authorityId = shortVideoMapper.getAuthorityId(id, SerialType.SHORT_VIDEO.value, CourseContentType.SERIAL.value);
|
||||
return authorityId == null ? null : Arrays.stream(authorityId.split(",|,")).distinct().collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
private Integer getShareCount(Integer id, String userId) {
|
||||
QueryWrapper<ShortVideoShare> wrapper = Wrappers.<ShortVideoShare>query()
|
||||
.select("ifnull(sum(count), 0) as count")
|
||||
.eq("video_id", id)
|
||||
.eq(userId != null, "user_id", userId);
|
||||
ShortVideoShare share = shortVideoShareMapper.selectOne(wrapper);
|
||||
return share == null ? 0 : share.getCount();
|
||||
}
|
||||
|
||||
private List<SerialVO> listSerial(Integer videoId, BackendUserVO userVO) {
|
||||
ListSerialQuery query = new ListSerialQuery();
|
||||
query.setStatus(SerialStatus.AUDITED.value);
|
||||
query.setContentId(videoId);
|
||||
query.setContentType(SerialType.SHORT_VIDEO.value);
|
||||
query.setCurrent(1);
|
||||
query.setSize(Integer.MAX_VALUE);
|
||||
query.setUserType(VideoUserType.MANAGER_USER.value);
|
||||
Pager<SerialVO> page = serialService.list(query, userVO);
|
||||
return page.getList();
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,6 +6,7 @@ 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.ReadGroupMessageAppQuery;
|
||||
import com.upchina.group.query.message.SendGroupMessageAppQuery;
|
||||
import com.upchina.group.service.app.AppGroupMessageService;
|
||||
import com.upchina.group.vo.message.GroupMessageVO;
|
||||
@ -48,4 +49,13 @@ public class AppGroupMessageController {
|
||||
return CommonResult.success(vo);
|
||||
}
|
||||
|
||||
@ApiOperation("APP保存消息已读")
|
||||
@PostMapping("/app/group/message/readMessage")
|
||||
public CommonResult<Void> readMessage(
|
||||
@Validated @RequestBody @ApiParam(required = true) ReadGroupMessageAppQuery query,
|
||||
@RequestAttribute(value = "frontUser", required = false) FrontUserVO frontUser) {
|
||||
appGroupMessageService.readMessage(query, frontUser);
|
||||
return CommonResult.success();
|
||||
}
|
||||
|
||||
}
|
||||
223
src/main/java/com/upchina/group/entity/GroupCollect.java
Normal file
223
src/main/java/com/upchina/group/entity/GroupCollect.java
Normal file
@ -0,0 +1,223 @@
|
||||
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.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 交易圈统计信息
|
||||
* </p>
|
||||
*
|
||||
* @author easonzhu
|
||||
* @since 2025-02-13
|
||||
*/
|
||||
public class GroupCollect implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@TableId(value = "group_id", type = IdType.AUTO)
|
||||
private Integer groupId;
|
||||
|
||||
/**
|
||||
* 日期
|
||||
*/
|
||||
private LocalDate date;
|
||||
|
||||
/**
|
||||
* 总成员数
|
||||
*/
|
||||
@TableField("total_members")
|
||||
private Integer totalMembers;
|
||||
|
||||
/**
|
||||
* 访问成员数
|
||||
*/
|
||||
@TableField("visited_members")
|
||||
private Integer visitedMembers;
|
||||
|
||||
/**
|
||||
* 新增成员数
|
||||
*/
|
||||
@TableField("new_members")
|
||||
private Integer newMembers;
|
||||
|
||||
/**
|
||||
* 发互动成员数
|
||||
*/
|
||||
@TableField("interaction_members")
|
||||
private Integer interactionMembers;
|
||||
|
||||
/**
|
||||
* 发私聊成员数
|
||||
*/
|
||||
@TableField("private_chat_members")
|
||||
private Integer privateChatMembers;
|
||||
|
||||
/**
|
||||
* 投顾发布互动数
|
||||
*/
|
||||
@TableField("advisor_group_content")
|
||||
private Integer advisorGroupContent;
|
||||
|
||||
/**
|
||||
* 助教发布互动数
|
||||
*/
|
||||
@TableField("assistant_group_content")
|
||||
private Integer assistantGroupContent;
|
||||
|
||||
/**
|
||||
* 用户发布互动数
|
||||
*/
|
||||
@TableField("customer_group_content")
|
||||
private Integer customerGroupContent;
|
||||
|
||||
/**
|
||||
* 投顾发布私聊数
|
||||
*/
|
||||
@TableField("advisor_private_content")
|
||||
private Integer advisorPrivateContent;
|
||||
|
||||
/**
|
||||
* 助教发布私聊数
|
||||
*/
|
||||
@TableField("assistant_private_content")
|
||||
private Integer assistantPrivateContent;
|
||||
|
||||
/**
|
||||
* 用户发布私聊数
|
||||
*/
|
||||
@TableField("customer_private_content")
|
||||
private Integer customerPrivateContent;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
public Integer getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(Integer groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
public Integer getTotalMembers() {
|
||||
return totalMembers;
|
||||
}
|
||||
|
||||
public void setTotalMembers(Integer totalMembers) {
|
||||
this.totalMembers = totalMembers;
|
||||
}
|
||||
public Integer getVisitedMembers() {
|
||||
return visitedMembers;
|
||||
}
|
||||
|
||||
public void setVisitedMembers(Integer visitedMembers) {
|
||||
this.visitedMembers = visitedMembers;
|
||||
}
|
||||
public Integer getNewMembers() {
|
||||
return newMembers;
|
||||
}
|
||||
|
||||
public void setNewMembers(Integer newMembers) {
|
||||
this.newMembers = newMembers;
|
||||
}
|
||||
public Integer getInteractionMembers() {
|
||||
return interactionMembers;
|
||||
}
|
||||
|
||||
public void setInteractionMembers(Integer interactionMembers) {
|
||||
this.interactionMembers = interactionMembers;
|
||||
}
|
||||
public Integer getPrivateChatMembers() {
|
||||
return privateChatMembers;
|
||||
}
|
||||
|
||||
public void setPrivateChatMembers(Integer privateChatMembers) {
|
||||
this.privateChatMembers = privateChatMembers;
|
||||
}
|
||||
public Integer getAdvisorGroupContent() {
|
||||
return advisorGroupContent;
|
||||
}
|
||||
|
||||
public void setAdvisorGroupContent(Integer advisorGroupContent) {
|
||||
this.advisorGroupContent = advisorGroupContent;
|
||||
}
|
||||
public Integer getAssistantGroupContent() {
|
||||
return assistantGroupContent;
|
||||
}
|
||||
|
||||
public void setAssistantGroupContent(Integer assistantGroupContent) {
|
||||
this.assistantGroupContent = assistantGroupContent;
|
||||
}
|
||||
public Integer getCustomerGroupContent() {
|
||||
return customerGroupContent;
|
||||
}
|
||||
|
||||
public void setCustomerGroupContent(Integer customerGroupContent) {
|
||||
this.customerGroupContent = customerGroupContent;
|
||||
}
|
||||
public Integer getAdvisorPrivateContent() {
|
||||
return advisorPrivateContent;
|
||||
}
|
||||
|
||||
public void setAdvisorPrivateContent(Integer advisorPrivateContent) {
|
||||
this.advisorPrivateContent = advisorPrivateContent;
|
||||
}
|
||||
public Integer getAssistantPrivateContent() {
|
||||
return assistantPrivateContent;
|
||||
}
|
||||
|
||||
public void setAssistantPrivateContent(Integer assistantPrivateContent) {
|
||||
this.assistantPrivateContent = assistantPrivateContent;
|
||||
}
|
||||
public Integer getCustomerPrivateContent() {
|
||||
return customerPrivateContent;
|
||||
}
|
||||
|
||||
public void setCustomerPrivateContent(Integer customerPrivateContent) {
|
||||
this.customerPrivateContent = customerPrivateContent;
|
||||
}
|
||||
public LocalDateTime getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GroupCollect{" +
|
||||
"groupId=" + groupId +
|
||||
", date=" + date +
|
||||
", totalMembers=" + totalMembers +
|
||||
", visitedMembers=" + visitedMembers +
|
||||
", newMembers=" + newMembers +
|
||||
", interactionMembers=" + interactionMembers +
|
||||
", privateChatMembers=" + privateChatMembers +
|
||||
", advisorGroupContent=" + advisorGroupContent +
|
||||
", assistantGroupContent=" + assistantGroupContent +
|
||||
", customerGroupContent=" + customerGroupContent +
|
||||
", advisorPrivateContent=" + advisorPrivateContent +
|
||||
", assistantPrivateContent=" + assistantPrivateContent +
|
||||
", customerPrivateContent=" + customerPrivateContent +
|
||||
", createTime=" + createTime +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
81
src/main/java/com/upchina/group/entity/GroupMessageRead.java
Normal file
81
src/main/java/com/upchina/group/entity/GroupMessageRead.java
Normal file
@ -0,0 +1,81 @@
|
||||
package com.upchina.group.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 交易圈消息已读
|
||||
* </p>
|
||||
*
|
||||
* @author easonzhu
|
||||
* @since 2025-02-13
|
||||
*/
|
||||
public class GroupMessageRead implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* 消息ID
|
||||
*/
|
||||
@TableField("message_id")
|
||||
private Integer messageId;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@TableField("user_id")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 交易圈ID
|
||||
*/
|
||||
@TableField("group_id")
|
||||
private Integer groupId;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
public Integer getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(Integer messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
public Integer getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(Integer groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
public LocalDateTime getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GroupMessageRead{" +
|
||||
"messageId=" + messageId +
|
||||
", userId=" + userId +
|
||||
", groupId=" + groupId +
|
||||
", createTime=" + createTime +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.upchina.group.mapper;
|
||||
|
||||
import com.upchina.common.mapper.EasyBaseMapper;
|
||||
import com.upchina.group.entity.GroupCollect;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 交易圈统计信息 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author easonzhu
|
||||
* @since 2025-02-13
|
||||
*/
|
||||
public interface GroupCollectMapper extends EasyBaseMapper<GroupCollect> {
|
||||
|
||||
}
|
||||
@ -2,9 +2,12 @@ package com.upchina.group.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.upchina.group.entity.GroupMessage;
|
||||
import com.upchina.group.entity.GroupMessageRead;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -25,4 +28,25 @@ public interface GroupMessageMapper extends BaseMapper<GroupMessage> {
|
||||
") t\n" +
|
||||
"WHERE rn = 1")
|
||||
List<GroupMessage> selectPrivateChatList(@Param("groupId") Integer groupId);
|
||||
|
||||
@Update("<script>" +
|
||||
"INSERT INTO group_message_read (message_id, user_id, group_id) VALUES " +
|
||||
"<foreach collection='list' item='item' separator=','>" +
|
||||
"(#{item.messageId}, #{item.userId}, #{item.groupId})" +
|
||||
"</foreach>" +
|
||||
" ON DUPLICATE KEY UPDATE message_id = message_id" +
|
||||
"</script>")
|
||||
void replaceBatch(List<GroupMessageRead> list);
|
||||
|
||||
@Select("SELECT group_id, interactive_type, user_type, COUNT(0) AS id \n" +
|
||||
"FROM group_message \n" +
|
||||
"WHERE create_time >= #{startTime} AND create_time < #{endTime} \n" +
|
||||
"GROUP BY group_id, interactive_type, user_type")
|
||||
List<GroupMessage> collectMessage(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
|
||||
|
||||
@Select("SELECT group_id, interactive_type, COUNT(DISTINCT user_id) AS id \n" +
|
||||
"FROM group_message \n" +
|
||||
"WHERE user_type = 2 AND create_time >= #{startTime} AND create_time < #{endTime} \n" +
|
||||
"GROUP BY group_id, interactive_type")
|
||||
List<GroupMessage> collectMessageMember(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
package com.upchina.group.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.upchina.group.entity.GroupMessageRead;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 交易圈消息已读 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author easonzhu
|
||||
* @since 2025-02-13
|
||||
*/
|
||||
public interface GroupMessageReadMapper extends BaseMapper<GroupMessageRead> {
|
||||
|
||||
}
|
||||
@ -1,13 +1,15 @@
|
||||
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.common.entity.OnlineUser;
|
||||
import com.upchina.common.mapper.EasyBaseMapper;
|
||||
import com.upchina.common.vo.IdCountVO;
|
||||
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.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -18,10 +20,22 @@ import java.util.List;
|
||||
* @author easonzhu
|
||||
* @since 2025-02-04
|
||||
*/
|
||||
public interface GroupUserFlowMapper extends BaseMapper<GroupUserFlow> {
|
||||
public interface GroupUserFlowMapper extends EasyBaseMapper<GroupUserFlow> {
|
||||
|
||||
@Select("SELECT h.group_id, h.user_id, h.session_id, 2 as is_online " +
|
||||
"FROM group_user_flow h " +
|
||||
"${ew.customSqlSegment}")
|
||||
List<OnlineUser> loadHis(@Param(Constants.WRAPPER) LambdaQueryWrapper<GroupUserFlow> wrapper);
|
||||
|
||||
@Select("SELECT \n" +
|
||||
" group_id, \n" +
|
||||
" COUNT(DISTINCT user_id) AS user_count \n" +
|
||||
"FROM \n" +
|
||||
" group_user_flow \n" +
|
||||
"WHERE \n" +
|
||||
" time >= #{startTime} \n" +
|
||||
" AND time < #{endTime} \n" +
|
||||
"GROUP BY \n" +
|
||||
" group_id")
|
||||
List<IdCountVO> selectGroupUserCount(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
|
||||
}
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.upchina.group.query.message;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
public class ReadGroupMessageAppQuery {
|
||||
|
||||
@ApiModelProperty("消息ID")
|
||||
@NotEmpty
|
||||
private List<Integer> messageIds;
|
||||
|
||||
public List<Integer> getMessageIds() {
|
||||
return messageIds;
|
||||
}
|
||||
|
||||
public void setMessageIds(List<Integer> messageIds) {
|
||||
this.messageIds = messageIds;
|
||||
}
|
||||
|
||||
}
|
||||
51
src/main/java/com/upchina/group/schedule/GroupTask.java
Normal file
51
src/main/java/com/upchina/group/schedule/GroupTask.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.upchina.group.schedule;
|
||||
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.service.CacheService;
|
||||
import com.upchina.group.service.common.GroupCommonService;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Component
|
||||
public class GroupTask {
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private GroupCommonService groupCommonService;
|
||||
|
||||
/**
|
||||
* 拉取云端视频转码状态
|
||||
*/
|
||||
@Scheduled(cron = "${cron.saveGroupMessageRead}")
|
||||
public void saveGroupMessageRead() {
|
||||
cacheService.lock(CacheKey.LockKey.SAVE_MESSAGE_READ,
|
||||
0, TimeUnit.SECONDS,
|
||||
4, TimeUnit.MINUTES,
|
||||
groupCommonService::saveGroupMessageRead
|
||||
);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "${cron.saveGroupUser}")
|
||||
public void saveGroupUser() {
|
||||
cacheService.lock(CacheKey.LockKey.SAVE_GROUP_USER,
|
||||
0, TimeUnit.SECONDS,
|
||||
4, TimeUnit.MINUTES,
|
||||
groupCommonService::saveGroupUser
|
||||
);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "${cron.collectGroupData}")
|
||||
public void collectGroupData() {
|
||||
cacheService.lock(CacheKey.LockKey.COLLECT_GROUP_DATA,
|
||||
0, TimeUnit.SECONDS,
|
||||
4, TimeUnit.MINUTES,
|
||||
groupCommonService::collectGroupData
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,12 +18,14 @@ 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.ReadGroupMessageAppQuery;
|
||||
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.GroupMessageReadVO;
|
||||
import com.upchina.group.vo.message.GroupMessageVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -127,4 +129,8 @@ public class AppGroupMessageService {
|
||||
return vo;
|
||||
}
|
||||
|
||||
public void readMessage(ReadGroupMessageAppQuery query, FrontUserVO frontUser) {
|
||||
GroupMessageReadVO vo = new GroupMessageReadVO(frontUser.getUserId(), query.getMessageIds());
|
||||
groupCacheService.readMessage(vo);
|
||||
}
|
||||
}
|
||||
@ -2,22 +2,23 @@ package com.upchina.group.service.common;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.upchina.advisor.vo.AdvisorBasicVO;
|
||||
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.entity.OnlineUser;
|
||||
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.GroupInfoMapper;
|
||||
import com.upchina.group.mapper.GroupMessageMapper;
|
||||
import com.upchina.group.mapper.GroupUserFlowMapper;
|
||||
import com.upchina.group.vo.message.GroupMessageReadVO;
|
||||
import com.upchina.group.vo.message.GroupMessageVO;
|
||||
import com.upchina.video.entity.OnlineUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -28,9 +29,15 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
public class GroupCacheService {
|
||||
|
||||
@Resource
|
||||
private HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private GroupInfoMapper groupInfoMapper;
|
||||
|
||||
@Resource
|
||||
private GroupMessageMapper groupMessageMapper;
|
||||
|
||||
@ -40,15 +47,6 @@ public class GroupCacheService {
|
||||
@Resource
|
||||
private IMap<String, Object> 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 NavigableSet<Integer> getMessageIdSet(Integer groupId, String userId, QueryGroupMessageType type) {
|
||||
String cacheKey = buildMessageIdSetKey(userId, type);
|
||||
return cacheService.get(groupCache, cacheKey, () -> {
|
||||
@ -136,7 +134,7 @@ public class GroupCacheService {
|
||||
}
|
||||
|
||||
public GroupMessageVO getMessage(GroupMessage message, Map<Integer, AdvisorBasicVO> advisorMap) {
|
||||
GroupMessageVO vo = new GroupMessageVO(message, advisorMap.get(message.getAdvisorId()));
|
||||
GroupMessageVO vo = new GroupMessageVO(message, advisorMap == null ? null : advisorMap.get(message.getAdvisorId()));
|
||||
vo.setReplyMessage(getMessage(message.getReplyId(), advisorMap));
|
||||
vo.setQuoteMessage(getMessage(message.getQuoteId(), advisorMap));
|
||||
return vo;
|
||||
@ -161,4 +159,9 @@ public class GroupCacheService {
|
||||
}
|
||||
return cacheKey;
|
||||
}
|
||||
|
||||
public void readMessage(GroupMessageReadVO vo) {
|
||||
hazelcastInstance.getList(CacheKey.GroupKey.TEMP_READ_LIST).add(vo);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,13 +1,236 @@
|
||||
package com.upchina.group.service.common;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.map.IMap;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.vo.IdCountVO;
|
||||
import com.upchina.group.constant.GroupInteractiveType;
|
||||
import com.upchina.group.constant.GroupMessageUserType;
|
||||
import com.upchina.group.entity.*;
|
||||
import com.upchina.group.mapper.GroupCollectMapper;
|
||||
import com.upchina.group.mapper.GroupInfoMapper;
|
||||
import com.upchina.group.mapper.GroupMessageMapper;
|
||||
import com.upchina.group.mapper.GroupUserFlowMapper;
|
||||
import com.upchina.group.vo.GroupVO;
|
||||
import com.upchina.group.vo.message.GroupMessageReadVO;
|
||||
import com.upchina.group.vo.message.GroupMessageVO;
|
||||
import com.upchina.video.schedule.CollectTask;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class GroupCommonService {
|
||||
|
||||
@Resource
|
||||
private HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Resource
|
||||
private GroupCacheService groupCacheService;
|
||||
|
||||
@Resource
|
||||
private GroupMessageMapper groupMessageMapper;
|
||||
|
||||
@Resource
|
||||
private GroupInfoMapper groupInfoMapper;
|
||||
|
||||
@Resource
|
||||
private GroupUserFlowMapper groupUserFlowMapper;
|
||||
|
||||
@Resource
|
||||
private GroupCollectMapper groupCollectMapper;
|
||||
private CollectTask collectTask;
|
||||
|
||||
public boolean validateUserPermission(String userId, GroupVO groupVO) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveGroupMessageRead() {
|
||||
List<GroupMessageReadVO> cacheList = hazelcastInstance.getList(CacheKey.GroupKey.TEMP_READ_LIST);
|
||||
// 合并重复项
|
||||
Map<Integer, Set<String>> map = new HashMap<>(cacheList.size());
|
||||
for (GroupMessageReadVO read : cacheList) {
|
||||
if (read != null) {
|
||||
read.getMessageIds().stream().forEach(m ->
|
||||
map.computeIfAbsent(m, k -> new HashSet<>()).add(read.getUserId()));
|
||||
}
|
||||
}
|
||||
List<GroupMessageRead> list = map.entrySet().stream()
|
||||
.map(entry -> {
|
||||
Integer messageId = entry.getKey();
|
||||
GroupMessageVO message = groupCacheService.getMessage(messageId, null);
|
||||
if (message == null
|
||||
|| !GroupInteractiveType.GROUP.value.equals(message.getInteractiveType())
|
||||
|| (!GroupMessageUserType.ADVISOR.value.equals(message.getUserType()) &&
|
||||
!GroupMessageUserType.ASSISTANT.value.equals(message.getUserType()))) {
|
||||
return null;
|
||||
}
|
||||
return entry.getValue().stream().map(userId -> {
|
||||
GroupMessageRead read = new GroupMessageRead();
|
||||
read.setMessageId(messageId);
|
||||
read.setUserId(userId);
|
||||
read.setGroupId(message.getGroupId());
|
||||
return read;
|
||||
}).collect(Collectors.toList());
|
||||
}).filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
groupMessageMapper.replaceBatch(list);
|
||||
}
|
||||
cacheList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入视频用户数据
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveGroupUser() {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LambdaQueryWrapper<GroupInfo> wrapper = Wrappers.<GroupInfo>lambdaQuery()
|
||||
.select(GroupInfo::getId, GroupInfo::getStatus);
|
||||
List<GroupInfo> groups = groupInfoMapper.selectList(wrapper);
|
||||
if (CollUtil.isEmpty(groups)) {
|
||||
return;
|
||||
}
|
||||
LocalDateTime time = now.withSecond(0).withNano(0);
|
||||
LocalDateTime startTime = now.plusMinutes(-1);
|
||||
|
||||
groups.forEach(g -> {
|
||||
Integer id = g.getId();
|
||||
IMap<String, OnlineUser> onlineMap = groupCacheService.getTotalOnlineMap(id);
|
||||
if (onlineMap == null || onlineMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 将实时在线人落库,并添加是否正在观看
|
||||
List<GroupUserFlow> onLineList = onlineMap.values().stream()
|
||||
.filter(u -> IsOrNot.IS.value.equals(u.getIsOnline()) || (u.getExitTime() != null && u.getExitTime().isAfter(startTime)))
|
||||
.map(o -> {
|
||||
GroupUserFlow groupUserFlow = new GroupUserFlow();
|
||||
groupUserFlow.setGroupId(id);
|
||||
groupUserFlow.setUserId(o.getUserId());
|
||||
groupUserFlow.setSessionId(o.getSessionId());
|
||||
groupUserFlow.setTime(time);
|
||||
groupUserFlow.setEnterTime(o.getCreateTime() != null ? o.getCreateTime().withSecond(0).withNano(0) : null);
|
||||
groupUserFlow.setExitTime(o.getExitTime() != null ? o.getExitTime().withSecond(0).withNano(0) : null);
|
||||
return groupUserFlow;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(onLineList)) {
|
||||
ListUtil.split(onLineList, 1000).stream().filter(CollUtil::isNotEmpty).forEach(groupUserFlowMapper::insertBatchSomeColumn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void collectGroupData() {
|
||||
LocalDate today = LocalDate.now();
|
||||
LocalDate yesterday = today.minusDays(1);
|
||||
LambdaQueryWrapper<GroupInfo> groupWrapper = Wrappers.<GroupInfo>lambdaQuery()
|
||||
.select(GroupInfo::getId);
|
||||
List<GroupInfo> groups = groupInfoMapper.selectList(groupWrapper);
|
||||
if (CollUtil.isEmpty(groups)) {
|
||||
return;
|
||||
}
|
||||
List<Integer> groupIds = groups.stream().map(GroupInfo::getId).collect(Collectors.toList());
|
||||
// 获取昨日数据
|
||||
// 如果昨天数据还是昨天统计的,那需要重新计算;否则不需计算
|
||||
QueryWrapper<GroupCollect> collectWrapper = Wrappers.<GroupCollect>query()
|
||||
.select("max(create_time) as create_time")
|
||||
.eq("date", yesterday);
|
||||
GroupCollect yesterdayLatest = groupCollectMapper.selectOne(collectWrapper);
|
||||
if (yesterdayLatest != null && yesterdayLatest.getCreateTime().isBefore(today.atStartOfDay())) {
|
||||
collectGroupData(groupIds, yesterday);
|
||||
}
|
||||
|
||||
collectGroupData(groupIds, today);
|
||||
}
|
||||
|
||||
public void collectGroupData(List<Integer> groupIds, LocalDate date) {
|
||||
LocalDateTime startTime = date.atStartOfDay();
|
||||
LocalDateTime endTime = date.plusDays(1).atStartOfDay();
|
||||
List<IdCountVO> visitMemberList = groupUserFlowMapper.selectGroupUserCount(startTime, endTime);
|
||||
Map<Integer, Integer> visitMemberMap = visitMemberList.stream().collect(Collectors.toMap(IdCountVO::getId, IdCountVO::getCount));
|
||||
|
||||
List<GroupMessage> messageMemberCollect = groupMessageMapper.collectMessageMember(startTime, endTime);
|
||||
Map<Integer, List<GroupMessage>> groupMessageMemberMap = messageMemberCollect.stream().collect(Collectors.groupingBy(GroupMessage::getGroupId));
|
||||
|
||||
List<GroupMessage> messageCollect = groupMessageMapper.collectMessage(startTime, endTime);
|
||||
Map<Integer, List<GroupMessage>> groupMessageMap = messageCollect.stream().collect(Collectors.groupingBy(GroupMessage::getGroupId));
|
||||
|
||||
List<GroupCollect> collectList = groupIds.stream().map(groupId -> {
|
||||
GroupCollect collect = new GroupCollect();
|
||||
collect.setGroupId(groupId);
|
||||
collect.setDate(date);
|
||||
collect.setTotalMembers(getTotalMembers(groupId));
|
||||
collect.setVisitedMembers(visitMemberMap.getOrDefault(groupId, 0));
|
||||
collect.setNewMembers(getNewMembers(groupId));
|
||||
collect.setInteractionMembers(0);
|
||||
collect.setPrivateChatMembers(0);
|
||||
List<GroupMessage> messageMemberList = groupMessageMemberMap.get(groupId);
|
||||
if (CollUtil.isNotEmpty(messageMemberList)) {
|
||||
for (GroupMessage groupMessage : messageMemberList) {
|
||||
if (GroupInteractiveType.GROUP.value.equals(groupMessage.getInteractiveType())) {
|
||||
collect.setInteractionMembers(groupMessage.getId());
|
||||
} else if (GroupInteractiveType.PRIVATE.value.equals(groupMessage.getInteractiveType())) {
|
||||
collect.setPrivateChatMembers(groupMessage.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
collect.setAdvisorGroupContent(0);
|
||||
collect.setAssistantGroupContent(0);
|
||||
collect.setCustomerGroupContent(0);
|
||||
collect.setAdvisorPrivateContent(0);
|
||||
collect.setAssistantPrivateContent(0);
|
||||
collect.setCustomerPrivateContent(0);
|
||||
List<GroupMessage> messageList = groupMessageMap.get(groupId);
|
||||
if (CollUtil.isNotEmpty(messageList)) {
|
||||
for (GroupMessage groupMessage : messageList) {
|
||||
if (GroupInteractiveType.GROUP.value.equals(groupMessage.getInteractiveType())) {
|
||||
if (GroupMessageUserType.ADVISOR.value.equals(groupMessage.getUserType())) {
|
||||
collect.setAdvisorGroupContent(groupMessage.getId());
|
||||
} else if (GroupMessageUserType.ASSISTANT.value.equals(groupMessage.getUserType())) {
|
||||
collect.setAssistantGroupContent(groupMessage.getId());
|
||||
} else if (GroupMessageUserType.CUSTOMER.value.equals(groupMessage.getUserType())) {
|
||||
collect.setCustomerGroupContent(groupMessage.getId());
|
||||
}
|
||||
} else if (GroupInteractiveType.PRIVATE.value.equals(groupMessage.getInteractiveType())) {
|
||||
if (GroupMessageUserType.ADVISOR.value.equals(groupMessage.getUserType())) {
|
||||
collect.setAdvisorPrivateContent(groupMessage.getId());
|
||||
} else if (GroupMessageUserType.ASSISTANT.value.equals(groupMessage.getUserType())) {
|
||||
collect.setAssistantPrivateContent(groupMessage.getId());
|
||||
} else if (GroupMessageUserType.CUSTOMER.value.equals(groupMessage.getUserType())) {
|
||||
collect.setCustomerPrivateContent(groupMessage.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return collect;
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(collectList)) {
|
||||
groupCollectMapper.delete(Wrappers.<GroupCollect>lambdaQuery().eq(GroupCollect::getDate, date));
|
||||
groupCollectMapper.insertBatchSomeColumn(collectList);
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getTotalMembers(Integer groupId) {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
private Integer getNewMembers(Integer groupId) {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import com.hazelcast.topic.ITopic;
|
||||
import com.upchina.advisor.service.AdvisorInfoService;
|
||||
import com.upchina.advisor.vo.AdvisorBasicVO;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.vo.FrontUserVO;
|
||||
import com.upchina.group.constant.GroupInteractiveType;
|
||||
import com.upchina.group.constant.GroupMessageChannel;
|
||||
@ -13,7 +14,6 @@ import com.upchina.group.constant.GroupMessageType;
|
||||
import com.upchina.group.entity.GroupMessage;
|
||||
import com.upchina.group.vo.message.GroupMessageVO;
|
||||
import com.upchina.group.vo.message.GroupWsMessageVO;
|
||||
import com.upchina.video.entity.OnlineUser;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package com.upchina.group.vo.message;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class GroupMessageReadVO implements Serializable {
|
||||
|
||||
private String userId;
|
||||
|
||||
private List<Integer> messageIds;
|
||||
|
||||
public GroupMessageReadVO() {}
|
||||
|
||||
public GroupMessageReadVO(String userId, List<Integer> messageIds) {
|
||||
this.userId = userId;
|
||||
this.messageIds = messageIds;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public List<Integer> getMessageIds() {
|
||||
return messageIds;
|
||||
}
|
||||
|
||||
public void setMessageIds(List<Integer> messageIds) {
|
||||
this.messageIds = messageIds;
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ import com.upchina.common.result.CommonResult;
|
||||
import com.upchina.common.vo.AuthVO;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.rbac.query.ChangeMobileQuery;
|
||||
import com.upchina.rbac.query.ChangePasswordQuery;
|
||||
import com.upchina.rbac.query.LoginDeptQuery;
|
||||
import com.upchina.rbac.query.LoginQuery;
|
||||
import com.upchina.rbac.service.AuthService;
|
||||
@ -82,6 +83,15 @@ public class AuthController {
|
||||
return CommonResult.success(vo);
|
||||
}
|
||||
|
||||
@ApiOperation("修改用户密码")
|
||||
@PostMapping("/changePassword")
|
||||
@Auth(role = AccessRole.LOGIN)
|
||||
public CommonResult<Void> changePassword(@Validated @RequestBody @ApiParam(required = true) ChangePasswordQuery query,
|
||||
@RequestAttribute(value = "backendUser", required = false) BackendUserVO backendUserVO) {
|
||||
authService.changePassword(query, backendUserVO);
|
||||
return CommonResult.success(null);
|
||||
}
|
||||
|
||||
@ApiOperation("修改用户手机号")
|
||||
@PostMapping("/changeMobile")
|
||||
@Auth(role = AccessRole.LOGIN)
|
||||
|
||||
@ -25,10 +25,7 @@ import com.upchina.rbac.entity.UserDept;
|
||||
import com.upchina.rbac.entity.UserLogin;
|
||||
import com.upchina.rbac.mapper.UserDeptMapper;
|
||||
import com.upchina.rbac.mapper.UserLoginMapper;
|
||||
import com.upchina.rbac.query.ChangeMobileQuery;
|
||||
import com.upchina.rbac.query.ListRoleByUserIdQuery;
|
||||
import com.upchina.rbac.query.LoginDeptQuery;
|
||||
import com.upchina.rbac.query.LoginQuery;
|
||||
import com.upchina.rbac.query.*;
|
||||
import com.upchina.rbac.vo.*;
|
||||
import com.upchina.video.constant.VideoUserType;
|
||||
import com.wf.captcha.SpecCaptcha;
|
||||
@ -183,6 +180,25 @@ public class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void changePassword(ChangePasswordQuery query, BackendUserVO backendUserVO) {
|
||||
Integer loginId = backendUserVO.getLoginId();
|
||||
UserLogin userInDB = userLoginMapper.selectById(loginId);
|
||||
if (userInDB == null) {
|
||||
throw new BizException(ResponseStatus.USER_STATUS_ERROR);
|
||||
}
|
||||
String originalPassword = query.getOriginalPassword();
|
||||
if (!CodecUtil.md5(originalPassword).equals(userInDB.getPassword())) {
|
||||
throw new BizException(ResponseStatus.ORIGINAL_PASSWORD_ERROR);
|
||||
}
|
||||
String md5 = CodecUtil.md5(query.getPassword());
|
||||
UserLogin user = new UserLogin();
|
||||
user.setLoginId(loginId);
|
||||
user.setPassword(md5);
|
||||
userLoginMapper.updateById(user);
|
||||
userService.clearCache();
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void changeMobile(ChangeMobileQuery query, BackendUserVO backendUserVO) {
|
||||
Integer loginId = backendUserVO.getLoginId();
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
package com.upchina.rbac.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.query.OnlyIdQuery;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.rbac.entity.Menu;
|
||||
import com.upchina.rbac.entity.Permission;
|
||||
import com.upchina.rbac.mapper.MenuMapper;
|
||||
import com.upchina.rbac.mapper.PermissionMapper;
|
||||
import com.upchina.rbac.query.ListMenuQuery;
|
||||
import com.upchina.rbac.query.SaveMenuQuery;
|
||||
import com.upchina.rbac.query.UpdateMenuQuery;
|
||||
import com.upchina.rbac.vo.MenuVO;
|
||||
import com.upchina.rbac.vo.PermissionVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class MenuService {
|
||||
|
||||
@Resource
|
||||
private MenuMapper menuMapper;
|
||||
|
||||
@Resource
|
||||
private PermissionMapper permissionMapper;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<MenuVO> list(ListMenuQuery query, Integer userId, boolean buildTree) {
|
||||
String keyword = query.getKeyword();
|
||||
Integer roleId = query.getRoleId();
|
||||
List<Menu> searchList;
|
||||
List<MenuVO> searchVoList;
|
||||
List<MenuVO> allVoList;
|
||||
QueryWrapper<Menu> wrapper = Wrappers.query();
|
||||
wrapper.orderByAsc("sort");
|
||||
boolean isAll = false;
|
||||
if (StringUtils.isNotEmpty(keyword)) {
|
||||
wrapper.like("name", keyword).or().like("path", keyword);
|
||||
searchList = menuMapper.selectList(wrapper);
|
||||
} else if (userId != null) {
|
||||
searchList = menuMapper.selectListByUserId(userId);
|
||||
} else if (roleId != null) {
|
||||
searchList = menuMapper.selectListByRoleId(roleId);
|
||||
} else {
|
||||
searchList = menuMapper.selectList(wrapper);
|
||||
isAll = true;
|
||||
}
|
||||
searchVoList = searchList.stream().map(MenuVO::new).collect(Collectors.toList());
|
||||
if (!buildTree) {
|
||||
return searchVoList;
|
||||
}
|
||||
if (isAll) {
|
||||
allVoList = searchVoList;
|
||||
} else {
|
||||
QueryWrapper allWrapper = Wrappers.query().orderByAsc("pid");
|
||||
List<Menu> allList = menuMapper.selectList(allWrapper);
|
||||
allVoList = allList.stream().map(MenuVO::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 填充权限
|
||||
List<Permission> allPermissionList = permissionMapper.selectList(Wrappers.emptyWrapper());
|
||||
Map<Integer, List<PermissionVO>> menuPermissionListMap = allPermissionList.stream().map(PermissionVO::new).collect(Collectors.groupingBy(PermissionVO::getMenuId));
|
||||
allVoList.forEach(menuVO -> menuVO.setPermissions(menuPermissionListMap.get(menuVO.getId())));
|
||||
|
||||
List<MenuVO> rootList = Collections.emptyList();
|
||||
if (!allVoList.isEmpty()) {
|
||||
rootList = (List<MenuVO>) new MenuVO().buildTree(searchVoList, allVoList);
|
||||
}
|
||||
|
||||
rootList.sort(Comparator.comparing(MenuVO::getSort));
|
||||
if (userId != null) {
|
||||
convertMenu(rootList);
|
||||
}
|
||||
return rootList;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void save(SaveMenuQuery query, BackendUserVO backendUserVO) {
|
||||
Menu menu = query.toPO();
|
||||
menuMapper.insert(menu);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void update(UpdateMenuQuery query, BackendUserVO backendUserVO) {
|
||||
Menu menu = query.toPO();
|
||||
int rows = menuMapper.updateById(menu);
|
||||
if (rows == 0) throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void delete(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
Integer id = query.getId();
|
||||
Menu menu = menuMapper.selectById(id);
|
||||
if (menu == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
QueryWrapper<Menu> wrapper = Wrappers.query();
|
||||
wrapper.eq("pid", id);
|
||||
Long count = menuMapper.selectCount(wrapper);
|
||||
if (count > 0) {
|
||||
throw new BizException(ResponseStatus.CHILD_EXIST_ERROR);
|
||||
}
|
||||
menuMapper.deleteById(query.getId());
|
||||
}
|
||||
|
||||
private void convertMenu(List<MenuVO> list) {
|
||||
list.forEach(vo -> {
|
||||
if (vo.getPid() == null || vo.getPid() <= 0) {
|
||||
vo.setPath("/" + vo.getPath());
|
||||
vo.setComponent(StringUtils.isEmpty(vo.getComponent()) ? "Layout" : vo.getComponent());
|
||||
}
|
||||
if (vo.getChildren() != null && !vo.getChildren().isEmpty()) {
|
||||
vo.setRedirect("noredirect");
|
||||
convertMenu(vo.getChildren());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,170 +0,0 @@
|
||||
package com.upchina.rbac.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.query.OnlyIdQuery;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.vo.BackendUserVO;
|
||||
import com.upchina.rbac.entity.*;
|
||||
import com.upchina.rbac.mapper.RoleMapper;
|
||||
import com.upchina.rbac.mapper.RolesMenusMapper;
|
||||
import com.upchina.rbac.mapper.RolesPermissionsMapper;
|
||||
import com.upchina.rbac.mapper.UsersRolesMapper;
|
||||
import com.upchina.rbac.query.*;
|
||||
import com.upchina.rbac.vo.RoleBasicVO;
|
||||
import com.upchina.rbac.vo.RoleVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author yetian
|
||||
*/
|
||||
@Service
|
||||
public class RoleService {
|
||||
|
||||
@Resource
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Resource
|
||||
private UsersRolesMapper usersRolesMapper;
|
||||
|
||||
@Resource
|
||||
private MenuService menuService;
|
||||
|
||||
@Resource
|
||||
private RolesMenusMapper rolesMenusMapper;
|
||||
|
||||
@Resource
|
||||
private RolesPermissionsMapper rolesPermissionsMapper;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private HazelcastInstance hazelcastInstance;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Pager<RoleVO> list(ListRoleQuery query) {
|
||||
String name = query.getName();
|
||||
QueryWrapper<Role> wrapper = Wrappers.query();
|
||||
wrapper.like(StringUtils.isNotEmpty(name), "name", name);
|
||||
Page<Role> page = roleMapper.selectPage(query.toPage(), wrapper);
|
||||
List<RoleVO> list = page.getRecords().stream().map(role -> {
|
||||
RoleVO vo = new RoleVO(role);
|
||||
ListMenuQuery listMenuQuery = new ListMenuQuery();
|
||||
listMenuQuery.setRoleId(role.getId());
|
||||
vo.setMenuList(menuService.list(listMenuQuery, null, false));
|
||||
vo.setPermissionList(permissionService.list(role.getId(), null));
|
||||
return vo;
|
||||
}).collect(Collectors.toList());
|
||||
return new Pager<>(list, page.getTotal());
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public RoleVO get(Integer id) {
|
||||
Role role = roleMapper.selectById(id);
|
||||
if (role == null) {
|
||||
return null;
|
||||
}
|
||||
RoleVO vo = new RoleVO(role);
|
||||
ListMenuQuery listMenuQuery = new ListMenuQuery();
|
||||
listMenuQuery.setRoleId(role.getId());
|
||||
vo.setMenuList(menuService.list(listMenuQuery, null, false));
|
||||
return vo;
|
||||
}
|
||||
|
||||
public List<RoleVO> listByUserId(ListRoleByUserIdQuery query) {
|
||||
List<Role> list = roleMapper.selectByUserId(query.getUserId());
|
||||
return list.stream().map(RoleVO::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Map<Integer, List<RoleBasicVO>> getUserRoleList(List<Integer> userIdList) {
|
||||
if (userIdList == null || userIdList.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String userIds = userIdList.stream().distinct().map(String::valueOf).collect(Collectors.joining(","));
|
||||
List<UserRoleEntity> list = roleMapper.selectByUserIds(userIds);
|
||||
return list.stream().collect(
|
||||
Collectors.groupingBy(UserRoleEntity::getUserId,
|
||||
Collectors.mapping(RoleBasicVO::new, Collectors.toList())));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void save(SaveRoleQuery query, BackendUserVO backendUserVO) {
|
||||
Role role = query.toPO();
|
||||
roleMapper.insert(role);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(UpdateRoleQuery query, BackendUserVO backendUserVO) {
|
||||
Role originRole = roleMapper.selectById(query.getId());
|
||||
if (originRole.getBuiltIn() == 1) {
|
||||
throw new BizException(ResponseStatus.BUILT_IN_CAN_NOT_MODIFY);
|
||||
}
|
||||
Role role = query.toPO();
|
||||
int rows = roleMapper.updateById(role);
|
||||
if (rows == 0) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void delete(OnlyIdQuery query, BackendUserVO backendUserVO) {
|
||||
Role originRole = roleMapper.selectById(query.getId());
|
||||
if (originRole.getBuiltIn() == 1) {
|
||||
throw new BizException(ResponseStatus.BUILT_IN_CAN_NOT_MODIFY);
|
||||
}
|
||||
QueryWrapper<UsersRoles> wrapper = Wrappers.query();
|
||||
wrapper.eq("role_id", originRole.getId());
|
||||
Long count = usersRolesMapper.selectCount(wrapper);
|
||||
if (count > 0) {
|
||||
throw new BizException(ResponseStatus.BE_USED_NOT_MODIFY);
|
||||
}
|
||||
int rows = roleMapper.deleteById(query.getId());
|
||||
if (rows == 0) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveMenus(SaveRoleMenusQuery query, BackendUserVO backendUserVO) {
|
||||
QueryWrapper<RolesMenus> wrapper = Wrappers.query();
|
||||
wrapper.eq("role_id", query.getRoleId());
|
||||
rolesMenusMapper.delete(wrapper);
|
||||
|
||||
List<RolesMenus> list = query.toPO();
|
||||
for (RolesMenus rm : list) {
|
||||
rolesMenusMapper.insert(rm);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void savePermissions(SaveRolePermissionsQuery query, BackendUserVO backendUserVO) {
|
||||
Role originRole = roleMapper.selectById(query.getRoleId());
|
||||
if (originRole.getBuiltIn() == 1) {
|
||||
throw new BizException(ResponseStatus.BUILT_IN_CAN_NOT_MODIFY);
|
||||
}
|
||||
QueryWrapper<RolesPermissions> wrapper = Wrappers.query();
|
||||
wrapper.eq("role_id", query.getRoleId());
|
||||
rolesPermissionsMapper.delete(wrapper);
|
||||
|
||||
List<RolesPermissions> list = query.toPO();
|
||||
for (RolesPermissions rp : list) {
|
||||
rolesPermissionsMapper.insert(rp);
|
||||
}
|
||||
Map<String, Object> cacheMap = hazelcastInstance.getMap(CacheKey.RBAC);
|
||||
cacheMap.remove(CacheKey.RbacKey.ROLE_PERMISSIONS_URL + query.getRoleId());
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package com.upchina.rbac.vo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public interface IParentChildVO<T extends IParentChildVO, PK> {
|
||||
|
||||
PK getId();
|
||||
|
||||
PK getPid();
|
||||
|
||||
List<T> getChildren();
|
||||
|
||||
void setChildren(List<T> list);
|
||||
|
||||
default void buildParent(IParentChildVO vo, Map<PK, IParentChildVO> allMap, Map<PK, IParentChildVO> rootMap) {
|
||||
PK pid = (PK) vo.getPid();
|
||||
if (pid != null && ((pid instanceof String && StringUtils.isNotEmpty((String) pid) && !"0".equals(pid) && !"-1".equals(pid))
|
||||
|| (pid instanceof Integer && (Integer) pid > 0)
|
||||
|| (pid instanceof Long && (Long) pid > 0))) {
|
||||
IParentChildVO parent = allMap.get(vo.getPid());
|
||||
if (parent == null) return;
|
||||
List<IParentChildVO> children = parent.getChildren();
|
||||
if (children != null) {
|
||||
if (children.stream().noneMatch(child -> child.getId().equals(vo.getId()))) {
|
||||
children.add(vo);
|
||||
}
|
||||
} else {
|
||||
parent.setChildren(Lists.newArrayList(vo));
|
||||
}
|
||||
buildParent(parent, allMap, rootMap);
|
||||
} else {
|
||||
rootMap.put((PK) vo.getId(), vo);
|
||||
}
|
||||
}
|
||||
|
||||
default List<? extends IParentChildVO> buildTree(List<? extends IParentChildVO> voList, List<? extends IParentChildVO> allList) {
|
||||
Map<PK, IParentChildVO> allMap = allList.stream().collect(Collectors.toMap(vo -> (PK) vo.getId(), Function.identity()));
|
||||
Map<PK, IParentChildVO> rootMap = new HashMap<>();
|
||||
voList.forEach(vo -> buildParent(vo, allMap, rootMap));
|
||||
return Lists.newArrayList(rootMap.values());
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,8 @@ package com.upchina.video.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.mapper.EasyBaseMapper;
|
||||
import com.upchina.video.entity.OnlineUser;
|
||||
import com.upchina.video.entity.VideoLiveTrend;
|
||||
import com.upchina.video.entity.VideoUserFlow;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,7 @@ import com.upchina.advisor.entity.AdvisorFollow;
|
||||
import com.upchina.advisor.mapper.AdvisorFollowMapper;
|
||||
import com.upchina.common.constant.IsFollow;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.video.constant.VideoLiveStatus;
|
||||
import com.upchina.video.constant.VideoMessageContentType;
|
||||
import com.upchina.video.constant.VideoMessageStatus;
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
package com.upchina.video.service.admin;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.video.constant.VideoTransStatus;
|
||||
import com.upchina.video.entity.VideoLiveLibrary;
|
||||
import com.upchina.video.mapper.VideoLiveLibraryMapper;
|
||||
import com.upchina.video.service.common.VideoCloudService;
|
||||
import com.upchina.video.vo.cloud.TaskDetailVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// admin资源Service
|
||||
@Service
|
||||
public class AdminVideoLibraryService {
|
||||
|
||||
@Resource
|
||||
private VideoLiveLibraryMapper videoLibraryMapper;
|
||||
|
||||
@Resource
|
||||
private VideoCloudService videoCloudService;
|
||||
|
||||
/**
|
||||
* 根据视频ID集合查询视频资源映射
|
||||
*
|
||||
* @param videoIds 视频ID集合
|
||||
* @return 视频资源映射
|
||||
*/
|
||||
public Map<Integer, Integer> selectLibraryMap(Collection<Integer> videoIds) {
|
||||
if (CollUtil.isEmpty(videoIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
LambdaQueryWrapper<VideoLiveLibrary> wrapper = Wrappers.<VideoLiveLibrary>lambdaQuery().in(VideoLiveLibrary::getVideoId, videoIds);
|
||||
List<VideoLiveLibrary> libraryList = videoLibraryMapper.selectList(wrapper);
|
||||
return libraryList.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
VideoLiveLibrary::getVideoId,
|
||||
Collectors.summingInt(VideoLiveLibrary::getDuration)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理视频转码
|
||||
*
|
||||
* @param videoId 视频ID
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<TaskDetailVO> processVideo(Integer videoId) {
|
||||
LambdaQueryWrapper<VideoLiveLibrary> wrapper = Wrappers.<VideoLiveLibrary>lambdaQuery()
|
||||
.eq(VideoLiveLibrary::getVideoId, videoId);
|
||||
List<VideoLiveLibrary> libraryList = videoLibraryMapper.selectList(wrapper);
|
||||
if (CollUtil.isEmpty(libraryList)) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "该视频直播没有需要下载转码的视频");
|
||||
}
|
||||
// 查该视频直播所有未转码下载链接的视屏
|
||||
List<VideoLiveLibrary> libraries = libraryList.stream().filter(l -> VideoTransStatus.NOT_START.value.equals(l.getDownloadStatus())).collect(Collectors.toList());
|
||||
libraries.forEach(library -> {
|
||||
String taskId = videoCloudService.ProcessMediaByProcedure(library.getFileId());
|
||||
if (StringUtils.isNotEmpty(taskId)) {
|
||||
LambdaQueryWrapper<VideoLiveLibrary> w = Wrappers.<VideoLiveLibrary>lambdaQuery()
|
||||
.eq(VideoLiveLibrary::getVideoId, videoId)
|
||||
.eq(VideoLiveLibrary::getFileId, library.getFileId());
|
||||
videoLibraryMapper.updateTask(taskId, w);
|
||||
}
|
||||
});
|
||||
return libraryList.stream().map(l -> {
|
||||
Integer downloadStatus = l.getDownloadStatus();
|
||||
if (VideoTransStatus.HAS_TRANSCODE.value.equals(downloadStatus)) {
|
||||
return new TaskDetailVO(l.getFileId(), 100L);
|
||||
} else {
|
||||
return new TaskDetailVO(l.getFileId(), 0L);
|
||||
}
|
||||
}).sorted(Comparator.comparing(TaskDetailVO::getFileId)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载转码视频
|
||||
*
|
||||
* @param videoId 视频ID
|
||||
* @return 视频地址
|
||||
*/
|
||||
public List<String> download(Integer videoId) {
|
||||
LambdaQueryWrapper<VideoLiveLibrary> wrapper = Wrappers.<VideoLiveLibrary>lambdaQuery()
|
||||
.eq(VideoLiveLibrary::getVideoId, videoId);
|
||||
List<VideoLiveLibrary> libraries = videoLibraryMapper.selectList(wrapper);
|
||||
if (CollUtil.isEmpty(libraries)) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "该直播暂无录播");
|
||||
}
|
||||
String[] fileIds = libraries.stream().map(VideoLiveLibrary::getFileId).toArray(String[]::new);
|
||||
return videoCloudService.buildDownloadUrl(fileIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询转码视频进度
|
||||
*
|
||||
* @param videoId 视频ID
|
||||
* @return 任务详情
|
||||
*/
|
||||
public List<TaskDetailVO> searchTasksDetail(Integer videoId) {
|
||||
// 查所有视频记录
|
||||
List<VideoLiveLibrary> libraryList = videoLibraryMapper.selectList(Wrappers.<VideoLiveLibrary>lambdaQuery()
|
||||
.eq(VideoLiveLibrary::getVideoId, videoId));
|
||||
return videoCloudService.searchTasksDetail(libraryList);
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,6 +15,7 @@ import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.constant.UserType;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.Pager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,328 +0,0 @@
|
||||
package com.upchina.video.service.app;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.advisor.service.AdvisorInfoService;
|
||||
import com.upchina.advisor.vo.AdvisorBasicVO;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.query.SaveCommentQuery;
|
||||
import com.upchina.common.result.AppPager;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.service.CacheService;
|
||||
import com.upchina.common.service.CommentService;
|
||||
import com.upchina.common.util.LoggerUtil;
|
||||
import com.upchina.common.util.TextUtil;
|
||||
import com.upchina.common.vo.FrontUserVO;
|
||||
import com.upchina.rbac.service.UserService;
|
||||
import com.upchina.video.constant.VideoMessageContentType;
|
||||
import com.upchina.video.constant.VideoPlayType;
|
||||
import com.upchina.video.entity.VideoLive;
|
||||
import com.upchina.video.entity.VideoLiveMessage;
|
||||
import com.upchina.video.entity.VideoSortEntity;
|
||||
import com.upchina.video.helper.VideoHelper;
|
||||
import com.upchina.video.mapper.VideoLiveMessageMapper;
|
||||
import com.upchina.video.query.common.UpdateVideoOptionQuery;
|
||||
import com.upchina.video.query.message.ListVideoAppQuery;
|
||||
import com.upchina.video.query.message.SendLiveMessageQuery;
|
||||
import com.upchina.video.service.common.VideoCacheService;
|
||||
import com.upchina.video.service.common.VideoCommonService;
|
||||
import com.upchina.video.service.common.VideoMessageService;
|
||||
import com.upchina.video.vo.common.VideoProductInfoVO;
|
||||
import com.upchina.video.vo.message.VideoMessageAppVO;
|
||||
import com.upchina.video.vo.message.VideoMessageBasicVO;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// app消息Service
|
||||
@Service
|
||||
public class AppVideoMessageService {
|
||||
|
||||
@Resource
|
||||
private VideoCommonService videoCommonService;
|
||||
|
||||
@Resource
|
||||
private VideoCacheService videoCacheService;
|
||||
|
||||
@Resource
|
||||
private VideoMessageService videoMessageService;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
@Resource
|
||||
private AdvisorInfoService advisorInfoService;
|
||||
|
||||
@Resource
|
||||
private CommentService commentService;
|
||||
|
||||
@Resource
|
||||
private VideoLiveMessageMapper videoLiveMessageMapper;
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
private final long userMessageInterval = 1000;
|
||||
|
||||
private final ConcurrentHashMap<Integer, List<VideoMessageAppVO>> tempMessageVOMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 从缓存中获取消息列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param frontUser 前端用户信息
|
||||
* @return 消息分页列表
|
||||
*/
|
||||
public AppPager<VideoMessageAppVO> getMessageListByCache(ListVideoAppQuery query, FrontUserVO frontUser) {
|
||||
if (query.getLastId() == null) {
|
||||
return cacheService.get(CacheKey.VIDEO_LIVE_MESSAGE, CacheKey.VideoLiveMessageKey.MESSAGE_TOP_20 + query.getId(), () -> getMessageList(query, frontUser));
|
||||
} else {
|
||||
return getMessageList(query, frontUser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中获取投顾消息列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @return 消息分页列表
|
||||
*/
|
||||
public AppPager<VideoMessageAppVO> getMessageAdvisorListByCache(ListVideoAppQuery query) {
|
||||
if (query.getLastId() == null) {
|
||||
return cacheService.get(CacheKey.VIDEO_LIVE_MESSAGE, CacheKey.VideoLiveMessageKey.MESSAGE_ADVISOR_TOP_20 + query.getId(), () -> getAdvisorMessageList(query));
|
||||
} else {
|
||||
return getAdvisorMessageList(query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param frontUser 前端用户信息
|
||||
* @return 消息分页列表
|
||||
*/
|
||||
public AppPager<VideoMessageAppVO> getMessageList(ListVideoAppQuery query, FrontUserVO frontUser) {
|
||||
if (query.getId() == null) {
|
||||
return AppPager.emptyPager();
|
||||
}
|
||||
SortedSet<VideoSortEntity> sortedSet;
|
||||
if (frontUser != null) {
|
||||
sortedSet = videoCacheService.getVideoMessageList(query.getId(), query.getStatus());
|
||||
} else {
|
||||
sortedSet = videoCacheService.getVideoMessageAdvisorList(query.getId());
|
||||
}
|
||||
Integer lastId = query.getLastId();
|
||||
Integer size = query.getSize();
|
||||
if (lastId != null) {
|
||||
VideoSortEntity lastEntity = sortedSet.stream().filter(v -> v.getId().equals(lastId)).findFirst().orElse(null);
|
||||
if (lastEntity == null) {
|
||||
//特殊情况,当lastId对应的消息被删除时
|
||||
List<VideoSortEntity> entityList = sortedSet.stream().filter(v -> v.getId() >= lastId).collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(entityList)) {
|
||||
return AppPager.emptyPager();
|
||||
}
|
||||
lastEntity = entityList.get(entityList.size() - 1);
|
||||
}
|
||||
sortedSet = ((NavigableSet<VideoSortEntity>) sortedSet).tailSet(lastEntity, false);
|
||||
}
|
||||
Map<Integer, AdvisorBasicVO> advisorMap = advisorInfoService.getAdvisorVoMap();
|
||||
List<VideoMessageAppVO> voList = new ArrayList<>(size);
|
||||
Iterator<VideoSortEntity> it = sortedSet.iterator();
|
||||
String userId = frontUser == null ? null : frontUser.getUserId();
|
||||
while (it.hasNext()) {
|
||||
VideoMessageAppVO vo = getMessageDetail(it.next().getId());
|
||||
vo.setAdvisorBasic(advisorMap.get(vo.getAdvisorId()));
|
||||
if (vo.getCreateUserId() != null) {
|
||||
vo.setCreateUserVO(userService.get(vo.getCreateUserId(), false));
|
||||
}
|
||||
if (frontUser != null) {
|
||||
vo.setIsCurrentUser(Objects.equals(userId, vo.getUserId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
|
||||
if (VideoHelper.isPhone(vo.getUserId())) {
|
||||
vo.setUserId(videoCommonService.encryptPhone(vo.getUserId()));
|
||||
}
|
||||
}
|
||||
voList.add(vo);
|
||||
if (--size == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new AppPager<>(voList, it.hasNext());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息详情
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @return 消息详情
|
||||
*/
|
||||
public VideoMessageAppVO getMessageDetail(Integer messageId) {
|
||||
VideoLiveMessage message = videoCacheService.getVideoMessageInfo(messageId);
|
||||
VideoMessageAppVO vo = new VideoMessageAppVO(message);
|
||||
if (StrUtil.isNotBlank(message.getUserId())) {
|
||||
vo.setIsForbid(videoCommonService.checkAppForbidden(message.getUserId()) ? IsOrNot.IS.value : IsOrNot.NOT.value);
|
||||
}
|
||||
if (message.getReplyId() != null) {
|
||||
VideoLiveMessage replyMessage = videoCacheService.getVideoMessageInfo(message.getReplyId());
|
||||
vo.setReplyBasic(VideoMessageBasicVO.toVo(replyMessage, true));
|
||||
}
|
||||
// 推荐产品
|
||||
if (StringUtils.isNotEmpty(message.getRecommendProduct())) {
|
||||
List<VideoProductInfoVO> prdList = videoCommonService.getMergeProductList(vo.getVideoId(), new String[]{message.getRecommendProduct()});
|
||||
vo.setProductBasic(prdList.isEmpty() ? null : prdList.get(0));
|
||||
}
|
||||
// 头像清晰度改写
|
||||
vo.setImgUrl(VideoMessageService.resizeImgUrl(vo.getImgUrl()));
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* APP发送消息
|
||||
*
|
||||
* @param query 发送消息查询对象
|
||||
* @param frontUser 前端用户信息
|
||||
* @return 发送的消息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public VideoMessageAppVO sendMessage(SendLiveMessageQuery query, FrontUserVO frontUser) {
|
||||
return saveUserTextMessage(query, frontUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直播间关注投顾消息
|
||||
*
|
||||
* @param query 更新视频选项查询对象
|
||||
* @param frontUser 前端用户信息
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void sendFollowMessage(UpdateVideoOptionQuery query, FrontUserVO frontUser) {
|
||||
videoMessageService.publishFollowMessage(query.getId(), frontUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取投顾消息列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @return 消息分页列表
|
||||
*/
|
||||
public AppPager<VideoMessageAppVO> getAdvisorMessageList(ListVideoAppQuery query) {
|
||||
return getMessageList(query, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存APP用户文本消息
|
||||
*
|
||||
* @param query 发送消息查询对象
|
||||
* @param frontUser 前端用户信息
|
||||
*/
|
||||
private VideoMessageAppVO saveUserTextMessage(SendLiveMessageQuery query, FrontUserVO frontUser) {
|
||||
String groupId = query.getGroupId();
|
||||
if (!StrUtil.isNumeric(groupId)) {
|
||||
throw new BizException(ResponseStatus.PARM_ERROR);
|
||||
}
|
||||
String userId = frontUser.getUserId();
|
||||
String imgUrl = frontUser.getImgUrl();
|
||||
String userName = frontUser.getUserName();
|
||||
String content = query.getText();
|
||||
Integer videoId = Integer.valueOf(groupId);
|
||||
try {
|
||||
videoCommonService.checkSensitiveWords(content);
|
||||
} catch (BizException e) {
|
||||
throw new BizException(ResponseStatus.SENSITIVE_WORD_ERROR);
|
||||
}
|
||||
try {
|
||||
VideoLive video = videoCacheService.getVideoInfo(videoId);
|
||||
if (video == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR);
|
||||
}
|
||||
if (!VideoHelper.VALID_STATUS_LIST.contains(video.getStatus())) {
|
||||
throw new BizException(ResponseStatus.STATUS_ERROR, "视频直播未上架");
|
||||
}
|
||||
boolean isLive = VideoPlayType.LIVE.value.equals(video.getPlayType());
|
||||
VideoLiveMessage message = query.toVo(videoId, userId, userName, video.getMessageAudit(), imgUrl);
|
||||
if (!isLive) {
|
||||
// 录播走评论接口
|
||||
SaveCommentQuery commentQuery = new SaveCommentQuery();
|
||||
commentQuery.setProductId(videoId);
|
||||
commentQuery.setProductType(ProductType.VIDEO_SINGLE.value);
|
||||
commentQuery.setCommentContent(query.getText());
|
||||
commentQuery.setSource(query.getSource());
|
||||
commentService.saveComment(commentQuery, frontUser);
|
||||
} else {
|
||||
// 直播走消息接口
|
||||
if (videoCommonService.checkAppForbidden(userId)) {
|
||||
throw new BizException(ResponseStatus.COMMENT_BLACK_USER_ERROR, "您已被禁言");
|
||||
}
|
||||
message.setContent(TextUtil.cleanUnsafeHtml(message.getContent()));
|
||||
}
|
||||
Integer messageCount = videoCacheService.getMessageCount(videoId, VideoMessageContentType.TEXT.value.equals(query.getType()) ? 1 : 0);
|
||||
videoLiveMessageMapper.insert(message);
|
||||
VideoMessageAppVO vo = new VideoMessageAppVO(message);
|
||||
if (StrUtil.isNotBlank(message.getUserId())) {
|
||||
if (videoCommonService.checkAppForbidden(message.getUserId())) {
|
||||
vo.setIsForbid(IsOrNot.IS.value);
|
||||
} else {
|
||||
vo.setIsForbid(IsOrNot.NOT.value);
|
||||
}
|
||||
}
|
||||
// 获取互动数
|
||||
vo.setMessageCount(messageCount);
|
||||
// 改写头像清晰度
|
||||
vo.setImgUrl(VideoMessageService.resizeImgUrl(vo.getImgUrl()));
|
||||
this.publishVideoMessage(vo, videoId);
|
||||
videoCacheService.clearVideoMessageCache(message.getVideoId(), null, message.getType(), message, null);
|
||||
return vo;
|
||||
} catch (BizException ex) {
|
||||
videoMessageService.publishVideoMessage(Integer.valueOf(groupId), ex);
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
BizException bizEx = new BizException(ResponseStatus.SYS_BUSY, ex);
|
||||
videoMessageService.publishVideoMessage(Integer.valueOf(groupId), bizEx);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void publishVideoMessage(VideoMessageAppVO vo, Integer videoId) {
|
||||
if (videoId == null) {
|
||||
return;
|
||||
}
|
||||
List<VideoMessageAppVO> list = tempMessageVOMap.get(videoId);
|
||||
if (vo != null) {
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
tempMessageVOMap.put(videoId, list);
|
||||
}
|
||||
list.add(vo);
|
||||
} else {
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
videoMessageService.publishVideoMessage(videoId, list);
|
||||
}
|
||||
tempMessageVOMap.remove(videoId);
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initPushUserMessageThread() {
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
tempMessageVOMap.keySet().forEach(videoId -> publishVideoMessage(null, videoId));
|
||||
Thread.sleep(userMessageInterval);
|
||||
} catch (InterruptedException e) {
|
||||
LoggerUtil.error("定时推送用户消息异常:" + ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,6 +15,7 @@ import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsActive;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.result.AppPager;
|
||||
import com.upchina.common.service.AdvertService;
|
||||
import com.upchina.common.service.CacheService;
|
||||
|
||||
@ -9,6 +9,7 @@ import com.google.common.collect.Table;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.service.*;
|
||||
|
||||
@ -1,584 +0,0 @@
|
||||
package com.upchina.video.service.common;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.google.common.collect.Table;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.common.config.cache.CacheKey;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.constant.ProductType;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.service.*;
|
||||
import com.upchina.common.util.Debounce;
|
||||
import com.upchina.common.util.LoggerUtil;
|
||||
import com.upchina.common.util.RsaUtil;
|
||||
import com.upchina.video.constant.*;
|
||||
import com.upchina.video.entity.*;
|
||||
import com.upchina.video.helper.AbstractVideoSortComparator;
|
||||
import com.upchina.video.helper.VideoHelper;
|
||||
import com.upchina.video.mapper.*;
|
||||
import com.upchina.video.query.common.PullVideoPlayInfoDataQuery;
|
||||
import com.upchina.video.service.admin.AdminVideoInteractionService;
|
||||
import com.upchina.video.service.admin.AdminVideoQuestionService;
|
||||
import com.upchina.video.service.app.AppVideoInfoService;
|
||||
import com.upchina.video.service.app.AppVideoInteractionService;
|
||||
import com.upchina.video.vo.cloud.VideoPlayInfoAdminVO;
|
||||
import com.upchina.video.vo.common.VideoProductInfoVO;
|
||||
import com.upchina.video.vo.info.VideoInfoAppVO;
|
||||
import com.upchina.video.vo.message.VideoPcAdvisorMessageVO;
|
||||
import com.upchina.video.vo.statistic.TXOnlineVO;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 公共函数 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author fangliangbao
|
||||
* @since 2022-09-21
|
||||
*/
|
||||
@Service
|
||||
public class VideoCommonService {
|
||||
|
||||
@Value("${rsa.pubKey}")
|
||||
private String ypPubKey;
|
||||
|
||||
@Resource
|
||||
private SensitiveWordService sensitiveWordService;
|
||||
|
||||
@Resource
|
||||
private VideoCloudService videoCloudService;
|
||||
|
||||
@Resource
|
||||
private RecommendService recommendService;
|
||||
|
||||
@Resource
|
||||
private MergeProductService mergeProductService;
|
||||
|
||||
@Resource
|
||||
private VideoCacheService videoCacheService;
|
||||
|
||||
@Resource
|
||||
private VideoLiveMapper videoLiveMapper;
|
||||
|
||||
@Resource
|
||||
private VideoMessageService videoMessageService;
|
||||
|
||||
@Resource
|
||||
private AdminVideoQuestionService adminVideoQuestionService;
|
||||
|
||||
@Resource
|
||||
private CommentBlackService commentBlackService;
|
||||
|
||||
@Resource
|
||||
private AppVideoInfoService appVideoInfoService;
|
||||
|
||||
@Resource
|
||||
private VideoCartMapper videoCartMapper;
|
||||
|
||||
@Resource
|
||||
private VideoBehaviorNotifyMapper videoBehaviorNotifyMapper;
|
||||
|
||||
@Resource
|
||||
private VideoUserFlowMapper videoUserFlowMapper;
|
||||
|
||||
@Resource
|
||||
private VideoLiveUserMapper videoLiveUserMapper;
|
||||
|
||||
@Resource
|
||||
private VideoLiveTagMapper videoLiveTagMapper;
|
||||
|
||||
@Resource
|
||||
private AppVideoInteractionService appVideoInteractionService;
|
||||
|
||||
@Resource
|
||||
private AdminVideoInteractionService adminVideoInteractionService;
|
||||
|
||||
@Resource
|
||||
private CacheService cacheService;
|
||||
|
||||
// 防抖延时(ms)
|
||||
private final long pushDelay = 10 * 1000;
|
||||
|
||||
private final Map<Integer, Debounce> debounceMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 生成播放信息,包含直播相关地址
|
||||
*
|
||||
* @param videoId 视频直播ID
|
||||
* @param isApp 是否APP查询
|
||||
* @return 地址信息
|
||||
*/
|
||||
public VideoPlayInfoAdminVO getPlayInfo(Integer videoId, boolean isApp) {
|
||||
VideoLive video = videoCacheService.getVideoInfo(videoId);
|
||||
if (video == null) {
|
||||
throw new BizException(ResponseStatus.PRODUCT_NOT_EXIST);
|
||||
}
|
||||
VideoPlayInfoAdminVO vo = new VideoPlayInfoAdminVO();
|
||||
if (VideoPlayType.LIVE.value.equals(video.getPlayType())) {
|
||||
// 直播(未结束)
|
||||
if (!isApp && !VideoLiveStatus.HAS_ENDED.value.equals(video.getLiveStatus())) {
|
||||
vo.setPushUrl(videoCloudService.getPushUrl(videoId));
|
||||
}
|
||||
vo.setPlayUrl(videoCloudService.getLivePlayUrl(videoId));
|
||||
vo.setFlvUrl(videoCloudService.getLivePlayUrl(videoId, "flv"));
|
||||
} else if (VideoPlayType.RECORD.value.equals(video.getPlayType())) {
|
||||
// 录播
|
||||
if (video.getLibraryId() == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "未选择视频资源");
|
||||
}
|
||||
VideoLiveLibrary library = videoCacheService.getVideoLibraryInfo(video.getLibraryId());
|
||||
if (library == null) {
|
||||
throw new BizException(ResponseStatus.ID_NOT_EXIST_ERROR, "视频资源不存在");
|
||||
}
|
||||
vo.setPlayUrl(videoCloudService.getPlayUrl(library.getFileId()));
|
||||
vo.setPlayUrl(videoCloudService.getPlayUrl(library.getFileId()));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号加密,非手机号直接返回
|
||||
*
|
||||
* @param phone 手机号
|
||||
* @return 加密后的手机号
|
||||
*/
|
||||
public String encryptPhone(String phone) {
|
||||
if (VideoHelper.isPhone(phone)) {
|
||||
return RsaUtil.pubKeyEncryption(ypPubKey, phone);
|
||||
}
|
||||
return phone;
|
||||
}
|
||||
|
||||
/**
|
||||
* 敏感词检测
|
||||
*
|
||||
* @param contents 检测内容集合
|
||||
*/
|
||||
public void checkSensitiveWords(String... contents) {
|
||||
List<String> words = Stream.of(contents).filter(StringUtils::isNotEmpty).collect(Collectors.toList());
|
||||
if (words.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
sensitiveWordService.check(words);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验推荐产品是否存在
|
||||
*
|
||||
* @param type 产品类型
|
||||
* @param id 产品ID
|
||||
* @param status 产品状态
|
||||
*/
|
||||
public void validateRecommend(ProductType type, Integer id, VideoStatus status) {
|
||||
if (VideoStatus.SOLD_OUT.equals(status)) {
|
||||
recommendService.validateRecommendExist(type, id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束暂停中或直播中的直播
|
||||
* 下架设置回放过期时间的直播
|
||||
*/
|
||||
public void stopLivingVideo() {
|
||||
// 结束前一天直播
|
||||
LambdaQueryWrapper<VideoLive> wrapper = Wrappers.lambdaQuery();
|
||||
wrapper.select(VideoLive::getId, VideoLive::getEndTime)
|
||||
.ne(VideoLive::getLiveStatus, VideoLiveStatus.HAS_ENDED.value)
|
||||
.lt(VideoLive::getEndTime, LocalDate.now().atStartOfDay());
|
||||
List<VideoLive> videoList = videoLiveMapper.selectList(wrapper);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
if (!videoList.isEmpty()) {
|
||||
Map<String, Object> cacheMap = videoCacheService.getVideoCacheMap();
|
||||
videoList.forEach(v -> {
|
||||
LocalDateTime realEndTime = LocalDateTime.of(v.getEndTime().toLocalDate(), LocalTime.MAX);
|
||||
VideoLive entity = new VideoLive(v.getId(), VideoLiveStatus.HAS_ENDED.value, realEndTime);
|
||||
entity.setStatus(VideoStatus.SOLD_OUT.value);
|
||||
entity.setOnlineTop(adminVideoInteractionService.getOnlineMax(v.getId()));
|
||||
// 插入直播观看人次
|
||||
Integer totalReadCount = adminVideoInteractionService.queryInteractionCount(v.getId(), VideoUserRecordType.READ, null, null, false);
|
||||
entity.setLiveNum(totalReadCount);
|
||||
videoLiveMapper.updateById(entity);
|
||||
endVideo(cacheMap, v);
|
||||
adminVideoQuestionService.stopByVideoId(v.getId());
|
||||
});
|
||||
}
|
||||
// 回放过期
|
||||
wrapper = Wrappers.<VideoLive>lambdaQuery()
|
||||
.eq(VideoLive::getPlayType, VideoPlayType.LIVE.value)
|
||||
.eq(VideoLive::getStatus, VideoStatus.PASS.value)
|
||||
.eq(VideoLive::getLiveStatus, VideoLiveStatus.HAS_ENDED.value)
|
||||
.isNotNull(VideoLive::getReplayExpireDays)
|
||||
.gt(VideoLive::getReplayExpireDays, 0);
|
||||
List<VideoLive> expireVideoList = videoLiveMapper.selectList(wrapper);
|
||||
for (VideoLive video : expireVideoList) {
|
||||
// 下架设置回放过期时间的直播
|
||||
Integer expireDays = video.getReplayExpireDays();
|
||||
if (expireDays != null && expireDays != 0) {
|
||||
LocalDateTime replayExpireTime = video.getStartTime().toLocalDate().plusDays(expireDays + 1).atStartOfDay();
|
||||
if (replayExpireTime.isBefore(now)) {
|
||||
VideoLive soldOutVideo = new VideoLive(video.getId(), VideoStatus.SOLD_OUT);
|
||||
videoLiveMapper.updateById(soldOutVideo);
|
||||
videoCacheService.clearVideoCache(video);
|
||||
}
|
||||
}
|
||||
}
|
||||
videoCacheService.clearVideoCache(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束直播
|
||||
*
|
||||
* @param cacheMap 缓存
|
||||
* @param video 直播信息
|
||||
*/
|
||||
public void endVideo(Map<String, Object> cacheMap, VideoLive video) {
|
||||
// 清除直播状态缓存
|
||||
videoCacheService.clearVideoInfoCache(video.getId(), cacheMap);
|
||||
// 已结束,中断投顾推流
|
||||
videoCloudService.dropLiveStream(video.getId());
|
||||
// 直播结束通知
|
||||
videoMessageService.publishLiveStatusNotification(video.getId(), VideoLiveStatus.HAS_ENDED.value);
|
||||
// 直播状态推送(pc)
|
||||
VideoPcAdvisorMessageVO pcAdvisorMessageVO = new VideoPcAdvisorMessageVO(VideoLiveStatus.HAS_ENDED.value);
|
||||
videoMessageService.publishPcAdvisorMessage(video.getId(), pcAdvisorMessageVO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新录播直播状态
|
||||
*/
|
||||
public void updateLiveStatus() {
|
||||
LambdaQueryWrapper<VideoLive> wrapper = Wrappers.<VideoLive>lambdaQuery()
|
||||
.select(VideoLive::getId, VideoLive::getStartTime, VideoLive::getEndTime)
|
||||
.ne(VideoLive::getLiveStatus, VideoLiveStatus.HAS_ENDED.value)
|
||||
.eq(VideoLive::getPlayType, VideoPlayType.RECORD.value)
|
||||
.isNotNull(VideoLive::getStartTime)
|
||||
.isNotNull(VideoLive::getEndTime);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<Integer> notStart = new ArrayList<>();
|
||||
List<Integer> started = new ArrayList<>();
|
||||
List<Integer> hasEnded = new ArrayList<>();
|
||||
|
||||
videoLiveMapper.selectList(wrapper).forEach(v -> {
|
||||
LocalDateTime startTime = v.getStartTime();
|
||||
LocalDateTime endTime = v.getEndTime();
|
||||
VideoLiveStatus videoLiveStatus = null;
|
||||
if (now.isBefore(startTime)) {
|
||||
notStart.add(v.getId());
|
||||
} else if (now.isAfter(startTime) && endTime.isAfter(now)) {
|
||||
started.add(v.getId());
|
||||
videoLiveStatus = VideoLiveStatus.LIVING;
|
||||
} else {
|
||||
hasEnded.add(v.getId());
|
||||
videoLiveStatus = VideoLiveStatus.HAS_ENDED;
|
||||
}
|
||||
if (videoLiveStatus != null) {
|
||||
videoMessageService.publishLiveStatusNotification(v.getId(), videoLiveStatus.value);
|
||||
// 直播状态推送(pc)
|
||||
VideoPcAdvisorMessageVO pcAdvisorMessageVO = new VideoPcAdvisorMessageVO(videoLiveStatus.value);
|
||||
videoMessageService.publishPcAdvisorMessage(v.getId(), pcAdvisorMessageVO);
|
||||
}
|
||||
});
|
||||
|
||||
updateLiveStatusByIds(notStart, VideoLiveStatus.NOT_STARTED.value);
|
||||
updateLiveStatusByIds(started, VideoLiveStatus.LIVING.value);
|
||||
updateLiveStatusByIds(hasEnded, VideoLiveStatus.HAS_ENDED.value);
|
||||
|
||||
Map<String, Object> cacheMap = videoCacheService.getVideoCacheMap();
|
||||
Stream.of(notStart, started, hasEnded).flatMap(List::stream)
|
||||
.forEach(v -> videoCacheService.clearVideoInfoCache(v, cacheMap));
|
||||
videoCacheService.clearVideoCache(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新视频直播及课程观看数及用户(定时任务执行)
|
||||
*/
|
||||
public void saveCountAndUser() {
|
||||
try {
|
||||
LoggerUtil.video.info(">>>刷新视频直播播放量及观看用户任务开始");
|
||||
long start = System.currentTimeMillis();
|
||||
// 记录观看、购物车点击、分享用户
|
||||
videoCacheService.loadVideoRecordUserToDb();
|
||||
// 点赞
|
||||
appVideoInteractionService.saveFavor();
|
||||
LoggerUtil.video.info(">>>刷新视频直播播放量及观看用户耗时:" + (System.currentTimeMillis() - start));
|
||||
} catch (Exception e) {
|
||||
LoggerUtil.error("刷新视频直播播放量及观看用户异常:" + ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户消息被回复推送消息提醒
|
||||
*/
|
||||
public void pushReplyVideoMessage(Integer videoId, Integer replyId, String replyText) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 直播互动消息禁言
|
||||
*
|
||||
* @param phone 客户手机号
|
||||
*/
|
||||
public boolean checkAppForbidden(String phone) {
|
||||
Set<String> blackUser = commentBlackService.getAllBlackUser();
|
||||
if (blackUser == null || StrUtil.isEmpty(phone)) {
|
||||
return false;
|
||||
}
|
||||
return blackUser.contains(phone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直播开播未观看提醒
|
||||
*
|
||||
* @param video 直播信息
|
||||
*/
|
||||
public void pushBeforeLiveMessage(VideoLive video) {
|
||||
try {
|
||||
Integer videoId = video.getId();
|
||||
String videoName = video.getTitle();
|
||||
List<OnlineUser> onlineList = videoCacheService.getOnlineList(videoId);
|
||||
Set<String> onlineUserIds = onlineList.stream().map(OnlineUser::getUserId).collect(Collectors.toSet());
|
||||
|
||||
// 查询预约的用户
|
||||
LambdaQueryWrapper<VideoLiveUser> subWrapper = Wrappers.<VideoLiveUser>lambdaQuery()
|
||||
.select(VideoLiveUser::getUserId, VideoLiveUser::getUserName)
|
||||
.eq(VideoLiveUser::getVideoId, videoId)
|
||||
.eq(VideoLiveUser::getType, VideoUserRecordType.SUBSCRIBE.value)
|
||||
.and(w -> w.isNull(VideoLiveUser::getHasNotified).or().eq(VideoLiveUser::getHasNotified, IsOrNot.NOT.value));
|
||||
List<VideoLiveUser> allSubUserList = videoLiveUserMapper.selectList(subWrapper);
|
||||
|
||||
// 预约后未进入直播间用户
|
||||
List<VideoLiveUser> notOnlineUser = allSubUserList.stream()
|
||||
.filter(user -> !onlineUserIds.contains(user.getUserId())).collect(Collectors.toList());
|
||||
|
||||
// 在线已退出用户,退出时间在直播已开始之后
|
||||
LambdaQueryWrapper<VideoUserFlow> queryWrapper = Wrappers.<VideoUserFlow>lambdaQuery()
|
||||
.select(VideoUserFlow::getUserId)
|
||||
.eq(VideoUserFlow::getVideoId, videoId);
|
||||
List<VideoUserFlow> dbReadRecords = videoUserFlowMapper.selectList(queryWrapper);
|
||||
List<String> dbUserIds = dbReadRecords.stream()
|
||||
.map(VideoUserFlow::getUserId).distinct().collect(Collectors.toList());
|
||||
|
||||
//预约后未进入直播间用户
|
||||
List<VideoLiveUser> notifyUserList = notOnlineUser.stream()
|
||||
.filter(user -> !dbUserIds.contains(user.getUserId())).collect(Collectors.toList());
|
||||
List<String> notifyUserIds = notifyUserList.stream().map(VideoLiveUser::getUserId).collect(Collectors.toList());
|
||||
|
||||
LoggerUtil.video.info("直播开播未及时观看提醒:直播【" + videoName + "】预约用户【" + notifyUserIds + "】");
|
||||
if (notifyUserIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 开播前5分钟直播记录所有待通知用户
|
||||
String content = String.format("预约的直播【%s】已开播5分钟,尚未进入直播间", videoName);
|
||||
List<VideoBehaviorNotify> notifyList = notifyUserList.stream().map(user -> {
|
||||
VideoBehaviorNotify notify = new VideoBehaviorNotify();
|
||||
notify.setUserId(user.getUserId());
|
||||
notify.setUserName(user.getUserName());
|
||||
notify.setPhone(user.getUserId());
|
||||
notify.setType(VideoNotifyType.SUBSCRIBE_UN_ENTER.value);
|
||||
notify.setVideoId(videoId);
|
||||
notify.setDescription(content);
|
||||
notify.setNotifyTime(LocalDateTime.now());
|
||||
notify.setCreateTime(LocalDateTime.now());
|
||||
return notify;
|
||||
}).collect(Collectors.toList());
|
||||
// 写入行为提醒
|
||||
videoBehaviorNotifyMapper.insertBatchSomeColumn(notifyList);
|
||||
// 更新用户已提醒
|
||||
LambdaQueryWrapper<VideoLiveUser> wrapper = Wrappers.<VideoLiveUser>lambdaQuery()
|
||||
.eq(VideoLiveUser::getType, VideoUserRecordType.SUBSCRIBE.value)
|
||||
.eq(VideoLiveUser::getVideoId, videoId)
|
||||
.in(VideoLiveUser::getUserId, notifyUserIds)
|
||||
.ne(VideoLiveUser::getHasNotified, IsOrNot.IS.value);
|
||||
videoLiveUserMapper.update(new VideoLiveUser(videoId, IsOrNot.IS.value, LocalDateTime.now()), wrapper);
|
||||
} catch (Exception e) {
|
||||
LoggerUtil.error("直播开播未观看提醒:异常-" + ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已排序的视频直播集合
|
||||
*
|
||||
* @param videoIds 视频直播ID集合
|
||||
* @return 观看用户
|
||||
*/
|
||||
public SortedSet<VideoSortEntity> getSortedVideos(Collection<Integer> videoIds) {
|
||||
SortedSet<VideoSortEntity> sortedSet = new TreeSet<>(AbstractVideoSortComparator.VIDEO_COMP_PLAN_LIST);
|
||||
|
||||
videoIds.forEach(videoId -> {
|
||||
VideoLive video = videoCacheService.getVideoInfo(videoId);
|
||||
if (video == null) return;
|
||||
|
||||
LocalDateTime startTime = Optional.ofNullable(video.getRealStartTime()).orElse(video.getStartTime());
|
||||
VideoLiveLibrary library = videoCacheService.getVideoLibrary(video.getId(), video.getLibraryId());
|
||||
boolean isRecord = IsOrNot.IS.value.equals(video.getIsGenerateRecord());
|
||||
|
||||
VideoSortEntity entity = new VideoSortEntity();
|
||||
entity.setId(video.getId());
|
||||
entity.setName(video.getTitle());
|
||||
entity.setTime(startTime);
|
||||
entity.setCount(videoCacheService.getVideoReadCount(videoId));
|
||||
entity.setPlayType(video.getPlayType());
|
||||
entity.setLiveStatus(video.getLiveStatus());
|
||||
entity.setLibCount(isRecord && library != null ? 1 : 0);
|
||||
entity.setCreateTime(video.getCreateTime());
|
||||
entity.setAuditTime(video.getAuditTime());
|
||||
entity.setNewLiveStatus(appVideoInfoService.getNewStatus(entity.getLiveStatus()));
|
||||
|
||||
sortedSet.add(entity);
|
||||
});
|
||||
|
||||
return sortedSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频直播关联产品信息
|
||||
*
|
||||
* @param videoId 视频直播ID
|
||||
* @param typeAndIds 关联产品类型和ID
|
||||
* @return 关联产品信息
|
||||
*/
|
||||
public List<VideoProductInfoVO> getMergeProductList(Integer videoId, String[] typeAndIds) {
|
||||
try {
|
||||
List<VideoLiveProduct> list = Arrays.stream(typeAndIds)
|
||||
.map(v -> {
|
||||
String[] typeAndId = v.split(":");
|
||||
VideoLiveProduct vp = new VideoLiveProduct();
|
||||
vp.setType(VideoRelativeProduct.RECOMMEND.value);
|
||||
vp.setProductType(Integer.parseInt(typeAndId[0]));
|
||||
vp.setProductId(Integer.parseInt(typeAndId[1]));
|
||||
return vp;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
if (list.size() == 1) {
|
||||
VideoLiveProduct product = list.get(0);
|
||||
if (ProductType.CUSTOM_PRODUCT.value.equals(product.getProductType())) {
|
||||
VideoCart cart = videoCartMapper.selectOne(Wrappers.<VideoCart>lambdaQuery()
|
||||
.eq(VideoCart::getVideoId, videoId)
|
||||
.eq(VideoCart::getProductId, product.getProductId())
|
||||
.eq(VideoCart::getProductType, product.getProductType()));
|
||||
return Collections.singletonList(new VideoProductInfoVO(cart.getProductId(), cart.getProductType(), cart.getProductName(), cart.getProductDesc(), cart.getUrl()));
|
||||
}
|
||||
}
|
||||
|
||||
Integer videoType = ProductType.VIDEO_SINGLE.value;
|
||||
List<VideoLiveProduct> pList = list.stream()
|
||||
.filter(v -> !(videoType.equals(v.getProductType()) && Objects.equals(videoId, v.getProductId())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
VideoLive video = videoCacheService.getVideoInfo(videoId);
|
||||
if (video == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Table<Integer, Integer, Object> table = mergeProductService.queryMergeProductInfo(pList, true);
|
||||
|
||||
return list.stream().map(v -> {
|
||||
VideoProductInfoVO prd;
|
||||
if (videoType.equals(v.getProductType()) && Objects.equals(videoId, v.getProductId())) {
|
||||
prd = new VideoProductInfoVO(v, video.getTitle(), video.getViewPoint(), video.getAuthorityId());
|
||||
} else {
|
||||
prd = getProductInfo(table, v);
|
||||
}
|
||||
return prd;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
LoggerUtil.error("视频直播查询关联产品错误:" + ExceptionUtils.getStackTrace(e));
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取腾讯云实时在线人数
|
||||
*
|
||||
* @param videoId 视频ID
|
||||
* @return TXOnlineVO对象,包含实时在线人数和直播状态
|
||||
*/
|
||||
public TXOnlineVO txonline(Integer videoId) {
|
||||
VideoLive videoInfo = videoCacheService.getVideoInfo(videoId);
|
||||
Integer txonline = cacheService.get(CacheKey.VIDEO_TX_ONLINE, CacheKey.TXKey.VIDEO_ONLINE_TX + videoId, () -> {
|
||||
LocalDateTime now = LocalDateTime.now().minusMinutes(1);
|
||||
VideoLive videoLive = videoLiveMapper.selectById(videoId);
|
||||
PullVideoPlayInfoDataQuery query = new PullVideoPlayInfoDataQuery();
|
||||
query.setVideoId(String.valueOf(videoId));
|
||||
query.setStartTime(videoLive.getRealStartTime());
|
||||
query.setEndTime(videoLive.getRealEndTime() == null ? now : videoLive.getRealEndTime());
|
||||
List<VideoLiveTrend> trends = videoCloudService.pullVideoPlayInfoData(query);
|
||||
return CollUtil.isEmpty(trends) ? 0 : trends.get(trends.size() - 1).getOnline();
|
||||
});
|
||||
return new TXOnlineVO(txonline, videoInfo == null ? null : videoInfo.getLiveStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查标签是否被使用
|
||||
*
|
||||
* @param id 标签ID
|
||||
* @return 是否被使用
|
||||
*/
|
||||
public boolean checkUseTag(Integer id) {
|
||||
LambdaQueryWrapper<VideoLiveTag> wrapper = Wrappers.<VideoLiveTag>lambdaQuery()
|
||||
.eq(VideoLiveTag::getTagId, id);
|
||||
return videoLiveTagMapper.exists(wrapper);
|
||||
}
|
||||
|
||||
private void updateLiveStatusByIds(List<Integer> ids, Integer status) {
|
||||
if (!ids.isEmpty()) {
|
||||
QueryWrapper<VideoLive> wrapper = Wrappers.query();
|
||||
wrapper.in("id", ids);
|
||||
videoLiveMapper.update(new VideoLive(status), wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
private VideoProductInfoVO getProductInfo(Table<Integer, Integer, Object> table, VideoLiveProduct product) {
|
||||
try {
|
||||
Integer productType = product.getProductType();
|
||||
Object detail = table.get(productType, product.getProductId());
|
||||
if (detail == null) {
|
||||
return null;
|
||||
}
|
||||
if (ProductType.VIDEO_SINGLE.value.equals(productType)) {
|
||||
VideoInfoAppVO vo = (VideoInfoAppVO) detail;
|
||||
return new VideoProductInfoVO(product, vo.getTitle(), vo.getViewPoint(), null);
|
||||
}
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
LoggerUtil.error("视频直播查询推广产品错误:" + ExceptionUtils.getStackTrace(e));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void publishPcMessageWithDebounce(Integer videoId) {
|
||||
Debounce debounce = debounceMap.get(videoId);
|
||||
if (debounce == null) {
|
||||
debounce = new Debounce();
|
||||
debounceMap.put(videoId, debounce);
|
||||
}
|
||||
// 防抖执行
|
||||
debounce.debounce(() -> this.publishPcMessage(videoId), pushDelay);
|
||||
}
|
||||
|
||||
private void publishPcMessage(Integer videoId) {
|
||||
//点赞数
|
||||
Integer favorCount = videoCacheService.getVideoFavorUserCount(videoId);
|
||||
//观看
|
||||
Integer readCount = videoCacheService.getVideoReadCount(videoId);
|
||||
//在线数
|
||||
int onlineCount = videoCacheService.getOnlineCount(videoId);
|
||||
VideoPcAdvisorMessageVO messageVO = new VideoPcAdvisorMessageVO(favorCount, readCount, onlineCount);
|
||||
videoMessageService.publishPcAdvisorMessage(videoId, messageVO);
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,13 +9,13 @@ import com.hazelcast.map.IMap;
|
||||
import com.hazelcast.topic.ITopic;
|
||||
import com.upchina.advisor.service.AdvisorInfoService;
|
||||
import com.upchina.common.constant.IsOrNot;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.common.handler.BizException;
|
||||
import com.upchina.common.result.ResponseStatus;
|
||||
import com.upchina.common.util.logger.LoggerUtil;
|
||||
import com.upchina.common.vo.FrontUserVO;
|
||||
import com.upchina.rbac.service.UserService;
|
||||
import com.upchina.video.constant.*;
|
||||
import com.upchina.video.entity.OnlineUser;
|
||||
import com.upchina.video.entity.VideoLive;
|
||||
import com.upchina.video.entity.VideoLiveMessage;
|
||||
import com.upchina.video.helper.VideoHelper;
|
||||
|
||||
@ -1,249 +0,0 @@
|
||||
package com.upchina.video.vo.cart;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.dyqh.QuerySendCouponReq;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CouponVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("领券关联id")
|
||||
public int sendCouponId = 0;
|
||||
|
||||
@ApiModelProperty("优惠券批次名称")
|
||||
public String name = "";
|
||||
|
||||
@ApiModelProperty("优惠券类型 1-体验券;2-折扣券 3-满减券")
|
||||
public int couponType = 0;
|
||||
|
||||
@ApiModelProperty("满减券-优惠金额 (单位:元)")
|
||||
public double discount = 0D;
|
||||
|
||||
@ApiModelProperty("折扣券-折扣率")
|
||||
public double discountPercent = 0D;
|
||||
|
||||
@ApiModelProperty("体验券,体验数")
|
||||
public int discountNum = 0;
|
||||
|
||||
@ApiModelProperty("体验券,体验时长,1-天;2-月")
|
||||
public int discountNumType = 0;
|
||||
|
||||
@ApiModelProperty("适用产品范围类型,逗号分割,0:全场通用1:指定产品可用")
|
||||
public int scopeType = 0;
|
||||
|
||||
@ApiModelProperty("优惠券有效期起始日")
|
||||
public String validStart;
|
||||
|
||||
@ApiModelProperty("优惠券有效期截止日")
|
||||
public String validEnd;
|
||||
|
||||
@ApiModelProperty("订单使用优惠券最低限额(单位:元)0表示不限制 使用优惠券前的订单金额大于等于此金额才能使用优惠券")
|
||||
public double basicConsume = 0D;
|
||||
|
||||
@ApiModelProperty("订单最低使用金额限制(单位:元)0表示不限制 使用优惠券后订单金额不能低于此金额")
|
||||
public double basicUsedAmt = 0D;
|
||||
|
||||
@ApiModelProperty("折扣券,最多优惠 (单位:元),0表示没有限制")
|
||||
public double discountLimit = 0D;
|
||||
|
||||
@ApiModelProperty("领取要求: 0 无要求, 1 关注主播, 2 评论互动")
|
||||
private Integer receiveRequirement;
|
||||
|
||||
@ApiModelProperty("视频直播id")
|
||||
private Integer liveId;
|
||||
|
||||
@ApiModelProperty("视频直播id")
|
||||
private Integer validDays;
|
||||
|
||||
@ApiModelProperty(value = "发放数量", example = "100")
|
||||
private Integer sendTotalNumber;
|
||||
|
||||
@ApiModelProperty(value = "已领取数量", example = "10")
|
||||
private Integer sendGottenNumber;
|
||||
|
||||
@ApiModelProperty(value = "已使用数量", example = "1")
|
||||
private Integer sendUsedNumber;
|
||||
|
||||
public CouponVO() {
|
||||
}
|
||||
|
||||
public int getSendCouponId() {
|
||||
return sendCouponId;
|
||||
}
|
||||
|
||||
public void setSendCouponId(int sendCouponId) {
|
||||
this.sendCouponId = sendCouponId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getCouponType() {
|
||||
return couponType;
|
||||
}
|
||||
|
||||
public void setCouponType(int couponType) {
|
||||
this.couponType = couponType;
|
||||
}
|
||||
|
||||
public double getDiscount() {
|
||||
return discount;
|
||||
}
|
||||
|
||||
public void setDiscount(double discount) {
|
||||
this.discount = discount;
|
||||
}
|
||||
|
||||
public double getDiscountPercent() {
|
||||
return discountPercent;
|
||||
}
|
||||
|
||||
public void setDiscountPercent(double discountPercent) {
|
||||
this.discountPercent = discountPercent;
|
||||
}
|
||||
|
||||
public int getDiscountNum() {
|
||||
return discountNum;
|
||||
}
|
||||
|
||||
public void setDiscountNum(int discountNum) {
|
||||
this.discountNum = discountNum;
|
||||
}
|
||||
|
||||
public int getDiscountNumType() {
|
||||
return discountNumType;
|
||||
}
|
||||
|
||||
public void setDiscountNumType(int discountNumType) {
|
||||
this.discountNumType = discountNumType;
|
||||
}
|
||||
|
||||
public int getScopeType() {
|
||||
return scopeType;
|
||||
}
|
||||
|
||||
public void setScopeType(int scopeType) {
|
||||
this.scopeType = scopeType;
|
||||
}
|
||||
|
||||
public String getValidStart() {
|
||||
return validStart;
|
||||
}
|
||||
|
||||
public void setValidStart(String validStart) {
|
||||
this.validStart = validStart;
|
||||
}
|
||||
|
||||
public String getValidEnd() {
|
||||
return validEnd;
|
||||
}
|
||||
|
||||
public void setValidEnd(String validEnd) {
|
||||
this.validEnd = validEnd;
|
||||
}
|
||||
|
||||
public Integer getReceiveRequirement() {
|
||||
return receiveRequirement;
|
||||
}
|
||||
|
||||
public void setReceiveRequirement(Integer receiveRequirement) {
|
||||
this.receiveRequirement = receiveRequirement;
|
||||
}
|
||||
|
||||
public double getBasicConsume() {
|
||||
return basicConsume;
|
||||
}
|
||||
|
||||
public void setBasicConsume(double basicConsume) {
|
||||
this.basicConsume = basicConsume;
|
||||
}
|
||||
|
||||
public double getBasicUsedAmt() {
|
||||
return basicUsedAmt;
|
||||
}
|
||||
|
||||
public void setBasicUsedAmt(double basicUsedAmt) {
|
||||
this.basicUsedAmt = basicUsedAmt;
|
||||
}
|
||||
|
||||
public double getDiscountLimit() {
|
||||
return discountLimit;
|
||||
}
|
||||
|
||||
public void setDiscountLimit(double discountLimit) {
|
||||
this.discountLimit = discountLimit;
|
||||
}
|
||||
|
||||
public Integer getLiveId() {
|
||||
return liveId;
|
||||
}
|
||||
|
||||
public void setLiveId(Integer liveId) {
|
||||
this.liveId = liveId;
|
||||
}
|
||||
|
||||
public Integer getValidDays() {
|
||||
return validDays;
|
||||
}
|
||||
|
||||
public void setValidDays(Integer validDays) {
|
||||
this.validDays = validDays;
|
||||
}
|
||||
|
||||
public Integer getSendTotalNumber() {
|
||||
return sendTotalNumber;
|
||||
}
|
||||
|
||||
public void setSendTotalNumber(Integer sendTotalNumber) {
|
||||
this.sendTotalNumber = sendTotalNumber;
|
||||
}
|
||||
|
||||
public Integer getSendGottenNumber() {
|
||||
return sendGottenNumber;
|
||||
}
|
||||
|
||||
public void setSendGottenNumber(Integer sendGottenNumber) {
|
||||
this.sendGottenNumber = sendGottenNumber;
|
||||
}
|
||||
|
||||
public Integer getSendUsedNumber() {
|
||||
return sendUsedNumber;
|
||||
}
|
||||
|
||||
public void setSendUsedNumber(Integer sendUsedNumber) {
|
||||
this.sendUsedNumber = sendUsedNumber;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CouponVO{" +
|
||||
"sendCouponId=" + sendCouponId +
|
||||
", name='" + name + '\'' +
|
||||
", couponType=" + couponType +
|
||||
", discount=" + discount +
|
||||
", discountPercent=" + discountPercent +
|
||||
", discountNum=" + discountNum +
|
||||
", discountNumType=" + discountNumType +
|
||||
", scopeType=" + scopeType +
|
||||
", validStart='" + validStart + '\'' +
|
||||
", validEnd='" + validEnd + '\'' +
|
||||
", basicConsume=" + basicConsume +
|
||||
", basicUsedAmt=" + basicUsedAmt +
|
||||
", discountLimit=" + discountLimit +
|
||||
", receiveRequirement=" + receiveRequirement +
|
||||
", liveId=" + liveId +
|
||||
", validDays=" + validDays +
|
||||
", sendTotalNumber=" + sendTotalNumber +
|
||||
", sendGottenNumber=" + sendGottenNumber +
|
||||
", sendUsedNumber=" + sendUsedNumber +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package com.upchina.video.vo.message;
|
||||
|
||||
import com.upchina.video.entity.OnlineUser;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@ -1,126 +0,0 @@
|
||||
package com.upchina.video.vo.message;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.upchina.video.entity.VideoLiveMessage;
|
||||
import com.upchina.video.helper.VideoHelper;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 互动消息基本信息
|
||||
* </p>
|
||||
*
|
||||
* @author fangliangbao
|
||||
* @since 2022-11-01
|
||||
*/
|
||||
@ApiModel("互动消息基本信息")
|
||||
public class VideoMessageBasicVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ApiModelProperty("ID")
|
||||
private Integer id;
|
||||
|
||||
@ApiModelProperty("用户ID")
|
||||
private String userId;
|
||||
|
||||
@ApiModelProperty("用户名称")
|
||||
private String userName;
|
||||
|
||||
@ApiModelProperty("消息内容")
|
||||
private String content;
|
||||
|
||||
@ApiModelProperty("回复时间")
|
||||
private LocalDateTime replyTime;
|
||||
|
||||
@ApiModelProperty("是否公开 1公开 2不公开")
|
||||
private Integer isOpen;
|
||||
|
||||
public static VideoMessageBasicVO toVo(VideoLiveMessage message, boolean needMask) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
}
|
||||
VideoMessageBasicVO vo = new VideoMessageBasicVO();
|
||||
vo.setId(message.getId());
|
||||
if (needMask) {
|
||||
vo.setUserId(VideoHelper.maskPhone(message.getUserId()));
|
||||
if (StringUtils.isNotEmpty(message.getUserName())) {
|
||||
// vo.setUserName(VideoHelper.maskUserName(message.getUserName()));
|
||||
vo.setUserName(message.getUserName());
|
||||
} else {
|
||||
vo.setUserName(VideoHelper.maskPhone(message.getUserId()));
|
||||
}
|
||||
} else {
|
||||
vo.setUserId(message.getUserId());
|
||||
vo.setUserName(message.getUserName());
|
||||
}
|
||||
vo.setContent(message.getContent());
|
||||
vo.setReplyTime(message.getCreateTime());
|
||||
vo.setIsOpen(message.getIsOpen());
|
||||
return vo;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
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 LocalDateTime getReplyTime() {
|
||||
return replyTime;
|
||||
}
|
||||
|
||||
public void setReplyTime(LocalDateTime replyTime) {
|
||||
this.replyTime = replyTime;
|
||||
}
|
||||
|
||||
public Integer getIsOpen() {
|
||||
return isOpen;
|
||||
}
|
||||
|
||||
public void setIsOpen(Integer isOpen) {
|
||||
this.isOpen = isOpen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VideoMessageBasicVO{" +
|
||||
"id=" + id +
|
||||
", userId='" + userId + '\'' +
|
||||
", userName='" + userName + '\'' +
|
||||
", content='" + content + '\'' +
|
||||
", replyTime=" + replyTime +
|
||||
", isOpen=" + isOpen +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
package com.upchina.video.vo.message;
|
||||
|
||||
import com.upchina.video.entity.OnlineUser;
|
||||
import com.upchina.common.entity.OnlineUser;
|
||||
import com.upchina.video.entity.VideoCart;
|
||||
import com.upchina.video.vo.cart.CouponVO;
|
||||
import com.upchina.video.vo.question.NotifyQuestionVO;
|
||||
|
||||
@ -15,16 +15,19 @@ hazelcast:
|
||||
serverPort: 5709 #自己作为缓存服务器监听的端口号
|
||||
scheduledEnable: true
|
||||
cron:
|
||||
collectLivingVideo: "30 1/5 * * * ?" #每分钟统计已开始但未结束的视频直播数据
|
||||
saveVideoCount: "30 2/2 * * * ?" #从cache刷新视频播放量到DB 每分钟的第10s执行
|
||||
saveVideoUserDataToDB: "30 3/5 * * * ?"
|
||||
saveCustomerDataToDB: "30 4/5 * * * ?" #收集用户信息
|
||||
refreshTranscodeStatus: "30 0/5 * * * ?" #从腾讯云拉取录播上传视频信息更新到DB
|
||||
collectLivingVideo: "30 0/5 * * * ?" #每分钟统计已开始但未结束的视频直播数据
|
||||
saveVideoCount: "30 1/2 * * * ?" #从cache刷新视频播放量到DB 每分钟的第10s执行
|
||||
saveVideoUserDataToDB: "30 2/5 * * * ?"
|
||||
saveCustomerDataToDB: "30 3/5 * * * ?" #收集用户信息
|
||||
refreshTranscodeStatus: "30 4/5 * * * ?" #从腾讯云拉取录播上传视频信息更新到DB
|
||||
updateLiveStatus: "0 1 * * * ?" #更新视频录播状态
|
||||
stopLivingVideo: "0 1-5 0 * * ?" #结束前一天直播中/暂停中的视频直播
|
||||
saveWatchSeconds: "0 0/5 * * * ?" #保存短视频观看时长
|
||||
collectLastWeek: "0 30 3 * * ?" #统计一周内的数据
|
||||
collectRecentEndVideo: "0 2/5 * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
|
||||
saveWatchSeconds: "0 0/5 * * * ?" #保存短视频观看时长
|
||||
collectRecentEndVideo: "0 1/5 * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
|
||||
saveGroupMessageRead: "0 2/5 * * * ?" #每5分钟统计已结束48小时以内的视频直播数据
|
||||
saveGroupUser: "0 3/5 * * * ?"
|
||||
collectGroupData: "0 4/5 * * * ?"
|
||||
user:
|
||||
admin:
|
||||
roles: 1,3,4,5 #管理员角色id,用逗号隔开
|
||||
|
||||
@ -1,192 +0,0 @@
|
||||
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<GroupInfoStatus> 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<String, Object> 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<Integer, AdvisorBasic> advisorMap = new HashMap<>();
|
||||
AdvisorBasic advisor = new AdvisorBasic();
|
||||
advisor.setId(100);
|
||||
advisorMap.put(100, advisor);
|
||||
|
||||
Map<Integer, UserDept> 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());
|
||||
}
|
||||
}
|
||||
@ -1,134 +0,0 @@
|
||||
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<GroupMessageStatus> 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,208 +0,0 @@
|
||||
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<String, Object> 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<Integer> 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);
|
||||
|
||||
// 执行测试
|
||||
AppPager<GroupMessageVO> 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<Integer> 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);
|
||||
|
||||
// 执行测试
|
||||
AppPager<GroupMessageVO> 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(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());
|
||||
|
||||
// 执行测试并验证异常
|
||||
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());
|
||||
|
||||
// 执行测试并验证异常
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user