com.neko233.skilltree.game.redDot.impl.RedDotTreeByMemory Maven / Gradle / Ivy
package com.neko233.skilltree.game.redDot.impl;
import com.neko233.skilltree.annotation.Nullable;
import com.neko233.skilltree.commons.core.utils.ListUtils233;
import com.neko233.skilltree.commons.core.utils.MapUtils233;
import com.neko233.skilltree.game.redDot.RedDotTree;
import com.neko233.skilltree.game.redDot.dto.RedDotDto;
import com.neko233.skilltree.game.redDot.dto.RedPointRemoveResult;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 内存红点树
*/
public class RedDotTreeByMemory implements RedDotTree {
// 根路径
public static final String ROOT = "/";
// 路径分隔符
public static final String PATH_SEPARATOR = "/";
//
private final Map pathToDataMap = new ConcurrentHashMap<>();
public static RedDotTreeByMemory from(List redDotDtoList) {
Map map = Optional.ofNullable(redDotDtoList).orElse(ListUtils233.of())
.stream()
.collect(Collectors.toMap(RedDotDto::getPath, Function.identity(), (v1, v2) -> v2));
return new RedDotTreeByMemory(map);
}
public static RedDotTreeByMemory create() {
return new RedDotTreeByMemory(null);
}
private RedDotTreeByMemory(Map map) {
Map dataMap = map == null ? MapUtils233.of() : map;
pathToDataMap.putAll(dataMap);
}
@Override
public List addRedDot(String path,
int value,
long expireTimeMs) {
long historyValue = getValueByRefresh(path);
RedDotDto redDotDto = pathToDataMap.get(path);
// 差异值
long diffCount;
if (redDotDto == null) {
diffCount = value;
redDotDto = createRedPointDto(path, expireTimeMs, value);
} else {
long targetValue = Math.max(historyValue + value, 0);
diffCount = targetValue - historyValue;
redDotDto.setCount(targetValue);
}
pathToDataMap.put(path, redDotDto);
updateRedDotValue(path, diffCount);
List allParentPathList = getAllParentPathList(path);
allParentPathList.add(path);
return allParentPathList;
}
@Nullable
@Override
public RedPointRemoveResult removeRedDot(String path) {
final List removeChildPathList = getAllChildPathAndTargetList(path);
final List updateParentPathList = getAllParentPathList(path);
for (String removeChildPath : removeChildPathList) {
RedDotDto removeObj = pathToDataMap.remove(removeChildPath);
// 数据
if (removeObj == null) {
return null;
}
long removeValue = removeObj.getCount();
long reduceValue = -removeValue;
updateRedDotValue(path, reduceValue);
}
return RedPointRemoveResult.builder()
.removeChildPathList(removeChildPathList)
.updateParentPathList(updateParentPathList)
.build();
}
@Override
public List getAllParentPathList(String path) {
if (StringUtils.isBlank(path)) {
return ListUtils233.of();
}
List list = new ArrayList<>(2);
String parentPath = getParentPath(path);
while (!"/".equalsIgnoreCase(parentPath)) {
list.add(parentPath);
parentPath = getParentPath(parentPath);
}
return list;
}
@Override
public long getRedDotValue(String path) {
// 懒删除
return getValueByRefresh(path);
}
/**
* 是否过期了
*
* @param path 路径
* @return ok ?
*/
public boolean isExpire(String path) {
RedDotDto redDotDto = pathToDataMap.get(path);
if (redDotDto == null) {
return false;
}
long currentMs = System.currentTimeMillis();
return currentMs < redDotDto.getExpireMs();
}
public boolean isDataExpire(String path,
long currentMs) {
RedDotDto redDotDto = pathToDataMap.get(path);
if (redDotDto == null) {
return false;
}
long expireTimeMs = redDotDto.getExpireMs();
if (expireTimeMs >= 0 && currentMs > expireTimeMs) {
return true;
}
return false;
}
/**
* 会更新所有父级路径
*
* @param path 路径
* @param childValue 子数据
* @return toUpdatePathList
*/
@Override
public List updateRedDotValue(String path,
long childValue) {
String parentPath = getParentPath(path);
if (parentPath != null) {
RedDotDto redDotDto = pathToDataMap.get(parentPath);
if (redDotDto == null) {
// 不存在, 视为永久
RedDotDto parentData = createRedPointDto(parentPath, -1L, childValue);
pathToDataMap.put(parentData.getPath(), parentData);
} else {
long parentValue = redDotDto.getCount();
redDotDto.setCount(parentValue + childValue);
pathToDataMap.put(parentPath, redDotDto);
}
// "/" 根节点退出
if (ROOT.equals(parentPath)) {
return ListUtils233.of();
}
updateRedDotValue(parentPath, childValue);
}
return getAllParentPathAndTargetList(path);
}
private String getParentPath(String path) {
int index = path.lastIndexOf('/');
if (index == -1) {
return null;
}
String parentPath = path.substring(0, index);
return StringUtils.isBlank(parentPath) ? "/" : parentPath;
}
/**
* 先执行懒删除后, 再获取
*
* @return 路径 : 数值 Map
*/
@Override
public Map getPathToValueMap() {
long currentMs = System.currentTimeMillis();
for (String path : pathToDataMap.keySet()) {
boolean isExpire = isDataExpire(path, currentMs);
if (isExpire) {
removeRedDot(path);
}
}
return pathToDataMap.values().stream()
.collect(Collectors.toMap(RedDotDto::getPath, RedDotDto::getCount));
}
@Override
public Map getAllDataMap() {
return getAllDataMap(pathToDataMap.keySet());
}
@Override
public Map getAllDataMap(Collection filterPathList) {
return pathToDataMap.values()
.stream()
.filter(Objects::nonNull)
.filter(data -> filterPathList.contains(data.getPath()))
.collect(Collectors.toMap(RedDotDto::getPath, Function.identity(), (v1, v2) -> v2));
}
private static RedDotDto createRedPointDto(String path,
Long expireTimeMs,
long value) {
long expireMs = Optional.ofNullable(expireTimeMs).orElse(RedDotTree.FOREVER_EXPIRE_MS);
return RedDotDto.builder()
.path(path)
.count(value)
.expireMs(expireMs)
.build();
}
@Override
public Long getExpireMsByPath(String path) {
RedDotDto data = getRedDot(path);
if (data == null) {
return null;
}
return data.getExpireMs();
}
@Override
public void clear() {
pathToDataMap.clear();
}
@Override
public RedDotDto getRedDot(String path) {
return pathToDataMap.get(path);
}
@Override
@Nullable
public RedDotDto getByRefresh(String path) {
// 所有子结构
final List childPathList = getAllChildPathList(path);
for (String childPath : childPathList) {
getAndRemoveExpire(childPath);
}
RedDotDto redDotDto = getAndRemoveExpire(path);
if (redDotDto == null) {
return null;
}
return redDotDto;
}
@NotNull
@Override
public List getAllChildPathList(String path) {
return pathToDataMap.keySet()
.stream()
// 不包含自己
.filter(historyPath -> historyPath.startsWith(path + PATH_SEPARATOR))
.collect(Collectors.toList());
}
@org.jetbrains.annotations.Nullable
private RedDotDto getAndRemoveExpire(String path) {
RedDotDto redDotDto = pathToDataMap.get(path);
if (redDotDto == null) {
return null;
}
boolean isExpire = isExpire(path);
if (isExpire) {
pathToDataMap.remove(path);
return null;
}
return redDotDto;
}
@Override
public List getAllRedDotList() {
return new ArrayList<>(pathToDataMap.values());
}
@Override
public void flush(List toUpdatePathList) {
}
}