package com.apobates.forum.core.impl.service; import com.apobates.forum.core.dao.BoardDao; import com.apobates.forum.core.dao.BoardGroupDao; import com.apobates.forum.core.dao.BoardModeratorDao; import com.apobates.forum.core.dao.BoardModeratorRoleHistoryDao; import com.apobates.forum.core.entity.Board; import com.apobates.forum.core.entity.BoardGroup; import com.apobates.forum.core.entity.BoardModerator; import com.apobates.forum.core.entity.BoardModeratorRoleHistory; import com.apobates.forum.core.entity.ModeratorLevelEnum; import com.apobates.forum.core.entity.proxy.BoardModeratorReplica; import com.apobates.forum.core.impl.event.ModeratorBornEvent; import com.apobates.forum.core.impl.event.ModeratorRecallEvent; import com.apobates.forum.core.service.BoardModeratorService; import com.apobates.forum.member.entity.Member; import com.apobates.forum.member.entity.MemberRoleEnum; import com.apobates.forum.utils.lang.TriFunction; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import com.github.davidmarquis.redisq.producer.MessageProducer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; /** * * @author xiaofanku * @since 20200514 */ @Service @CacheConfig public class BoardModeratorServiceImpl implements BoardModeratorService{ @Autowired private BoardGroupDao boardGroupDao; @Autowired private BoardDao boardDao; @Autowired private BoardModeratorDao boardModeratorDao; @Autowired private BoardModeratorRoleHistoryDao boardModeratorRoleHistoryDao; @Autowired @Qualifier("moderatorBornProducer") private MessageProducer moderatorBornProducer; @Autowired @Qualifier("moderatorRecallProducer") private MessageProducer moderatorRecallProducer; private final static Logger logger = LoggerFactory.getLogger(BoardModeratorServiceImpl.class); @Cacheable(value="boardCache", key = "'moderator_'+#boardId", condition="#boardId > 0") @Override public List getAllUsedByBoardId(long boardId) { if (boardId > 0) { return boardModeratorDao.findUsedByBoardId(boardId); } return Collections.emptyList(); } @CacheEvict(value="boardCache", key="'moderator_'+#boardId", condition="#boardId > 0") @Override public Optional create(int volumesId, long boardId, Member member, ModeratorLevelEnum level)throws IllegalArgumentException { if (0 == boardId) { //若是大版主 return create(volumesId, member, level); } if (null == member) { throw new IllegalArgumentException("会员不存在"); } BoardModerator moderator = new BoardModerator(volumesId, boardId, level, member.getId(), member.getNickname()); BoardModeratorRoleHistory memberRoleHistory = new BoardModeratorRoleHistory(-1L, member.getMrole(), MemberRoleEnum.BM, volumesId, boardId, member.getId()); //需要回填版主的ID return storeReplicaModerator(moderator, member, memberRoleHistory); } @Override public Optional create(int volumesId, Member member, ModeratorLevelEnum level)throws IllegalArgumentException { if (null == member) { throw new IllegalArgumentException("会员不存在"); } BoardModerator moderator = new BoardModerator(volumesId, 0, level, member.getId(), member.getNickname(), true); BoardModeratorRoleHistory memberRoleHistory = new BoardModeratorRoleHistory(-1L, member.getMrole(), MemberRoleEnum.MASTER, volumesId, member.getId()); //需要回填版主的ID return storeReplicaModerator(moderator, member, memberRoleHistory); } //在权限检查中使用频繁 @Override public Stream getAllUsedByBoardId(final int boardGroupId, final long boardId) { final TriFunction action = (bg, b, bm) ->{ BoardModeratorReplica bmr = Optional.ofNullable(bm).map(BoardModeratorReplica.copyModerator).orElseGet(BoardModeratorReplica::new); if(null != b){ bmr.setBoard(b); } if (null !=bg && bg.getId() > 0) { bmr.setVolumes(bg); } return bmr; }; CompletableFuture> bm = CompletableFuture.supplyAsync(()->boardModeratorDao.findUsedByBoardId(boardId)).completeOnTimeout(Collections.emptyList(), 1, TimeUnit.SECONDS); CompletableFuture> gbm = CompletableFuture.supplyAsync(()->boardModeratorDao.findAllUsedByBoardGroup(boardGroupId)).completeOnTimeout(Stream.empty(), 1, TimeUnit.SECONDS); return bm.thenCombine(gbm, (mb, gmb)-> Stream.concat(mb.stream(), gmb)).thenCompose(allMS-> CompletableFuture.supplyAsync(()-> boardDao.findOne(boardId).orElse(Board.empty(boardId))) .thenCombine(CompletableFuture.supplyAsync(()-> boardGroupDao.findOne(boardGroupId).orElse(BoardGroup.empty())), (board, boardGroup)->{ return allMS.map(moderator->action.apply(boardGroup, board, moderator)); })).orTimeout(1, TimeUnit.SECONDS).join(); } @Override public Optional remove(int volumesId, long memberId)throws IllegalStateException { return remove(volumesId, 0, memberId); } @Override public Optional remove(int volumesId, long boardId, long memberId)throws IllegalStateException { List his = boardModeratorRoleHistoryDao.findAllByMember(memberId).collect(Collectors.toList()); //20200616移除的角色变化记录 BoardModeratorRoleHistory removeHis = null; //其它的记录 List otherHis = new ArrayList<>(); for (BoardModeratorRoleHistory bmr : his) { if (bmr.getBoardId() == boardId && bmr.getVolumesId() == volumesId && bmr.getMemberId() == memberId) { removeHis = bmr; } else { otherHis.add(bmr); } } if (null == removeHis) { throw new IllegalStateException("角色变化记录不存在"); } //会员更新的角色 MemberRoleEnum updateRole; if (otherHis.isEmpty()) { updateRole = MemberRoleEnum.NO; } else { updateRole = otherHis.stream().map(BoardModeratorRoleHistory::getInvestRole).max(Comparator.comparing(MemberRoleEnum::getSymbol)).get(); } //版主记录 //版主权限 try{ boardModeratorDao.deleteModerator(removeHis); moderatorRecallProducer.create(new ModeratorRecallEvent(removeHis, updateRole)).submit(); return Optional.of(true); }catch(Exception e){ if(logger.isDebugEnabled()){ logger.debug("版主卸任失败", e); } } return Optional.empty(); } @Override public Optional edit(long id, BoardModerator updateModerator)throws IllegalArgumentException { BoardModerator bm = get(id).orElseThrow(() -> new IllegalArgumentException("版主不存在")); bm.setVolumesId(updateModerator.getVolumesId()); //不能从小版主换成大版主 //不能从大版主换成小版主 if (bm.getBoardId() > 0 && updateModerator.getBoardId() > 0) { bm.setBoardId(updateModerator.getBoardId()); } bm.setMemberId(updateModerator.getMemberId()); bm.setMemberNickname(updateModerator.getMemberNickname()); bm.setStatus(updateModerator.isStatus()); bm.setLevel(updateModerator.getLevel()); return boardModeratorDao.edit(bm); } @Override public Stream getAll() { List rs = boardModeratorDao.findAll().collect(Collectors.toList()); if (null == rs || rs.isEmpty()) { return Stream.empty(); } //java.lang.IllegalStateException: Duplicate key 1 (attempted merging values 1 and 0) //bug: final Map allQueryParam = Commons.collectMap(rs.stream(), BoardModerator::getVolumesId, BoardModerator::getBoardId); final Map> allQueryParam = rs.stream().collect(Collectors.groupingBy(BoardModerator::getVolumesId, Collectors.mapping(BoardModerator::getBoardId, Collectors.toSet()))); final TriFunction action = (bg, b, bm) ->{ BoardModeratorReplica bmr = Optional.ofNullable(bm).map(BoardModeratorReplica.copyModerator).orElseGet(BoardModeratorReplica::new); if(null != b){ bmr.setBoard(b); } if (null !=bg && bg.getId() > 0) { bmr.setVolumes(bg); } return bmr; }; Set boardIds = allQueryParam.values().stream().flatMap(innerSet->innerSet.stream()).filter(boardId->boardId>0).collect(Collectors.toSet()); CompletableFuture> boardMap = CompletableFuture.supplyAsync(()->boardDao.findAllById(boardIds)).thenApply(boards -> boards.collect(Collectors.toMap(Board::getId, Function.identity()))).completeOnTimeout(Collections.emptyMap(), 1, TimeUnit.SECONDS); CompletableFuture> boardGroupMap = CompletableFuture.supplyAsync(()->boardGroupDao.findAllById(allQueryParam.keySet())).thenApply(boardGroups->boardGroups.collect(Collectors.toMap(BoardGroup::getId, Function.identity()))).completeOnTimeout(Collections.emptyMap(), 1, TimeUnit.SECONDS); return boardMap.thenCombine(boardGroupMap, (bmap, bgmap)->{ return rs.parallelStream().map(ibm->action.apply(bgmap.get(ibm.getVolumesId()), bmap.get(ibm.getBoardId()), ibm)); }).orTimeout(1, TimeUnit.SECONDS).join(); } @Override public Stream getAllByBoardId(long boardId) { if (boardId > 0) { //可能大版主不显示 return boardModeratorDao.findAllByBoardId(boardId); } return Stream.empty(); } @Override public Stream getAllUsedByBoardGroupId(int boardGroupId) { if(0>=boardGroupId){ return Stream.empty(); } return boardModeratorDao.findAllUsedByBoardGroup(boardGroupId); } @Override public Optional get(int boardGroupId, long boardId, long memberId) { if (boardGroupId == 0 || boardId == 0 || memberId == 0) { return Optional.empty();//.failure("参数不合法或不被接受"); } CompletableFuture> data=CompletableFuture.supplyAsync(()->get(boardId, memberId)).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS); return data.thenCombine( CompletableFuture.supplyAsync(()->boardModeratorDao.findOneByBoardGroupAndMember(boardGroupId, memberId)).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS), (obm, obgbm)->{ if(obm.isPresent()){ return obm; } return obgbm; }).orTimeout(1, TimeUnit.SECONDS).join(); } @Override public Optional get(long id) { if (id > 0) { return boardModeratorDao.findOne(id); } return Optional.empty(); //("参数不合法或不被接受"); } private Optional storeReplicaModerator(BoardModerator moderator, Member member, BoardModeratorRoleHistory memberRoleHistory){ try{ Optional data = boardModeratorDao.pushModerator(moderator, memberRoleHistory); if(data.isPresent()){ moderatorBornProducer.create(new ModeratorBornEvent(moderator, member, memberRoleHistory)).submit(); } return data; } catch (Exception e) { if(logger.isDebugEnabled()){ logger.debug("版主创建失败", e); } } return Optional.empty(); } private Optional get(long boardId, long memberId) { if(boardId > 0 && memberId > 0){ return boardModeratorDao.findOneByBoardAndMember(boardId, memberId); } return Optional.empty(); } }