package com.izouma.nineth.service; import cn.hutool.core.convert.Convert; import com.izouma.nineth.TokenHistory; import com.izouma.nineth.annotations.Debounce; import com.izouma.nineth.config.Constants; import com.izouma.nineth.config.GeneralProperties; import com.izouma.nineth.config.RedisKeys; import com.izouma.nineth.converter.LongArrayConverter; import com.izouma.nineth.converter.StringArrayConverter; import com.izouma.nineth.domain.Collection; import com.izouma.nineth.domain.*; import com.izouma.nineth.domain.nftdomain.Cart; import com.izouma.nineth.dto.*; import com.izouma.nineth.enums.*; import com.izouma.nineth.exception.BusinessException; import com.izouma.nineth.repo.*; import com.izouma.nineth.repo.nftdomain.CartRepo; import com.izouma.nineth.utils.JpaUtils; import com.izouma.nineth.utils.SecurityUtils; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.Range; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.BeanUtils; import org.springframework.cache.annotation.Cacheable; import org.springframework.core.env.Environment; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.TaskScheduler; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import javax.annotation.PostConstruct; import javax.persistence.criteria.Join; import javax.persistence.criteria.Predicate; import javax.transaction.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Service @AllArgsConstructor @Slf4j public class CollectionService { private CollectionRepo collectionRepo; private LikeRepo likeRepo; private BlindBoxItemRepo blindBoxItemRepo; private AppointmentRepo appointmentRepo; private UserRepo userRepo; private TaskScheduler taskScheduler; private CacheService cacheService; private RedisTemplate redisTemplate; private RocketMQTemplate rocketMQTemplate; private GeneralProperties generalProperties; private Environment env; private OrderRepo orderRepo; private TokenHistoryRepo tokenHistoryRepo; private TagRepo tagRepo; private UserBalanceService userBalanceService; private SysConfigService sysConfigService; private NewsRepo newsRepo; private AssetRepo assetRepo; private CartRepo cartRepo; private final Map> tasks = new HashMap<>(); @PostConstruct public void init() { if (Arrays.asList(env.getActiveProfiles()).contains("dev")) { return; } List collections = collectionRepo .findByScheduleSaleTrueAndOnShelfFalseAndStartTimeBeforeAndDelFalse(LocalDateTime.now()); for (Collection collection : collections) { onShelfTask(collection); } } @Cacheable(value = "collectionList", key = "#pageQuery.hashCode()") public PageWrapper all(PageQuery pageQuery) { Map query = pageQuery.getQuery(); query.put("del", false); // String type = MapUtils.getString(pageQuery.getQuery(), "type", "DEFAULT"); // pageQuery.getQuery().remove("type"); Specification specification = JpaUtils.toSpecification(pageQuery, Collection.class); PageRequest pageRequest = JpaUtils.toPageRequest(pageQuery); //筛选最低价格 if (query.containsKey("minPrice")) { BigDecimal minPrice = Convert.toBigDecimal(query.get("minPrice")); query.remove("minPrice"); if (minPrice.compareTo(BigDecimal.ZERO) > 0) { specification = specification .and((Specification) (root, criteriaQuery, criteriaBuilder) -> { List and = new ArrayList<>(); and.add(criteriaBuilder.greaterThanOrEqualTo(root.get("price"), minPrice)); return criteriaBuilder.and(and.toArray(new Predicate[0])); }); } } if (query.containsKey("distinct")) { query.remove("distinct"); specification = specification.and((Specification) (root, criteriaQuery, criteriaBuilder) -> { criteriaQuery.groupBy(root.get("name")); return criteriaBuilder.and(); }); } if (query.containsKey("distinctPrefix")) { query.remove("distinctPrefix"); specification = specification.and((Specification) (root, criteriaQuery, criteriaBuilder) -> { criteriaQuery.groupBy(root.get("prefixName")); return criteriaBuilder.and(); }); } if (query.containsKey("rarityLabel")) { String rarityLabel = String.valueOf(query.get("rarityLabel")); query.remove("rarityLabel"); String not = null; if (StringUtils.isNotBlank(rarityLabel)) { if (Constants.Rarity.SSR.equals(rarityLabel)) { not = Constants.Rarity.U_LIKE; } if (Constants.Rarity.SR.equals(rarityLabel)) { not = Constants.Rarity.SSR_LIKE; } if (Constants.Rarity.U.equals(rarityLabel)) { not = Constants.Rarity.R_LIKE; } if (Constants.Rarity.R.equals(rarityLabel)) { not = Constants.Rarity.SR_LIKE; } String finalNotLike = not; String finalLike = "%" + rarityLabel + " #%"; specification = specification .and((Specification) (root, criteriaQuery, criteriaBuilder) -> { List and = new ArrayList<>(); and.add(criteriaBuilder.like(root.get("name"), finalLike)); and.add(criteriaBuilder.notLike(root.get("name"), finalNotLike)); return criteriaBuilder.and(and.toArray(new Predicate[0])); }); } } // 筛选去除指定名称不展示 if (query.containsKey("notLike")) { String notLike = Convert.toStr(query.get("notLike")); query.remove("notLike"); StringArrayConverter converter = new StringArrayConverter(); List notLikes = converter.convertToEntityAttribute(notLike); specification = specification.and((Specification) (root, criteriaQuery, criteriaBuilder) -> { List and = new ArrayList<>(); notLikes.forEach(str -> { and.add(criteriaBuilder.notLike(root.get("name"), "%" + str + "%")); }); return criteriaBuilder.and(and.toArray(new Predicate[0])); }); } // if (pageRequest.getSort().stream().noneMatch(order -> order.getProperty().equals("createdAt"))) { // pageRequest = PageRequest.of(pageRequest.getPageNumber(), pageQuery.getSize(), // pageRequest.getSort().and(Sort.by("createdAt").descending())); // } // specification = specification.and((Specification) (root, criteriaQuery, criteriaBuilder) -> { // List and = new ArrayList<>(); // // if (StringUtils.isNotEmpty(type) && !"all".equalsIgnoreCase(type)) { // try { // if (type.contains(",")) { // and.add(root.get("type") // .in(Arrays.stream(type.split(",")).map(s -> Enum.valueOf(CollectionType.class, s)) // .collect(Collectors.toList()))); // } else { // and.add(criteriaBuilder.equal(root.get("type"), Enum.valueOf(CollectionType.class, type))); // } // } catch (Exception e) { // // } // } // return criteriaBuilder.and(and.toArray(new Predicate[0])); // }); Page page = collectionRepo.findAll(specification, pageRequest); return new PageWrapper<>(page.getContent(), page.getPageable().getPageNumber(), page.getPageable().getPageSize(), page.getTotalElements()); } @Transactional public Collection create(Collection record) { User minter = userRepo.findById(record.getMinterId()).orElse(SecurityUtils.getAuthenticatedUser()); if (record.getCompanyId() != 1L) { BigDecimal price = sysConfigService.getBigDecimal("company_collection_price"); BigDecimal totalPrice = new BigDecimal(record.getTotal()).multiply(price); if (!userBalanceService.checkBalance(record.getCompanyId(), totalPrice)) { throw new BusinessException("余额不足"); } userBalanceService .modifyBalance(record.getCompanyId(), totalPrice.negate(), BalanceType.PAY, null, false, null); } if (record.getNewsId() != null) { News news = newsRepo.findById(record.getNewsId()).orElseThrow(new BusinessException("未找到")); record.setNewsId(news.getId()); record.setNewsPic(news.getPic()); record.setNewsTitle(news.getTitle()); record.setNewsCreatedTime(news.getCreatedAt()); } record.setMinter(minter.getNickname()); record.setMinterId(minter.getId()); record.setMinterAvatar(minter.getAvatar()); record.setOwner(minter.getNickname()); record.setOwnerId(minter.getId()); record.setOwnerAvatar(minter.getAvatar()); record.setStock(record.getTotal()); record.setSale(0); record.setVipQuota(record.getTotalQuota()); if (!record.getTags().isEmpty()) { record.setTags(new HashSet<>(tagRepo.findAllById(record.getTags().stream().map(Tag::getId) .collect(Collectors.toList())))); } if (record.isScheduleSale()) { if (record.getStartTime() == null) { throw new BusinessException("请填写定时发布时间"); } if (record.getStartTime().isBefore(LocalDateTime.now())) { record.setOnShelf(true); record.setSalable(true); record.setStartTime(null); } } record = collectionRepo.save(record); onShelfTask(record); redisTemplate.opsForValue().set(RedisKeys.COLLECTION_STOCK + record.getId(), record.getStock()); redisTemplate.opsForValue().set(RedisKeys.COLLECTION_SALE + record.getId(), record.getSale()); return record; } public Collection update(Collection record) { // collectionRepo.update(record.getId(), record.isOnShelf(), record.isSalable(), // record.getStartTime(), record.isScheduleSale(), record.getSort(), // record.getDetail(), JSON.toJSONString(record.getPrivileges()), // JSON.toJSONString(record.getProperties()), JSON.toJSONString(record.getModel3d()), // record.getMaxCount(), record.getCountId(), record.isScanCode(), record.isNoSoldOut(), // record.getAssignment(), record.isCouponPayment(), record.getShareBg(), record.getRegisterBg(), // record.getVipQuota(), record.getTimeDelay(), record.getSaleTime(), record.getHoldDays(), // record.getOpenQuota(), record.getTotalQuota(), record.getMinimumCharge()); Collection collection = collectionRepo.findDetailById(record.getId()).orElseThrow(new BusinessException("无记录")); collection.setOnShelf(record.isOnShelf()); collection.setSalable(record.isSalable()); collection.setStartTime(record.getStartTime()); collection.setScheduleSale(record.isScheduleSale()); collection.setSort(record.getSort()); collection.setDetail(record.getDetail()); collection.setPrivileges(record.getPrivileges()); collection.setProperties(record.getProperties()); collection.setModel3d(record.getModel3d()); collection.setMaxCount(record.getMaxCount()); collection.setCountId(record.getCountId()); collection.setScanCode(record.isScanCode()); collection.setNoSoldOut(record.isNoSoldOut()); collection.setAssignment(record.getAssignment()); collection.setCouponPayment(record.isCouponPayment()); collection.setShareBg(record.getShareBg()); collection.setRegisterBg(record.getRegisterBg()); collection.setVipQuota(record.getVipQuota()); collection.setTimeDelay(record.getTimeDelay()); collection.setSaleTime(record.getSaleTime()); collection.setHoldDays(record.getHoldDays()); collection.setOpenQuota(record.getOpenQuota()); collection.setTotalQuota(record.getTotalQuota()); collection.setMinimumCharge(record.getMinimumCharge()); collection.setRule(record.getRule()); collection.setPrefixName(record.getPrefixName()); if (record.getTags().isEmpty()) { collection.setTags(null); } else { collection.setTags(new HashSet<>(tagRepo.findAllById(record.getTags().stream().map(Tag::getId) .collect(Collectors.toList())))); } if (record.getNewsId() != null) { News news = newsRepo.findById(record.getNewsId()).orElseThrow(new BusinessException("未找到新闻")); collection.setNewsId(news.getId()); collection.setNewsTitle(news.getTitle()); collection.setNewsPic(news.getPic()); collection.setNewsCreatedTime(news.getCreatedAt()); } collection = collectionRepo.save(collection); onShelfTask(collection); return collection; } private void onShelfTask(Collection record) { ScheduledFuture task = tasks.get(record.getId()); if (task != null) { if (!task.cancel(true)) { return; } } if (record.isScheduleSale()) { if (record.getStartTime().minusSeconds(2).isAfter(LocalDateTime.now())) { Date date = Date.from(record.getStartTime().atZone(ZoneId.systemDefault()).toInstant()); ScheduledFuture future = taskScheduler.schedule(() -> { collectionRepo.scheduleOnShelf(record.getId(), !record.isScanCode()); tasks.remove(record.getId()); }, date); tasks.put(record.getId(), future); } else { collectionRepo.scheduleOnShelf(record.getId(), !record.isScanCode()); } } } public CollectionDTO toDTO(Collection collection) { return toDTO(collection, true, false); } public CollectionDTO toDTO(Collection collection, boolean join, boolean showVip) { CollectionDTO collectionDTO = new CollectionDTO(); BeanUtils.copyProperties(collection, collectionDTO); if (join) { User user = SecurityUtils.getAuthenticatedUser(); if (user != null) { List list = likeRepo.findByUserIdAndCollectionId(user.getId(), collection.getId()); collectionDTO.setLiked(!list.isEmpty()); if (collection.getType() == CollectionType.BLIND_BOX) { collectionDTO.setAppointment(appointmentRepo.findFirstByBlindBoxId(collection.getId()).isPresent()); } if (showVip && collection.getAssignment() > 0 && user.getVipPurchase() > 0) { int purchase = orderRepo .countByUserIdAndCollectionIdAndVipTrueAndStatusIn(user.getId(), collection.getId(), Arrays .asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING)); collectionDTO.setVipSurplus(user.getVipPurchase() - purchase); } } } return collectionDTO; } public Collection queryUserDetail(Collection collection, boolean join, boolean showVip) { if (join) { User user = SecurityUtils.getAuthenticatedUser(); if (user != null) { List list = likeRepo.findByUserIdAndCollectionId(user.getId(), collection.getId()); collection.setLiked(!list.isEmpty()); if (collection.getType() == CollectionType.BLIND_BOX) { collection.setAppointment(appointmentRepo.findFirstByBlindBoxId(collection.getId()).isPresent()); } Cart cart = cartRepo .findFirstByCollectionIdAndUserIdAndDel(collection.getId(), user.getId(), false); collection.setInCart(cart != null); // if (showVip && collection.getAssignment() > 0 && user.getVipPurchase() > 0) { // int purchase = orderRepo.countByUserIdAndCollectionIdAndVipTrueAndStatusIn(user.getId(), collection.getId(), Arrays.asList(OrderStatus.FINISH, OrderStatus.NOT_PAID, OrderStatus.PROCESSING)); collection.setVipSurplus(user.getVipPurchase()); // } } } return collection; } public List toDTO(List collections) { List likes = new ArrayList<>(); List appointments = new ArrayList<>(); if (SecurityUtils.getAuthenticatedUser() != null) { likes.addAll(likeRepo.findByUserIdAndCollectionIdIsNotNull(SecurityUtils.getAuthenticatedUser().getId())); appointments.addAll(appointmentRepo.findByUserId(SecurityUtils.getAuthenticatedUser().getId())); } return collections.stream().parallel().map(collection -> { CollectionDTO dto = toDTO(collection, false, false); if (!likes.isEmpty()) { dto.setLiked(likes.stream().anyMatch(l -> l.getCollectionId().equals(collection.getId()))); } if (!appointments.isEmpty()) { dto.setAppointment(appointments.stream().anyMatch(a -> a.getBlindBoxId().equals(collection.getId()))); } return dto; }).collect(Collectors.toList()); } public void queryUserDetail(List collections) { List likes = new ArrayList<>(); List appointments = new ArrayList<>(); List carts = new ArrayList<>(); if (SecurityUtils.getAuthenticatedUser() != null) { likes.addAll(likeRepo.findByUserIdAndCollectionIdIsNotNull(SecurityUtils.getAuthenticatedUser().getId())); appointments.addAll(appointmentRepo.findByUserId(SecurityUtils.getAuthenticatedUser().getId())); carts.addAll(cartRepo.findByUserIdAndDel(SecurityUtils.getAuthenticatedUser().getId(), false)); } collections.stream().parallel().forEach(collection -> { queryUserDetail(collection, false, false); if (!likes.isEmpty()) { collection.setLiked(likes.stream().anyMatch(l -> l.getCollectionId().equals(collection.getId()))); } if (!appointments.isEmpty()) { collection.setAppointment(appointments.stream() .anyMatch(a -> a.getBlindBoxId().equals(collection.getId()))); } if (!carts.isEmpty()) { collection.setInCart(carts.stream() .anyMatch(a -> a.getCollectionId().equals(collection.getId()))); } }); } public Page toDTO(Page collections) { List userDTOS = toDTO(collections.getContent()); return new PageImpl<>(userDTOS, collections.getPageable(), collections.getTotalElements()); } @Transactional public Collection createBlindBox(CreateBlindBox createBlindBox) throws Exception { Collection blindBox = createBlindBox.getBlindBox(); blindBox.setCompanyId(Optional.ofNullable(createBlindBox.getCompanyId()).orElse(1L)); if (blindBox.getId() != null) { throw new BusinessException("无法完成此操作"); } List list = collectionRepo.findAllById(createBlindBox.getItems().stream().map(BlindBoxItem::getCollectionId) .collect(Collectors.toSet())); for (BlindBoxItem item : createBlindBox.getItems()) { Collection collection = list.stream().filter(i -> i.getId().equals(item.getCollectionId())).findAny() .orElseThrow(new BusinessException("所选藏品不存在")); if (item.getTotal() > collection.getStock()) { throw new BusinessException("所选藏品库存不足:" + collection.getName()); } } User user = userRepo.findById(blindBox.getMinterId()).orElse(SecurityUtils.getAuthenticatedUser()); blindBox.setMinter(user.getNickname()); blindBox.setMinterId(user.getId()); blindBox.setMinterAvatar(user.getAvatar()); blindBox.setOwner(user.getNickname()); blindBox.setOwnerId(user.getId()); blindBox.setOwnerAvatar(user.getAvatar()); blindBox.setTotal(createBlindBox.getItems().stream().mapToInt(BlindBoxItem::getTotal).sum()); blindBox.setStock(blindBox.getTotal()); blindBox.setSale(0); collectionRepo.save(blindBox); new ForkJoinPool(128).submit(() -> { createBlindBox.getItems().stream().parallel().forEach(item -> { Collection collection = list.stream().filter(i -> i.getId().equals(item.getCollectionId())) .findFirst().get(); decreaseStock(collection.getId(), item.getTotal()); BlindBoxItem blindBoxItem = new BlindBoxItem(); BeanUtils.copyProperties(collection, blindBoxItem); blindBoxItem.setId(null); blindBoxItem.setOasisId(collection.getOasisId()); blindBoxItem.setCollectionId(item.getCollectionId()); blindBoxItem.setSale(0); blindBoxItem.setTotal(item.getTotal()); blindBoxItem.setStock(item.getTotal()); blindBoxItem.setRare(item.isRare()); blindBoxItem.setRaceId(collection.getRaceId()); blindBoxItem.setGroupId(collection.getGroupId()); blindBoxItem.setItemId(collection.getItemId()); blindBoxItem.setBlindBoxId(blindBox.getId()); blindBoxItem.setCompanyId(collection.getCompanyId()); blindBoxItemRepo.saveAndFlush(blindBoxItem); log.info("createBlindBoxItemSuccess" + blindBoxItem.getId()); }); }).get(); return blindBox; } public void appointment(Long id, Long userId) { Collection collection = collectionRepo.findById(id).orElseThrow(new BusinessException("无记录")); if (collection.getType() != CollectionType.BLIND_BOX) { throw new BusinessException("非盲盒,无需预约"); } if (collection.getStartTime().isBefore(LocalDateTime.now())) { throw new BusinessException("盲盒已开售,无需预约"); } appointmentRepo.save(Appointment.builder() .userId(userId) .blindBoxId(id) .build()); } public BlindBoxItem draw(Long userId, Long collectionId) { log.info("blindBoxDraw, userId={}, collectionId={}", userId, collectionId); List items = blindBoxItemRepo.findByBlindBoxId(collectionId); BoundHashOperations operations = redisTemplate .boundHashOps(RedisKeys.DRAW_BLIND_BOX + collectionId); Map entries = operations.entries(); Map> randomRange = new HashMap<>(); int c = 0, sum = 0; for (BlindBoxItem item : items) { if (item.getStock() > 0) { int stock = Optional.ofNullable(entries.get(item.getCollectionId() + "")).map(i -> (int) i) .orElse(item.getStock()); randomRange.put(item, Range.between(c, c + item.getStock())); c += stock; sum += stock; } } int retry = 0; BlindBoxItem winItem = null; while (winItem == null) { retry++; if (userId == 3453161L || userId == 7194L || userId == 134613L) { winItem = items.stream().filter(i -> i.getName().contains("SSR") && i.getStock() > 0).findFirst() .orElse(null); } if (winItem == null) { int rand = RandomUtils.nextInt(0, sum + 1); for (Map.Entry> entry : randomRange.entrySet()) { BlindBoxItem item = entry.getKey(); Range 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 { 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; } } } } } if (retry > 100 && winItem == null) { throw new BusinessException("盲盒抽卡失败"); } if (winItem != null) { operations.putIfAbsent(winItem.getCollectionId() + "", winItem.getStock()); int stock = Math.toIntExact(operations.increment(winItem.getCollectionId() + "", -1)); if (stock < 0) { log.info("over draw {} {}", stock, winItem.getCollectionId()); operations.increment(winItem.getCollectionId() + "", 1); winItem = null; } } } // winItem.setStock(winItem.getStock() - 1); // winItem.setSale(winItem.getSale() + 1); // blindBoxItemRepo.saveAndFlush(winItem); blindBoxItemRepo.decreaseStockAndIncreaseSale(winItem.getId(), 1); blindBoxItemRepo.flush(); return winItem; } public synchronized Integer getNextNumber(Long collectionId) { BoundValueOperations opt = redisTemplate .boundValueOps(RedisKeys.COLLECTION_NUMBER + collectionId); opt.setIfAbsent(collectionRepo.getCurrentNumber(collectionId).orElse(0)); int num = Math.toIntExact(Optional.ofNullable(opt.increment()).orElse(1L)); collectionRepo.setNumber(collectionId, num); return num; } public synchronized Integer getNextNumber(Collection collection) { return collection.getTotal() > 1 ? getNextNumber(collection.getId()) : null; } public synchronized Integer getNextNumber(BlindBoxItem collection) { return collection.getTotal() > 1 ? getNextNumber(collection.getId()) : null; } public void addStock(Long id, int number) { Collection collection = collectionRepo.findById(id).orElseThrow(new BusinessException("无记录")); if (collection.getSource() != CollectionSource.OFFICIAL) { throw new BusinessException("用户转售无法增发"); } if (collection.getType() == CollectionType.BLIND_BOX) { throw new BusinessException("盲盒无法增发"); } increaseStock(id, number); collectionRepo.increaseTotal(id, number); } public synchronized Long increaseStock(Long id, int number) { BoundValueOperations ops = redisTemplate.boundValueOps(RedisKeys.COLLECTION_STOCK + id); if (ops.get() == null) { Boolean success = ops.setIfAbsent(Optional.ofNullable(collectionRepo.getStock(id)) .orElse(0), 7, TimeUnit.DAYS); log.info("创建redis库存:{}", success); } Long stock = ops.increment(number); rocketMQTemplate.convertAndSend(generalProperties.getUpdateStockTopic(), id); return stock; } public synchronized Integer getStock(Long id) { BoundValueOperations ops = redisTemplate.boundValueOps(RedisKeys.COLLECTION_STOCK + id); Integer stock = (Integer) ops.get(); if (stock == null) { Boolean success = ops.setIfAbsent(Optional.ofNullable(collectionRepo.getStock(id)) .orElse(0), 7, TimeUnit.DAYS); log.info("创建redis库存:{}", success); return (Integer) ops.get(); } else { return stock; } } public synchronized Long decreaseStock(Long id, int number) { return increaseStock(id, -number); } public synchronized Long increaseSale(Long id, int number) { BoundValueOperations ops = redisTemplate.boundValueOps(RedisKeys.COLLECTION_SALE + id); if (ops.get() == null) { Boolean success = ops.setIfAbsent(Optional.ofNullable(collectionRepo.getSale(id)) .orElse(0), 7, TimeUnit.DAYS); log.info("创建redis销量:{}", success); } Long sale = ops.increment(number); redisTemplate.opsForHash().increment(RedisKeys.UPDATE_SALE, id.toString(), 1); // rocketMQTemplate.convertAndSend(generalProperties.getUpdateSaleTopic(), id); return sale; } public synchronized Long decreaseSale(Long id, int number) { return increaseSale(id, -number); } // @Debounce(key = "#id", delay = 500) public void syncStock(Long id) { Integer stock = (Integer) redisTemplate.opsForValue().get(RedisKeys.COLLECTION_STOCK + id); if (stock != null) { log.info("同步库存信息{}", id); collectionRepo.updateStock(id, stock); cacheService.clearCollection(id); } } // @Debounce(key = "#id", delay = 500) public void syncSale(Long id) { Integer sale = (Integer) redisTemplate.opsForValue().get(RedisKeys.COLLECTION_SALE + id); if (sale != null) { log.info("同步销量信息{}", id); collectionRepo.updateSale(id, sale); cacheService.clearCollection(id); } } @Debounce(key = "#id", delay = 500) public void syncQuota(Long id) { Integer quota = (Integer) redisTemplate.opsForValue().get(RedisKeys.COLLECTION_QUOTA + id); if (quota != null) { log.info("同步额度信息{}", id); collectionRepo.updateVipQuota(id, quota); cacheService.clearCollection(id); } } public synchronized Long decreaseQuota(Long id, int number) { BoundValueOperations ops = redisTemplate.boundValueOps(RedisKeys.COLLECTION_QUOTA + id); if (ops.get() == null) { Boolean success = ops.setIfAbsent(Optional.ofNullable(collectionRepo.getVipQuota(id)) .orElse(0), 7, TimeUnit.DAYS); log.info("创建redis额度:{}", success); } Long stock = ops.increment(-number); rocketMQTemplate.convertAndSend(generalProperties.getUpdateQuotaTopic(), id); return stock; } @Cacheable(value = "recommendLegacy", key = "#type") public List recommendLegacy(@RequestParam String type, Long companyId) { return collectionRepo.recommend(type, companyId).stream().map(rc -> { if (StringUtils.isNotBlank(rc.getRecommend().getPic())) { rc.getCollection().setPic(Collections.singletonList(new FileObject(null, rc.getRecommend() .getPic(), null, null))); } CollectionDTO collectionDTO = new CollectionDTO(); BeanUtils.copyProperties(rc.getCollection(), collectionDTO); return collectionDTO; }).collect(Collectors.toList()); } public List savePoint(Long collectionId, LocalDateTime time) { Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("无藏品")); //库存 // int stock = collection.getStock(); //是否开启白名单 int assignment = collection.getAssignment(); if (assignment <= 0) { return null; } List users = userRepo.findAllByCollectionId(collectionId); //邀请者 Map> userMap = users.stream() .filter(user -> ObjectUtils.isNotEmpty(user.getCollectionInvitor())) .collect(Collectors.groupingBy(User::getCollectionInvitor)); AtomicInteger sum = new AtomicInteger(); AtomicInteger sum1 = new AtomicInteger(); List dtos = new ArrayList<>(); Map historyMap = tokenHistoryRepo.userBuy(userMap.keySet()) .stream() .collect(Collectors .groupingBy(TokenHistory::getToUserId, Collectors .reducing(BigDecimal.ZERO, TokenHistory::getPrice, BigDecimal::add))); DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); userMap.forEach((key, value) -> { //邀请达到数量 if (value.size() >= collection.getAssignment()) { value.sort(Comparator.comparing(User::getCreatedAt)); BigDecimal buy = historyMap.get(key); //满足条件的时间 User user = value.get(collection.getAssignment() - 1); //作弊得已屏蔽 if ((ObjectUtils.isEmpty(buy) || buy.compareTo(BigDecimal.valueOf(500)) < 0) && user.getCreatedAt() .isBefore(time)) { sum1.getAndIncrement(); System.out.println(key + "," + dft.format(user.getCreatedAt()) + "," + buy); } else { //实名数量 long identitySum = value.stream().filter(u -> AuthStatus.SUCCESS.equals(u.getAuthStatus())).count(); dtos.add(new PointDTO(key, user.getCreatedAt(), value.size(), (int) identitySum, buy)); sum.getAndIncrement(); } } }); log.info("完成任务人数:{}", sum); log.info("作弊任务人数:{}", sum1); LongArrayConverter longArrayConverter = new LongArrayConverter(); List collect = dtos.stream() .filter(dto -> time.isBefore(dto.getCreatedAt())) .map(PointDTO::getId) .collect(Collectors.toList()); log.info(dft.format(time) + "前完成任务人数:{}", collect.size()); // log.info("sql: update user set vip_point = 1 where id in ({})", longArrayConverter.convertToDatabaseColumn(collect)); List collect1 = dtos.stream() .filter(dto -> time.isAfter(dto.getCreatedAt())) .collect(Collectors.toList()); log.info(dft.format(time) + "后完成任务人数:{}", collect1.size()); List collect2 = dtos.stream() .filter(dto -> dto.getIdentitySum() > 0) .map(PointDTO::getId) .collect(Collectors.toList()); log.info("邀请实名认证人量:{}", collect2.size()); // log.info("sql: update user set vip_point = 1 where id in ({})", longArrayConverter.convertToDatabaseColumn(collect2)); return dtos; } public Page byTag(Long tagId, List excludeUserId, Pageable pageable) { if (excludeUserId.isEmpty()) { excludeUserId.add(0L); } return collectionRepo.findAll((Specification) (root, query, criteriaBuilder) -> { Join join = root.join("tags"); return criteriaBuilder.and(criteriaBuilder.equal(join.get("id"), tagId), criteriaBuilder.equal(root.get("source"), CollectionSource.TRANSFER), criteriaBuilder.equal(root.get("salable"), true), criteriaBuilder.equal(root.get("onShelf"), true), criteriaBuilder.not(root.get("ownerId").in(excludeUserId))); }, pageable); } public List setOasisScancode(List oasisIds) { List collectionSources = new ArrayList<>(); collectionSources.add(CollectionSource.COMPANY); collectionSources.add(CollectionSource.OFFICIAL); List collections = collectionRepo .findAllByOasisIdInAndSourceInAndStockGreaterThan(oasisIds, collectionSources, 0); List result = new ArrayList<>(); collections.forEach(collection -> { collection.setOnShelf(false); collection.setScanCode(true); collection.setSalable(true); collectionRepo.save(collection); result.add(collection); }); return result; } public List setOasisOnShelf(List oasisIds) { List collectionSources = new ArrayList<>(); collectionSources.add(CollectionSource.COMPANY); collectionSources.add(CollectionSource.OFFICIAL); List collections = collectionRepo .findAllByOasisIdInAndSourceInAndStockGreaterThan(oasisIds, collectionSources, 0); List result = new ArrayList<>(); collections.forEach(collection -> { collection.setOnShelf(true); collection.setScanCode(false); collection.setSalable(true); collectionRepo.save(collection); result.add(collection); }); return result; } public Long countDestroyAssets(String search) { return assetRepo.countDestroyed("%" + search + "%", AssetStatus.DESTROYED); } @Cacheable(value = "countByPrefix", key = "#prefixName") public Integer countNum(String prefixName) { return collectionRepo.countByPrefixNameAndOnShelfAndDelAndSalable(prefixName, true, false, true); } }