Răsfoiți Sursa

盲盒随机算法

xiongzhu 4 ani în urmă
părinte
comite
0cef7a46ef

+ 3 - 0
src/main/java/com/izouma/nineth/repo/BlindBoxItemRepo.java

@@ -7,10 +7,13 @@ import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.jpa.repository.Query;
 
 
 import javax.transaction.Transactional;
 import javax.transaction.Transactional;
+import java.util.List;
 
 
 public interface BlindBoxItemRepo extends JpaRepository<BlindBoxItem, Long>, JpaSpecificationExecutor<BlindBoxItem> {
 public interface BlindBoxItemRepo extends JpaRepository<BlindBoxItem, Long>, JpaSpecificationExecutor<BlindBoxItem> {
     @Query("update BlindBoxItem t set t.del = true where t.id = ?1")
     @Query("update BlindBoxItem t set t.del = true where t.id = ?1")
     @Modifying
     @Modifying
     @Transactional
     @Transactional
     void softDelete(Long id);
     void softDelete(Long id);
+
+    List<BlindBoxItem> findByBlindBoxId(Long blindBoxId);
 }
 }

+ 56 - 14
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -14,16 +14,15 @@ import com.github.binarywang.wxpay.service.WxPayService;
 import com.izouma.nineth.config.AlipayProperties;
 import com.izouma.nineth.config.AlipayProperties;
 import com.izouma.nineth.config.WxPayProperties;
 import com.izouma.nineth.config.WxPayProperties;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.domain.*;
+import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.dto.NFTAccount;
 import com.izouma.nineth.dto.NFTAccount;
 import com.izouma.nineth.dto.PageQuery;
 import com.izouma.nineth.dto.PageQuery;
+import com.izouma.nineth.enums.CollectionType;
 import com.izouma.nineth.enums.OrderStatus;
 import com.izouma.nineth.enums.OrderStatus;
 import com.izouma.nineth.enums.PayMethod;
 import com.izouma.nineth.enums.PayMethod;
 import com.izouma.nineth.event.CreateAssetEvent;
 import com.izouma.nineth.event.CreateAssetEvent;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.exception.BusinessException;
-import com.izouma.nineth.repo.CollectionRepo;
-import com.izouma.nineth.repo.OrderRepo;
-import com.izouma.nineth.repo.UserAddressRepo;
-import com.izouma.nineth.repo.UserRepo;
+import com.izouma.nineth.repo.*;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.JpaUtils;
 import com.izouma.nineth.utils.JsonUtils;
 import com.izouma.nineth.utils.JsonUtils;
 import com.izouma.nineth.utils.SecurityUtils;
 import com.izouma.nineth.utils.SecurityUtils;
@@ -33,6 +32,8 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.EncoderException;
 import org.apache.commons.codec.EncoderException;
 import org.apache.commons.codec.net.URLCodec;
 import org.apache.commons.codec.net.URLCodec;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.commons.lang3.Range;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.springframework.context.event.EventListener;
 import org.springframework.context.event.EventListener;
 import org.springframework.core.env.Environment;
 import org.springframework.core.env.Environment;
@@ -43,10 +44,7 @@ import org.springframework.ui.Model;
 import javax.transaction.Transactional;
 import javax.transaction.Transactional;
 import java.math.BigDecimal;
 import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Optional;
-import java.util.UUID;
+import java.util.*;
 
 
 @Service
 @Service
 @AllArgsConstructor
 @AllArgsConstructor
