前言
- 在当下区块链相关的应用项目种,一般会有质押奖励、锁仓奖励、节点奖励等等奖励。最为头疼的就是节点奖励。应为他的逻辑太过于复杂。从树状结构计算又需要拆分链路至上往下计算,从下往上统计数据。
bean 对象
@Data
public class UserBean {
//用户id
String userId;
//节点级别
String nodeRank;
//对应节点的奖励级别比例
String newAddP;
//父类id
String parentId;
//理财宝金额
BigDecimal allMoney;
// 以上为传递参数,以下为计算参数不需要传值
//分摊到每一条链路上的投资总额额度
BigDecimal avgAllMoney;
//该用户链路数
int lineNum;
}
@Data
public class NotPageListBean {
String lcLogId;
}
接口
public interface JdWardData {
//节点奖励-获取所有用户并且与他购买的资产包数据
List<UserBean> getUserList(String date);
//节点奖励-获取最末端用户数据
List<UserBean> getBranchUserList(String date);
//获取当前未计算的理财包数据
List<Map<String, Object>> getNotPageList(String date);
//这次计算完成后,将刚刚未计算的理财包数据记录
void insertNotPageList(List<Map<String, Object>> list,String date);
//发放奖金到待领取表中,等待转账时间进行发放
void insertLogMoeny(List<Map<String,Object>> maps25,List<Map<String,Object>> maps75);
}
执行助手类
package com.ruoyi.business.jdreward;
import com.ruoyi.business.jdreward.bean.UserBean;
import com.ruoyi.common.enums.ZzTypeEnum;
import com.utils.MyMath;
import com.utils.VerifyUtil;
import com.utils.exceptinos.OutException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 节点奖励逻辑封装
*/
public class JdWardHelper {
//用户列表
List<UserBean> userList;
//记录普通用户父类不包含自己
Map<String, Object> branchUserMap;
//记录从下往上记录节点父类用户 节点大于1的 包括自己
Map<String, Object> branchUserMapNode;
//记录普通用户父类包含自己
Map<String, Object> branchUserMapAll;
JdWardTools jdWardTools;
JdWardData jdWardData;
boolean isDeBug;
String date;
public JdWardHelper(JdWardData jdWardData, boolean isDeBug, String dateStr) {
this.jdWardData = jdWardData;
this.isDeBug = isDeBug;
this.date = dateStr;
initData();
}
/**
* 链路数据
*/
private Map<String, Object> linkData = new HashMap<>();
/**
* 链路数据
*/
private Map<String, Object> linkDataAll = new HashMap<>();
public static String getDate(String dateStr) {
if (VerifyUtil.isEmpty(dateStr)) {
return VerifyUtil.getDate();
} else {
dateStr=dateStr+" 23:59:59";
System.out.println(dateStr);
//创建SimpleDateFormat对象实例并定义好转换格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
// System.out.println("把当前时间转换成字符串:" + sdf.format(new Date()));
Date date = null;
try {
// 注意格式需要与上面一致,不然会出现异常
date = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
System.out.println("=====================时间格式不正确yyyyMMdd");
return VerifyUtil.getDate();
// throw new OutException("时间格式不正确yyyyMMdd");
}
String dataFromYMDHMS = VerifyUtil.getDataFromYMDHMS(date);
return dataFromYMDHMS;
}
}
public static void main(String[] args) {
System.out.println(getDate("20210620"));
System.out.println(getDate("20210520"));
System.out.println(getDate("20210610"));
System.out.println(getDate("20210612"));
System.out.println(getDate("2020"));
System.out.println(getDate("2024550"));
}
/**
* 初始化数据
*/
public void initData() {
//节点奖励-获取所有用户并且与他购买的资产包数据
userList = jdWardData.getUserList(date);
//获取需要计算的理财包数据,该数据以在userList allMoney 中合并统计
List<Map<String, Object>> notPageList = jdWardData.getNotPageList(date);
//计算工具
jdWardTools = new JdWardTools(userList);
//记录链路数据量不包含自己
branchUserMap = new HashMap<>();
//记录链路数据量不包含自己节点数据
branchUserMapNode = new HashMap<>();
//记录链路数据量包含自己
branchUserMapAll = new HashMap<>();
List<UserBean> branchUserList = jdWardData.getBranchUserList(date);
//从末端数据记录链路数据
for (UserBean userBean : branchUserList) {
//节点用户id
String id = userBean.getUserId();
String parentId = userBean.getParentId();
String parentIds = jdWardTools.getPraentIds(parentId);
parentIds = VerifyUtil.endClear(parentIds);
//记录普通用户父类不包含自己
branchUserMap.put(id, parentIds);
//记录从下往上记录节点父类用户 节点大于1的 包括自己
branchUserMapNode.put(id, id + "," + jdWardTools.getPraentIdsNode(parentId));
//记录普通用户父类包含自己
branchUserMapAll.put(id, id + "," + parentIds);
}
//初始化没个人链路数据,每个用户
initLineMumber();
//计算节点奖励数据
initAward();
System.out.println(VerifyUtil.toJson(linkDataAll));
insertMoney();
if (!isDeBug) {
//记录计算完后的理财包数据
jdWardData.insertNotPageList(notPageList, date);
}
}
/**
* 记录金额
*/
private void insertMoney() {
List<Map<String, Object>> list25 = new ArrayList<>();
List<Map<String, Object>> list75 = new ArrayList<>();
for (String userId : linkDataAll.keySet()) {
String money = linkDataAll.get(userId) + "";
System.out.println("userId:" + userId + "____money:" + money);
String money25 = MyMath.testToFixed("%s*0.25", 8, money);
String money75 = MyMath.testToFixed("%s*0.75", 8, money);
Map<String, Object> map25 = new HashMap<>();
map25.put("id", UUID.randomUUID().toString());
map25.put("lcLogId", "");
map25.put("userId", userId);
map25.put("cTime", date);
map25.put("moneyNum", money25);
map25.put("payDate", date);
map25.put("moneyType", ZzTypeEnum.DL_25_MONEY.code);
map25.put("moneyTypeDes", ZzTypeEnum.DL_25_MONEY.des);
list25.add(map25);
Map<String, Object> map75 = new HashMap<>();
map75.put("id", UUID.randomUUID().toString());
map75.put("lcLogId", "");
map75.put("userId", userId);
map75.put("lcId", "");
map75.put("cTime", date);
map75.put("payTime", date);
map75.put("allNum", money75);
map75.put("lcNumber", "");
map75.put("moneyType", ZzTypeEnum.DL_75_MONEY.code);
map75.put("moneyTypeDes", ZzTypeEnum.DL_75_MONEY.des);
list75.add(map75);
}
if (!isDeBug) {
jdWardData.insertLogMoeny(list25,list75);
}
}
/**
* 计算奖励
* 1.节点数据从下往上算,先算下面的节点收益再往上算节点收益
* 2.计算节点收益从上往下去减
*/
private void initAward() {
/**
* 所有链路
* 便利节点用户
* */
for (String lineCode : branchUserMapNode.keySet()) {
//s链路数据
//链路节点的数据包含自己的
String lineData = branchUserMapNode.get(lineCode) + "";
//1.从下往上顺序
String[] split = lineData.split(",");
for (int i = 0; i < split.length; i++) {
//当前用户
String onUserId = split[i];
insertAddMoney(lineCode, lineData, onUserId);
}
}
}
/**
* 从上往下扣除子类的数据
*
* @param lineCode
* @param onUserId
*/
private void insertAddMoney(String lineCode, String lineData, String onUserId) {
UserBean userBeanByUserId = jdWardTools.getUserBeanByUserId(userList, onUserId);
//用户级别
String onNodeRank = userBeanByUserId.getNodeRank();
if (VerifyUtil.isEmpty(onNodeRank)) onNodeRank = "0";
//当前链路下的平均收益
String onWillSy = getWillSy(lineCode, onUserId);
String newAddP = userBeanByUserId.getNewAddP();
if (VerifyUtil.isEmpty(newAddP)) newAddP = "0";
String newAddLinkNewMoney = MyMath.testToFixed("%s*%s", 8, onWillSy, newAddP);
//从上往下链路
String nextClient = lineData.substring(0, lineData.indexOf(onUserId));
String[] split1 = nextClient.split(",");
for (int i1 = split1.length - 1; i1 > 0; i1--) {
//下一级的用户
String nextUserId = split1[i1];
UserBean nextUserBeanByUserId = jdWardTools.getUserBeanByUserId(userList, nextUserId);
//下一级节点用户节点等级
String nextNodeRank = nextUserBeanByUserId.getNodeRank();
if (VerifyUtil.isEmpty(nextNodeRank)) nextNodeRank = "0";
if (MyMath.test2("%s<=%s", onNodeRank, nextNodeRank)) {
String willSy = getWillSy(lineCode, nextUserId);
newAddLinkNewMoney = MyMath.testToFixed("%s-%s*%s", 8, newAddLinkNewMoney, willSy, newAddP);
break;
} else {
String linkNewAddItem = getLinkNewAdd(nextUserId, lineCode, "end");
newAddLinkNewMoney = MyMath.testToFixed("%s-%s", 8, newAddLinkNewMoney, linkNewAddItem);
}
}
//记录当前链对应的用户数据
if (!MyMath.test2("%s==0", newAddLinkNewMoney)) {
putLinkNewAdd(onUserId, lineCode, newAddLinkNewMoney, "end");
}
}
/**
* 获取支链数据
*
* @param userId
* @param zlCode
* @param str 是否取原始数据
* @return
*/
private String getLinkNewAdd(String userId, String zlCode, String str) {
String s = linkData.get(userId + "_" + zlCode + str) + "";
return VerifyUtil.isEmpty(s) ? "0" : s;
}
/**
* 存入数据
*
* @param userId
* @param zlCode
* @param newAddLinkNewMoney
* @param str 是否存原始数
*/
private void putLinkNewAdd(String userId, String zlCode, String newAddLinkNewMoney, String str) {
linkData.put(userId + "_" + zlCode + str, newAddLinkNewMoney);
if ("end".equals(str)) {
String value = linkDataAll.get(userId) + "";
if (VerifyUtil.isEmpty(value)) value = "0";
linkDataAll.put(userId, MyMath.test("%s+%s", value, newAddLinkNewMoney));
}
}
/**
* 个人
* 获取单链路上以下的收益
*
* @param lineCode
* @param userId
* @return
*/
private String getWillSy(String lineCode, String userId) {
String userIds = branchUserMapAll.get(lineCode) + "";
String allMoney = "0";
String[] split = userIds.split(",");
for (String s : split) {
//从下检测到上,遇到当前自己则跳出
if (s.equals(userId)) break;
UserBean userBeanByUserId = jdWardTools.getUserBeanByUserId(userList, s);
BigDecimal avgAllMoney = userBeanByUserId.getAvgAllMoney();
String avgAllMoneyStr = "0";
if (!VerifyUtil.isEmpty(avgAllMoney)) {
avgAllMoneyStr = avgAllMoney.toString();
}
allMoney = MyMath.testToFixed("%s+%s", 8, allMoney, avgAllMoneyStr);
}
return allMoney;
}
/**
* 计算所有人的链路数,将总投资金额平台到每一条链路上,计算使用平摊后的链路
*/
private void initLineMumber() {
//计算每个用户有多少条链路
for (UserBean userBean : userList) {
for (String s : branchUserMapAll.keySet()) {
//链路
String s1 = branchUserMapAll.get(s) + "";
if (s1.contains(userBean.getUserId()))
userBean.setLineNum(userBean.getLineNum() + 1);
}
//计算平摊总投资金额
BigDecimal allMoney = userBean.getAllMoney();
String allMoneyStr = VerifyUtil.isEmpty(allMoney) ? "0" : allMoney.toString();
String avgAllMoney = MyMath.testToFixed("%s/%s", 8, allMoneyStr, userBean.getLineNum() + "");
userBean.setAvgAllMoney(new BigDecimal(avgAllMoney));
}
}
}
工具类
package com.ruoyi.business.jdreward;
import com.ruoyi.business.jdreward.bean.UserBean;
import com.utils.MyMath;
import com.utils.VerifyUtil;
import java.util.List;
import java.util.Map;
/**
* 公用功能
*/
public class JdWardTools {
List<UserBean> userList;
public JdWardTools(List<UserBean> userList) {
this.userList=userList;
}
/**
* 递归获取往上用户id数据
* 普通用户
*
* @param parentId
* @return
*/
public String getPraentIds(String parentId) {
String parentIds = "";
for (UserBean userBean : userList) {
if ((parentId + "").equals(userBean.getUserId() + "")) {
String userId = userBean.getUserId();
String parentId1 = userBean.getParentId();
parentIds += (userId + ",");
parentIds += getPraentIds(parentId1);
}
}
return parentIds;
}
/**
* 获取所有节点父类用户数据
* 节点用户
* 从下往上记录节点用户数据
*
* @param parentId
* @return
*/
public String getPraentIdsNode(String parentId) {
String parentIds = "";
for (UserBean userBean : userList) {
if ((parentId + "").equals(userBean.getUserId() + "")) {
String userId = userBean.getUserId();
String parentId1 = userBean.getParentId();
String nodeRank = userBean.getNodeRank();
if ((!VerifyUtil.isEmpty(nodeRank)) && MyMath.test2("%s>1", nodeRank))
parentIds += (userId + ",");
parentIds += getPraentIds(parentId1);
}
}
return parentIds;
}
/**
* 获取用户数据
*
* @param userId
* @return
*/
public UserBean getUserBeanByUserId(List<UserBean> userList, String userId) {
for (UserBean userBean : userList) {
if (userBean.getUserId().equals(userId))
return userBean;
}
return null;
}
}
数据装载
@Override
public List<UserBean> getUserList(String date) {
List<UserBean> listUser = new ArrayList<>();
// //节点奖励-获取所有用户并且与他购买的资产包数据
Map<String, Object> param = new HashMap<>();
param.put("baseDate", date);
List<Map<String, Object>> list = apiSql.runSqlByParam(param, "jsUser", "userPagerData");
list.forEach(map -> {
UserBean userBean = new UserBean();
userBean.setAllMoney(new BigDecimal(VerifyUtil.isEmpty(map.get("allMoney") + "") ? "0" : map.get("allMoney") + ""));
userBean.setUserId(map.get("userId") + "");
userBean.setNodeRank(map.get("gradeType") + "");
userBean.setNewAddP(map.get("syP") + "");
userBean.setParentId(map.get("parentId") + "");
listUser.add(userBean);
});
return listUser;
}
@Override
public List<UserBean> getBranchUserList(String date) {
List<UserBean> listUser = new ArrayList<>();
Map<String, Object> param = new HashMap<>();
param.put("baseDate", date);
//初始化节点用户
List<Map<String, Object>> branchUserList = apiSql.runSqlByParam(param, "jsUser", "userEndBranch");
branchUserList.forEach(map -> {
UserBean userBean = new UserBean();
userBean.setUserId(map.get("id") + "");
userBean.setParentId(map.get("parentId") + "");
listUser.add(userBean);
});
return listUser;
}
@Override
public List<Map<String, Object>> getNotPageList(String date) {
Map<String, Object> param = new HashMap<>();
param.put("baseDate", date);
List<Map<String, Object>> list1 = apiSql.runSqlByParam(param, "lc", "notPageList");
return list1;
}
@Override
public void insertNotPageList(List<Map<String, Object>> list, String date) {
// for (Map<String, Object> map : list) {
// apiSql.insertTableMapNotKey(new HashMap<String, Object>() {{
// put("id", UUID.randomUUID().toString());
// put("cTime", date);
// put("lcLogId", map.get("id"));
// }}, "t_node_log");
// }
List<Map<String, Object>> list2 = new ArrayList<>();
for (Map<String, Object> stringObjectMap : list) {
Map<String, Object> item = new HashMap<>();
item.put("id", UUID.randomUUID().toString());
item.put("cTime", date);
item.put("lcLogId", stringObjectMap.get("id"));
list2.add(item);
}
apiSql.insertListTableMapNotKey(list2, "t_node_log");
}
@Override
public void insertLogMoeny(List<Map<String, Object>> maps25, List<Map<String, Object>> maps75) {
apiSql.insertListTableMapNotKey(maps25, "t_lc_package_item");
apiSql.insertListTableMapNotKey(maps75, "t_lc_locked");
}