Forráskód Böngészése

Merge branch 'dev' into 2D展厅

licailing 4 éve
szülő
commit
a38e4f17e4

+ 6 - 0
pom.xml

@@ -424,6 +424,12 @@
             <artifactId>hutool-core</artifactId>
             <version>5.7.21</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.github.vladimir-bukhtoyarov</groupId>
+            <artifactId>bucket4j-core</artifactId>
+            <version>7.3.0</version>
+        </dependency>
     </dependencies>
 
 </project>

+ 6 - 0
src/main/java/com/izouma/nineth/config/RedisKeys.java

@@ -26,4 +26,10 @@ public class RedisKeys {
     public static final String UPDATE_STOCK = "updateSale";
 
     public static final String LIMIT_REQ = "limitReq::";
+
+    public static final String LIMIT_USER = "limitUser::";
+
+    public static final String BLACK_LIST = "blackList::";
+
+    private static final String VIP_COLLECTION_LIST = "vipCollectionList";
 }

+ 19 - 0
src/main/java/com/izouma/nineth/dto/PointDTO.java

@@ -0,0 +1,19 @@
+package com.izouma.nineth.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class PointDTO {
+    private Long          id;
+    private LocalDateTime createdAt;
+    private int           inviteSum;
+    private int           identitySum;
+    private BigDecimal    buy;
+}

+ 4 - 0
src/main/java/com/izouma/nineth/repo/PointRecordRepo.java

@@ -3,6 +3,10 @@ package com.izouma.nineth.repo;
 import com.izouma.nineth.domain.PointRecord;
 import org.springframework.data.jpa.repository.JpaRepository;
 
+import java.util.List;
+
 public interface PointRecordRepo extends JpaRepository<PointRecord, Long> {
     int countByUserIdAndCollectionId(Long userId, Long collectionId);
+
+    List<PointRecord> findAllByCollectionId(Long collectionId);
 }

+ 4 - 0
src/main/java/com/izouma/nineth/repo/SmsRecordRepo.java

@@ -21,4 +21,8 @@ public interface SmsRecordRepo extends JpaRepository<SmsRecord, Long> {
     @Modifying
     @Transactional
     void expire(String phone);
+
+    @Modifying
+    @Transactional
+    void deleteByCreatedAtBefore(LocalDateTime time);
 }

+ 4 - 0
src/main/java/com/izouma/nineth/repo/TokenHistoryRepo.java

@@ -9,6 +9,7 @@ import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
 
 import javax.transaction.Transactional;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
@@ -32,6 +33,9 @@ public interface TokenHistoryRepo extends JpaRepository<TokenHistory, Long>, Jpa
     @Query("select t from TokenHistory t where t.toUserId in ?1 and t.price is not null")
     List<TokenHistory> userBuy(Collection<Long> userId);
 
+    @Query("select sum(t.price) from TokenHistory t where t.toUserId = ?1 and t.price is not null")
+    BigDecimal userBuy(Long userId);
+
     @Transactional
     @Modifying
     int deleteByTokenId(String tokenId);

+ 3 - 1
src/main/java/com/izouma/nineth/repo/UserRepo.java

@@ -202,10 +202,12 @@ public interface UserRepo extends JpaRepository<User, Long>, JpaSpecificationExe
             " where collection_id = ?1" +
             " group by collection_invitor" +
             " having count(id) >= ?2")
-    List<Long> findAllByCollectionId(Long collectionId, int size);
+    List<Long> findInvitorByCollectionId(Long collectionId, int size);
 
     @Transactional
     @Modifying
     @Query("update User set vipPoint = 1 where vipPoint = 0 and id in ?1")
     void updateAllByInvitor(Collection<Long> ids);
+
+    List<User> findAllByCollectionId(Long collectionId);
 }

+ 67 - 0
src/main/java/com/izouma/nineth/service/CollectionService.java

@@ -7,6 +7,7 @@ import com.izouma.nineth.config.RedisKeys;
 import com.izouma.nineth.domain.Collection;
 import com.izouma.nineth.domain.*;
 import com.izouma.nineth.dto.*;
+import com.izouma.nineth.enums.AuthStatus;
 import com.izouma.nineth.enums.CollectionSource;
 import com.izouma.nineth.enums.CollectionType;
 import com.izouma.nineth.enums.OrderStatus;
@@ -17,6 +18,7 @@ import com.izouma.nineth.utils.SecurityUtils;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.RandomUtils;
 import org.apache.commons.lang3.Range;
 import org.apache.commons.lang3.StringUtils;
