licailing преди 5 години
родител
ревизия
7640857133
променени са 44 файла, в които са добавени 1578 реда и са изтрити 282 реда
  1. 15 14
      src/main/java/com/izouma/jiashanxia/domain/Attractions.java
  2. 0 30
      src/main/java/com/izouma/jiashanxia/domain/AttractionsContent.java
  3. 0 22
      src/main/java/com/izouma/jiashanxia/domain/BigCustom.java
  4. 0 30
      src/main/java/com/izouma/jiashanxia/domain/Coach.java
  5. 0 20
      src/main/java/com/izouma/jiashanxia/domain/CoachPrice.java
  6. 0 65
      src/main/java/com/izouma/jiashanxia/domain/CompanyWithdraw.java
  7. 16 9
      src/main/java/com/izouma/jiashanxia/domain/GoodsInfo.java
  8. 0 23
      src/main/java/com/izouma/jiashanxia/domain/GroupCustom.java
  9. 0 14
      src/main/java/com/izouma/jiashanxia/domain/GroupCustomContent.java
  10. 44 0
      src/main/java/com/izouma/jiashanxia/domain/OrderRefund.java
  11. 2 0
      src/main/java/com/izouma/jiashanxia/domain/Package.java
  12. 0 23
      src/main/java/com/izouma/jiashanxia/domain/PromoteRecord.java
  13. 4 1
      src/main/java/com/izouma/jiashanxia/domain/UserPackage.java
  14. 3 0
      src/main/java/com/izouma/jiashanxia/domain/UserPackageFlow.java
  15. 32 0
      src/main/java/com/izouma/jiashanxia/domain/UserPackagePeriod.java
  16. 0 6
      src/main/java/com/izouma/jiashanxia/domain/Withdraw.java
  17. 3 0
      src/main/java/com/izouma/jiashanxia/dto/WriteOffSaveVO.java
  18. 2 1
      src/main/java/com/izouma/jiashanxia/enums/OrderInfoStatus.java
  19. 18 0
      src/main/java/com/izouma/jiashanxia/enums/RefundStatus.java
  20. 16 0
      src/main/java/com/izouma/jiashanxia/repo/AttractionsRepo.java
  21. 2 0
      src/main/java/com/izouma/jiashanxia/repo/OrderInfoRepo.java
  22. 22 0
      src/main/java/com/izouma/jiashanxia/repo/OrderRefundRepo.java
  23. 2 0
      src/main/java/com/izouma/jiashanxia/repo/PackageRepo.java
  24. 16 0
      src/main/java/com/izouma/jiashanxia/repo/UserPackagePeriodRepo.java
  25. 4 4
      src/main/java/com/izouma/jiashanxia/repo/UserPackageRepo.java
  26. 20 0
      src/main/java/com/izouma/jiashanxia/service/AttractionsService.java
  27. 2 6
      src/main/java/com/izouma/jiashanxia/service/OrderInfoService.java
  28. 170 0
      src/main/java/com/izouma/jiashanxia/service/OrderRefundService.java
  29. 32 10
      src/main/java/com/izouma/jiashanxia/service/UserPackageFlowService.java
  30. 20 0
      src/main/java/com/izouma/jiashanxia/service/UserPackagePeriodService.java
  31. 60 0
      src/main/java/com/izouma/jiashanxia/web/AttractionsController.java
  32. 60 0
      src/main/java/com/izouma/jiashanxia/web/OrderRefundController.java
  33. 60 0
      src/main/java/com/izouma/jiashanxia/web/UserPackagePeriodController.java
  34. 7 4
      src/main/java/com/izouma/jiashanxia/web/WxController.java
  35. 1 0
      src/main/resources/genjson/Attractions.json
  36. 1 0
      src/main/resources/genjson/OrderRefund.json
  37. 1 0
      src/main/resources/genjson/UserPackagePeriod.json
  38. 50 0
      src/main/vue/src/router.js
  39. 112 0
      src/main/vue/src/views/AttractionsEdit.vue
  40. 169 0
      src/main/vue/src/views/AttractionsList.vue
  41. 138 0
      src/main/vue/src/views/OrderRefundEdit.vue
  42. 176 0
      src/main/vue/src/views/OrderRefundList.vue
  43. 139 0
      src/main/vue/src/views/UserPackagePeriodEdit.vue
  44. 159 0
      src/main/vue/src/views/UserPackagePeriodList.vue

+ 15 - 14
src/main/java/com/izouma/jiashanxia/domain/Attractions.java

@@ -1,5 +1,6 @@
 package com.izouma.jiashanxia.domain;
 package com.izouma.jiashanxia.domain;
 
 
+import com.izouma.jiashanxia.converter.StringArrayConverter;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
@@ -7,31 +8,31 @@ import lombok.Builder;
 import lombok.Data;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.NoArgsConstructor;
 
 
+import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.Entity;
+import java.util.List;
+
 @Data
 @Data
 @AllArgsConstructor
 @AllArgsConstructor
 @NoArgsConstructor
 @NoArgsConstructor
 @Builder
 @Builder
-@ApiModel("景区内容管理")
+@Entity
+@ApiModel("景区管理")
 public class Attractions extends BaseEntity {
 public class Attractions extends BaseEntity {
-    @ApiModelProperty(name = "名称")
+    @ApiModelProperty(value = "名称")
     private String name;
     private String name;
 
 
-    @ApiModelProperty(name = "介绍")
+    @ApiModelProperty(value = "介绍")
     private String introduction;
     private String introduction;
 
 
-    @ApiModelProperty(name = "地址")
+    @ApiModelProperty(value = "地址")
     private String address;
     private String address;
 
 
-    @ApiModelProperty(name = "电话")
+    @ApiModelProperty(value = "电话")
     private String phone;
     private String phone;
 
 
-    /*
-    景区 AA BB
-    游玩
-    餐饮
-    住宿
-
-    日期由谁决定?团建选择
-    日期区间
-     */
+    @Convert(converter = StringArrayConverter.class)
+    @Column(columnDefinition = "TEXT")
+    private List<String> img;
 }
 }

+ 0 - 30
src/main/java/com/izouma/jiashanxia/domain/AttractionsContent.java

@@ -1,30 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import com.izouma.jiashanxia.enums.ItemType;
-import io.swagger.annotations.ApiModel;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import java.math.BigDecimal;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@Builder
-@ApiModel("景区内项目")
-public class AttractionsContent extends BaseEntity {
-    private Long attractionsId;
-
-    @Enumerated(EnumType.STRING)
-    private ItemType type;
-
-    private String name;
-
-    private BigDecimal price;
-
-    private String remark;
-}

+ 0 - 22
src/main/java/com/izouma/jiashanxia/domain/BigCustom.java

@@ -1,22 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-
-import java.time.LocalDateTime;
-
-@ApiModel(value = "大团定制表单")
-public class BigCustom {
-    private String name;
-
-    private String phone;
-
-    private String email;
-
-    @ApiModelProperty(value = "人数")
-    private String numberOfPeople;
-
-    private LocalDateTime startTime;
-
-    private LocalDateTime endTime;
-}

+ 0 - 30
src/main/java/com/izouma/jiashanxia/domain/Coach.java

