This commit is contained in:
easonzhu 2025-02-13 19:08:52 +08:00
parent fdc4a4aeb9
commit f1b0cba4bd
24 changed files with 0 additions and 6940 deletions

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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) {
}
}

View File

@ -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);
}
}

View File

@ -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("禁言用户,禁止发言");
}
}
}

View File

@ -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;
}
}

View File

@ -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) + "*";
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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(","));
}
}

View File

@ -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();
}
}

View File

@ -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());
}
});
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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 +
'}';
}
}

View File

@ -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 +
'}';
}
}