@@ -43,6 +45,7 @@ import java.time.ZoneId;
 import java.util.*;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 @Service
@@ -62,6 +65,8 @@ public class CollectionService {
     private GeneralProperties             generalProperties;
     private Environment                   env;
     private OrderRepo                     orderRepo;
+    private TokenHistoryRepo              tokenHistoryRepo;
+    private PointRecordRepo               pointRecordRepo;
 
     private final Map<Long, ScheduledFuture<?>> tasks = new HashMap<>();
 
@@ -470,4 +475,66 @@ public class CollectionService {
             return collectionDTO;
         }).collect(Collectors.toList());
     }
+
+
+    public List<PointDTO> savePoint(Long collectionId) {
+        Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("无藏品"));
+        //库存
+        int stock = collection.getStock();
+        //是否开启白名单
+        int assignment = collection.getAssignment();
+        if (assignment <= 0) {
+            return null;
+        }
+        List<User> users = userRepo.findAllByCollectionId(collectionId);
+        //邀请者
+        Map<Long, List<User>> userMap = users.stream()
+                .filter(user -> ObjectUtils.isNotEmpty(user.getCollectionInvitor()))
+                .collect(Collectors.groupingBy(User::getCollectionInvitor));
+
+        AtomicInteger sum = new AtomicInteger();
+        List<PointDTO> dtos = new ArrayList<>();
+        userMap.forEach((key, value) -> {
+            //邀请达到数量
+            if (value.size() >= collection.getAssignment()) {
+                value.sort(Comparator.comparing(User::getCreatedAt));
+                //满足条件的时间
+                User user = value.get(collection.getAssignment() - 1);
+                //实名数量
+                long identitySum = value.stream().filter(u -> AuthStatus.SUCCESS.equals(u.getAuthStatus())).count();
+                dtos.add(new PointDTO(key, user.getCreatedAt(), value.size(), (int) identitySum, tokenHistoryRepo.userBuy(key)));
+                sum.getAndIncrement();
+            }
+        });
+        log.info("完成任务人数:{}", sum);
+
+        //只留库存数量
+        List<PointDTO> result = dtos.stream()
+                .sorted(Comparator.comparing(PointDTO::getCreatedAt))
+                .limit(stock)
+                .collect(Collectors.toList());
+        List<Long> userIds = result.stream().map(PointDTO::getId).collect(Collectors.toList());
+        Map<Long, User> resultMap = userRepo.findAllById(userIds)
+                .stream()
+                .collect(Collectors.toMap(User::getId, user -> user));
+
+        //加积分,存记录
+//        result.forEach(pointDTO -> {
+//            User user = resultMap.get(pointDTO.getId());
+//            if (user.getVipPoint() <= 0) {
+//                user.setVipPoint(1);
+//                userRepo.save(user);
+////                userRepo.updateVipPoint(pointDTO.getId(), 1);
+//                pointRecordRepo.save(PointRecord.builder()
+//                        .collectionId(collectionId)
+//                        .userId(pointDTO.getId())
+//                        .type("VIP_POINT")
+//                        .point(1)
+//                        .build());
+//            }
+//
+//        });
+
+        return dtos;
+    }
 }

+ 19 - 5
src/main/java/com/izouma/nineth/service/OrderService.java

@@ -111,12 +111,26 @@ public class OrderService {
             throw new BusinessException("签名已过期");
         }
 
-//        Integer stock = collectionService.getStock(collectionId);
-//        if (stock == null || stock <= 0) {
-//            throw new BusinessException("藏品已售罄", ErrorCode.SOLD_OUT);
-//        }
+        if (redisTemplate.opsForValue().get(RedisKeys.BLACK_LIST + userId) != null) {
+            throw new BusinessException("频繁操作,请稍后再试");
+        }
+        BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.LIMIT_USER + userId);
+        ops.setIfAbsent(0, Duration.ofSeconds(10));
+        long val = Optional.ofNullable(ops.increment()).orElse(0L);
+        if (val > 5) {
+            if (val > 10) {
+                redisTemplate.opsForValue().set(RedisKeys.BLACK_LIST + userId, 1, Duration.ofSeconds(60 * 10));
+            }
+            throw new BusinessException("频繁操作,请稍后再试");
+        }
+
         limitReq(collectionId);
 