@@ -64,6 +62,7 @@ public class OrderService {
     private WxPayProperties  wxPayProperties;
     private WxPayProperties  wxPayProperties;
     private AssetService     assetService;
     private AssetService     assetService;
     private SysConfigService sysConfigService;
     private SysConfigService sysConfigService;
+    private BlindBoxItemRepo blindBoxItemRepo;
 
 
     public Page<Order> all(PageQuery pageQuery) {
     public Page<Order> all(PageQuery pageQuery) {
         return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
         return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
@@ -217,12 +216,55 @@ public class OrderService {
     public void notifyAlipay(Long orderId, Map<String, String> params) {
     public void notifyAlipay(Long orderId, Map<String, String> params) {
         Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
         Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
         if (order.getStatus() == OrderStatus.NOT_PAID) {
         if (order.getStatus() == OrderStatus.NOT_PAID) {
-            order.setStatus(OrderStatus.PROCESSING);
-            order.setPayTime(LocalDateTime.now());
-            order.setTransactionId(MapUtils.getString(params, "trade_no"));
-            order.setPayMethod(PayMethod.ALIPAY);
-            orderRepo.save(order);
-            assetService.createAsset(order);
+            if (order.getType() == CollectionType.BLIND_BOX) {
+                List<BlindBoxItem> items = blindBoxItemRepo.findByBlindBoxId(order.getCollectionId());
+                Map<BlindBoxItem, Range<Integer>> randomRange = new HashMap<>();
+                int c = 0, sum = 0;
+                for (BlindBoxItem item : items) {
+                    randomRange.put(item, Range.between(c, c + item.getStock()));
+                    c += item.getStock();
+                    sum += item.getStock();
+                }
+
+                boolean win = false;
+                int retry = 0;
+                BlindBoxItem winItem = null;
+                while (winItem == null) {
+                    int rand = RandomUtils.nextInt(0, sum + 1);
+                    for (Map.Entry<BlindBoxItem, Range<Integer>> entry : randomRange.entrySet()) {
+                        BlindBoxItem item = entry.getKey();
+                        Range<Integer> range = entry.getValue();
+                        if (rand >= range.getMinimum() && rand < range.getMaximum()) {
+                            if (item.isRare()) {
+                                int total = items.stream().filter(i -> !i.isRare())
+                                        .mapToInt(BlindBoxItem::getTotal).sum();
+                                int stock = items.stream().filter(i -> !i.isRare())
+                                        .mapToInt(BlindBoxItem::getStock).sum();
+
+                                double nRate = stock / (double) total;
+                                double rRate = item.getStock() / (double) item.getTotal();
+
+                                if (Math.abs(nRate - rRate) < 0.05 || retry > 1) {
+                                    winItem = item;
+                                } else {
+                                    retry++;
+                                }
+                            } else {
+                                winItem = item;
+                            }
+                        }
+                    }
+                }
+
+
+            } else {
+                order.setStatus(OrderStatus.PROCESSING);
+                order.setPayTime(LocalDateTime.now());
+                order.setTransactionId(MapUtils.getString(params, "trade_no"));
+                order.setPayMethod(PayMethod.ALIPAY);
+                orderRepo.save(order);
+                assetService.createAsset(order);
+            }
         }
         }
     }
     }
 
 

+ 79 - 0
src/test/java/com/izouma/nineth/CommonTest.java

@@ -1,6 +1,7 @@
 package com.izouma.nineth;
 package com.izouma.nineth;
 
 
 import com.izouma.nineth.domain.BaseEntity;
 import com.izouma.nineth.domain.BaseEntity;
+import com.izouma.nineth.domain.BlindBoxItem;
 import com.izouma.nineth.domain.User;
 import com.izouma.nineth.domain.User;
 import com.izouma.nineth.enums.AuthorityName;
 import com.izouma.nineth.enums.AuthorityName;
 import com.izouma.nineth.security.Authority;
 import com.izouma.nineth.security.Authority;
@@ -15,6 +16,8 @@ import lombok.SneakyThrows;
 import org.apache.commons.codec.EncoderException;
 import org.apache.commons.codec.EncoderException;
 import org.apache.commons.codec.net.URLCodec;
 import org.apache.commons.codec.net.URLCodec;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.apache.commons.lang3.Range;
 import org.apache.commons.text.CaseUtils;
 import org.apache.commons.text.CaseUtils;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.poi.util.TempFile;
 import org.apache.poi.util.TempFile;
@@ -207,4 +210,80 @@ public class CommonTest {
         System.out.println(put.hash.toBase58());
         System.out.println(put.hash.toBase58());
         System.out.println(multihash.toBase58());
         System.out.println(multihash.toBase58());
     }
     }
+
+    @Test
+    public void testWin() {
+        List<BlindBoxItem> items = new ArrayList<>();
+        items.add(BlindBoxItem.builder()
+                .name("普通1")
+                .total(100)
+                .stock(100)
+                .build());
+        items.add(BlindBoxItem.builder()
+                .name("普通2")
+                .total(100)
+                .stock(100)
+                .build());
+        items.add(BlindBoxItem.builder()
+                .name("稀有1")
+                .total(5)
+                .stock(5)
+                .rare(true)
+                .build());
+        items.add(BlindBoxItem.builder()
+                .name("稀有2")
+                .total(5)
+                .stock(5)
+                .rare(true)
+                .build());
+        for (int k = 0; k < items.stream().mapToInt(BlindBoxItem::getTotal).sum(); k++) {
+            Map<BlindBoxItem, Range<Integer>> randomRange = new HashMap<>();
+            int c = 0, sum = 0;
+            for (BlindBoxItem item : items) {
+                randomRange.put(item, Range.between(c, c + item.getStock()));
+                c += item.getStock();
+                sum += item.getStock();
+            }
+
+            boolean win = false;
+            int retry = 0;
+            BlindBoxItem winItem = null;
+            while (winItem == null) {
+                int rand = RandomUtils.nextInt(0, sum + 1);
+                for (Map.Entry<BlindBoxItem, Range<Integer>> entry : randomRange.entrySet()) {
+                    BlindBoxItem item = entry.getKey();
+                    Range<Integer> range = entry.getValue();
+                    if (rand >= range.getMinimum() && rand < range.getMaximum()) {
+                        int total = items.stream().filter(i -> !i.isRare())
+                                .mapToInt(BlindBoxItem::getTotal).sum();
+                        int stock = items.stream().filter(i -> !i.isRare())
+                                .mapToInt(BlindBoxItem::getStock).sum();
+                        if (item.isRare()) {
+                            double nRate = stock / (double) total;
+                            double rRate = (item.getStock() - 1) / (double) item.getTotal();
+
+                            if (Math.abs(nRate - rRate) < (1 / (double) item.getTotal()) || retry > 1 || rRate == 0) {
+                                if (!(nRate > 0.1 && item.getStock() == 1)) {
+                                    winItem = item;
+                                }
+                            } else {
+                                retry++;
+                            }
+                        } else {
+                            double nRate = (stock - 1) / (double) total;
+                            double rRate = item.getStock() / (double) item.getTotal();
+
+                            if (Math.abs(nRate - rRate) < 0.2 || retry > 1 || nRate == 0) {
+                                winItem = item;
+                            } else {
+                                retry++;
+                            }
+                        }
+                    }
+                }
+            }
+            winItem.setStock(winItem.getStock() - 1);
+            System.out.println(winItem.getName() + (winItem.isRare() ? "\t+++" : ""));
+        }
+    }
 }
 }