|
@@ -1,13 +1,20 @@
|
|
|
package modules.user;
|
|
package modules.user;
|
|
|
|
|
|
|
|
import com.jfinal.plugin.activerecord.Db;
|
|
import com.jfinal.plugin.activerecord.Db;
|
|
|
|
|
+import com.jfinal.plugin.activerecord.Record;
|
|
|
import common.jfinal.AppConfig;
|
|
import common.jfinal.AppConfig;
|
|
|
import common.model.DepositLog;
|
|
import common.model.DepositLog;
|
|
|
|
|
+import common.model.Nftt;
|
|
|
import common.model.Order;
|
|
import common.model.Order;
|
|
|
import common.model.User;
|
|
import common.model.User;
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
import java.math.BigDecimal;
|
|
|
import java.math.RoundingMode;
|
|
import java.math.RoundingMode;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Set;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
public class UserTeamShareTask implements Runnable {
|
|
public class UserTeamShareTask implements Runnable {
|
|
|
Order order;
|
|
Order order;
|
|
@@ -136,7 +143,118 @@ public class UserTeamShareTask implements Runnable {
|
|
|
* 1. 10%的部分
|
|
* 1. 10%的部分
|
|
|
* 2. 4%的部分
|
|
* 2. 4%的部分
|
|
|
*/
|
|
*/
|
|
|
- public void class3() {}
|
|
|
|
|
|
|
+ public void class3() {
|
|
|
|
|
+ String queryUserIdSql = "SELECT user_id, COUNT(id) AS valid_order_count " +
|
|
|
|
|
+ "FROM t_order " +
|
|
|
|
|
+ "WHERE order_status = 60 AND delivery_status = 1 " +
|
|
|
|
|
+ "GROUP BY user_id HAVING COUNT(id) > 0";
|
|
|
|
|
+ List<Record> userOrderCounts = Db.find(queryUserIdSql);
|
|
|
|
|
+ // 提取所有唯一的 user_id,用于下一步批量查询 User 对象
|
|
|
|
|
+ Set<Long> eligibleUserIds = userOrderCounts.stream()
|
|
|
|
|
+ .map(record -> record.getLong("user_id"))
|
|
|
|
|
+ .collect(Collectors.toSet());
|
|
|
|
|
+ // 如果没有符合条件的用户,直接返回
|
|
|
|
|
+ if (eligibleUserIds.isEmpty()) {
|
|
|
|
|
+ AppConfig.LOGGER.info("没有符合分润条件的用户。");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 第二步:根据这些 user_id,批量查询出对应的 User 对象
|
|
|
|
|
+ // JFinal 的 IN 操作比较方便,但是当列表过大时,分批查询 User 仍然是最佳实践
|
|
|
|
|
+ // 假设 eligibleUserIds 不会过大,可以直接查询
|
|
|
|
|
+ String inSql = eligibleUserIds.stream()
|
|
|
|
|
+ .map(String::valueOf)
|
|
|
|
|
+ .collect(Collectors.joining(","));
|
|
|
|
|
+
|
|
|
|
|
+ AppConfig.LOGGER.info("开始查询用户");
|
|
|
|
|
+ List<User> userList = User.dao.find("SELECT id, balance FROM t_user WHERE id IN (" + inSql + ")");
|
|
|
|
|
+ AppConfig.LOGGER.info("用户查询完毕");
|
|
|
|
|
+
|
|
|
|
|
+ // 为了方便处理,将 list 转换为 map,userOrderCounts 也转换为 map
|
|
|
|
|
+ Map<Long, User> userMap = userList.stream()
|
|
|
|
|
+ .collect(Collectors.toMap(User::getId, user -> user));
|
|
|
|
|
+
|
|
|
|
|
+ Map<Long, Integer> userValidOrderCountMap = userOrderCounts.stream()
|
|
|
|
|
+ .collect(Collectors.toMap(
|
|
|
|
|
+ record -> record.getLong("user_id"),
|
|
|
|
|
+ record -> record.getLong("valid_order_count").intValue()
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ // 现在,你可以遍历 userOrderCounts,结合 userMap 进行分润处理
|
|
|
|
|
+ List<User> usersToUpdate = new ArrayList<>();
|
|
|
|
|
+ List<DepositLog> depositLogsToSave = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 假设分润规则是:每个用户获得的总利润 = (该用户的有效订单数量 / 所有用户的总有效订单数量) * 总利润
|
|
|
|
|
+ // 或者:简单地每个用户的固定份额
|
|
|
|
|
+ // 这里我们用总订单数量来计算每个用户的分润比例
|
|
|
|
|
+ long totalValidOrders = userValidOrderCountMap.values().stream().mapToLong(Integer::longValue).sum();
|
|
|
|
|
+ // 避免除以零
|
|
|
|
|
+ if (totalValidOrders == 0) {
|
|
|
|
|
+ AppConfig.LOGGER.warn("所有符合条件用户的总订单数为0,无法按订单比例分润。");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 总价值
|
|
|
|
|
+ BigDecimal totalPrice = BigDecimal.valueOf(order.getTotalPrice());
|
|
|
|
|
+ Nftt nftt = Nftt.dao.findById(10000);
|
|
|
|
|
+ for (Record record : userOrderCounts) {
|
|
|
|
|
+ // 参与分润的用户
|
|
|
|
|
+ Long userId = record.getLong("user_id");
|
|
|
|
|
+ // 当前用户的有效订单数量
|
|
|
|
|
+ int validOrderCount = record.getLong("valid_order_count").intValue();
|
|
|
|
|
+
|
|
|
|
|
+ User user = userMap.get(userId);
|
|
|
|
|
+ if (user == null) {
|
|
|
|
|
+ AppConfig.LOGGER.warn("未找到用户ID为 {} 的User对象,跳过分润。", userId);
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 分润1,其中4%作为NFT持有者分享(0.04 * validOrderCount / totalValidOrders)
|
|
|
|
|
+ BigDecimal shareArtio1 = BigDecimal.valueOf(0.04);
|
|
|
|
|
+ BigDecimal userShareRatio1 = shareArtio1.multiply(BigDecimal.valueOf(validOrderCount))
|
|
|
|
|
+ .divide(BigDecimal.valueOf(totalValidOrders), 8, RoundingMode.HALF_UP);// 确保高精度
|
|
|
|
|
+ BigDecimal shareAmount1 = totalPrice.multiply(userShareRatio1).setScale(2, RoundingMode.HALF_UP); // 四舍五入到两位小数
|
|
|
|
|
+ // 分润2,NFT持有者共享总数的10%,还没售卖出去的部分归平台所有 (0.1 * validOrderCount / Nftt.maxQuantity)
|
|
|
|
|
+ BigDecimal shareArtio2 = BigDecimal.valueOf(0.1);
|
|
|
|
|
+ BigDecimal userShareRatio2 = shareArtio2.multiply(BigDecimal.valueOf(validOrderCount))
|
|
|
|
|
+ .divide(BigDecimal.valueOf(nftt.getMaxQuantity()), 8, RoundingMode.HALF_UP);// 确保高精度,注意这里的底数是MaxQuantity
|
|
|
|
|
+ BigDecimal shareAmount2 = totalPrice.multiply(userShareRatio2).setScale(2, RoundingMode.HALF_UP); // 四舍五入到两位小数
|
|
|
|
|
+
|
|
|
|
|
+ // 更新用户余额
|
|
|
|
|
+ BigDecimal currentBalance = user.getBigDecimal("balance");
|
|
|
|
|
+ if (currentBalance == null) {
|
|
|
|
|
+ currentBalance = BigDecimal.ZERO;
|
|
|
|
|
+ }
|
|
|
|
|
+ user.set("balance", currentBalance.add(shareAmount1).add(shareAmount2).floatValue());
|
|
|
|
|
+ usersToUpdate.add(user);
|
|
|
|
|
+
|
|
|
|
|
+ // 创建分润日志
|
|
|
|
|
+ DepositLog log1 = new DepositLog();
|
|
|
|
|
+ log1.set("create_time", System.currentTimeMillis());
|
|
|
|
|
+ log1.set("is_deleted", 0);
|
|
|
|
|
+ log1.set("description", "4%作为NFT持有者分享," + validOrderCount + " / " + totalValidOrders); // 加入订单数量信息
|
|
|
|
|
+ log1.set("amount", shareAmount1);
|
|
|
|
|
+ log1.set("user_id", userId);
|
|
|
|
|
+ depositLogsToSave.add(log1);
|
|
|
|
|
+
|
|
|
|
|
+ DepositLog log2 = new DepositLog();
|
|
|
|
|
+ log2.set("create_time", System.currentTimeMillis());
|
|
|
|
|
+ log2.set("is_deleted", 0);
|
|
|
|
|
+ log2.set("description", "NFT持有者共享总数的10%," + validOrderCount + " / " + nftt.getMaxQuantity()); // 加入订单数量信息
|
|
|
|
|
+ log2.set("amount", shareAmount1);
|
|
|
|
|
+ log2.set("user_id", userId);
|
|
|
|
|
+ depositLogsToSave.add(log2);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 在一个事务中执行所有批量更新和插入
|
|
|
|
|
+ Db.tx(() -> {
|
|
|
|
|
+ int[] userUpdateResult = Db.batchUpdate(usersToUpdate, 1000); // 批量更新用户
|
|
|
|
|
+ int[] logSaveResult = Db.batchSave(depositLogsToSave, 1000); // 批量保存日志
|
|
|
|
|
+
|
|
|
|
|
+ AppConfig.LOGGER.info("已批量更新 {} 个用户余额,批量保存 {} 条分润日志。", userUpdateResult.length, logSaveResult.length);
|
|
|
|
|
+ return true; // 事务成功
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 第四类、持有者激励分润,这个最复杂最后做,可能需要为user加一个时间戳字段
|
|
* 第四类、持有者激励分润,这个最复杂最后做,可能需要为user加一个时间戳字段
|