+        Integer stock = collectionService.getStock(collectionId);
+        if (stock == null || stock <= 0) {
+            throw new BusinessException("藏品已售罄", ErrorCode.SOLD_OUT);
+        }
+
         Long id = snowflakeIdWorker.nextId();
         SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
                 new CreateOrderEvent(id, userId, collectionId, qty, addressId, userCouponId, invitor, vip), 100000);
@@ -130,7 +144,7 @@ public class OrderService {
         ops.setIfAbsent(3000, Duration.ofSeconds(30));
         Long val = ops.decrement();
         if (val == null || val < 0) {
-            throw new BusinessException("很遗憾,藏品已售罄");
+            throw new BusinessException("前方拥堵,请稍后再试");
         }
     }
 

+ 117 - 36
src/main/java/com/izouma/nineth/service/UserService.java

@@ -36,6 +36,7 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.context.event.EventListener;
+import org.springframework.core.env.Environment;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageImpl;
 import org.springframework.data.jpa.domain.Specification;
@@ -46,7 +47,6 @@ import org.springframework.stereotype.Service;
 import javax.persistence.criteria.Predicate;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
-import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
@@ -73,8 +73,7 @@ public class UserService {
     private TokenHistoryRepo      tokenHistoryRepo;
     private CollectionRepo        collectionRepo;
     private AdapayMerchantService adapayMerchantService;
-    private PointRecordRepo       pointRecordRepo;
-    private CollectionService     collectionService;
+    private Environment           env;
 
     public User update(User user) {
         if (!SecurityUtils.hasRole(AuthorityName.ROLE_ADMIN)) {
@@ -150,7 +149,9 @@ public class UserService {
             user.setPassword(new BCryptPasswordEncoder().encode(userRegister.getPassword()));
         }
         user = userRepo.saveAndFlush(user);
-        nftService.createAccount(user.getId());
+        if (Arrays.asList(env.getActiveProfiles()).contains("prod")) {
+            nftService.createAccount(user.getId());
+        }
         return user;
     }
 
@@ -171,15 +172,19 @@ public class UserService {
             invite = inviteRepo.findFirstByCode(inviteCode).orElse(null);
         }
         smsService.verify(phone, code);
-        Collection collection = null;
+        Collection collection;
         if (collectionId != null) {
             collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("无藏品"));
-            if (!collection.isOnShelf() || !collection.isSalable()) {
+//            if (!collection.isOnShelf() || !collection.isSalable()) {
+//                collectionId = null;
+//            } else if (collection.isScheduleSale()) {
+//                if (collection.getStartTime().isAfter(LocalDateTime.now())) {
+//                    collectionId = null;
+//                }
+//            }
+            // 只看是否开去分享
+            if (ObjectUtils.isEmpty(collection.getOpenQuota()) || !collection.getOpenQuota()) {
                 collectionId = null;
-            } else if (collection.isScheduleSale()) {
-                if (collection.getStartTime().isAfter(LocalDateTime.now())) {
-                    collectionId = null;
-                }
             }
         }
         User user = create(UserRegister.builder()
@@ -200,34 +205,109 @@ public class UserService {
         }
 
         // 加积分
-        if (collectionId != null && invitor != null) {
-            // 额度或者额度为空, 库存不为空
-            if (collection.getStock() > 0 && (collection.getVipQuota() > 0 || ObjectUtils.isEmpty(collection.getVipQuota()))) {
-                int countUser = userRepo.countAllByCollectionIdAndCollectionInvitor(collectionId, invitor);
-                // 邀请人数
-                if (countUser >= collection.getAssignment()) {
-                    int point = pointRecordRepo.countByUserIdAndCollectionId(invitor, collectionId);
-                    // 是否已有积分
-                    if (point <= 0) {
-                        long count = userRepo.countAllByCollectionIdAndCollectionInvitor(collectionId, invitor);
-                        if (count >= collection.getAssignment()) {
-                            // 扣除藏品额度
-                            if (ObjectUtils.isNotEmpty(collection.getVipQuota())) {
-                                collectionService.decreaseQuota(collectionId, 1);
-                            }
-                            userRepo.updateVipPoint(invitor, 1);
-                            pointRecordRepo.save(PointRecord.builder()
-                                    .collectionId(collectionId)
-                                    .userId(invitor)
-                                    .type("VIP_POINT")
-                                    .point(1)
-                                    .build());
-
-                        }
-                    }
-                }
+//        if (collectionId != null && invitor != null) {
+//            // 额度或者额度为空, 库存不为空
+//            if (collection.getStock() > 0 && (collection.getVipQuota() > 0 || ObjectUtils.isEmpty(collection.getVipQuota()))) {
+//                int countUser = userRepo.countAllByCollectionIdAndCollectionInvitor(collectionId, invitor);
+//                // 邀请人数
+//                if (countUser >= collection.getAssignment()) {
+//                    int point = pointRecordRepo.countByUserIdAndCollectionId(invitor, collectionId);
+//                    // 是否已有积分
+//                    if (point <= 0) {
+//                        long count = userRepo.countAllByCollectionIdAndCollectionInvitor(collectionId, invitor);
+//                        if (count >= collection.getAssignment()) {
+//                            // 扣除藏品额度
+//                            if (ObjectUtils.isNotEmpty(collection.getVipQuota())) {
+//                                collectionService.decreaseQuota(collectionId, 1);
+//                            }
+//                            userRepo.updateVipPoint(invitor, 1);
+//                            pointRecordRepo.save(PointRecord.builder()
+//                                    .collectionId(collectionId)
+//                                    .userId(invitor)
+//                                    .type("VIP_POINT")
+//                                    .point(1)
+//                                    .build());
+//
+//                        }
+//                    }
+//                }
+//            }
+//        }
+        return user;
+    }
+
+    public User testPhoneRegister() {
+        String phone = "19" + RandomStringUtils.randomNumeric(11);
+        String password = "123456";
+        String inviteCode = null;
+        Long invitor = null;
+        Long collectionId = null;
+        String name = "9th_" + RandomStringUtils.randomAlphabetic(8);
+        Invite invite = null;
+        if (StringUtils.isNotBlank(inviteCode)) {
+            invite = inviteRepo.findFirstByCode(inviteCode).orElse(null);
+        }
+        Collection collection;
+        if (collectionId != null) {
+            collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("无藏品"));
+//            if (!collection.isOnShelf() || !collection.isSalable()) {
+//                collectionId = null;
+//            } else if (collection.isScheduleSale()) {
+//                if (collection.getStartTime().isAfter(LocalDateTime.now())) {
+//                    collectionId = null;
+//                }
+//            }
+            // 只看是否开去分享
+            if (ObjectUtils.isEmpty(collection.getOpenQuota()) || !collection.getOpenQuota()) {
+                collectionId = null;
             }
         }
+        User user = create(UserRegister.builder()
+                .authorities(Collections.singleton(Authority.get(AuthorityName.ROLE_USER)))
+                .username(name)
+                .nickname(name)
+                .password(password)
+                .avatar(Constants.DEFAULT_AVATAR)
+                .phone(phone)
+                .invitorPhone(Optional.ofNullable(invite).map(Invite::getPhone).orElse(null))
+                .invitorName(Optional.ofNullable(invite).map(Invite::getName).orElse(null))
+                .inviteCode(Optional.ofNullable(invite).map(Invite::getCode).orElse(null))
+                .collectionInvitor(invitor)
+                .collectionId(collectionId)
+                .build());
+        if (invite != null) {
+            inviteRepo.increaseNum(invite.getId());
+        }
+
+        // 加积分
+//        if (collectionId != null && invitor != null) {
+//            // 额度或者额度为空, 库存不为空
+//            if (collection.getStock() > 0 && (collection.getVipQuota() > 0 || ObjectUtils.isEmpty(collection.getVipQuota()))) {
+//                int countUser = userRepo.countAllByCollectionIdAndCollectionInvitor(collectionId, invitor);
+//                // 邀请人数
+//                if (countUser >= collection.getAssignment()) {
+//                    int point = pointRecordRepo.countByUserIdAndCollectionId(invitor, collectionId);
+//                    // 是否已有积分
+//                    if (point <= 0) {
+//                        long count = userRepo.countAllByCollectionIdAndCollectionInvitor(collectionId, invitor);
+//                        if (count >= collection.getAssignment()) {
+//                            // 扣除藏品额度
+//                            if (ObjectUtils.isNotEmpty(collection.getVipQuota())) {
+//                                collectionService.decreaseQuota(collectionId, 1);
+//                            }
+//                            userRepo.updateVipPoint(invitor, 1);
+//                            pointRecordRepo.save(PointRecord.builder()
+//                                    .collectionId(collectionId)
+//                                    .userId(invitor)
+//                                    .type("VIP_POINT")
+//                                    .point(1)
+//                                    .build());
+//
+//                        }
+//                    }
+//                }
+//            }
+//        }
         return user;
     }
 
@@ -672,4 +752,5 @@ public class UserService {
     public User my(Long id) {
         return userRepo.findById(id).orElseThrow(new BusinessException("用户不存在"));
     }
+
 }

+ 14 - 1
src/main/java/com/izouma/nineth/service/sms/AliSmsService.java

@@ -19,8 +19,11 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.configurationprocessor.json.JSONException;
 import org.springframework.boot.configurationprocessor.json.JSONObject;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
+import java.time.Duration;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.util.Date;
@@ -41,7 +44,9 @@ public class AliSmsService implements SmsService {
     private String sellOutCode;
 
     @Autowired
-    private SmsRecordRepo smsRecordRepo;
+    private SmsRecordRepo                 smsRecordRepo;
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
 
     @Override
     public String sendVerify(String phone) {
@@ -102,6 +107,10 @@ public class AliSmsService implements SmsService {
 
     @Override
     public String verify(String phone, String code) {
+        if (Boolean.FALSE.equals(redisTemplate.opsForValue()
+                .setIfAbsent("verify::" + phone, 1, Duration.ofSeconds(10)))) {
+            throw new BusinessException("频繁操作,请稍后再试");
+        }
         SmsRecord smsRecord = smsRecordRepo.findLastByPhoneAndExpiresAtAfterAndExpiredFalse(phone, LocalDateTime.now())
                 .orElseThrow(new BusinessException("验证码错误"));
         if (!smsRecord.getCode().equalsIgnoreCase(code)) {
@@ -151,4 +160,8 @@ public class AliSmsService implements SmsService {
         }
     }
 
+    @Scheduled(cron = "0 0 3 * * ?")
+    void deleteExpired() {
+        smsRecordRepo.deleteByCreatedAtBefore(LocalDateTime.now().minusDays(2));
+    }
 }

+ 7 - 0
src/main/java/com/izouma/nineth/web/AuthenticationController.java

@@ -77,6 +77,13 @@ public class AuthenticationController {
         return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
     }
 
+    @PostMapping("/testphoneRegister")
+    @ApiOperation(value = "手机号密码注册")
+    public String phonePwdLogin() {
+        User user = userService.testPhoneRegister();
+        return jwtTokenUtil.generateToken(JwtUserFactory.create(user));
+    }
+
     @PostMapping("/mpLogin")
     @ApiOperation(value = "公众号登录")
     public String mpLogin(String code) {

+ 2 - 1
src/main/java/com/izouma/nineth/web/SmsController.java

@@ -4,6 +4,7 @@ import com.izouma.nineth.config.Constants;
 import com.izouma.nineth.exception.BusinessException;
 import com.izouma.nineth.service.sms.SmsService;
 import lombok.AllArgsConstructor;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
@@ -15,7 +16,7 @@ import java.util.regex.Pattern;
 @RequestMapping("/sms")
 @AllArgsConstructor
 public class SmsController {
-    private SmsService smsService;
+    private SmsService                    smsService;
 
     @GetMapping("/sendVerify")
     public String sendVerify(@RequestParam String phone) {

+ 0 - 0
src/main/resources/lua/mqCreate.lua


+ 1 - 2
src/test/java/com/izouma/nineth/repo/UserRepoTest.java

@@ -12,7 +12,6 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit4.SpringRunner;
 
 import java.time.LocalDate;
-import java.time.LocalDateTime;
 import java.util.*;
 
 @RunWith(SpringRunner.class)
@@ -67,7 +66,7 @@ public class UserRepoTest {
 
     @Test
     public void test1() {
-        List<Long> userIds = userRepo.findAllByCollectionId(3460186L, 2);
+        List<Long> userIds = userRepo.findInvitorByCollectionId(3460186L, 2);
         System.out.println(userIds.size());
         userRepo.updateAllByInvitor(userIds);
     }

+ 6 - 1
src/test/java/com/izouma/nineth/service/CollectionServiceTest.java

@@ -131,11 +131,16 @@ class CollectionServiceTest extends ApplicationTests {
         if (!collection.isOnShelf() || !collection.isSalable()) {
             collectionId = null;
         } else if (collection.isScheduleSale()) {
-            if (collection.getStartTime().isAfter(LocalDateTime.now())){
+            if (collection.getStartTime().isAfter(LocalDateTime.now())) {
                 collectionId = null;
             }
         }
         System.out.println(collectionId);
     }
 
+    @Test
+    public void savePoint() {
+        collectionService.savePoint(196332L).forEach(System.out::println);
+    }
+
 }