Browse Source

2019/08/16

x1ongzhu 6 years ago
parent
commit
bea6f342ad

+ 1 - 1
src/main/java/com/izouma/walkchina/constant/AppConstants.java

@@ -22,7 +22,7 @@ public interface AppConstants {
     String DEFAULT_TIME_FORMAT      = "HH:mm:ss";
 
     // 1步=多少米
-    double STEP_TO_DISTANCE_RATE = 1;
+    double STEP_TO_DISTANCE_RATE = .7;
 
     // 1米=多少商城币
     double DISTANCE_TO_COIN_RATE = 0.01;

+ 15 - 7
src/main/java/com/izouma/walkchina/constant/Strings.java

@@ -2,21 +2,27 @@ package com.izouma.walkchina.constant;
 
 public interface Strings {
     String MSG_JOURNEY_PROGRESS = "你正在从 <b>%s</b> 向 <b>%s</b> 前进," +
-            "你今日步数<span style=\"color:#FF9500;font-weight:bold\">%s</span>步," +
-            "已行走<span style=\"color:#FF9500;font-weight:bold\">%s</span>天," +
-            "共<span style=\"color:#FF9500;font-weight:bold\">%d</span>步," +
-            "已完成进度<span style=\"color:#FF9500;font-weight:bold\">%d%%</span>";
+                                  "你今日步数<span style=\"color:#FF9500;font-weight:bold\">%s</span>步," +
+                                  "已行走<span style=\"color:#FF9500;font-weight:bold\">%s</span>天," +
+                                  "共<span style=\"color:#FF9500;font-weight:bold\">%d</span>步," +
+                                  "已完成进度<span style=\"color:#FF9500;font-weight:bold\">%d%%</span>";
+
+    String MSG_HIRE = "你使用<span style=\"color:#FF9500;font-weight:bold\">%.2f</span>商城币雇佣了<span style=\"color:#FF9500;font-weight:bold\">%s</span>";
 
     String MSG_HIRED = "%s使用<span style=\"color:#FF9500;font-weight:bold\">%.2f</span>商城币雇佣了你";
 
     String MSG_MEMBER_GRABBED = "你的队员<span style=\"color:#FF9500;font-weight:bold\">%s</span>," +
-            "被<span style=\"color:#FF9500;font-weight:bold\">%s</span>抢走了";
+                                "被<span style=\"color:#FF9500;font-weight:bold\">%s</span>抢走了";
 
     String MSG_AWARD_STOLEN = "<span style=\"color:#FF9500;font-weight:bold\">%s</span>" +
-            "偷了你<span style=\"color:#FF9500;font-weight:bold\">%s</span>商城币";
+                              "偷了你<span style=\"color:#FF9500;font-weight:bold\">%s</span>商城币";
 
     String MSG_BE_REWARD = "<span style=\"color:#FF9500;font-weight:bold\">%s</span>" +
-            "打赏了你<span style=\"color:#FF9500;font-weight:bold\">%s</span>商城币";
+                           "打赏了你<span style=\"color:#FF9500;font-weight:bold\">%s</span>商城币";
+
+    String MSG_ARRIVAL = "你已到达<span style=\"color:#FF9500;font-weight:bold\">%s</span>,获得奖励<span style=\"color:#FF9500;font-weight:bold\">%s</span>商城币";
+
+    String MSG_RECRUIT = "你招募了<span style=\"color:#FF9500;font-weight:bold\">%s</span>";
 
     String REMARK_HIRE = "雇佣队员";
 
@@ -32,6 +38,8 @@ public interface Strings {
 
     String REMARK_ARRIVAL = "到达终点";
 
+    String REMARK_BUY = "兑换商品";
+
     String GRAB_FAIL = "很遗憾,Ta已经被别人抢走了~";
 
 }

+ 4 - 0
src/main/java/com/izouma/walkchina/domain/Product.java

@@ -72,4 +72,8 @@ public class Product {
     private BigDecimal shippingCost;
 
     private Float userRate;
+
+    private Boolean freeExchange;
+
+    private Integer sort;
 }

+ 51 - 0
src/main/java/com/izouma/walkchina/domain/RewardRecord.java

@@ -0,0 +1,51 @@
+package com.izouma.walkchina.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@EntityListeners(AuditingEntityListener.class)
+public class RewardRecord {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    private Long id;
+
+    private Long userId;
+
+    private Long targetUserId;
+
+    @Column(precision = 10, scale = 2)
+    private BigDecimal amount;
+
+    private LocalDate date;
+
+    @CreatedBy
+    private String createdBy;
+
+    @CreatedDate
+    private LocalDateTime createdAt;
+
+    @LastModifiedBy
+    private String modifiedBy;
+
+    @LastModifiedDate
+    private LocalDateTime modifiedAt;
+}

+ 3 - 0
src/main/java/com/izouma/walkchina/domain/UserOrder.java

@@ -6,6 +6,8 @@ import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.hibernate.annotations.NotFound;
+import org.hibernate.annotations.NotFoundAction;
 import org.hibernate.annotations.Where;
 import org.springframework.data.annotation.CreatedBy;
 import org.springframework.data.annotation.CreatedDate;
@@ -52,6 +54,7 @@ public class UserOrder {
 
     @OneToOne
     @JoinColumn(name = "productId", insertable = false, updatable = false)
+    @NotFound(action = NotFoundAction.IGNORE)
     private Product product;
 
     private Long productSpecId;

+ 1 - 0
src/main/java/com/izouma/walkchina/dto/UserDTO.java

@@ -20,6 +20,7 @@ public class UserDTO {
     private Long       hiredBy;
     private LocalDate  hireDate;
     private boolean    shouldWake;
+    private boolean    canReward;
 
     public UserDTO(Long userId, String nickname, String avatar, Long totalSteps, BigDecimal price) {
         this.userId = userId;

+ 1 - 0
src/main/java/com/izouma/walkchina/dto/UserWalkStats.java

@@ -18,6 +18,7 @@ public class UserWalkStats {
     private Long       todaySteps;
     private Long       totalSteps;
     private Long       teamSteps;
+    private Long       teamTotalSteps;
     private Integer    walkCities;
     private BigDecimal price;
 }

+ 7 - 0
src/main/java/com/izouma/walkchina/repo/RewardRecordRepository.java

@@ -0,0 +1,7 @@
+package com.izouma.walkchina.repo;
+
+import com.izouma.walkchina.domain.RewardRecord;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface RewardRecordRepository extends JpaRepository<RewardRecord, Long> {
+}

+ 4 - 1
src/main/java/com/izouma/walkchina/repo/UserOrderRepository.java

@@ -4,12 +4,15 @@ import com.izouma.walkchina.constant.OrderStatus;
 import com.izouma.walkchina.domain.UserOrder;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
-import org.springframework.data.domain.Sort;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
 
 import java.util.List;
 
 public interface UserOrderRepository extends JpaRepository<UserOrder, String>, JpaSpecificationExecutor<UserOrder> {
     Page<UserOrder> findByUserIdAndStatusIn(Long userId, List<OrderStatus> statuses, Pageable pageable);
+
+    @Query("select o from UserOrder o where o.userId = ?1 and o.productId in (select p.id from Product p where p.freeExchange = true)")
+    List<UserOrder> findFreeExchangeOrder(Long userId);
 }

+ 25 - 13
src/main/java/com/izouma/walkchina/service/CoinService.java

@@ -3,10 +3,12 @@ package com.izouma.walkchina.service;
 import com.izouma.walkchina.constant.AppConstants;
 import com.izouma.walkchina.constant.Strings;
 import com.izouma.walkchina.domain.Message;
+import com.izouma.walkchina.domain.RewardRecord;
 import com.izouma.walkchina.domain.UserCoinRecord;
 import com.izouma.walkchina.domain.UserInfo;
 import com.izouma.walkchina.exception.ServiceException;
 import com.izouma.walkchina.repo.MessageRepository;
+import com.izouma.walkchina.repo.RewardRecordRepository;
 import com.izouma.walkchina.repo.UserCoinRecordRepository;
 import com.izouma.walkchina.repo.UserInfoRepository;
 import lombok.extern.slf4j.Slf4j;
@@ -17,6 +19,7 @@ import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
+import java.time.LocalDate;
 import java.util.Optional;
 
 @Slf4j
@@ -29,6 +32,8 @@ public class CoinService {
     private UserInfoRepository       userInfoRepository;
     @Autowired
     private MessageRepository        messageRepository;
+    @Autowired
+    private RewardRecordRepository   rewardRecordRepository;
 
     public void reward(Long userId, Long target, BigDecimal amount) {
         UserInfo userInfo = userInfoRepository.findById(userId).orElseThrow(new ServiceException("用户不存在"));
@@ -40,7 +45,14 @@ public class CoinService {
         balanceChange(userInfo, amount.negate(), AppConstants.CoinRecordType.REWARD, Strings.REMARK_REWARD, null);
 
         balanceChange(targetUserInfo, amount, AppConstants.CoinRecordType.BE_REWARD, Strings.REMARK_BE_REWARD,
-                String.format(Strings.MSG_BE_REWARD, userInfo.getNickname(), amount));
+                      String.format(Strings.MSG_BE_REWARD, userInfo.getNickname(), amount));
+
+        rewardRecordRepository.save(RewardRecord.builder()
+                                                .userId(userId)
+                                                .targetUserId(target)
+                                                .amount(amount)
+                                                .date(LocalDate.now())
+                                                .build());
     }
 
     public void balanceChange(UserInfo userInfo, BigDecimal amount, int type, String remark, String message) {
@@ -50,22 +62,22 @@ public class CoinService {
         userInfoRepository.save(userInfo);
 
         UserCoinRecord record = UserCoinRecord.builder()
-                .userId(userInfo.getId())
-                .modify(amount)
-                .balance(balance)
-                .type(type)
-                .remark(remark)
-                .build();
+                                              .userId(userInfo.getId())
+                                              .modify(amount)
+                                              .balance(balance)
+                                              .type(type)
+                                              .remark(remark)
+                                              .build();
         userCoinRecordRepository.save(record);
 
         if (StringUtils.isNotEmpty(message)) {
             Message msg = Message.builder()
-                    .userId(userInfo.getId())
-                    .content(message)
-                    .type(AppConstants.MessageType.NORMAL)
-                    .isRead(false)
-                    .active(true)
-                    .build();
+                                 .userId(userInfo.getId())
+                                 .content(message)
+                                 .type(AppConstants.MessageType.NORMAL)
+                                 .isRead(false)
+                                 .active(true)
+                                 .build();
             messageRepository.save(msg);
         }
     }

+ 11 - 4
src/main/java/com/izouma/walkchina/service/FriendInfoService.java

@@ -1,11 +1,14 @@
 package com.izouma.walkchina.service;
 
 import com.izouma.walkchina.domain.FriendInfo;
+import com.izouma.walkchina.dto.UserDTO;
 import com.izouma.walkchina.repo.FriendInfoRepository;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
+
 @Slf4j
 @Service
 public class FriendInfoService {
@@ -16,15 +19,19 @@ public class FriendInfoService {
     public void saveFriend(Long userId1, Long userId2) {
         try {
             friendInfoRepository.save(FriendInfo.builder()
-                    .userId(userId1)
-                    .friendId(userId2).build());
+                                                .userId(userId1)
+                                                .friendId(userId2).build());
         } catch (Exception ignored) {
         }
         try {
             friendInfoRepository.save(FriendInfo.builder()
-                    .userId(userId2)
-                    .friendId(userId1).build());
+                                                .userId(userId2)
+                                                .friendId(userId1).build());
         } catch (Exception ignored) {
         }
     }
+
+    public List<UserDTO> findUserFriend(Long userId) {
+        return friendInfoRepository.findUserFriend(userId);
+    }
 }

+ 21 - 12
src/main/java/com/izouma/walkchina/service/JourneyService.java

@@ -130,6 +130,7 @@ public class JourneyService {
 
     private void createStageAward(UserJourney userJourney, JourneyStage journeyStage, Long needSteps) {
         StageAward latest = stageAwardRepository.findLatest(userJourney.getUserId(), journeyStage.getId());
+        UserInfo userInfo = userInfoRepository.findById(userJourney.getUserId()).orElseThrow(new ServiceException("用户不存在"));
 
         if (latest != null && latest.getNeedSteps() > journeyStage.getCurrentSteps()) {
             return;
@@ -142,13 +143,14 @@ public class JourneyService {
 
         List<Double> extracted = mapService.extractPolyline(journeyStage.getPolyline());
         int end = mapService.endPoint(journeyStage.getRouteSteps(), journeyStage.getDistance() * progress);
+        BigDecimal coin = Optional.ofNullable(userInfo.getPrice()).orElse(AppConstants.MIN_PRICE).multiply(BigDecimal.valueOf(10));
         StageAward stageAward = StageAward.builder()
                                           .userId(userJourney.getUserId())
                                           .stageId(journeyStage.getId())
                                           .latitude(extracted.get(end - 1))
                                           .longitude(extracted.get(end))
                                           .journeyId(userJourney.getId())
-                                          .coin(new BigDecimal(AppConstants.STAGE_AWARD))
+                                          .coin(coin)
                                           .needProgress(progress)
                                           .needSteps((long) (progress * journeyStage
                                               .getDistance() / AppConstants.STEP_TO_DISTANCE_RATE))
@@ -197,7 +199,7 @@ public class JourneyService {
                                      latestStage.getOrigin().getName(),
                                      latestStage.getDestination().getName(),
                                      (todayWalkData != null ? todayWalkData.getSteps() : 0),
-                                     DAYS.between(latestStage.getCreatedAt(), LocalDateTime.now()) + 1,
+                                     DAYS.between(latestStage.getCreatedAt().toLocalDate(), LocalDate.now()) + 1,
                                      steps,
                                      Math.round(progress * 100)))
                                  .type(AppConstants.MessageType.PROGRESS_UPDATE)
@@ -215,25 +217,32 @@ public class JourneyService {
         if (latestStage.getProgress() < 1) {
             throw new ServiceException("当前阶段未完成");
         }
-        LocalDateTime now = LocalDateTime.now();
-        latestStage.setFinishAt(now);
-        Long mySteps = walkDataRepository.sumUserWalkSteps(userId, latestStage.getCreatedAt(), now).orElse(0L);
-        Long teamSteps = walkDataRepository.sumTeamWalkSteps(userId, latestStage.getCreatedAt(), now).orElse(0L);
+
+        LocalDateTime finishAt = LocalDateTime.now();
+        if (latestStage.getFinishAt() == null) {
+            coinService.balanceChange(userInfo, latestStage
+                                          .getAward(), AppConstants.CoinRecordType.ARRIVAL, Strings.REMARK_ARRIVAL,
+                                      String.format(Strings.MSG_ARRIVAL, latestStage.getDestination().getName(), latestStage.getAward()));
+            userInfo.setWalkCities(Optional.ofNullable(userInfo.getWalkCities()).orElse(0) + 1);
+            userInfoService.updateUserLevel(userInfo);
+
+            latestStage.setFinishAt(finishAt);
+            journeyStageRepository.save(latestStage);
+        } else {
+            finishAt = latestStage.getFinishAt();
+        }
+        Long mySteps = walkDataRepository.sumUserWalkSteps(userId, latestStage.getCreatedAt(), finishAt).orElse(0L);
+        Long teamSteps = walkDataRepository.sumTeamWalkSteps(userId, latestStage.getCreatedAt(), finishAt).orElse(0L);
         JourneyStageStat stageStat = JourneyStageStat.builder()
                                                      .award(latestStage.getAward())
                                                      .mySteps(mySteps)
                                                      .teamSteps(teamSteps)
                                                      .days((int) DAYS
-                                                         .between(latestStage.getCreatedAt(), LocalDateTime.now()) + 1)
+                                                         .between(latestStage.getCreatedAt().toLocalDate(), LocalDate.now()) + 1)
                                                      .origin(latestStage.getOrigin())
                                                      .destination(latestStage.getDestination())
                                                      .totalSteps(mySteps + teamSteps)
                                                      .build();
-
-        coinService.balanceChange(userInfo, latestStage
-            .getAward(), AppConstants.CoinRecordType.ARRIVAL, Strings.REMARK_ARRIVAL, null);
-        userInfo.setWalkCities(Optional.ofNullable(userInfo.getWalkCities()).orElse(0) + 1);
-        userInfoService.updateUserLevel(userInfo);
         return stageStat;
     }
 }

+ 17 - 15
src/main/java/com/izouma/walkchina/service/MapService.java

@@ -15,6 +15,7 @@ import com.izouma.walkchina.dto.webservice.DirectionResponse;
 import com.izouma.walkchina.dto.webservice.RouteStep;
 import com.izouma.walkchina.exception.ServiceException;
 import com.izouma.walkchina.repo.*;
+import com.izouma.walkchina.utils.SecurityUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -23,7 +24,7 @@ import org.springframework.stereotype.Service;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.time.LocalDateTime;
+import java.time.LocalDate;
 import java.util.*;
 
 import static java.time.temporal.ChronoUnit.DAYS;
@@ -49,11 +50,11 @@ public class MapService {
     public List<City> citiesInRegion(MapRegion mapRegion) {
         Specification<City> specification = (Specification<City>) (root, criteriaQuery, criteriaBuilder) -> {
             return criteriaBuilder.and(criteriaBuilder.between(root.get("longitude"),
-                mapRegion.getSouthwest().getLongitude(),
-                mapRegion.getNortheast().getLongitude()),
-                criteriaBuilder.between(root.get("latitude"),
-                    mapRegion.getSouthwest().getLatitude(),
-                    mapRegion.getNortheast().getLatitude()));
+                                                               mapRegion.getSouthwest().getLongitude(),
+                                                               mapRegion.getNortheast().getLongitude()),
+                                       criteriaBuilder.between(root.get("latitude"),
+                                                               mapRegion.getSouthwest().getLatitude(),
+                                                               mapRegion.getNortheast().getLatitude()));
         };
         return cityRepository.findAll(specification);
     }
@@ -64,9 +65,9 @@ public class MapService {
 
     public Collection<UserMarker> usersInRegion(Long userId, MapRegion mapRegion) {
         List<UserMarker> list = userInfoRepository.findInRegion(userId, mapRegion.getSouthwest().getLatitude(), mapRegion.getSouthwest().getLongitude(),
-            mapRegion.getNortheast().getLongitude(), mapRegion.getNortheast().getLongitude());
+                                                                mapRegion.getNortheast().getLongitude(), mapRegion.getNortheast().getLongitude());
         for (UserMarker userMarker : list) {
-            userMarker.setCanSteal(stageAwardRepository.countAllByUserIdAndReceivedEquals(userMarker.getUserId(), true) > 0);
+            userMarker.setCanSteal(stageAwardRepository.countAllByUserIdAndReceivedEquals(userMarker.getUserId(), false) > 1);
             userMarker.setIcon(ossDomain + "/marker/user/" + userMarker.getUserId() + ".png");
         }
         return list;
@@ -88,22 +89,22 @@ public class MapService {
         City destination = Optional.ofNullable(userJourney.getDestination()).orElseThrow(new ServiceException("无记录"));
         UserInfo userInfo = userInfoRepository.findById(userId).orElseThrow(new ServiceException("用户不存在"));
         UserMap userMap = UserMap.builder()
-            .polyline(minifyPolyline(userJourney.getPolyline()))
-            .origin(origin)
-            .destination(destination)
-            .build();
+                                 .polyline(minifyPolyline(userJourney.getPolyline()))
+                                 .origin(origin)
+                                 .destination(destination)
+                                 .build();
         List<Double> progressPolyline = new ArrayList<>();
         List<JourneyStage> stages = journeyStageRepository.findAllByJourneyIdOrderById(userJourney.getId());
         for (int i = 0; i < stages.size() - 1; i++) {
             progressPolyline.addAll(stages.get(i).getPolyline());
         }
         JourneyStage latestStage = stages.get(stages.size() - 1);
-
+        userMap.setDestination(latestStage.getDestination());
         userMap.setNearOrigin(latestStage.getOrigin());
         userMap.setProgress(latestStage.getProgress());
         userMap.setCurrentSteps(latestStage.getCurrentSteps());
         userMap.setStageAwards(stageAwardRepository.findAllByUserIdAndStageId(userId, latestStage.getId()));
-        userMap.setDays((int) DAYS.between(latestStage.getCreatedAt(), LocalDateTime.now()) + 1);
+        userMap.setDays((int) DAYS.between(latestStage.getCreatedAt().toLocalDate(), LocalDate.now()) + 1);
 
         List<RouteStep> steps = latestStage.getRouteSteps();
         // int distance = 0;
@@ -206,7 +207,8 @@ public class MapService {
         }
         Integer distance = response.getResult().getRoutes().get(0).getDistance();
         Long step = Math.round(distance / AppConstants.STEP_TO_DISTANCE_RATE);
-        Long award = Math.round(distance * AppConstants.DISTANCE_TO_COIN_RATE);
+        UserInfo userInfo = userInfoRepository.findById(SecurityUtils.getAuthenticatedUser().getId()).orElseThrow(new ServiceException("用户不存在"));
+        BigDecimal award = userInfo.getPrice();
         Map<String, Object> map = new HashMap<>();
         map.put("step", step);
         map.put("award", award);

+ 31 - 5
src/main/java/com/izouma/walkchina/service/OrderService.java

@@ -9,9 +9,9 @@ import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.kevinsawicki.http.HttpRequest;
 import com.izouma.walkchina.constant.AppConstants;
 import com.izouma.walkchina.constant.OrderStatus;
+import com.izouma.walkchina.constant.Strings;
 import com.izouma.walkchina.domain.*;
 import com.izouma.walkchina.dto.LogisticsInfo;
-import com.izouma.walkchina.dto.Result;
 import com.izouma.walkchina.exception.ServiceException;
 import com.izouma.walkchina.repo.*;
 import com.izouma.walkchina.utils.CommonUtils;
@@ -58,6 +58,8 @@ public class OrderService {
     private String                   wxNotifyUrl;
     @Autowired
     private ProductCommentRepository productCommentRepository;
+    @Autowired
+    private CoinService              coinService;
 
     public Page<UserOrder> all(Pageable pageable) {
         PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by(Sort.Direction.DESC, "createdAt"));
@@ -79,6 +81,13 @@ public class OrderService {
 
     public WxPayMpOrderResult makeOrder(Long userId, Long productId, Long specId, Integer payMode, Long addressId, Integer amount, String remark) {
         Product product = productRepository.findById(productId).orElseThrow(new ServiceException("商品不存在"));
+
+        if (product.getFreeExchange()) {
+            if (!canFreeExchange(userId)) {
+                throw new ServiceException("免费兑换次数已用完");
+            }
+        }
+
         ProductSpec spec = product.getSpecs().stream()
                                   .filter(productSpec -> specId.equals(productSpec.getId()))
                                   .findFirst().orElseThrow(new ServiceException("规格不存在"));
@@ -91,7 +100,7 @@ public class OrderService {
         } else {
             money = spec.getOriginalPrice();
         }
-        if (Optional.ofNullable(userInfo.getCoin()).orElse(BigDecimal.ZERO).multiply(BigDecimal.valueOf(amount)).compareTo(coin) < 0) {
+        if (Optional.ofNullable(userInfo.getCoin()).orElse(BigDecimal.ZERO).compareTo(coin.multiply(BigDecimal.valueOf(amount))) < 0) {
             throw new ServiceException("商城币不足", AppConstants.ResultCode.COIN_INSUFFICIENT);
         }
         UserAddress userAddress = userAddressRepository.findById(addressId).orElseThrow(new ServiceException("地址不存在"));
@@ -114,7 +123,7 @@ public class OrderService {
                                        .build();
         userOrderRepository.save(userOrder);
 
-        return payOrder(userOrder, userInfo.getOpenId(), false);
+        return payOrder(userOrder, userInfo.getOpenId());
     }
 
     public WxPayMpOrderResult pay(String id) {
@@ -126,10 +135,22 @@ public class OrderService {
             throw new ServiceException("支付失败");
         }
         UserInfo userInfo = userInfoRepository.findById(userOrder.getUserId()).orElseThrow(new ServiceException("用户不存在"));
-        return payOrder(userOrder, userInfo.getOpenId(), true);
+        return payOrder(userOrder, userInfo.getOpenId());
     }
 
-    public WxPayMpOrderResult payOrder(UserOrder userOrder, String openId, boolean closeOld) {
+    public WxPayMpOrderResult payOrder(UserOrder userOrder, String openId) {
+        if (userOrder.getMoney().compareTo(BigDecimal.ZERO) == 0) {
+            UserInfo userInfo = userInfoRepository.findById(userOrder.getUserId()).orElseThrow(new ServiceException("用户不存在"));
+            if (Optional.ofNullable(userInfo.getCoin()).orElse(BigDecimal.ZERO).compareTo(userOrder.getCoin()) < 0) {
+                throw new ServiceException("商城币不足", AppConstants.ResultCode.COIN_INSUFFICIENT);
+            }
+            coinService.balanceChange(userInfo, userOrder.getCoin(), AppConstants.CoinRecordType.BUY, Strings.REMARK_BUY, null);
+
+            userOrder.setStatus(OrderStatus.PAID);
+            userOrderRepository.save(userOrder);
+            return null;
+        }
+
         WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
         request.setBody(userOrder.getProduct().getName());
         // request.setOutTradeNo(userOrder.getId().toString());
@@ -252,4 +273,9 @@ public class OrderService {
         userOrder.setStatus(OrderStatus.RATED);
         userOrderRepository.save(userOrder);
     }
+
+    public boolean canFreeExchange(Long userId) {
+        List<UserOrder> list = userOrderRepository.findFreeExchangeOrder(userId);
+        return list.size() == 0;
+    }
 }

+ 43 - 17
src/main/java/com/izouma/walkchina/service/TeamService.java

@@ -5,10 +5,7 @@ import cn.binarywang.wx.miniapp.bean.WxMaTemplateData;
 import cn.binarywang.wx.miniapp.bean.WxMaTemplateMessage;
 import com.izouma.walkchina.constant.AppConstants;
 import com.izouma.walkchina.constant.Strings;
-import com.izouma.walkchina.domain.Message;
-import com.izouma.walkchina.domain.TeamMember;
-import com.izouma.walkchina.domain.UserInfo;
-import com.izouma.walkchina.domain.WalkData;
+import com.izouma.walkchina.domain.*;
 import com.izouma.walkchina.dto.UserDTO;
 import com.izouma.walkchina.exception.ServiceException;
 import com.izouma.walkchina.repo.*;
@@ -16,6 +13,7 @@ import com.izouma.walkchina.utils.ImageUtils;
 import lombok.extern.slf4j.Slf4j;
 import me.chanjar.weixin.common.error.WxErrorException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
 import org.springframework.stereotype.Service;
 
 import javax.imageio.ImageIO;
@@ -36,25 +34,28 @@ import static java.time.temporal.ChronoUnit.DAYS;
 @Slf4j
 public class TeamService {
     @Autowired
-    private TeamMemberRepository teamMemberRepository;
+    private TeamMemberRepository   teamMemberRepository;
     @Autowired
-    private UserInfoRepository   userInfoRepository;
+    private UserInfoRepository     userInfoRepository;
     @Autowired
-    private MessageRepository    messageRepository;
+    private MessageRepository      messageRepository;
     @Autowired
-    private WalkDataRepository   walkDataRepository;
+    private WalkDataRepository     walkDataRepository;
     @Autowired
-    private FriendInfoRepository friendInfoRepository;
+    private FriendInfoService      friendInfoService;
     @Autowired
-    private CoinService          coinService;
+    private CoinService            coinService;
     @Autowired
-    private WxMaService          wxMaService;
+    private WxMaService            wxMaService;
     @Autowired
-    private FormIdService        formIdService;
+    private FormIdService          formIdService;
     @Autowired
-    private UserInfoService      userInfoService;
+    private UserInfoService        userInfoService;
+    @Autowired
+    private RewardRecordRepository rewardRecordRepository;
 
     public void hire(Long userId, Long target) {
+        friendInfoService.saveFriend(userId, target);
         LocalDateTime now = LocalDateTime.now();
         UserInfo userInfo = userInfoRepository.findById(userId).orElseThrow(new ServiceException("用户不存在"));
         UserInfo targetUserInfo = userInfoRepository.findById(target).orElseThrow(new ServiceException("用户不存在"));
@@ -81,7 +82,7 @@ public class TeamService {
             messageRepository.save(grabMsg);
         }
 
-        BigDecimal price = Optional.ofNullable(userInfo.getPrice()).orElse(AppConstants.MIN_PRICE);
+        BigDecimal price = Optional.ofNullable(targetUserInfo.getPrice()).orElse(AppConstants.MIN_PRICE);
         BigDecimal balance = Optional.ofNullable(userInfo.getCoin()).orElse(BigDecimal.ZERO);
         if (balance.compareTo(price) < 0) {
             throw new ServiceException("雇佣失败,商城币不足");
@@ -103,6 +104,15 @@ public class TeamService {
         teamMemberRepository.save(newTeamMember);
 
         userInfoService.updateUserPrice(targetUserInfo);
+
+        Message grabMsg = Message.builder()
+                                 .userId(userId)
+                                 .type(AppConstants.MessageType.NORMAL)
+                                 .content(String.format(Strings.MSG_HIRE, price, targetUserInfo.getNickname()))
+                                 .active(true)
+                                 .isRead(false)
+                                 .build();
+        messageRepository.save(grabMsg);
     }
 
     public boolean canHire(Long userId, Long target) {
@@ -116,7 +126,7 @@ public class TeamService {
         return true;
     }
 
-    public List<UserDTO> userTeam(Long userId) {
+    public List<UserDTO> userTeam(Long userId, boolean queryReward) {
         List<UserDTO> list = teamMemberRepository.findUserTeam(userId);
         for (UserDTO userDTO : list) {
             WalkData walkData = walkDataRepository.findByUserIdAndDate(userDTO.getUserId(), LocalDate.now());
@@ -126,13 +136,21 @@ public class TeamService {
                 userDTO.setTodaySteps(0L);
                 userDTO.setShouldWake(true);
             }
+            if (queryReward) {
+                LocalDate now = LocalDate.now();
+                RewardRecord rewardRecord = new RewardRecord();
+                rewardRecord.setUserId(userId);
+                rewardRecord.setTargetUserId(userDTO.getUserId());
+                rewardRecord.setDate(now);
+                userDTO.setCanReward(rewardRecordRepository.findAll(Example.of(rewardRecord)).size() == 0);
+            }
         }
         return list;
     }
 
     public List<UserDTO> userFriend(Long userId) {
         LocalDate now = LocalDate.now();
-        List<UserDTO> list = friendInfoRepository.findUserFriend(userId);
+        List<UserDTO> list = friendInfoService.findUserFriend(userId);
         for (UserDTO userDTO : list) {
             WalkData walkData = walkDataRepository.findByUserIdAndDate(userDTO.getUserId(), now);
             if (walkData != null) {
@@ -190,7 +208,7 @@ public class TeamService {
         ImageUtils.drawCenteredString(g, userInfo.getNickname(), new Rectangle(0, 230 * 3, shareImg.getWidth(), 20 * 3), nicknameFont);
 
         Font walkDataFont = pingFangMedium.deriveFont(17f * 3);
-        ImageUtils.drawCenteredString(g, DAYS.between(userInfo.getCreatedAt(), LocalDateTime.now()) + 1 + "", new Rectangle(20 * 3, 295 * 3, 295 * 3 / 2, 24 * 3), walkDataFont);
+        ImageUtils.drawCenteredString(g, DAYS.between(userInfo.getCreatedAt().toLocalDate(), LocalDate.now()) + 1 + "", new Rectangle(20 * 3, 295 * 3, 295 * 3 / 2, 24 * 3), walkDataFont);
         ImageUtils.drawCenteredString(g, String.valueOf(userInfo.getWalkCities()), new Rectangle(295 * 3 / 2 + 20 * 3, 295 * 3, 295 * 3 / 2, 24 * 3), walkDataFont);
         ImageUtils.drawCenteredString(g, String.valueOf(userInfo.getTotalSteps()), new Rectangle(20 * 3, 353 * 3, 295 * 3 / 2, 24 * 3), walkDataFont);
         ImageUtils.drawCenteredString(g, coinService.totalAward(userId).toString(), new Rectangle(295 * 3 / 2 + 20 * 3, 353 * 3, 295 * 3 / 2, 24 * 3), walkDataFont);
@@ -257,5 +275,13 @@ public class TeamService {
         teamMemberRepository.save(newTeamMember);
 
         userInfoService.updateUserPrice(targetUserInfo);
+
+        messageRepository.save(Message.builder()
+                                      .userId(userId)
+                                      .content(String.format(Strings.MSG_RECRUIT, targetUserInfo.getNickname()))
+                                      .type(AppConstants.MessageType.NORMAL)
+                                      .active(true)
+                                      .isRead(false)
+                                      .build());
     }
 }

+ 2 - 1
src/main/java/com/izouma/walkchina/service/UserInfoService.java

@@ -101,7 +101,7 @@ public class UserInfoService {
     }
 
     public UserInfo getMiniAppUserInfo(String sessionKey, String rawData, String signature,
-        String encryptedData, String iv) {
+                                       String encryptedData, String iv) {
         // 用户信息校验
         if (!wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature)) {
             throw new ServiceException("获取用户信息失败");
@@ -204,6 +204,7 @@ public class UserInfoService {
                             .build();
     }
 
+
     public void acceptInvite(Long userId, Long inviter, boolean recruit) {
         if (userId.equals(inviter)) {
             return;

+ 2 - 2
src/main/java/com/izouma/walkchina/web/CoinController.java

@@ -16,8 +16,8 @@ public class CoinController {
     private CoinService coinService;
 
     @PostMapping("/reward")
-    public Result reward(@RequestParam("userId") Long userId) {
-        coinService.reward(SecurityUtils.getAuthenticatedUser().getId(), userId, BigDecimal.valueOf(10));
+    public Result reward(@RequestParam Long userId, @RequestParam BigDecimal amount) {
+        coinService.reward(SecurityUtils.getAuthenticatedUser().getId(), userId, amount);
         return Result.ok();
     }
 

+ 10 - 11
src/main/java/com/izouma/walkchina/web/OrderController.java

@@ -1,27 +1,16 @@
 package com.izouma.walkchina.web;
 
-import com.github.kevinsawicki.http.HttpRequest;
 import com.izouma.walkchina.domain.ProductComment;
 import com.izouma.walkchina.domain.UserOrder;
-import com.izouma.walkchina.dto.LogisticsInfo;
 import com.izouma.walkchina.dto.Result;
 import com.izouma.walkchina.dto.UserOrderParam;
 import com.izouma.walkchina.service.OrderService;
 import com.izouma.walkchina.utils.SecurityUtils;
-import org.apache.commons.text.StringEscapeUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.configurationprocessor.json.JSONArray;
-import org.springframework.boot.configurationprocessor.json.JSONObject;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.web.bind.annotation.*;
 
-import javax.servlet.http.HttpServletResponse;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.List;
-
 @RestController
 @RequestMapping("/order")
 public class OrderController {
@@ -93,4 +82,14 @@ public class OrderController {
         orderService.comment(productComment);
         return Result.ok();
     }
+
+    @GetMapping("/canFreeExchange")
+    public Result canFreeExchange() {
+        boolean canFreeExchange = orderService.canFreeExchange(SecurityUtils.getAuthenticatedUser().getId());
+        if (canFreeExchange) {
+            return Result.ok();
+        } else {
+            return Result.error();
+        }
+    }
 }

+ 2 - 2
src/main/java/com/izouma/walkchina/web/TeamController.java

@@ -42,11 +42,11 @@ public class TeamController {
     }
 
     @GetMapping("/userTeam")
-    public Result userTeam(Long userId) {
+    public Result userTeam(Long userId, @RequestParam(defaultValue = "false") Boolean queryReward) {
         if (userId == null) {
             userId = SecurityUtils.getAuthenticatedUser().getId();
         }
-        return Result.ok(teamService.userTeam(userId));
+        return Result.ok(teamService.userTeam(userId, queryReward));
     }
 
     @GetMapping("/userFriend")