Browse Source

第三类、NFT持有者分润,等待测试

skyfffire 1 tháng trước cách đây
mục cha
commit
6c73043b90

+ 2 - 1
src/main/java/common/jfinal/AppConfig.java

@@ -71,7 +71,8 @@ public class AppConfig extends JFinalConfig {
         ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
         // 是否打印SQL
         // arp.setShowSql(DEV_MODE.equals("1"));
-        arp.setShowSql(true);
+        // arp.setShowSql(true);
+        arp.setShowSql(false);
         // 加载sql模板
         arp.addSqlTemplate("/common/all.sqlt");
         // 映射实体

+ 119 - 1
src/main/java/modules/user/UserTeamShareTask.java

@@ -1,13 +1,20 @@
 package modules.user;
 
 import com.jfinal.plugin.activerecord.Db;
+import com.jfinal.plugin.activerecord.Record;
 import common.jfinal.AppConfig;
 import common.model.DepositLog;
+import common.model.Nftt;
 import common.model.Order;
 import common.model.User;
 
 import java.math.BigDecimal;
 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 {
     Order order;
@@ -136,7 +143,118 @@ public class UserTeamShareTask implements Runnable {
      *      1. 10%的部分
      *      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加一个时间戳字段