@@ -1,30 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import com.izouma.jiashanxia.converter.StringArrayConverter;
-import io.swagger.annotations.ApiModel;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import javax.persistence.Convert;
-import java.util.List;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@Builder
-@ApiModel(value = "教练表")
-public class Coach extends BaseEntity {
-
-    private String name;
-
-    private String avatar;
-
-    @Convert(converter = StringArrayConverter.class)
-    private List<String> img;
-
-    private String introduction;
-
-    private int level;
-}

+ 0 - 20
src/main/java/com/izouma/jiashanxia/domain/CoachPrice.java

@@ -1,20 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import io.swagger.annotations.ApiModel;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.math.BigDecimal;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@Builder
-@ApiModel("教练级别价格表")
-public class CoachPrice extends BaseEntity {
-    private int level;
-
-    private BigDecimal price;
-}

+ 0 - 65
src/main/java/com/izouma/jiashanxia/domain/CompanyWithdraw.java

@@ -1,65 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import com.alibaba.excel.annotation.ExcelIgnore;
-import com.izouma.jiashanxia.annotations.Searchable;
-import com.izouma.jiashanxia.enums.PayMethod;
-import com.izouma.jiashanxia.enums.WithdrawStatus;
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-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 javax.persistence.*;
-import java.io.Serializable;
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-//@Entity
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@ApiModel(value = "企业提现申请表")
-@Where(clause = "del = 0")
-public class CompanyWithdraw extends BaseEntity implements Serializable {
-
-    @ApiModelProperty(value = "企业id", name = "companyId")
-    private Long companyId;
-
-    @ApiModelProperty(value = "提现金额", name = "amount")
-    private BigDecimal amount;
-
-    @ApiModelProperty(value = "剩余金额", name = "balance")
-    private BigDecimal balance;
-
-    @ApiModelProperty(value = "审核时间", name = "auditTime")
-    private LocalDateTime auditTime;
-
-    @Enumerated(EnumType.STRING)
-    @ApiModelProperty(value = "提现方式", name = "payMethod")
-    private PayMethod payMethod;
-
-    @Enumerated(EnumType.STRING)
-    @ApiModelProperty(value = "提现状态", name = "status")
-    private WithdrawStatus status;
-
-    @Searchable
-    @ApiModelProperty(value = "账号", name = "account")
-    private String account;
-
-    @Searchable
-    @ApiModelProperty(value = "真实姓名", name = "realName")
-    private String realName;
-
-    @ManyToOne(fetch = FetchType.LAZY)
-    @JoinColumn(name = "companyId", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
-    @NotFound(action = NotFoundAction.IGNORE)
-    @ExcelIgnore
-    private Company company;
-
-}

+ 16 - 9
src/main/java/com/izouma/jiashanxia/domain/GoodsInfo.java

@@ -1,5 +1,7 @@
 package com.izouma.jiashanxia.domain;
 package com.izouma.jiashanxia.domain;
 
 
+import com.izouma.jiashanxia.enums.ItemType;
+import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Builder;
@@ -16,10 +18,11 @@ import java.math.BigDecimal;
 @Builder
 @Builder
 @Entity
 @Entity
 @Where(clause = "del = 0")
 @Where(clause = "del = 0")
-public class GoodsInfo {
-    @Id
-    @GeneratedValue(strategy = GenerationType.AUTO)
-    private Long id;
+@ApiModel("景区内商品")
+public class GoodsInfo extends BaseEntity {
+//    @Id
+//    @GeneratedValue(strategy = GenerationType.AUTO)
+//    private Long id;
 
 
     private String name;
     private String name;
 
 
@@ -29,12 +32,16 @@ public class GoodsInfo {
     @ApiModelProperty(value = "单位", name = "unit")
     @ApiModelProperty(value = "单位", name = "unit")
     private String unit;
     private String unit;
 
 
-//    @ApiModelProperty(value = "是否下架", name = "isOffShelf")
-//    private Boolean isOffShelf;
+    @ApiModelProperty(value = "景区id")
+    private Long attractionsId;
 
 
-    @Builder.Default
-    @Column(nullable = false)
-    private Boolean del = false;
+    @ApiModelProperty(value = "类型")
+    @Enumerated(EnumType.STRING)
+    private ItemType type;
+
+//    @Builder.Default
+//    @Column(nullable = false)
+//    private Boolean del = false;
 
 
     private String img;
     private String img;
 }
 }

+ 0 - 23
src/main/java/com/izouma/jiashanxia/domain/GroupCustom.java

@@ -1,23 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-@ApiModel(value = "团建定制")
-public class GroupCustom {
-    @ApiModelProperty(value = "人数")
-    private String numberOfPeople;
-
-    private LocalDateTime startTime;
-
-    private LocalDateTime endTime;
-
-    private Long attractionsId;
-
-    private Long coachLevel;
-
-    private BigDecimal amount;
-}

+ 0 - 14
src/main/java/com/izouma/jiashanxia/domain/GroupCustomContent.java

@@ -1,14 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import io.swagger.annotations.ApiModel;
-
-import java.time.LocalDateTime;
-
-@ApiModel("项目内容")
-public class GroupCustomContent {
-    private Long attractionsContentId;
-
-    private Integer num;
-
-    private LocalDateTime date;
-}

+ 44 - 0
src/main/java/com/izouma/jiashanxia/domain/OrderRefund.java

@@ -0,0 +1,44 @@
+package com.izouma.jiashanxia.domain;
+
+import com.izouma.jiashanxia.enums.RefundStatus;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel(value = "订单退款")
+public class OrderRefund extends BaseEntity {
+    @ApiModelProperty(value = "订单id")
+    private Long orderInfoId;
+
+    @ApiModelProperty(value = "备注", name = "remark")
+    private String remark;
+
+    @Enumerated(EnumType.STRING)
+    @ApiModelProperty(value = "状态", name = "status")
+    private RefundStatus status;
+
+    @ApiModelProperty(value = "申请时间", name = "applyTime")
+    private LocalDateTime applyTime;
+
+    @ApiModelProperty(value = "审核时间", name = "auditTime")
+    private LocalDateTime auditTime;
+
+    @ApiModelProperty(value = "退款时间", name = "refundTime")
+    private LocalDateTime refundTime;
+
+    @ApiModelProperty(value = "退款单号", name = "refundId")
+    private String refundId;
+}

+ 2 - 0
src/main/java/com/izouma/jiashanxia/domain/Package.java

@@ -20,6 +20,8 @@ import java.util.List;
 @NoArgsConstructor
 @NoArgsConstructor
 @ApiModel(value = "套餐信息")
 @ApiModel(value = "套餐信息")
 public class Package extends BaseEntity {
 public class Package extends BaseEntity {
+    @ApiModelProperty(value = "景区id")
+    private Long attractionsId;
 
 
     @ApiModelProperty(value = "套餐名称")
     @ApiModelProperty(value = "套餐名称")
     private String name;
     private String name;

+ 0 - 23
src/main/java/com/izouma/jiashanxia/domain/PromoteRecord.java

@@ -1,23 +0,0 @@
-package com.izouma.jiashanxia.domain;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-@Builder
-@ApiModel(value = "推广记录表")
-public class PromoteRecord extends BaseEntity {
-    @ApiModelProperty(value = "推广人员")
-    private Long userId;
-
-    @ApiModelProperty(value = "被推广用户")
-    private Long promoteUserId;
-
-
-}

+ 4 - 1
src/main/java/com/izouma/jiashanxia/domain/UserPackage.java

@@ -18,12 +18,15 @@ import javax.persistence.Enumerated;
 @NoArgsConstructor
 @NoArgsConstructor
 @Builder
 @Builder
 @Entity
 @Entity
-@ApiModel(value = "用户套餐")
+@ApiModel(value = "用户套餐内容")
 @Where(clause = "del = 0")
 @Where(clause = "del = 0")
 public class UserPackage extends BaseEntity {
 public class UserPackage extends BaseEntity {
 
 
     private Long userId;
     private Long userId;
 
 
+    @ApiModelProperty(value = "用户套餐id")
+    private Long userPackagePeriodId;
+
     private Long goodsInfoId;
     private Long goodsInfoId;
 
 
     @ApiModelProperty(value = "数量")
     @ApiModelProperty(value = "数量")

+ 3 - 0
src/main/java/com/izouma/jiashanxia/domain/UserPackageFlow.java

@@ -30,6 +30,9 @@ public class UserPackageFlow extends BaseEntity {
     @Enumerated(EnumType.STRING)
     @Enumerated(EnumType.STRING)
     private PackageType packageType;
     private PackageType packageType;
 
 
+    @ApiModelProperty(value = "套餐有效期id")
+    private Long userPackagePeriodId;
+
     /*
     /*
     存储内容
     存储内容
     [{"goodsInfoId":11,"num":1},{"goodsInfoId":12,"num":1},{"goodsInfoId":13,"num":4}]
     [{"goodsInfoId":11,"num":1},{"goodsInfoId":12,"num":1},{"goodsInfoId":13,"num":4}]

+ 32 - 0
src/main/java/com/izouma/jiashanxia/domain/UserPackagePeriod.java

@@ -0,0 +1,32 @@
+package com.izouma.jiashanxia.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Builder
+@ApiModel(value = "用户套餐")
+public class UserPackagePeriod extends BaseEntity {
+
+    private Long userId;
+
+    @ApiModelProperty(value = "套餐id")
+    private Long packageId;
+
+    @ApiModelProperty(value = "使用日期")
+    private LocalDateTime useDate;
+
+    @ApiModelProperty(value = "截止日期")
+    private LocalDateTime endDate;
+
+}

+ 0 - 6
src/main/java/com/izouma/jiashanxia/domain/Withdraw.java

@@ -56,16 +56,10 @@ public class Withdraw extends BaseEntity implements Serializable {
     @ApiModelProperty(value = "真实姓名", name = "realName")
     @ApiModelProperty(value = "真实姓名", name = "realName")
     private String realName;
     private String realName;
 
 
-//    @ApiModelProperty(value = "是否成功", name = "consent")
-//    @Column(nullable = false)
-//    private Boolean consent;
-
     @ManyToOne(fetch = FetchType.LAZY)
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "userId", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
     @JoinColumn(name = "userId", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "none", value = ConstraintMode.NO_CONSTRAINT))
     @NotFound(action = NotFoundAction.IGNORE)
     @NotFound(action = NotFoundAction.IGNORE)
     @ExcelIgnore
     @ExcelIgnore
     private User user;
     private User user;
 
 
-//    @ApiModelProperty(value = "是否经销商流水", name = "isDealer")
-//    private Boolean isDealer;
 }
 }

+ 3 - 0
src/main/java/com/izouma/jiashanxia/dto/WriteOffSaveVO.java

@@ -26,6 +26,9 @@ public class WriteOffSaveVO extends BaseEntity {
     @ApiModelProperty(value = "类型")
     @ApiModelProperty(value = "类型")
     private PackageType type;
     private PackageType type;
 
 
+    @ApiModelProperty(value = "套餐有效期id")
+    private Long userPackagePeriodId;
+
     /*
     /*
     存储内容
     存储内容
     "userPackageId":1,
     "userPackageId":1,

+ 2 - 1
src/main/java/com/izouma/jiashanxia/enums/OrderInfoStatus.java

@@ -5,7 +5,8 @@ public enum OrderInfoStatus {
     PAID("已支付"),
     PAID("已支付"),
     CANCELLED("已取消"),
     CANCELLED("已取消"),
     OFFLINE_PAID("线下已付"),
     OFFLINE_PAID("线下已付"),
-
+    REFUNDED("已退款"),
+    REQUEST_REFUND("申请退款中"),
     ;
     ;
 
 
     private final String description;
     private final String description;

+ 18 - 0
src/main/java/com/izouma/jiashanxia/enums/RefundStatus.java

@@ -0,0 +1,18 @@
+package com.izouma.jiashanxia.enums;
+
+public enum RefundStatus {
+    PENDING("待处理"),
+    REFUNDING("退款中"),
+    SUCCESS("成功"),
+    DENY("失败"),
+    CANCEL("取消退款");
+    private final String description;
+
+    RefundStatus(String description) {
+        this.description = description;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 16 - 0
src/main/java/com/izouma/jiashanxia/repo/AttractionsRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.jiashanxia.repo;
+
+import com.izouma.jiashanxia.domain.Attractions;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface AttractionsRepo extends JpaRepository<Attractions, Long>, JpaSpecificationExecutor<Attractions> {
+    @Query("update Attractions t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 2 - 0
src/main/java/com/izouma/jiashanxia/repo/OrderInfoRepo.java

@@ -25,4 +25,6 @@ public interface OrderInfoRepo extends JpaRepository<OrderInfo, Long>, JpaSpecif
     List<OrderInfo> findAllByPaidAtBetweenAndStatus(LocalDateTime paidAt, LocalDateTime paidAt2, OrderInfoStatus status);
     List<OrderInfo> findAllByPaidAtBetweenAndStatus(LocalDateTime paidAt, LocalDateTime paidAt2, OrderInfoStatus status);
 
 
     long countByUserId(Long userId);
     long countByUserId(Long userId);
+
+    OrderInfo findByTransactionId(String transactionId);
 }
 }

+ 22 - 0
src/main/java/com/izouma/jiashanxia/repo/OrderRefundRepo.java

@@ -0,0 +1,22 @@
+package com.izouma.jiashanxia.repo;
+
+import com.izouma.jiashanxia.domain.OrderRefund;
+import com.izouma.jiashanxia.enums.RefundStatus;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+import java.util.Optional;
+
+public interface OrderRefundRepo extends JpaRepository<OrderRefund, Long>, JpaSpecificationExecutor<OrderRefund> {
+    @Query("update OrderRefund t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+
+    Optional<OrderRefund> findByOrderInfoIdAndStatus(Long orderInfoId, RefundStatus status);
+
+    OrderRefund findByRefundId(String refundId);
+}

+ 2 - 0
src/main/java/com/izouma/jiashanxia/repo/PackageRepo.java

@@ -17,4 +17,6 @@ public interface PackageRepo extends JpaRepository<Package, Long>, JpaSpecificat
     void softDelete(Long id);
     void softDelete(Long id);
 
 
     List<Package> findAllByTypeAndDelFalse(PackageType type);
     List<Package> findAllByTypeAndDelFalse(PackageType type);
+
+    List<Package> findAllByAttractionsId(Long attractionsId);
 }
 }

+ 16 - 0
src/main/java/com/izouma/jiashanxia/repo/UserPackagePeriodRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.jiashanxia.repo;
+
+import com.izouma.jiashanxia.domain.UserPackagePeriod;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface UserPackagePeriodRepo extends JpaRepository<UserPackagePeriod, Long>, JpaSpecificationExecutor<UserPackagePeriod> {
+    @Query("update UserPackagePeriod t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 4 - 4
src/main/java/com/izouma/jiashanxia/repo/UserPackageRepo.java

@@ -17,14 +17,14 @@ public interface UserPackageRepo extends JpaRepository<UserPackage, Long>, JpaSp
     @Transactional
     @Transactional
     void softDelete(Long id);
     void softDelete(Long id);
 
 
-    List<UserPackage> findAllByUserId(Long userId);
-
     List<UserPackage> findAllByUserIdAndType(Long userId, PackageType type);
     List<UserPackage> findAllByUserIdAndType(Long userId, PackageType type);
 
 
+    List<UserPackage> findAllByUserPackagePeriodId(Long userPackagePeriodId);
+
     @Query("update UserPackage t set t.userId = ?2 where t.userId = ?1 and t.type = 'TEAM'")
     @Query("update UserPackage t set t.userId = ?2 where t.userId = ?1 and t.type = 'TEAM'")
     @Modifying
     @Modifying
     @Transactional
     @Transactional
-    void updateUserId(Long oldUserId,Long newUserId);
+    void updateUserId(Long oldUserId, Long newUserId);
 
 
     @Query(value = "select goods_info.*, ifnull(user_package.num, 0) as num " +
     @Query(value = "select goods_info.*, ifnull(user_package.num, 0) as num " +
             "from goods_info " +
             "from goods_info " +
@@ -36,5 +36,5 @@ public interface UserPackageRepo extends JpaRepository<UserPackage, Long>, JpaSp
             "from goods_info " +
             "from goods_info " +
             "inner join user_package on goods_info.id = user_package.goods_info_id and user_package.user_id = ?1 " +
             "inner join user_package on goods_info.id = user_package.goods_info_id and user_package.user_id = ?1 " +
             "where goods_info.del = 0 and user_package.type = ?2", nativeQuery = true)
             "where goods_info.del = 0 and user_package.type = ?2", nativeQuery = true)
-    List<UserPackageDTO> userPackageByType(Long userId,String type);
+    List<UserPackageDTO> userPackageByType(Long userId, String type);
 }
 }

+ 20 - 0
src/main/java/com/izouma/jiashanxia/service/AttractionsService.java

@@ -0,0 +1,20 @@
+package com.izouma.jiashanxia.service;
+
+import com.izouma.jiashanxia.domain.Attractions;
+import com.izouma.jiashanxia.dto.PageQuery;
+import com.izouma.jiashanxia.repo.AttractionsRepo;
+import com.izouma.jiashanxia.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class AttractionsService {
+
+    private AttractionsRepo attractionsRepo;
+
+    public Page<Attractions> all(PageQuery pageQuery) {
+        return attractionsRepo.findAll(JpaUtils.toSpecification(pageQuery, Attractions.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 2 - 6
src/main/java/com/izouma/jiashanxia/service/OrderInfoService.java

@@ -105,6 +105,7 @@ public class OrderInfoService {
             user.setVip(true);
             user.setVip(true);
             userRepo.save(user);
             userRepo.save(user);
         }
         }
+
         // 加入套餐商品
         // 加入套餐商品
         userPackageService.joinUserPackage(userId, setGoodsList, 1, PackageType.PERSONAL);
         userPackageService.joinUserPackage(userId, setGoodsList, 1, PackageType.PERSONAL);
 
 
@@ -131,9 +132,6 @@ public class OrderInfoService {
 
 
     /*
     /*
     分销
     分销
-    又有团队,员工自己又购买了套餐,企业是否有收益?
-    企业拉员工,员工的上级是否改为企业领导人?
-    如果上级和企业不是一个人,收益如何算?
      */
      */
     public void distribution(Long userId, Long parent, String transactionId, BigDecimal amount) {
     public void distribution(Long userId, Long parent, String transactionId, BigDecimal amount) {
         User parentUser = userRepo.findById(parent).orElseThrow(new BusinessException("无用户"));
         User parentUser = userRepo.findById(parent).orElseThrow(new BusinessException("无用户"));
@@ -146,9 +144,7 @@ public class OrderInfoService {
             this.companyDis(parentUser.getCompanyId(), transactionId, userId, amount);
             this.companyDis(parentUser.getCompanyId(), transactionId, userId, amount);
         }
         }
         if (!flag) {
         if (!flag) {
-            // 是否购买了套餐
-//            List<UserSet> parentSets = userSetRepo.findAllByUserId(parent);
-//            if (CollUtil.isNotEmpty(parentSets)) {
+
             if (parentUser.isVip()) {
             if (parentUser.isVip()) {
                 flag = true;
                 flag = true;
             }
             }

+ 170 - 0
src/main/java/com/izouma/jiashanxia/service/OrderRefundService.java

@@ -0,0 +1,170 @@
+package com.izouma.jiashanxia.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
+import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.izouma.jiashanxia.domain.OrderInfo;
+import com.izouma.jiashanxia.domain.OrderRefund;
+import com.izouma.jiashanxia.domain.WxFee;
+import com.izouma.jiashanxia.dto.PageQuery;
+import com.izouma.jiashanxia.enums.OrderInfoStatus;
+import com.izouma.jiashanxia.enums.RefundStatus;
+import com.izouma.jiashanxia.exception.BusinessException;
+import com.izouma.jiashanxia.repo.OrderInfoRepo;
+import com.izouma.jiashanxia.repo.OrderRefundRepo;
+import com.izouma.jiashanxia.repo.WxFeeRepo;
+import com.izouma.jiashanxia.utils.JpaUtils;
+import com.izouma.jiashanxia.utils.SnowflakeIdWorker;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.core.env.Environment;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Map;
+
+@Service
+@AllArgsConstructor
+@Slf4j
+public class OrderRefundService {
+
+    private final OrderRefundRepo orderRefundRepo;
+    private final OrderInfoRepo   orderInfoRepo;
+    private final WxFeeRepo       wxFeeRepo;
+    private final WxPayService    wxPayService;
+    private final Environment     env;
+
+    public Page<OrderRefund> all(PageQuery pageQuery) {
+        return orderRefundRepo.findAll(JpaUtils.toSpecification(pageQuery, OrderRefund.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    /*
+    申请退款
+     */
+    public OrderRefund apply(Long orderId) {
+        OrderInfo orderInfo = orderInfoRepo.findById(orderId).orElseThrow(new BusinessException("无订单"));
+        switch (orderInfo.getStatus()) {
+            case UNPAID:
+                throw new BusinessException("暂未支付");
+            case CANCELLED:
+                throw new BusinessException("已取消");
+            case REFUNDED:
+                throw new BusinessException("已退款");
+            case REQUEST_REFUND:
+                throw new BusinessException("申请中");
+        }
+        orderInfo.setStatus(OrderInfoStatus.REQUEST_REFUND);
+
+        OrderRefund refund = OrderRefund.builder()
+                .orderInfoId(orderId)
+                .applyTime(LocalDateTime.now())
+                .status(RefundStatus.PENDING)
+                .build();
+
+        orderInfoRepo.save(orderInfo);
+        return orderRefundRepo.save(refund);
+    }
+
+
+    /*
+    取消退款
+    */
+    public void cancel(Long orderId) {
+        // 恢复订单状态
+        OrderInfo orderInfo = orderInfoRepo.findById(orderId).orElseThrow(new BusinessException("无订单"));
+        orderInfo.setStatus(OrderInfoStatus.PAID);
+        orderInfoRepo.save(orderInfo);
+
+        // 修改退款状态
+        OrderRefund OrderRefund = orderRefundRepo.findByOrderInfoIdAndStatus(orderId, RefundStatus.PENDING)
+                .orElseThrow(new BusinessException("未申请退款"));
+        OrderRefund.setStatus(RefundStatus.CANCEL);
+        orderRefundRepo.save(OrderRefund);
+    }
+
+    /**
+     * 审核退款
+     *
+     * @param orderRefundId 申请退款ID
+     * @param pass          是否通过
+     */
+    @Transactional
+    public void audit(Long orderRefundId, boolean pass) {
+        // 退款记录
+        OrderRefund refund = orderRefundRepo.findById(orderRefundId).orElseThrow(new BusinessException("无记录"));
+        if (refund.getStatus() != RefundStatus.PENDING) {
+            return;
+        }
+        // 审核时间
+        refund.setAuditTime(LocalDateTime.now());
+
+        // 订单记录
+        OrderInfo orderInfo = orderInfoRepo.findById(refund.getOrderInfoId()).orElseThrow(new BusinessException("无订单"));
+
+        if (pass) {
+            String refundId = String.valueOf(new SnowflakeIdWorker(1, 1).nextId());
+            refund.setStatus(RefundStatus.REFUNDING);
+//            if (StringUtils.isEmpty(refund.getRefundId())) {
+            refund.setRefundId(refundId);
+//            }
+            orderRefundRepo.save(refund);
+            // 退款
+            payRefund(orderInfo, refundId);
+            return;
+        }
+        refund.setStatus(RefundStatus.DENY);
+        orderInfo.setStatus(OrderInfoStatus.PAID);
+        orderInfoRepo.save(orderInfo);
+        orderRefundRepo.save(refund);
+    }
+
+    /*
+    微信退款
+     */
+    private void payRefund(OrderInfo orderInfo, String refundId) {
+        try {
+            WxPayRefundRequest req = WxPayRefundRequest.newBuilder()
+                    .transactionId(orderInfo.getTransactionId())
+                    .outRefundNo(refundId)
+                    .totalFee(orderInfo.getPrice().multiply(BigDecimal.valueOf(100)).intValue())
+                    .refundFee(orderInfo.getPrice().multiply(BigDecimal.valueOf(100)).intValue())
+                    .notifyUrl(env.getProperty("wx.pay.refundNotifyUrl"))
+                    .build();
+
+            wxPayService.refund(req);
+        } catch (WxPayException e) {
+            log.error("发起微信退款", e);
+            throw new BusinessException("操作失败,请稍后再试", e.getMessage());
+        }
+    }
+
+    public void handleRefundNotify(WxPayRefundNotifyResult notifyResult) {
+        OrderInfo orderInfo = orderInfoRepo.findByTransactionId(notifyResult.getReqInfo().getTransactionId());
+
+        // 微信退款流水
+        WxFee wxFee = WxFee.builder()
+                .amount(BigDecimal.valueOf(notifyResult.getReqInfo().getRefundFee() / 100.0))
+                .isRefund(true)
+                .orderId(orderInfo.getId())
+                .type("refund")
+                .transactionId(notifyResult.getReqInfo().getRefundId())
+                .userId(orderInfo.getUserId())
+                .build();
+        wxFeeRepo.save(wxFee);
+
+        // 订单状态
+        orderInfo.setStatus(OrderInfoStatus.REFUNDED);
+        orderInfoRepo.save(orderInfo);
+
+        // 退款状态
+        OrderRefund refund = orderRefundRepo.findByRefundId(notifyResult.getReqInfo().getRefundId());
+        refund.setStatus(RefundStatus.SUCCESS);
+        refund.setAuditTime(LocalDateTime.now());
+    }
+}

+ 32 - 10
src/main/java/com/izouma/jiashanxia/service/UserPackageFlowService.java

@@ -7,11 +7,13 @@ import com.alibaba.fastjson.JSONObject;
 import com.izouma.jiashanxia.domain.GoodsInfo;
 import com.izouma.jiashanxia.domain.GoodsInfo;
 import com.izouma.jiashanxia.domain.UserPackage;
 import com.izouma.jiashanxia.domain.UserPackage;
 import com.izouma.jiashanxia.domain.UserPackageFlow;
 import com.izouma.jiashanxia.domain.UserPackageFlow;
+import com.izouma.jiashanxia.domain.UserPackagePeriod;
 import com.izouma.jiashanxia.dto.*;
 import com.izouma.jiashanxia.dto.*;
 import com.izouma.jiashanxia.enums.FlowType;
 import com.izouma.jiashanxia.enums.FlowType;
 import com.izouma.jiashanxia.exception.BusinessException;
 import com.izouma.jiashanxia.exception.BusinessException;
 import com.izouma.jiashanxia.repo.GoodsInfoRepo;
 import com.izouma.jiashanxia.repo.GoodsInfoRepo;
 import com.izouma.jiashanxia.repo.UserPackageFlowRepo;
 import com.izouma.jiashanxia.repo.UserPackageFlowRepo;
+import com.izouma.jiashanxia.repo.UserPackagePeriodRepo;
 import com.izouma.jiashanxia.repo.UserPackageRepo;
 import com.izouma.jiashanxia.repo.UserPackageRepo;
 import com.izouma.jiashanxia.utils.JpaUtils;
 import com.izouma.jiashanxia.utils.JpaUtils;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
@@ -19,6 +21,7 @@ import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import javax.persistence.criteria.Predicate;
 import javax.persistence.criteria.Predicate;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.*;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
@@ -26,9 +29,10 @@ import java.util.stream.Collectors;
 @AllArgsConstructor
 @AllArgsConstructor
 public class UserPackageFlowService {
 public class UserPackageFlowService {
 
 
-    private final UserPackageFlowRepo userPackageFlowRepo;
-    private final GoodsInfoRepo       goodsInfoRepo;
-    private final UserPackageRepo     userPackageRepo;
+    private final UserPackageFlowRepo   userPackageFlowRepo;
+    private final GoodsInfoRepo         goodsInfoRepo;
+    private final UserPackageRepo       userPackageRepo;
+    private final UserPackagePeriodRepo userPackagePeriodRepo;
 
 
     public Page<UserPackageFlow> all(PageQuery pageQuery) {
     public Page<UserPackageFlow> all(PageQuery pageQuery) {
         pageQuery.setSort("createdAt,desc");
         pageQuery.setSort("createdAt,desc");
@@ -128,14 +132,24 @@ public class UserPackageFlowService {
         }
         }
 
 
         // 用户已有套餐
         // 用户已有套餐
-        Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserIdAndType(writeOffSaveVO.getUserId(), writeOffSaveVO
-                .getType())
-                .stream()
-                .collect(Collectors.toMap(UserPackage::getGoodsInfoId, userPackage -> userPackage));
-
-//        Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserId(writeOffSaveVO.getUserId())
+//        Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserIdAndType(writeOffSaveVO.getUserId(), writeOffSaveVO
+//                .getType())
 //                .stream()
 //                .stream()
-//                .collect(Collectors.toMap(UserPackage::getId, userPackage -> userPackage));
+//                .collect(Collectors.toMap(UserPackage::getGoodsInfoId, userPackage -> userPackage));
+
+        // 套餐有效期
+        UserPackagePeriod userPackagePeriod = userPackagePeriodRepo.findById(writeOffSaveVO.getUserPackagePeriodId())
+                .orElseThrow(new BusinessException("无套餐"));
+        LocalDateTime now = LocalDateTime.now();
+
+        if (ObjectUtil.isNotEmpty(userPackagePeriod.getEndDate()) && userPackagePeriod.getEndDate().isBefore(now)) {
+            throw new BusinessException("套餐已过期");
+        }
+
+        // 核销套餐内容
+        Map<Long, UserPackage> userPackageMap = userPackageRepo.findAllByUserPackagePeriodId(writeOffSaveVO.getUserPackagePeriodId())
+                .stream()
+                .collect(Collectors.toMap(UserPackage::getId, userPackage -> userPackage));
 
 
         List<GoodsDTO> goodsDTOS = JSONObject.parseArray(writeOffSaveVO.getContent(), GoodsDTO.class);
         List<GoodsDTO> goodsDTOS = JSONObject.parseArray(writeOffSaveVO.getContent(), GoodsDTO.class);
         goodsDTOS.forEach(goodsDTO -> {
         goodsDTOS.forEach(goodsDTO -> {
@@ -155,6 +169,13 @@ public class UserPackageFlowService {
             goodsDTO.setNum(-goodsDTO.getNum());
             goodsDTO.setNum(-goodsDTO.getNum());
         });
         });
 
 
+        // 触发有效期
+        if (ObjectUtil.isEmpty(userPackagePeriod.getUseDate())) {
+            userPackagePeriod.setUseDate(now);
+            userPackagePeriod.setEndDate(now.toLocalDate().atTime(23, 59, 59));
+            userPackagePeriodRepo.save(userPackagePeriod);
+        }
+
         // 保存套餐流水
         // 保存套餐流水
         String content = JSONObject.toJSONString(goodsDTOS);
         String content = JSONObject.toJSONString(goodsDTOS);
         return userPackageFlowRepo.save(
         return userPackageFlowRepo.save(
@@ -162,6 +183,7 @@ public class UserPackageFlowService {
                         .userId(writeOffSaveVO.getUserId())
                         .userId(writeOffSaveVO.getUserId())
                         .content(content)
                         .content(content)
                         .type(FlowType.WRITE_OFF)
                         .type(FlowType.WRITE_OFF)
+                        .userPackagePeriodId(writeOffSaveVO.getUserPackagePeriodId())
                         .packageType(writeOffSaveVO.getType())
                         .packageType(writeOffSaveVO.getType())
                         .writeOffUserId(writeOffSaveVO.getWriteOffUserId())
                         .writeOffUserId(writeOffSaveVO.getWriteOffUserId())
                         .build());
                         .build());

+ 20 - 0
src/main/java/com/izouma/jiashanxia/service/UserPackagePeriodService.java

@@ -0,0 +1,20 @@
+package com.izouma.jiashanxia.service;
+
+import com.izouma.jiashanxia.domain.UserPackagePeriod;
+import com.izouma.jiashanxia.dto.PageQuery;
+import com.izouma.jiashanxia.repo.UserPackagePeriodRepo;
+import com.izouma.jiashanxia.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class UserPackagePeriodService {
+
+    private UserPackagePeriodRepo userPackagePeriodRepo;
+
+    public Page<UserPackagePeriod> all(PageQuery pageQuery) {
+        return userPackagePeriodRepo.findAll(JpaUtils.toSpecification(pageQuery, UserPackagePeriod.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 60 - 0
src/main/java/com/izouma/jiashanxia/web/AttractionsController.java

@@ -0,0 +1,60 @@
+package com.izouma.jiashanxia.web;
+import com.izouma.jiashanxia.domain.Attractions;
+import com.izouma.jiashanxia.service.AttractionsService;
+import com.izouma.jiashanxia.dto.PageQuery;
+import com.izouma.jiashanxia.exception.BusinessException;
+import com.izouma.jiashanxia.repo.AttractionsRepo;
+import com.izouma.jiashanxia.utils.ObjUtils;
+import com.izouma.jiashanxia.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/attractions")
+@AllArgsConstructor
+public class AttractionsController extends BaseController {
+    private AttractionsService attractionsService;
+    private AttractionsRepo attractionsRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public Attractions save(@RequestBody Attractions record) {
+        if (record.getId() != null) {
+            Attractions orig = attractionsRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return attractionsRepo.save(orig);
+        }
+        return attractionsRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<Attractions> all(@RequestBody PageQuery pageQuery) {
+        return attractionsService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public Attractions get(@PathVariable Long id) {
+        return attractionsRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        attractionsRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<Attractions> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 60 - 0
src/main/java/com/izouma/jiashanxia/web/OrderRefundController.java

@@ -0,0 +1,60 @@
+package com.izouma.jiashanxia.web;
+import com.izouma.jiashanxia.domain.OrderRefund;
+import com.izouma.jiashanxia.service.OrderRefundService;
+import com.izouma.jiashanxia.dto.PageQuery;
+import com.izouma.jiashanxia.exception.BusinessException;
+import com.izouma.jiashanxia.repo.OrderRefundRepo;
+import com.izouma.jiashanxia.utils.ObjUtils;
+import com.izouma.jiashanxia.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/orderRefund")
+@AllArgsConstructor
+public class OrderRefundController extends BaseController {
+    private OrderRefundService orderRefundService;
+    private OrderRefundRepo orderRefundRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public OrderRefund save(@RequestBody OrderRefund record) {
+        if (record.getId() != null) {
+            OrderRefund orig = orderRefundRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return orderRefundRepo.save(orig);
+        }
+        return orderRefundRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<OrderRefund> all(@RequestBody PageQuery pageQuery) {
+        return orderRefundService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public OrderRefund get(@PathVariable Long id) {
+        return orderRefundRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        orderRefundRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<OrderRefund> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 60 - 0
src/main/java/com/izouma/jiashanxia/web/UserPackagePeriodController.java

@@ -0,0 +1,60 @@
+package com.izouma.jiashanxia.web;
+import com.izouma.jiashanxia.domain.UserPackagePeriod;
+import com.izouma.jiashanxia.service.UserPackagePeriodService;
+import com.izouma.jiashanxia.dto.PageQuery;
+import com.izouma.jiashanxia.exception.BusinessException;
+import com.izouma.jiashanxia.repo.UserPackagePeriodRepo;
+import com.izouma.jiashanxia.utils.ObjUtils;
+import com.izouma.jiashanxia.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/userPackagePeriod")
+@AllArgsConstructor
+public class UserPackagePeriodController extends BaseController {
+    private UserPackagePeriodService userPackagePeriodService;
+    private UserPackagePeriodRepo userPackagePeriodRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public UserPackagePeriod save(@RequestBody UserPackagePeriod record) {
+        if (record.getId() != null) {
+            UserPackagePeriod orig = userPackagePeriodRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return userPackagePeriodRepo.save(orig);
+        }
+        return userPackagePeriodRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<UserPackagePeriod> all(@RequestBody PageQuery pageQuery) {
+        return userPackagePeriodService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public UserPackagePeriod get(@PathVariable Long id) {
+        return userPackagePeriodRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        userPackagePeriodRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<UserPackagePeriod> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 7 - 4
src/main/java/com/izouma/jiashanxia/web/WxController.java

@@ -11,6 +11,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.izouma.jiashanxia.exception.BusinessException;
 import com.izouma.jiashanxia.exception.BusinessException;
 import com.izouma.jiashanxia.service.ConsumptionService;
 import com.izouma.jiashanxia.service.ConsumptionService;
+import com.izouma.jiashanxia.service.OrderRefundService;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
@@ -33,10 +34,11 @@ import java.io.IOException;
 @RestController
 @RestController
 @RequestMapping("/wx")
 @RequestMapping("/wx")
 public class WxController {
 public class WxController {
-    private WxMpService        wxMpService;
-    private WxPayService       wxPayService;
-    private Environment        env;
-    private ConsumptionService consumptionService;
+    private final WxMpService        wxMpService;
+    private final WxPayService       wxPayService;
+    private final Environment        env;
+    private final ConsumptionService consumptionService;
+    private final OrderRefundService orderRefundService;
 
 
     @RequestMapping("/testMp")
     @RequestMapping("/testMp")
     public ModelAndView testPay(@RequestParam(required = false) String code, HttpServletRequest request,
     public ModelAndView testPay(@RequestParam(required = false) String code, HttpServletRequest request,
@@ -122,6 +124,7 @@ public class WxController {
         log.info("微信退款回调: {}", xmlData);
         log.info("微信退款回调: {}", xmlData);
         final WxPayRefundNotifyResult notifyResult = wxPayService.parseRefundNotifyResult(xmlData);
         final WxPayRefundNotifyResult notifyResult = wxPayService.parseRefundNotifyResult(xmlData);
         notifyResult.checkResult(wxPayService, "MD5", true);
         notifyResult.checkResult(wxPayService, "MD5", true);
+        orderRefundService.handleRefundNotify(notifyResult);
         return WxPayNotifyResponse.success("OK");
         return WxPayNotifyResponse.success("OK");
     }
     }
 
 

+ 1 - 0
src/main/resources/genjson/Attractions.json

@@ -0,0 +1 @@
+{"tableName":"Attractions","className":"Attractions","remark":"景区管理","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/java/com/izouma/jiashanxia","viewPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/vue/src/views","routerPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/vue/src","resourcesPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"name","modelName":"name","remark":"名称","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"introduction","modelName":"introduction","remark":"介绍","showInList":true,"showInForm":true,"formType":"textarea"},{"name":"address","modelName":"address","remark":"地址","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"phone","modelName":"phone","remark":"电话","showInList":true,"showInForm":true,"formType":"singleLineText","validate":true,"validatorType":"phone"},{"name":"img","modelName":"img","remark":"图片","showInList":true,"showInForm":true,"formType":"multiImage"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.jiashanxia","tablePackage":"com.izouma.jiashanxia.domain.Attractions"}

+ 1 - 0
src/main/resources/genjson/OrderRefund.json

@@ -0,0 +1 @@
+{"tableName":"OrderRefund","className":"OrderRefund","remark":"订单退款","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/java/com/izouma/jiashanxia","viewPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/vue/src/views","routerPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/vue/src","resourcesPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"orderInfoId","modelName":"orderInfoId","remark":"订单id","showInList":true,"showInForm":true,"formType":"number"},{"name":"remark","modelName":"remark","remark":"备注","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"status","modelName":"status","remark":"状态","showInList":true,"showInForm":true,"formType":"select","apiFlag":"1","optionsValue":"[{\"label\":\"待处理\",\"value\":\"PENDING\"},{\"label\":\"退款中\",\"value\":\"REFUNDING\"},{\"label\":\"成功\",\"value\":\"SUCCESS\"},{\"label\":\"失败\",\"value\":\"DENY\"},{\"label\":\"取消退款\",\"value\":\"CANCEL\"}]"},{"name":"applyTime","modelName":"applyTime","remark":"申请时间","showInList":true,"showInForm":true,"formType":"datetime"},{"name":"auditTime","modelName":"auditTime","remark":"审核时间","showInList":true,"showInForm":true,"formType":"datetime"},{"name":"refundTime","modelName":"refundTime","remark":"退款时间","showInList":true,"showInForm":true,"formType":"datetime"},{"name":"refundId","modelName":"refundId","remark":"退款单号","showInList":true,"showInForm":true,"formType":"singleLineText"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.jiashanxia","tablePackage":"com.izouma.jiashanxia.domain.OrderRefund"}

+ 1 - 0
src/main/resources/genjson/UserPackagePeriod.json

@@ -0,0 +1 @@
+{"tableName":"UserPackagePeriod","className":"UserPackagePeriod","remark":"用户套餐","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/java/com/izouma/jiashanxia","viewPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/vue/src/views","routerPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/vue/src","resourcesPath":"/Users/qiufangchao/Desktop/project/jiashanxia/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"userId","modelName":"userId","remark":"用户id","showInList":true,"showInForm":true,"formType":"number"},{"name":"packageId","modelName":"packageId","remark":"套餐id","showInList":true,"showInForm":true,"formType":"select","apiFlag":"2","optionsMethod":"/package/allList","optionsValue":"id","optionsLabel":"name"},{"name":"useDate","modelName":"useDate","remark":"使用日期","showInList":true,"showInForm":true,"formType":"datetime"},{"name":"endDate","modelName":"endDate","remark":"截止日期","showInList":true,"showInForm":true,"formType":"datetime"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.jiashanxia","tablePackage":"com.izouma.jiashanxia.domain.UserPackagePeriod"}

+ 50 - 0
src/main/vue/src/router.js

@@ -315,6 +315,56 @@ const router = new Router({
                     meta: {
                     meta: {
                         title: '反馈'
                         title: '反馈'
                     }
                     }
+                },
+                {
+                    path: '/orderRefundEdit',
+                    name: 'OrderRefundEdit',
+                    component: () => import(/* webpackChunkName: "orderRefundEdit" */ '@/views/OrderRefundEdit.vue'),
+                    meta: {
+                        title: '订单退款编辑'
+                    }
+                },
+                {
+                    path: '/orderRefundList',
+                    name: 'OrderRefundList',
+                    component: () => import(/* webpackChunkName: "orderRefundList" */ '@/views/OrderRefundList.vue'),
+                    meta: {
+                        title: '订单退款'
+                    }
+                },
+                {
+                    path: '/attractionsEdit',
+                    name: 'AttractionsEdit',
+                    component: () => import(/* webpackChunkName: "attractionsEdit" */ '@/views/AttractionsEdit.vue'),
+                    meta: {
+                        title: '景区管理编辑'
+                    }
+                },
+                {
+                    path: '/attractionsList',
+                    name: 'AttractionsList',
+                    component: () => import(/* webpackChunkName: "attractionsList" */ '@/views/AttractionsList.vue'),
+                    meta: {
+                        title: '景区管理'
+                    }
+                },
+                {
+                    path: '/userPackagePeriodEdit',
+                    name: 'UserPackagePeriodEdit',
+                    component: () =>
+                        import(/* webpackChunkName: "userPackagePeriodEdit" */ '@/views/UserPackagePeriodEdit.vue'),
+                    meta: {
+                        title: '用户套餐编辑'
+                    }
+                },
+                {
+                    path: '/userPackagePeriodList',
+                    name: 'UserPackagePeriodList',
+                    component: () =>
+                        import(/* webpackChunkName: "userPackagePeriodList" */ '@/views/UserPackagePeriodList.vue'),
+                    meta: {
+                        title: '用户套餐'
+                    }
                 }
                 }
                 /**INSERT_LOCATION**/
                 /**INSERT_LOCATION**/
             ]
             ]

+ 112 - 0
src/main/vue/src/views/AttractionsEdit.vue

@@ -0,0 +1,112 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="52px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="name" label="名称">
+                <el-input v-model="formData.name"></el-input>
+            </el-form-item>
+            <el-form-item prop="introduction" label="介绍">
+                <el-input type="textarea" v-model="formData.introduction"></el-input>
+            </el-form-item>
+            <el-form-item prop="address" label="地址">
+                <el-input v-model="formData.address"></el-input>
+            </el-form-item>
+            <el-form-item prop="phone" label="电话">
+                <el-input v-model="formData.phone"></el-input>
+            </el-form-item>
+            <el-form-item prop="img" label="图片">
+                <multi-upload v-model="formData.img"></multi-upload>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+export default {
+    name: 'AttractionsEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('attractions/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {
+                phone: [
+                    {
+                        pattern: /^1[3-9]\d{9}$/,
+                        message: '请输入正确的手机号',
+                        trigger: 'blur'
+                    }
+                ]
+            }
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/attractions/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/attractions/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 169 - 0
src/main/vue/src/views/AttractionsList.vue

@@ -0,0 +1,169 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="name" label="名称"> </el-table-column>
+            <el-table-column prop="introduction" label="介绍"> </el-table-column>
+            <el-table-column prop="address" label="地址"> </el-table-column>
+            <el-table-column prop="phone" label="电话"> </el-table-column>
+            <el-table-column prop="img" label="图片">
+                <template slot-scope="{ row }">
+                    <el-image
+                        style="width: 30px; height: 30px"
+                        :src="row.img[0]"
+                        fit="cover"
+                        :preview-src-list="row.img"
+                    ></el-image>
+                </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'AttractionsList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/attractions/all',
+            downloading: false
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        beforeGetData() {
+            return { search: this.search };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/attractionsEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/attractionsEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/attractions/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/attractions/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 138 - 0
src/main/vue/src/views/OrderRefundEdit.vue

@@ -0,0 +1,138 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="80px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="orderInfoId" label="订单id">
+                <el-input-number type="number" v-model="formData.orderInfoId"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="remark" label="备注">
+                <el-input v-model="formData.remark"></el-input>
+            </el-form-item>
+            <el-form-item prop="status" label="状态">
+                <el-select v-model="formData.status" clearable filterable placeholder="请选择">
+                    <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value">
+                    </el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item prop="applyTime" label="申请时间">
+                <el-date-picker
+                    v-model="formData.applyTime"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item prop="auditTime" label="审核时间">
+                <el-date-picker
+                    v-model="formData.auditTime"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item prop="refundTime" label="退款时间">
+                <el-date-picker
+                    v-model="formData.refundTime"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item prop="refundId" label="退款单号">
+                <el-input v-model="formData.refundId"></el-input>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+export default {
+    name: 'OrderRefundEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('orderRefund/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {},
+            statusOptions: [
+                { label: '待处理', value: 'PENDING' },
+                { label: '退款中', value: 'REFUNDING' },
+                { label: '成功', value: 'SUCCESS' },
+                { label: '失败', value: 'DENY' },
+                { label: '取消退款', value: 'CANCEL' }
+            ]
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/orderRefund/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/orderRefund/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 176 - 0
src/main/vue/src/views/OrderRefundList.vue

@@ -0,0 +1,176 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="orderInfoId" label="订单id"> </el-table-column>
+            <el-table-column prop="remark" label="备注"> </el-table-column>
+            <el-table-column prop="status" label="状态" :formatter="statusFormatter"> </el-table-column>
+            <el-table-column prop="applyTime" label="申请时间"> </el-table-column>
+            <el-table-column prop="auditTime" label="审核时间"> </el-table-column>
+            <el-table-column prop="refundTime" label="退款时间"> </el-table-column>
+            <el-table-column prop="refundId" label="退款单号"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'OrderRefundList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/orderRefund/all',
+            downloading: false,
+            statusOptions: [
+                { label: '待处理', value: 'PENDING' },
+                { label: '退款中', value: 'REFUNDING' },
+                { label: '成功', value: 'SUCCESS' },
+                { label: '失败', value: 'DENY' },
+                { label: '取消退款', value: 'CANCEL' }
+            ]
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        statusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return { search: this.search };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/orderRefundEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/orderRefundEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/orderRefund/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/orderRefund/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 139 - 0
src/main/vue/src/views/UserPackagePeriodEdit.vue

@@ -0,0 +1,139 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="80px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="userId" label="用户id">
+                <el-input-number type="number" v-model="formData.userId"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="packageId" label="套餐id">
+                <el-select v-model="formData.packageId" clearable filterable placeholder="请选择">
+                    <el-option
+                        v-for="item in packageIdOptions"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                    >
+                    </el-option>
+                </el-select>
+            </el-form-item>
+            <el-form-item prop="useDate" label="使用日期">
+                <el-date-picker
+                    v-model="formData.useDate"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item prop="endDate" label="截止日期">
+                <el-date-picker
+                    v-model="formData.endDate"
+                    type="datetime"
+                    value-format="yyyy-MM-dd HH:mm:ss"
+                    placeholder="选择日期时间"
+                >
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+export default {
+    name: 'UserPackagePeriodEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('userPackagePeriod/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+        // , { size: 1000, query: { del: false } }
+        this.$http
+            .get('/package/allList')
+            .then(res => {
+                if (res.length > 0) {
+                    res.forEach(item => {
+                        this.packageIdOptions.push({
+                            label: item.name,
+                            value: item.id
+                        });
+                    });
+                }
+            })
+            .catch(e => {
+                console.log(e);
+                this.$message.error(e.error);
+            });
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {},
+            packageIdOptions: []
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/userPackagePeriod/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/userPackagePeriod/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 159 - 0
src/main/vue/src/views/UserPackagePeriodList.vue

@@ -0,0 +1,159 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="userId" label="用户id"> </el-table-column>
+            <el-table-column prop="packageId" label="套餐id"> </el-table-column>
+            <el-table-column prop="useDate" label="使用日期"> </el-table-column>
+            <el-table-column prop="endDate" label="截止日期"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'UserPackagePeriodList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/userPackagePeriod/all',
+            downloading: false
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        beforeGetData() {
+            return { search: this.search };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/userPackagePeriodEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/userPackagePeriodEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/userPackagePeriod/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/userPackagePeriod/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>