Browse Source

Merge branch 'fix_order_refund' into sign_draw

# Conflicts:
#	netflix-common/src/main/java/com/cyksj/common/util/StringUtil.java
#	netflix-dao/src/main/java/com/cyksj/model/entity/BaseEntity.java
#	netflix-dao/src/main/java/com/cyksj/model/entity/CouponUser.java
#	netflix-service/src/main/java/com/cyksj/redis/RedisService.java
#	netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
#	netflix-web/src/main/java/com/cyksj/config/SaTokenConfigure.java
#	netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java
#	netflix-web/src/main/java/com/cyksj/web/controller/user/AuthorizationController.java
zoujiajian 3 năm trước cách đây
mục cha
commit
3d8d9735fb
64 tập tin đã thay đổi với 1557 bổ sung119 xóa
  1. 3 0
      netflix-common/src/main/java/com/cyksj/common/constant/TemplateEnum.java
  2. 18 0
      netflix-common/src/main/java/com/cyksj/common/util/StringUtil.java
  3. 18 0
      netflix-common/src/main/java/com/cyksj/enums/GatewayApiCode.java
  4. 2 2
      netflix-dao/src/main/java/com/cyksj/mapper/AccountMapper.java
  5. 21 0
      netflix-dao/src/main/java/com/cyksj/mapper/CustomerServiceRenewOrderRecordMapper.java
  6. 4 0
      netflix-dao/src/main/java/com/cyksj/mapper/GroupsRelationMapper.java
  7. 4 2
      netflix-dao/src/main/java/com/cyksj/mapper/RegisterOrderDonMapper.java
  8. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/SpotifyUnLimitRecordMapper.java
  9. 3 3
      netflix-dao/src/main/java/com/cyksj/mapper/manage/statistics/StatisticsUserDataMapper.java
  10. 4 3
      netflix-dao/src/main/java/com/cyksj/model/entity/BaseEntity.java
  11. 23 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CustomerServiceRenewOrderRecord.java
  12. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/GoodsDonSku.java
  13. 0 5
      netflix-dao/src/main/java/com/cyksj/model/entity/GoodsDonStockRecord.java
  14. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/GroupsRelation.java
  15. 1 1
      netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java
  16. 47 0
      netflix-dao/src/main/java/com/cyksj/model/entity/SpotifyUnLimitRecord.java
  17. 4 0
      netflix-dao/src/main/java/com/cyksj/model/entity/User.java
  18. 6 0
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelAccountData.java
  19. 26 0
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelAccountTemplateData.java
  20. 48 0
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelManageAccountData.java
  21. 153 0
      netflix-dao/src/main/java/com/cyksj/model/excel/ExcelOrderDonData.java
  22. 5 0
      netflix-dao/src/main/java/com/cyksj/model/manage/request/ReqGoodsSkuInsert.java
  23. 8 1
      netflix-dao/src/main/java/com/cyksj/model/manage/views/AccountView.java
  24. 8 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/GroupsRelationView.java
  25. 3 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java
  26. 18 0
      netflix-dao/src/main/java/com/cyksj/model/request/BatchServiceReq.java
  27. 10 0
      netflix-dao/src/main/java/com/cyksj/model/request/LoginPhoneReq.java
  28. 2 0
      netflix-dao/src/main/java/com/cyksj/model/views/CustomerServiceHandleOrderToMonthView.java
  29. 33 0
      netflix-dao/src/main/java/com/cyksj/model/views/CustomerServiceOrdersDetailView.java
  30. 10 0
      netflix-dao/src/main/java/com/cyksj/model/views/ExpiredAccountDataView.java
  31. 20 0
      netflix-dao/src/main/java/com/cyksj/model/views/GroupsTripsWaitingView.java
  32. 59 0
      netflix-dao/src/main/java/com/cyksj/model/views/ServiceOrdersDetailView.java
  33. 52 0
      netflix-dao/src/main/java/com/cyksj/model/views/ServiceRenewOrdersDetailView.java
  34. 34 4
      netflix-dao/src/main/resources/mapper/AccountMapper.xml
  35. 2 1
      netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml
  36. 1 1
      netflix-dao/src/main/resources/mapper/OrderDonMapper.xml
  37. 6 0
      netflix-service/pom.xml
  38. 1 0
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  39. 4 2
      netflix-service/src/main/java/com/cyksj/service/mange/CmsAccountService.java
  40. 16 0
      netflix-service/src/main/java/com/cyksj/service/mange/SpotifySendMsgService.java
  41. 52 4
      netflix-service/src/main/java/com/cyksj/service/mange/account/CmsAccountServiceImpl.java
  42. 2 13
      netflix-service/src/main/java/com/cyksj/service/mange/coupon/impl/CouponCommonServiceImpl.java
  43. 72 0
      netflix-service/src/main/java/com/cyksj/service/mange/impl/SpotifySendMsgServiceImpl.java
  44. 2 2
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/StatisticsDataService.java
  45. 6 6
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataServiceImpl.java
  46. 110 18
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  47. 6 0
      netflix-service/src/main/java/com/cyksj/service/register/RegisterOrderDonService.java
  48. 74 1
      netflix-service/src/main/java/com/cyksj/service/register/impl/RegisterOrderDonServiceImpl.java
  49. 38 7
      netflix-service/src/main/java/com/cyksj/service/register/impl/SpotifyServiceImpl.java
  50. 21 0
      netflix-service/src/main/java/com/cyksj/task/OrderCustomerHandleScheduler.java
  51. 1 1
      netflix-service/src/main/java/com/cyksj/task/Scheduler.java
  52. 3 1
      netflix-web/src/main/java/com/cyksj/config/SaTokenConfigure.java
  53. 18 1
      netflix-web/src/main/java/com/cyksj/web/GlobalExceptionHandler.java
  54. 15 0
      netflix-web/src/main/java/com/cyksj/web/controller/mail/MailService.java
  55. 64 0
      netflix-web/src/main/java/com/cyksj/web/controller/mail/impl/MailServiceImpl.java
  56. 42 2
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java
  57. 83 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/account/CmsAccountController.java
  58. 9 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupController.java
  59. 33 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/register/CmsRegisterController.java
  60. 82 7
      netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java
  61. 51 0
      netflix-web/src/main/java/com/cyksj/web/controller/register/RegisterController.java
  62. 73 31
      netflix-web/src/main/java/com/cyksj/web/controller/user/AuthorizationController.java
  63. 0 0
      netflix-web/src/main/resources/application-dev.yml
  64. 0 0
      netflix-web/src/main/resources/application-prd.yml

+ 3 - 0
netflix-common/src/main/java/com/cyksj/common/constant/TemplateEnum.java

@@ -20,6 +20,9 @@ public enum TemplateEnum {
 	EMPTY_PRD_TEMPLATE("正式环境空车位通知模板", "T88yYYsfhBIKALWGmGTY2ZJGWHx91pcAtinoRGZ0krw", " "),
 	SPOTIFY_ENV_TEMPLATE("Spotify注册测试环境模板", "gsGFRefPlGKfvNoqB5Vd43ZOsmCsZNsH4UDWefWG0sg", " "),
 	SPOTIFY_PRD_TEMPLATE("Spotify注册正式环境模板", "VPQ3cYd_OMYpyHzyaa1X5oOeWh9ZcgwZlrBw_cGqEIA", " "),
+	PLAT_ACCOUNT_UPDATE_PRD_TEMPLATE("账号修改密码正式环境通知模板", "imXjz3p0fb08Xq_hrVg0puxTKWaNdTmAYl7T1chQ8S0", " "),
+	SPOTIFY_ACCOUNT_EXPIRED_PRD_TEMPLATE("Spotify账号到期正式环境模板", "pNGASpViLNO9QsVm6nkg7P6XwwzULgdBCgytWlgsrYo", " "),
+	SPOTIFY_ACCOUNT_UN_LIMIT_SUCCESS_PRD_TEMPLATE("Spotify账号解除限制成功正式环境模板", "-K78ZoC2kBBHUVB_bWmLtkV_z98-16G7wIPqryXyTGo", " "),
 
 	;
 	private String desc;

+ 18 - 0
netflix-common/src/main/java/com/cyksj/common/util/StringUtil.java

@@ -6,6 +6,7 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Random;
 
 /**
  * @author  chan 字符串操作辅助类
@@ -409,6 +410,23 @@ public final class StringUtil {
 		}
 	}
 
+	/**
+	 * 生成4位\6位随机数
+	 */
+	public static String getRandomCodeStr(Integer digit) {
+		if (null == digit) {
+			digit = 6;
+		}
+		String code;
+		// 生成验证码
+		if (digit == 4) {
+			code = new Random().nextInt(8999) + 1000 + "";
+		} else {
+			code = new Random().nextInt(899999) + 100000 + "";
+		}
+		return code;
+	}
+
 	/**
 	 * 姓名匿名
 	 */

+ 18 - 0
netflix-common/src/main/java/com/cyksj/enums/GatewayApiCode.java

@@ -58,6 +58,24 @@ public enum GatewayApiCode {
     GROUP_INDEX_NOT_FIND(40001,"该车位不存在"),
 
     GROUP_INDEX_ALREADY_BE_USER(40002,"该车位已有人"),
+
+
+    //用户未登录
+    CMS_USER_NO_LOGIN(20000,"用户未登录!"),
+    //用户不存在
+    CMS_USER_NO_FIND(20001,"用户不存在!"),
+    //密码错误
+    CMS_USER_PASSWORD_ERROR(20002,"密码错误!"),
+    //用户无权限
+    CMS_USER_NO_PERMISSION(20003,"您没有此项操作权限"),
+    //用户无角色权限
+    CMS_USER_NO_ROLE(20004,"您没有此项操作角色"),
+    //无登录权限
+    CMS_USER_NO_LOGIN_PERMISSION(20005,"您无法登录,请联系管理员"),
+    //用户登录信息失效
+    CMS_TOKEN_VALID(20006,"登录信息已失效,请重新登录!"),
+    //踢下线
+    CMS_TOKEN_OUT_LINE(20007,"您已被管理员踢下线,请重新登录!"),
     ;
 
     private Integer code;

+ 2 - 2
netflix-dao/src/main/java/com/cyksj/mapper/AccountMapper.java

@@ -18,11 +18,11 @@ import java.util.List;
  */
 public interface AccountMapper extends BaseMapper<Account> {
 
-	IPage<ExpiredAccountDataView> getExpiredAccountView(@Param("goodsId") Integer goodsId, @Param("account") String account, @Param("renewStatus") Boolean renewStatus, @Param("handleStatus") Boolean handleStatus, @Param("cnAccount") String cnAccount, @Param("usAccount") String usAccount, @Param("userId") Integer userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("now") String now, @Param("page") Page page);
+	IPage<ExpiredAccountDataView> getExpiredAccountView(@Param("isCorpWx") Boolean isCorpWx, @Param("customerService") String customerService, @Param("goodsId") Integer goodsId, @Param("account") String account, @Param("renewStatus") Boolean renewStatus, @Param("handleStatus") Boolean handleStatus, @Param("cnAccount") String cnAccount, @Param("usAccount") String usAccount, @Param("userId") Integer userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("now") String now, @Param("page") Page page);
 
 	List<Account> getNoAppleOneAccount(String appleOne);
 
 	List<OrderDon> getNoAppleOneOrderDon(@Param("skuIds") List<Long> skuIds, @Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate, @Param("noOrderStatus") List<String> noOrderStatus);
 
-	List<ExcelAccountData> getExpiredAccountViewNoPage(@Param("goodsId") Integer goodsId, @Param("account") String account, @Param("renewStatus") Boolean renewStatus, @Param("handleStatus") Boolean handleStatus, @Param("cnAccount") String cnAccount, @Param("usAccount") String usAccount, @Param("userId") Integer userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("now") String now);
+	List<ExcelAccountData> getExpiredAccountViewNoPage(@Param("isCorpWx") Boolean isCorpWx, @Param("customerService") String customerService, @Param("goodsId") Integer goodsId, @Param("account") String account, @Param("renewStatus") Boolean renewStatus, @Param("handleStatus") Boolean handleStatus, @Param("cnAccount") String cnAccount, @Param("usAccount") String usAccount, @Param("userId") Integer userId, @Param("startTime") String startTime, @Param("endTime") String endTime, @Param("now") String now);
 }

+ 21 - 0
netflix-dao/src/main/java/com/cyksj/mapper/CustomerServiceRenewOrderRecordMapper.java

@@ -0,0 +1,21 @@
+package com.cyksj.mapper;
+
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CustomerServiceRenewOrderRecord;
+import com.cyksj.model.views.CustomerServiceHandleOrderToMonthView;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CustomerServiceRenewOrderRecordMapper
+ *创建者: JavaZou
+ *创建时间:2022/12/30 14:33
+ */
+public interface CustomerServiceRenewOrderRecordMapper extends BaseMapper<CustomerServiceRenewOrderRecord> {
+	@Select("select count(*) as numOfOrder,customer_service as renewServiceName from customer_service_renew_order_record where created_time between #{begin} and #{now} group by customer_service order by customer_service")
+	List<CustomerServiceHandleOrderToMonthView> selectCustomerToMonthData(@Param("now") DateTime now, @Param("begin") DateTime begin, @Param("type") Integer type);
+}

+ 4 - 0
netflix-dao/src/main/java/com/cyksj/mapper/GroupsRelationMapper.java

@@ -3,6 +3,7 @@ package com.cyksj.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.GroupsRelation;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
 
 import java.util.Date;
 import java.util.List;
@@ -18,4 +19,7 @@ public interface GroupsRelationMapper extends BaseMapper<GroupsRelation> {
     Integer updateExpiryTime(@Param("entity") GroupsRelation relation, @Param("expireTime") Date expireTime);
 
 	Long getGroupTripsSkuIdByRelationId(Long relationId);
+
+	@Update("update groups_relation set customer_service = null where id = #{id}")
+	void updateCustomerService(Long id);
 }

+ 4 - 2
netflix-dao/src/main/java/com/cyksj/mapper/RegisterOrderDonMapper.java

@@ -17,8 +17,7 @@ public interface RegisterOrderDonMapper extends BaseMapper<RegisterOrderDon> {
 	@Select("select platform,count(distinct user_id) as numOfUser," +
 			"count(user_id) as numOfOrder,sum(money) as money" +
 			" from register_order_don" +
-			" where created_time between #{yesZeroDate} and #{thisZeroDate} and status != 'noPayment' and platform = #{platform}" +
-			" group by platform")
+			" where created_time between #{yesZeroDate} and #{thisZeroDate} and status not in ('noPayment','close') and platform = #{platform}")
 	RegisterAccountData registerAccountDataEveryDay(@Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate, @Param("platform") String platform);
 
 	@Select("select count(user_id) as 'numOfRefundOrder',"+
@@ -26,4 +25,7 @@ public interface RegisterOrderDonMapper extends BaseMapper<RegisterOrderDon> {
 			" from register_order_don" +
 			" where created_time between #{yesZeroDate} and #{thisZeroDate} and status = 'refund' and platform = #{platform}")
 	RegisterAccountData selectRefundOrderDetail(@Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate, @Param("platform") String platform);
+
+	@Select("select id from `order_don` where sku_id = 14 and user_id = #{userId} and status not in ('close','noPayment') limit 1")
+	Integer isPaySpotifyYearTicket(Long userId);
 }

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/SpotifyUnLimitRecordMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.SpotifyUnLimitRecord;
+
+/*
+ *项目名: netflix
+ *文件名: SpotifyUnLimitRecordMapper
+ *创建者: JavaZou
+ *创建时间:2022/12/20 14:12
+ */
+public interface SpotifyUnLimitRecordMapper extends BaseMapper<SpotifyUnLimitRecord> {
+}

+ 3 - 3
netflix-dao/src/main/java/com/cyksj/mapper/manage/statistics/StatisticsUserDataMapper.java

@@ -20,13 +20,13 @@ public interface StatisticsUserDataMapper extends BaseMapper<StatisticsUserData>
 	@Select("select count(distinct unionid) from user where update_time between #{yesZeroDate} and #{thisZeroDate} and unionid != ''")
 	Integer getNumOfActiveUser(@Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate);
 
-	@Select("select count(*) from user where login_phone != ''")
+	@Select("select count(*) from user where (login_phone != '' or email != '')")
 	Integer getPcTotalOfUser();
 
-	@Select("select count(*) from user where created_time between #{yesZeroDate} and #{thisZeroDate} and login_phone != ''")
+	@Select("select count(*) from user where created_time between #{yesZeroDate} and #{thisZeroDate} and (login_phone != '' or email != '')")
 	Integer getPcNumOfNewUser(@Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate);
 
-	@Select("select count(*) from user where update_time between #{yesZeroDate} and #{thisZeroDate} and login_phone != ''")
+	@Select("select count(*) from user where update_time between #{yesZeroDate} and #{thisZeroDate} and (login_phone != '' or email != '')")
 	Integer getPcNumOfActiveUser(@Param("yesZeroDate") Date yesZeroDate, @Param("thisZeroDate") Date thisZeroDate);
 
 	@Select("select count(*) from user_distribute_shared where created_time between #{yesZeroDate} and #{thisZeroDate}")

+ 4 - 3
netflix-dao/src/main/java/com/cyksj/model/entity/BaseEntity.java

@@ -1,8 +1,9 @@
 package com.cyksj.model.entity;
 
+import com.baomidou.mybatisplus.annotation.FieldFill;
 import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
-import com.ejlchina.searcher.bean.DbField;
 import lombok.Data;
 import lombok.experimental.Accessors;
 
@@ -23,12 +24,12 @@ public class BaseEntity {
      * 创建时间
      */
 
-    @DbField("created_time")
+    @TableField(fill = FieldFill.INSERT)
     private Date createdTime;
 
     /**
      * 修改时间
      */
-    @DbField("update_time")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
     private Date updateTime;
 }

+ 23 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CustomerServiceRenewOrderRecord.java

@@ -0,0 +1,23 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CustomerServiceRenewOrderRecord
+ *创建者: JavaZou
+ *创建时间:2022/12/30 14:32
+ */
+@Getter
+@Setter
+public class CustomerServiceRenewOrderRecord extends BaseEntity{
+	private Long userId;
+
+	private Long orderId;
+
+	/**
+	 * 客服
+	 */
+	private String customerService;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/GoodsDonSku.java

@@ -43,6 +43,11 @@ public class GoodsDonSku extends BaseEntity {
      */
     private String picture;
 
+    /**
+     * 商品详情图
+     */
+    private String detail;
+
     /**
      * 标签
      */

+ 0 - 5
netflix-dao/src/main/java/com/cyksj/model/entity/GoodsDonStockRecord.java

@@ -1,16 +1,11 @@
 package com.cyksj.model.entity;
 
-import com.baomidou.mybatisplus.annotation.FieldFill;
-import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
 import com.ejlchina.searcher.bean.DbField;
-import com.ejlchina.searcher.bean.DbIgnore;
 import com.ejlchina.searcher.bean.SearchBean;
 import lombok.Getter;
 import lombok.Setter;
 
-import java.io.Serializable;
 import java.math.BigDecimal;
 import java.util.Date;
 

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/GroupsRelation.java

@@ -46,6 +46,11 @@ public class GroupsRelation extends BaseEntity {
      */
     private Boolean isHandle;
 
+    /**
+     * 客服
+     */
+    private String customerService;
+
 
     @AllArgsConstructor
     @Getter

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/entity/OrderDon.java

@@ -115,7 +115,7 @@ public class OrderDon extends BaseEntity implements Serializable {
         /**
          * 订单状态
          */
-        close("关闭订单"),
+        close("关闭"),
         noPayment("待付款"),
         hasPayment("已付款"),
         transport("已发货"),

+ 47 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/SpotifyUnLimitRecord.java

@@ -0,0 +1,47 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotEmpty;
+
+/*
+ *项目名: netflix
+ *文件名: SpotifyUnLimitRecord
+ *创建者: JavaZou
+ *创建时间:2022/12/20 12:24
+ */
+@Getter
+@Setter
+@TableName("spotify_unLimit_record")
+@SearchBean(tables = "spotify_unLimit_record")
+public class SpotifyUnLimitRecord extends BaseEntity {
+	private Long userId;
+
+	@NotEmpty
+	private String account;
+
+	@NotEmpty
+	private String password;
+
+	private Boolean isSend;
+
+	private Status status;
+
+
+	@Getter
+	public enum Status {
+		Processing("待解除"),
+		Success("解除成功"),
+			Fail("解除失败"),
+		;
+
+		String desc;
+
+		Status(String desc) {
+			this.desc = desc;
+		}
+	}
+}

+ 4 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/User.java

@@ -19,6 +19,10 @@ public class User extends BaseEntity implements Serializable{
     private String phone ;
     /** 手机号登录 */
     private String loginPhone;
+    /**
+     * 邮箱登录
+     */
+    private String email;
     /** 昵称;昵称 */
     private String nickname ;
     /** 头像;头像 */

+ 6 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelAccountData.java

@@ -81,4 +81,10 @@ public class ExcelAccountData {
 	 */
 	@ExcelProperty("是否处理过")
 	private Boolean isHandle;
+
+	@ExcelProperty("企业微信")
+	private String isCorpWx;
+
+	@ExcelProperty("客服")
+	private String customerService;
 }

+ 26 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelAccountTemplateData.java

@@ -0,0 +1,26 @@
+package com.cyksj.model.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExcelAccountTemplateData
+ *创建者: JavaZou
+ *创建时间:2022/12/27 14:10
+ */
+@Getter
+@Setter
+public class ExcelAccountTemplateData {
+	@ExcelProperty("账号")
+	private String account;
+
+	@ExcelProperty("密码")
+	private String password;
+
+	@ExcelProperty("续费时间")
+	private Date expiryTime;
+}

+ 48 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelManageAccountData.java

@@ -0,0 +1,48 @@
+package com.cyksj.model.excel;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExcelManageAccountData
+ *创建者: JavaZou
+ *创建时间:2022/12/27 13:59
+ */
+@Getter
+@Setter
+@SearchBean(tables = "account a left join goods_don g on a.goods_id = g.id " +
+		"left join groups_trips gt on gt.account_id = a.id " +
+		"left join goods_don_sku gdk on gdk.id = gt.sku_id")
+public class ExcelManageAccountData {
+
+	@ExcelIgnore
+	@DbField("a.id")
+	private Long id;
+
+	@ExcelProperty("平台")
+	@DbField("g.title")
+	private String title;
+
+	@ExcelProperty("规格")
+	@DbField("gdk.spec_val")
+	private String specVal;
+
+	@ExcelProperty("账号")
+	@DbField("a.account")
+	private String account;
+
+	@ExcelProperty("密码")
+	@DbField("a.password")
+	private String password;
+
+	@ExcelProperty("到期时间")
+	@DbField("a.expiry_time")
+	private Date expiryTime;
+}

+ 153 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelOrderDonData.java

@@ -0,0 +1,153 @@
+package com.cyksj.model.excel;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.cyksj.model.entity.OrderDon;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ExcelOrderDonData
+ *创建者: JavaZou
+ *创建时间:2022/12/29 10:25
+ */
+@Getter
+@Setter
+@SearchBean(tables = "order_don o left join goods_don_sku sku on o.sku_id = sku.id " +
+		"left join goods_don g on g.id = sku.goods_id " +
+		"left join user u on o.user_id = u.id " +
+		"left join groups_relation gr on gr.id = o.relation_id " +
+		"left join channel_popularize cp on o.popularize_id = cp.id " +
+		"left join channel c on c.id = cp.channel_id "
+)
+public class ExcelOrderDonData {
+
+	@DbField("o.id")
+	@ExcelIgnore
+	private Long id;
+
+	@DbField("c.id")
+	@ExcelIgnore
+	private Long channelId;
+
+	@DbField("o.relation_id")
+	@ExcelIgnore
+	private Long relationId;
+
+	@DbField("o.transaction_id")
+	@ExcelIgnore
+	private String transactionId;
+
+	@DbField("g.type")
+	@ExcelIgnore
+	private Integer goodsDonType;
+
+	@DbField("o.order_no")
+	@ExcelProperty("订单号")
+	private String orderNo;
+
+	@DbField("c.name")
+	@ExcelProperty("渠道")
+	private String channel;
+
+	@DbField("g.title")
+	@ExcelProperty("商品")
+	private String title;
+
+	@DbIgnore
+	@ExcelProperty("优惠券")
+	private String couponName;
+
+	@DbField("o.coupon_money")
+	@ExcelProperty("减免金额")
+	private BigDecimal couponMoney;
+
+	@DbField("sku.spec_val")
+	@ExcelProperty("规格")
+	private String specVal;
+
+	@DbField("o.money")
+	@ExcelProperty("金额")
+	private BigDecimal money;
+
+	@DbField("u.id")
+	@ExcelProperty("用户ID")
+	private Long userId;
+
+	@DbField("u.nickname")
+	@ExcelProperty("用户名")
+	private String nickname;
+
+	@DbField("gr.account")
+	@ExcelProperty("账号")
+	private String account;
+
+	@DbIgnore
+	@ExcelProperty("售前")
+	private String beforeServiceName;
+
+	@DbIgnore
+	@ExcelProperty("售后")
+	private String afterServiceName;
+
+	@DbField("o.status")
+	@ExcelIgnore
+	private OrderDon.Status status;
+
+	@ExcelProperty("状态")
+	private String statusStr;
+
+	@DbField("o.created_time")
+	@ExcelProperty("创建时间")
+	private Date createdTime;
+
+	@DbField("o.type")
+	@ExcelIgnore
+	private OrderDon.Type type;
+
+	@ExcelProperty("支付方式")
+	private String typeStr;
+
+	@ExcelProperty("收货地址")
+	@DbIgnore
+	private String transportStr;
+
+	@ExcelProperty("收货人")
+	@DbIgnore
+	private String receiver;
+
+	@ExcelProperty("手机号")
+	@DbIgnore
+	private String phone;
+
+	@ExcelProperty("物流公司")
+	@DbIgnore
+	private String codeCn;
+
+	@ExcelProperty("物流单号")
+	@DbIgnore
+	private String trackingNumber;
+
+	@DbField("o.order_type")
+	@ExcelIgnore
+	private Integer orderType;
+
+	@DbField("o.num")
+	@ExcelIgnore
+	private Integer num;
+
+	@DbField("o.coupon_user_id")
+	@ExcelIgnore
+	private Long couponUserId;
+
+	@DbField("cp.id")
+	@ExcelIgnore
+	private Long popularizeId;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/request/ReqGoodsSkuInsert.java

@@ -39,6 +39,11 @@ public class ReqGoodsSkuInsert {
          */
         private String picture;
 
+        /**
+         * 商品详细图
+         */
+        private String detail;
+
         /**
          * 规格说明信息
          */

+ 8 - 1
netflix-dao/src/main/java/com/cyksj/model/manage/views/AccountView.java

@@ -16,7 +16,8 @@ import java.util.Date;
 @Getter
 @Setter
 @SearchBean(tables = "account a left join goods_don g on a.goods_id = g.id " +
-        "left join groups_trips gt on gt.account_id = a.id")
+        "left join groups_trips gt on gt.account_id = a.id " +
+        "left join goods_don_sku gdk on gdk.id = gt.sku_id")
 public class AccountView {
 
     @DbField("a.id")
@@ -70,4 +71,10 @@ public class AccountView {
     @DbField("a.remark")
     private String remark;
 
+    /**
+     * 规格
+     */
+    @DbField("gdk.spec_val")
+    private String specVal;
+
 }

+ 8 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/GroupsRelationView.java

@@ -3,6 +3,7 @@ package com.cyksj.model.manage.views;
 import com.cyksj.model.entity.GroupsRelation;
 import com.ejlchina.searcher.bean.DbField;
 import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -22,6 +23,13 @@ public class GroupsRelationView {
     @DbField("gr.id")
     private Long id;
 
+    @DbField("gt.account_id")
+    @JsonIgnore
+    private Long accountId;
+
+    @DbField("a.account")
+    private String tripsAccount;
+
     @DbField("g.id")
     private Long goodsId;
 

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/OrderDonView.java

@@ -60,6 +60,9 @@ public class OrderDonView {
     @DbField("sku.spec_val")
     private String specVal;
 
+    @DbField("sku.months")
+    private Integer months;
+
     @DbField("u.id")
     private Long userId;
 

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/model/request/BatchServiceReq.java

@@ -0,0 +1,18 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: BatchServiceReq
+ *创建者: JavaZou
+ *创建时间:2022/12/30 14:43
+ */
+@Getter
+@Setter
+public class BatchServiceReq {
+	private Long relationId;
+
+	private String customerService;
+}

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/model/request/LoginPhoneReq.java

@@ -14,6 +14,11 @@ import lombok.Setter;
 public class LoginPhoneReq {
 	private String phone;
 
+	/**
+	 * 邮箱
+	 */
+	private String email;
+
 	/**
 	 * 验证码
 	 */
@@ -23,4 +28,9 @@ public class LoginPhoneReq {
 	 * 分销人用户id
 	 */
 	private Long sharedId;
+
+	/**
+	 * 1短信登录、2邮箱登录
+	 */
+	private Integer type;
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CustomerServiceHandleOrderToMonthView.java

@@ -18,6 +18,8 @@ public class CustomerServiceHandleOrderToMonthView {
 
 	private String afterServiceName;
 
+	private String renewServiceName;
+
 	/**
 	 * 订单数量
 	 */

+ 33 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CustomerServiceOrdersDetailView.java

@@ -0,0 +1,33 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.SearchResult;
+import lombok.Getter;
+import lombok.Setter;
+
+
+/*
+ *项目名: netflix
+ *文件名: CustomerServiceOrdersDetailView
+ *创建者: JavaZou
+ *创建时间:2022/12/30 15:05
+ */
+@Getter
+@Setter
+public class CustomerServiceOrdersDetailView {
+	/**
+	 * 总数量
+	 */
+	private Integer orders;
+
+	/**
+	 * Chromecast订单数量
+	 */
+	private Integer AppleTV;
+
+	/**
+	 * Chromecast订单数量
+	 */
+	private Integer Chromecast;
+
+	private SearchResult result;
+}

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/model/views/ExpiredAccountDataView.java

@@ -98,4 +98,14 @@ public class ExpiredAccountDataView {
 	 * 处理状态
 	 */
 	private Boolean status;
+
+	/**
+	 * 是否加入了企业微信 true是
+	 */
+	private Boolean isCorpWx;
+
+	/**
+	 * 客服
+	 */
+	private String customerService;
 }

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/model/views/GroupsTripsWaitingView.java

@@ -0,0 +1,20 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@SearchBean(tables = "groups_trips gt left join goods_don_sku sku on gt.sku_id = sku.id " +
+        "left join goods_don g on g.id = sku.goods_id",
+        where = "gt.status = 'waiting' and g.type = 1 and g.second_type = 2",
+        groupBy = "g.title")
+public class GroupsTripsWaitingView {
+    @DbField("g.title")
+    private String title;
+
+    @DbField("count(*)")
+    private Integer waitingNum;
+}

+ 59 - 0
netflix-dao/src/main/java/com/cyksj/model/views/ServiceOrdersDetailView.java

@@ -0,0 +1,59 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.OrderDon;
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ServiceOrdersDetail
+ *创建者: JavaZou
+ *创建时间:2022/12/30 15:07
+ */
+@Getter
+@Setter
+@SearchBean(tables = "real_goods_order_handle rh left join order_don od on od.id = rh.order_id" +
+		" left join goods_don_sku gdk on gdk.id = od.sku_id" +
+		" left join goods_don gd on gd.id = gdk.goods_id" +
+		" left join user u on u.id = od.user_id")
+public class ServiceOrdersDetailView {
+	@DbField("gd.id")
+	private Long goodsId;
+
+	@DbField("gd.title")
+	private String goodsName;
+
+	@DbField("gdk.id")
+	private Long skuId;
+
+	@DbField("gdk.spec_val")
+	private String specVal;
+
+	@DbField("od.money")
+	private Long money;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("u.id")
+	private Long userId;
+
+	@DbField("od.status")
+	private OrderDon.Status status;
+
+	@DbField("od.created_time")
+	private Date createdTime;
+
+	@DbField("rh.before_service_name")
+	private String beforeServiceName;
+
+	@DbField("rh.after_service_name")
+	private String afterServiceName;
+
+	@DbField("rh.created_time")
+	private Date recordCreatedTime;
+}

+ 52 - 0
netflix-dao/src/main/java/com/cyksj/model/views/ServiceRenewOrdersDetailView.java

@@ -0,0 +1,52 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: ServiceRenewOrdersDetailView
+ *创建者: JavaZou
+ *创建时间:2022/12/30 15:18
+ */
+@Getter
+@Setter
+@SearchBean(tables = "customer_service_renew_order_record cr left join order_don od on od.id = cr.order_id" +
+		" left join goods_don_sku gdk on gdk.id = od.sku_id" +
+		" left join goods_don gd on gd.id = gdk.goods_id" +
+		" left join user u on u.id = od.user_id")
+public class ServiceRenewOrdersDetailView {
+	@DbField("gd.id")
+	private Long goodsId;
+
+	@DbField("gd.title")
+	private String goodsName;
+
+	@DbField("gdk.id")
+	private Long skuId;
+
+	@DbField("gdk.spec_val")
+	private String specVal;
+
+	@DbField("od.money")
+	private Long money;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("u.id")
+	private Long userId;
+
+	@DbField("od.created_time")
+	private Date createdTime;
+
+	@DbField("cr.customer_service")
+	private String customerService;
+
+	@DbField("cr.created_time")
+	private Date recordCreatedTime;
+}

+ 34 - 4
netflix-dao/src/main/resources/mapper/AccountMapper.xml

@@ -17,15 +17,30 @@
                gr.start_time as payTime,
                gr.expiry_time,
                gr.renew_status,
-               gr.is_handle
+               gr.is_handle,
+               gr.customer_service,
+               if(cu.id is null,false,true) as isCorpWx
         from `account` a left join `groups_trips` gt on gt.account_id = a.id
         left join `groups_relation` gr on gr.groups_id = gt.id
         left join `goods_don_sku` gdk on gdk.id = gt.sku_id
         left join `goods_don` gd on gd.id = a.goods_id
         left join `user` u on gr.user_id = u.id
+        left join (select id,unionid from `corp_user` where unionid != '') cu on cu.unionid = u.unionid
         <where>
+            <if test="isCorpWx != null and isCorpWx">
+                cu.id is not null
+            </if>
+            <if test="isCorpWx != null and !isCorpWx">
+                cu.id is null
+            </if>
+            <if test="customerService != null and customerService != '' and customerService != 'waiting'">
+                and gr.customer_service = #{customerService}
+            </if>
+            <if test="customerService == 'waiting'">
+                and gr.customer_service is null
+            </if>
             <if test="goodsId != null">
-                a.goods_id = #{goodsId}
+                and a.goods_id = #{goodsId}
             </if>
             <if test="account != null and account != ''">
                 and a.account like '%${account}%'
@@ -92,15 +107,30 @@
         gr.start_time as payTime,
         gr.expiry_time,
         gr.renew_status,
-        gr.is_handle
+        gr.is_handle,
+        gr.customer_service,
+        if(cu.id is null,'未加入','已加入') as isCorpWx
         from `account` a left join `groups_trips` gt on gt.account_id = a.id
         left join `groups_relation` gr on gr.groups_id = gt.id
         left join `goods_don_sku` gdk on gdk.id = gt.sku_id
         left join `goods_don` gd on gd.id = a.goods_id
         left join `user` u on gr.user_id = u.id
+        left join (select id,unionid from `corp_user` where unionid != '') cu on cu.unionid = u.unionid
         <where>
+            <if test="isCorpWx != null and isCorpWx">
+                cu.id is not null
+            </if>
+            <if test="isCorpWx != null and !isCorpWx">
+                cu.id is null
+            </if>
+            <if test="customerService != null and customerService != '' and customerService != 'waiting'">
+                and gr.customer_service = #{customerService}
+            </if>
+            <if test="customerService == 'waiting'">
+                and gr.customer_service is null
+            </if>
             <if test="goodsId != null">
-                a.goods_id = #{goodsId}
+                and a.goods_id = #{goodsId}
             </if>
             <if test="account != null and account != ''">
                 and a.account like '%${account}%'

+ 2 - 1
netflix-dao/src/main/resources/mapper/GroupRelationMapper.xml

@@ -10,7 +10,8 @@
             renew_status = #{entity.renewStatus},
             is_handle    = #{entity.isHandle},
             user_id      = #{entity.userId},
-            account = #{entity.account}
+            account = #{entity.account},
+            customer_service = null
         where id = #{entity.id} and expiry_time = #{expireTime}
     </update>
 

+ 1 - 1
netflix-dao/src/main/resources/mapper/OrderDonMapper.xml

@@ -38,7 +38,7 @@
     </select>
 
     <select id="getAppleOneOrderDons" resultType="com.cyksj.model.entity.OrderDon">
-        select distinct s.* from (select id,sku_id,user_id,money,is_distribute from `order_don`
+        select distinct s.* from (select id,sku_id,user_id,money,is_distribute,`type` from `order_don`
         where sku_id in
         <foreach collection="skuIds" item="item" open="(" separator="," close=")">
             #{item}

+ 6 - 0
netflix-service/pom.xml

@@ -29,6 +29,12 @@
             <artifactId>spring-boot-configuration-processor</artifactId>
             <optional>true</optional>
         </dependency>
+
+        <!-- 邮箱jar -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 1 - 0
netflix-service/src/main/java/com/cyksj/redis/RedisService.java

@@ -590,6 +590,7 @@ public class RedisService {
 	    MAPPING_INDEX_KEY("mapper:index:", "授权跳转", 60 * 10L),
         WX_LOGIN_AUTH_URI("wx_login_auth_uri:%s", "微信网页授权登录uri", 60 * 60 * 24L),
         RECOMMEND_COUPON_DAY("recommend_coupon_day:%s", "推荐优惠券弹窗", 60 * 60 * 24L),
+        EMAIL_CODE_KEY("email_code_key:%s", "邮箱验证码", 60 * 3),
         LOTTERY_KEY("lottery_key:%s", "抽奖key", 60 * 60 * 24l),
         TASK_TIME_LIMIT_KEY("task_time_limit:%s", "一次性任务key", 30),
         ;

+ 4 - 2
netflix-service/src/main/java/com/cyksj/service/mange/CmsAccountService.java

@@ -15,11 +15,13 @@ import java.util.List;
  * @date 2022/9/27 6:23 PM
  */
 public interface CmsAccountService extends IService<Account> {
-	IPage<ExpiredAccountDataView> getExpiredAccountView(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now, Long offset, Long limit);
+	IPage<ExpiredAccountDataView> getExpiredAccountView(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now, Long offset, Long limit);
 
 	List<Account> getNoAppleOneAccount(String appleOne);
 
 	List<OrderDon> getNoAppleOneOrderDon(List<Long> skuIds, Date yesZeroDate, Date thisZeroDate, List<String> noOrderStatus);
 
-	List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startDate, String endDate, String toString);
+	List<ExcelAccountData> getExpiredAccountViewNoPage(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startDate, String endDate, String toString);
+
+	void sendAccountTemplateMsg(Account account) throws Exception;
 }

+ 16 - 0
netflix-service/src/main/java/com/cyksj/service/mange/SpotifySendMsgService.java

@@ -0,0 +1,16 @@
+package com.cyksj.service.mange;
+
+import com.cyksj.model.entity.SpotifyUnLimitRecord;
+
+/*
+ *项目名: netflix
+ *文件名: SpotifySchedule
+ *创建者: JavaZou
+ *创建时间:2022/12/21 12:10
+ */
+public interface SpotifySendMsgService {
+	/**
+	 * 发放Spotify账号注册成功模板消息
+	 */
+	void sendSpotifyUnLimitSuccessMsg(SpotifyUnLimitRecord record) throws Exception;
+}

+ 52 - 4
netflix-service/src/main/java/com/cyksj/service/mange/account/CmsAccountServiceImpl.java

@@ -1,14 +1,27 @@
 package com.cyksj.service.mange.account;
 
+import cn.hutool.core.date.DateTime;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.EnvCommonService;
+import com.cyksj.common.constant.TemplateEnum;
+import com.cyksj.common.util.Jsons;
 import com.cyksj.mapper.AccountMapper;
+import com.cyksj.mapper.UserMapper;
+import com.cyksj.model.dto.WxMpTemplateData;
+import com.cyksj.model.dto.WxMpTemplateMessage;
 import com.cyksj.model.entity.Account;
 import com.cyksj.model.entity.OrderDon;
+import com.cyksj.model.entity.User;
 import com.cyksj.model.excel.ExcelAccountData;
+import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.views.ExpiredAccountDataView;
 import com.cyksj.service.mange.CmsAccountService;
+import com.cyksj.service.wechat.WeChatService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -26,14 +39,49 @@ import java.util.List;
 public class CmsAccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements CmsAccountService {
 	private final AccountMapper accountMapper;
 
+	private final WeChatService weChatService;
+
+	private final BeanSearcher beanSearcher;
+
+	private final EnvCommonService envCommonService;
+
+	private final UserMapper userMapper;
+
+	@Override
+	public IPage<ExpiredAccountDataView> getExpiredAccountView(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now, Long offset, Long limit) {
+		return accountMapper.getExpiredAccountView(isCorpWx, customerService, goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, now, new Page<>(offset, limit));
+	}
+
 	@Override
-	public IPage<ExpiredAccountDataView> getExpiredAccountView(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now, Long offset, Long limit) {
-		return accountMapper.getExpiredAccountView(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, now, new Page<>(offset, limit));
+	public List<ExcelAccountData> getExpiredAccountViewNoPage(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now) {
+		return accountMapper.getExpiredAccountViewNoPage(isCorpWx, customerService, goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, now);
 	}
 
 	@Override
-	public List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, String startTime, String endTime, String now) {
-		return accountMapper.getExpiredAccountViewNoPage(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, now);
+	public void sendAccountTemplateMsg(Account account) throws Exception {
+		List<GroupsRelationView> relations = beanSearcher.searchList(GroupsRelationView.class, MapUtils.builder()
+				.field(GroupsRelationView::getAccountId, account.getId()).op(Operator.Equal)
+				.field(GroupsRelationView::getStatus, "validity").op(Operator.Equal)
+				.build());
+		String url = String.format("%sauth/weChat?url=%syinhe/web/ticket", envCommonService.getHost(), envCommonService.getDomain());
+		log.info("开始发送账号修改密码模板消息,大小:{}", relations.size());
+		for (GroupsRelationView relation : relations) {
+			TemplateEnum templateEnum = TemplateEnum.PLAT_ACCOUNT_UPDATE_PRD_TEMPLATE;
+			User user = userMapper.selectById(relation.getUserId());
+			if (user != null) {
+				WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
+						.setToUser(user.getOpenId())
+						.setTemplateId(templateEnum.getTemplateId())
+						.setUrl(url);
+				WxMpTemplateMessage.TemplateData data = templateMessage.getData();
+				data.setFirst(new WxMpTemplateData(String.format("%s账号密码更改提醒", relation.getTitle())));
+				data.setKeyword1(new WxMpTemplateData(relation.getTripsAccount()));
+				data.setKeyword2(new WxMpTemplateData(DateTime.now().toString("YYYY-MM-mm HH:mm:ss")));
+				data.setRemark(new WxMpTemplateData("您的账号密码已经更新,请到我的车票查看,长期用户请选择拼车/年付。\n" +
+						"如您不是在公众号购买,请在浏览器登录查看我的车票"));
+				weChatService.sendTemplateMessage(weChatService.getAccessToken(), Jsons.toJson(templateMessage));
+			}
+		}
 	}
 
 	@Override

+ 2 - 13
netflix-service/src/main/java/com/cyksj/service/mange/coupon/impl/CouponCommonServiceImpl.java

@@ -184,19 +184,8 @@ public class CouponCommonServiceImpl implements CouponCommonService {
 		if (coupon == null || !coupon.getDeleted()) {
 			throw BusinessRuntimeException.getInstance("该优惠券已被删除");
 		}
-		CouponUser couponUser = new CouponUser();
-		couponUser.setUserId(user.getId());
-		couponUser.setCouponId(corpCouponSendReq.getCouponId());
-		//领卷中心
-		couponUser.setChannel(CouponUser.Channel.wxCorp);
-		couponUser.setStatus(CouponUser.Status.unused);
-		couponFontService.updateCouponUserValidTime(couponId, couponUser);
-		try {
-			couponUserMapper.insert(couponUser);
-		} catch (DuplicateKeyException e) {
-			log.info("用户:{}重复领取优惠券{}插入数据错误", user.getId(), corpCouponSendReq.getCouponId());
-			throw BusinessRuntimeException.getInstance("已发放该优惠券");
-		}
+		//发放优惠券给用户
+		this.sendCouponToUser(user.getId(), couponId, null, null,null, CouponUser.Channel.wxCorp);
 	}
 
 	@Override

+ 72 - 0
netflix-service/src/main/java/com/cyksj/service/mange/impl/SpotifySendMsgServiceImpl.java

@@ -0,0 +1,72 @@
+package com.cyksj.service.mange.impl;
+
+import cn.hutool.core.date.DateUtil;
+import com.cyksj.common.EnvCommonService;
+import com.cyksj.common.constant.TemplateEnum;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.mapper.SpotifyUnLimitRecordMapper;
+import com.cyksj.mapper.UserMapper;
+import com.cyksj.model.dto.WxMpTemplateData;
+import com.cyksj.model.dto.WxMpTemplateMessage;
+import com.cyksj.model.entity.SpotifyUnLimitRecord;
+import com.cyksj.model.entity.User;
+import com.cyksj.service.mange.SpotifySendMsgService;
+import com.cyksj.service.wechat.WeChatService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+
+/*
+ *项目名: netflix
+ *文件名: SpotifySendMsgServiceImpl
+ *创建者: JavaZou
+ *创建时间:2022/12/21 16:01
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class SpotifySendMsgServiceImpl implements SpotifySendMsgService {
+	private final EnvCommonService envCommonService;
+
+	private final WeChatService weChatService;
+
+	private final UserMapper userMapper;
+
+	private final SpotifyUnLimitRecordMapper spotifyUnLimitRecordMapper;
+
+	private static String spotifyUnLimitSuccessUrl;
+
+	@PostConstruct
+	public void init() {
+		String host = envCommonService.getHost();
+		String domain = envCommonService.getDomain();
+		String redirectUrl = String.format("%s%s", domain, "yinhe/web/?temp=couponId?1?goodsId?4?skuId?14");
+		spotifyUnLimitSuccessUrl = String.format("%sauth/weChat?url=%s", host, redirectUrl);
+	}
+
+	@Override
+	public void sendSpotifyUnLimitSuccessMsg(SpotifyUnLimitRecord record) throws Exception {
+		if (!EnvCommonService.active.equals(envCommonService.getEnv())) {
+			SpotifyUnLimitRecord unLimitRecord = spotifyUnLimitRecordMapper.selectById(record.getId());
+			if (unLimitRecord != null && !unLimitRecord.getIsSend()) {
+				User user = userMapper.selectById(unLimitRecord.getUserId());
+				if (user != null) {
+					WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
+							.setToUser(user.getOpenId())
+							.setTemplateId(TemplateEnum.SPOTIFY_ACCOUNT_UN_LIMIT_SUCCESS_PRD_TEMPLATE.getTemplateId())
+							.setUrl(spotifyUnLimitSuccessUrl);
+					WxMpTemplateMessage.TemplateData data = templateMessage.getData();
+					data.setFirst(new WxMpTemplateData("您的Spotify账号登录限制解除成功"));
+					data.setKeyword1(new WxMpTemplateData(user.getNickname()));
+					data.setKeyword2(new WxMpTemplateData(DateUtil.format(unLimitRecord.getUpdateTime(), "yyyy-MM-dd HH:mm:ss")));
+					data.setRemark(new WxMpTemplateData("建议您购买Spotify年付会员,无需每过14天解除限制,点击了解"));
+					weChatService.sendTemplateMessage(weChatService.getAccessToken(), Jsons.toJson(templateMessage));
+					unLimitRecord.setIsSend(true);
+					spotifyUnLimitRecordMapper.updateById(unLimitRecord);
+				}
+			}
+		}
+	}
+}

+ 2 - 2
netflix-service/src/main/java/com/cyksj/service/mange/statistics/StatisticsDataService.java

@@ -17,11 +17,11 @@ import java.util.List;
 public interface StatisticsDataService {
 	Result<BackgroundDataView> getStatisticsByCondition(Long startTime, Long endTime);
 
-	IPage<ExpiredAccountDataView> getExpiredAccountView(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer uid, Long startTime, Long endTime, Long start, Long limit);
+	IPage<ExpiredAccountDataView> getExpiredAccountView(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer uid, Long startTime, Long endTime, Long start, Long limit);
 
 	Result<String> updateRenewStatus(Integer relationId);
 
 	Result<String> updateHandleStatus(Integer relationId);
 
-	List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime);
+	List<ExcelAccountData> getExpiredAccountViewNoPage(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime);
 }

+ 6 - 6
netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataServiceImpl.java

@@ -113,7 +113,7 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 	}
 
 	@Override
-	public IPage<ExpiredAccountDataView> getExpiredAccountView(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, Long start, Long limit) {
+	public IPage<ExpiredAccountDataView> getExpiredAccountView(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, Long start, Long limit) {
 		String startDate = null;
 		String endDate = null;
 		if (startTime != null) {
@@ -122,12 +122,12 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 		if (endTime != null) {
 			endDate = DateTime.of(endTime).toString();
 		}
-		IPage<ExpiredAccountDataView> expiredAccountDataViews = accountService.getExpiredAccountView(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startDate, endDate, DateTime.now().toString(), start, limit);
+		IPage<ExpiredAccountDataView> expiredAccountDataViews = accountService.getExpiredAccountView(isCorpWx, customerService, goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startDate, endDate, DateTime.now().toString(), start, limit);
 		return expiredAccountDataViews;
 	}
 
 	@Override
-	public List<ExcelAccountData> getExpiredAccountViewNoPage(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime) {
+	public List<ExcelAccountData> getExpiredAccountViewNoPage(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime) {
 		String startDate = null;
 		String endDate = null;
 		if (startTime != null) {
@@ -136,7 +136,7 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 		if (endTime != null) {
 			endDate = DateTime.of(endTime).toString();
 		}
-		List<ExcelAccountData> expiredAccountDataViews = accountService.getExpiredAccountViewNoPage(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startDate, endDate, DateTime.now().toString());
+		List<ExcelAccountData> expiredAccountDataViews = accountService.getExpiredAccountViewNoPage(isCorpWx, customerService, goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startDate, endDate, DateTime.now().toString());
 		return expiredAccountDataViews;
 	}
 
@@ -227,7 +227,7 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 
 	private void headerData(BackgroundDataView backgroundDataView, Date startDate, Date endDate) {
 		//总订单数
-		List<OrderDon> orderDons = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).notIn(OrderDon::getStatus, StatisticsDataScheduler.noOrderStatus).ge(ObjectUtil.isNotNull(startDate), OrderDon::getCreatedTime, startDate).le(ObjectUtil.isNotNull(endDate), OrderDon::getCreatedTime, endDate))).orElse(new ArrayList<>());
+		List<OrderDon> orderDons = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).notIn(OrderDon::getStatus, StatisticsDataScheduler.noOrderStatus).ge(ObjectUtil.isNotNull(startDate), OrderDon::getCreatedTime, startDate).lt(ObjectUtil.isNotNull(endDate), OrderDon::getCreatedTime, endDate))).orElse(new ArrayList<>());
 		backgroundDataView.setTotalNumOfOrder(orderDons.size());
 		//总下单用户数
 		backgroundDataView.setTotalNumOfUser(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet()).size());
@@ -235,7 +235,7 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 		//总订单金额
 		backgroundDataView.setTotalOrderAmount(totalMoney.isPresent() ? totalMoney.get().divide(BigDecimal.valueOf(100)) : BigDecimal.ZERO);
 		//退款列表
-		List<OrderDon> refundOrders = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getStatus, OrderDon.Status.refund).ge(ObjectUtil.isNotNull(startDate), OrderDon::getCreatedTime, startDate).le(ObjectUtil.isNotNull(endDate), OrderDon::getCreatedTime, endDate).select(OrderDon::getId, OrderDon::getRefundMoney))).orElse(new ArrayList<>());
+		List<OrderDon> refundOrders = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getStatus, OrderDon.Status.refund).ge(ObjectUtil.isNotNull(startDate), OrderDon::getCreatedTime, startDate).lt(ObjectUtil.isNotNull(endDate), OrderDon::getCreatedTime, endDate).select(OrderDon::getId, OrderDon::getRefundMoney))).orElse(new ArrayList<>());
 		backgroundDataView.setTotalNumOfRefundOrder(refundOrders.size());
 		//退款金额
 		Optional<BigDecimal> totalRefundAmount = refundOrders.stream().map(OrderDon::getRefundMoney).reduce(BigDecimal::add);

+ 110 - 18
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -57,6 +57,7 @@ import com.cyksj.service.market.task.TaskService;
 import com.cyksj.service.order.OrderDonService;
 import com.cyksj.service.wechat.WeChatService;
 import com.cyksj.task.JobManager;
+import com.cyksj.task.OrderCustomerHandleScheduler;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapUtils;
@@ -156,6 +157,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final TaskTypeMapper taskTypeMapper;
 
+	private final CustomerServiceRenewOrderRecordMapper customerServiceRenewOrderRecordMapper;
+
 	@Transactional(rollbackFor = Throwable.class)
 	@Override
 	public OrderDon submit(OrderPayRequest payRequest) throws Exception {
@@ -430,6 +433,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			//无车队,新增车队
 			checkNoGroupsTripsAndSendMsg(sku, order);
 		}
+		//客服续费订单
+		handleServiceRenewOrder(order);
 		//完成任务
 		TASK_POOL.execute(() -> {
 			TaskType taskType = taskTypeMapper.selectOne(Wrappers.lambdaQuery(TaskType.class)
@@ -686,6 +691,9 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (null == orderDon) {
 			throw BusinessRuntimeException.getInstance("订单不存在.");
 		}
+		if (orderDon.getPopularizeId() == null) {
+			orderDon.setShopId(null);
+		}
 		boolean signVerified = AlipaySignature.rsaCheckV1(params,shopConfigMapper.getByAppId((Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID))).getPublicKey(), BaseZfbConfig.charset, BaseZfbConfig.signType);
 		if (!signVerified) {
 			log.error("支付宝回调校验签名错误,自定义入参:{}", params.get("body"));
@@ -719,6 +727,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			//无车队,新增车队
 			checkNoGroupsTripsAndSendMsg(sku, orderDon);
 		}
+		//客服续费订单
+		handleServiceRenewOrder(orderDon);
 		//完成任务
 		TASK_POOL.execute(() -> {
 			TaskType taskType = taskTypeMapper.selectOne(Wrappers.lambdaQuery(TaskType.class)
@@ -740,7 +750,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			throw BusinessRuntimeException.getInstance("该订单不是微信支付的订单");
 		}
 		//校验订单是否符合退款
-		checkOrderRefund(orderDon, goodsDon);
+		checkOrderRefund(orderDon, goodsDon, sku);
 		if (wxOrderRefundReq.getRefundMoney().compareTo(orderDon.getMoney()) > 0) {
 			throw BusinessRuntimeException.getInstance("退款金额大于订单金额");
 		}
@@ -777,7 +787,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			goodsDonStockService.upStockRelate(null, orderDon.getId(),"refund");
 		});
 		//虚物订单退款,清出车队,车票变为已过期
-		clearUnrealGoodsDetail(orderDon, goodsDon);
+		clearUnrealGoodsDetail(orderDon, goodsDon, sku);
 		//若存在优惠券使用,退还优惠券
 		if (orderDon.getCouponUserId() != 0) {
 			redisService.set(String.format("%s-%s", orderDon.getId(), orderDon.getCouponUserId()), orderDon.getId(), 120l);
@@ -799,7 +809,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			throw BusinessRuntimeException.getInstance("该订单不是支付宝支付的订单");
 		}
 		//校验订单是否符合退款
-		checkOrderRefund(orderDon, goodsDon);
+		checkOrderRefund(orderDon, goodsDon, sku);
 		if (zfbOrderRefundReq.getRefundMoney().compareTo(orderDon.getMoney()) > 0) {
 			throw BusinessRuntimeException.getInstance("退款金额大于订单金额");
 		}
@@ -812,6 +822,9 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		}
 		String outRefundNo = "";
 		if (OrderDon.Type.EX_CODE != orderDon.getType()) {
+			if (orderDon.getPopularizeId() == null) {
+				orderDon.setShopId(null);
+			}
 			//支付宝退款
 			AlipayClient alipayClient = AliPayCommon.getAlipayClient(shopConfigMapper.getByAppId(Optional.ofNullable(orderDon.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID)));
 			AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
@@ -850,7 +863,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				goodsDonStockService.upStockRelate(null, orderDon.getId(),"refund");
 			});
 			//虚物订单退款,清出车队,车票变为已过期
-			clearUnrealGoodsDetail(orderDon, goodsDon);
+			clearUnrealGoodsDetail(orderDon, goodsDon, sku);
 			if (orderDon.getCouponUserId() != 0) {
 				//zfb订单退款,返还优惠券
 				updateOrderDonCouponStatus(orderDon);
@@ -865,16 +878,53 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	 * @param orderDon
 	 * @param goodsDon
 	 */
-	private void clearUnrealGoodsDetail(OrderDon orderDon, GoodsDon goodsDon) {
+	private void clearUnrealGoodsDetail(OrderDon orderDon, GoodsDon goodsDon, GoodsDonSku sku) {
 		if (goodsDon != null && goodsDon.getType() == 1) {
 			//清出车队,车位变为已过期
 			Long relationId = orderDon.getRelationId();
+			Integer months = sku.getMonths();
+			Integer num = orderDon.getNum();
+			DateTime now = DateTime.now();
 			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+			Date expiryTime = relation.getExpiryTime();
+			Boolean isFlag = true;
+			if (orderDon.getOrderType() == 2) {
+				//是否有其他付款订单是否过期
+				OrderDon otherOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+						.eq(OrderDon::getRelationId, relationId)
+						.eq(OrderDon::getUserId, orderDon.getUserId())
+						.ne(OrderDon::getId, orderDon.getId())
+						.notIn(OrderDon::getStatus, OrderCustomerHandleScheduler.noOrderStatus)
+						.orderByDesc(OrderDon::getId)
+						.last("limit 1"));
+				if (otherOrder != null) {
+					DateTime endTime = DateUtil.offsetMonth(otherOrder.getCreatedTime(), months * num);
+					if (now.isBefore(endTime)) {
+						isFlag = false;
+					}
+				}
+			}
 			if (relation != null) {
-				relation.setUserId(0l);
-				relation.setStatus(GroupsRelation.Status.none);
-				groupsRelationMapper.updateById(relation);
-				delRelation(relation.getId());
+				if (isFlag) {
+					relation.setUserId(0l);
+					relation.setStatus(GroupsRelation.Status.none);
+					if (expiryTime != null) {
+						relation.setStartTime(null);
+						relation.setExpiryTime(null);
+						relation.setIsHandle(false);
+						relation.setRenewStatus(false);
+						relation.setAccount(null);
+						groupsRelationMapper.updateExpiryTime(relation, expiryTime);
+					}else {
+						groupsRelationMapper.updateById(relation);
+					}
+					delRelation(relation.getId());
+				} else {
+					if (expiryTime != null) {
+						relation.setExpiryTime(DateUtil.offsetMonth(expiryTime, -months * num));
+						groupsRelationMapper.updateById(relation);
+					}
+				}
 			}
 		}
 	}
@@ -883,7 +933,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	 * 校验订单是否符合退款操作
 	 * @param orderDon
 	 */
-	private void checkOrderRefund(OrderDon orderDon, GoodsDon goodsDon) {
+	private void checkOrderRefund(OrderDon orderDon,GoodsDon goodsDon, GoodsDonSku sku) {
 		if (orderDon == null) {
 			throw BusinessRuntimeException.getInstance("订单不存在");
 		}
@@ -896,16 +946,40 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (OrderDon.Status.noPayment == orderDon.getStatus() || OrderDon.Status.close == orderDon.getStatus()) {
 			throw BusinessRuntimeException.getInstance("订单{0}状态不支持退款", orderDon.getStatus());
 		}
-		//续费订单
-		if (orderDon.getOrderType() == 2 && OrderDon.Type.EX_CODE != orderDon.getType()) {
-			throw BusinessRuntimeException.getInstance("续费订单不支持退款");
-		}
-		if (goodsDon != null && OrderDon.Type.EX_CODE != orderDon.getType()) {
-			//虚物订单 7天内可退款
-			if (goodsDon.getType() == 1 && DateTime.now().isAfter(DateUtil.offsetDay(orderDon.getPayTime(), 7))) {
-				throw BusinessRuntimeException.getInstance("该虚物订单已过退款时间");
+		if (goodsDon.getType() == 1) {
+			//虚拟原订单是否已过期
+			Integer months = sku.getMonths();
+			Integer num = orderDon.getNum();
+			DateTime now = DateTime.now();
+			DateTime endTime = DateUtil.offsetMonth(orderDon.getCreatedTime(), months * num);
+			if (now.isAfter(endTime)) {
+				throw BusinessRuntimeException.getInstance("该订单车票已过期");
+			}
+			if (orderDon.getOrderType() == 1) {
+				//是否有其他付款订单是否过期
+				OrderDon otherOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+						.eq(OrderDon::getRelationId, orderDon.getRelationId())
+						.eq(OrderDon::getUserId, orderDon.getUserId())
+						.ne(OrderDon::getId, orderDon.getId())
+						.notIn(OrderDon::getStatus, OrderCustomerHandleScheduler.noOrderStatus)
+						.eq(OrderDon::getOrderType, 2)
+						.orderByDesc(OrderDon::getId)
+						.last("limit 1"));
+				if (otherOrder != null) {
+					throw BusinessRuntimeException.getInstance("该笔订单存在续费订单,请先退掉续费订单");
+				}
 			}
 		}
+		//续费订单
+//		if (orderDon.getOrderType() == 2 && OrderDon.Type.EX_CODE != orderDon.getType()) {
+//			throw BusinessRuntimeException.getInstance("续费订单不支持退款");
+//		}
+//		if (goodsDon != null && OrderDon.Type.EX_CODE != orderDon.getType()) {
+//			//虚物订单 7天内可退款
+//			if (goodsDon.getType() == 1 && DateTime.now().isAfter(DateUtil.offsetDay(orderDon.getPayTime(), 7))) {
+//				throw BusinessRuntimeException.getInstance("该虚物订单已过退款时间");
+//			}
+//		}
 	}
 
 	/**
@@ -1398,4 +1472,22 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		}
 		return relation;
 	}
+
+	public void handleServiceRenewOrder(OrderDon orderDon) {
+		//续费订单
+		if (orderDon.getOrderType() == 2) {
+			Long relationId = orderDon.getRelationId();
+			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+			if (StrUtil.isNotBlank(relation.getCustomerService())) {
+				//记录客服续费订单业绩
+				CustomerServiceRenewOrderRecord renewOrderRecord = new CustomerServiceRenewOrderRecord();
+				renewOrderRecord.setUserId(orderDon.getUserId());
+				renewOrderRecord.setOrderId(orderDon.getId());
+				renewOrderRecord.setCustomerService(relation.getCustomerService());
+				customerServiceRenewOrderRecordMapper.insert(renewOrderRecord);
+
+				groupsRelationMapper.updateCustomerService(relation.getId());
+			}
+		}
+	}
 }

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/register/RegisterOrderDonService.java

@@ -4,8 +4,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.cyksj.dto.Result;
 import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.RegisterOrderDon;
+import com.cyksj.model.entity.SpotifyUnLimitRecord;
 import com.cyksj.model.manage.request.RegisterReq;
 import com.cyksj.model.request.WxOrderRefundReq;
+import com.cyksj.model.views.CouponUserView;
 
 import java.util.Map;
 
@@ -28,4 +30,8 @@ public interface RegisterOrderDonService extends IService<RegisterOrderDon> {
     void wxRefundNotify(Map<String, String> map) throws Exception;
 
     void updateAppleAutoStatus(Long orderId);
+
+    CouponUserView spotifyUnLimit(SpotifyUnLimitRecord spotifyUnLimitRecord);
+
+    CouponUserView getAvailableSpotifyCoupon(long userId);
 }

+ 74 - 1
netflix-service/src/main/java/com/cyksj/service/register/impl/RegisterOrderDonServiceImpl.java

@@ -1,6 +1,7 @@
 package com.cyksj.service.register.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -12,25 +13,33 @@ import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.Xmls;
 import com.cyksj.config.WeChatConfig;
 import com.cyksj.dto.RedisKey;
-import com.cyksj.mapper.AppleAutoRegisterDataMapper;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.AppleAutoRegisterDataMapper;
 import com.cyksj.mapper.RegisterOrderDonMapper;
+import com.cyksj.mapper.SpotifyUnLimitRecordMapper;
 import com.cyksj.mapper.UserMapper;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
 import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.request.RegisterReq;
 import com.cyksj.model.request.OrderAttach;
 import com.cyksj.model.request.WxOrderRefundReq;
+import com.cyksj.model.views.CouponUserView;
 import com.cyksj.redis.RedisService;
+import com.cyksj.service.mange.coupon.CouponCommonService;
 import com.cyksj.service.register.RegisterOrderDonService;
 import com.cyksj.service.register.SpotifyService;
 import com.cyksj.service.sys.SysConfigService;
 import com.cyksj.service.wechat.WeChatService;
 import com.cyksj.task.JobManager;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -68,6 +77,14 @@ public class RegisterOrderDonServiceImpl extends ServiceImpl<RegisterOrderDonMap
 
     private final RegisterOrderDonMapper registerOrderDonMapper;
 
+    private final CouponMapper couponMapper;
+
+    private final CouponCommonService couponCommonService;
+
+    private final SpotifyUnLimitRecordMapper spotifyUnLimitRecordMapper;
+
+    private final BeanSearcher beanSearcher;
+
     private static final Long EXPIRY_TIME = 1000 * 60 * 5L;
 
     /**
@@ -168,6 +185,62 @@ public class RegisterOrderDonServiceImpl extends ServiceImpl<RegisterOrderDonMap
         }
     }
 
+	@Override
+	public CouponUserView spotifyUnLimit(SpotifyUnLimitRecord spotifyUnLimitRecord) {
+		Boolean isFlag = false;
+		Long spotifyUnLimitCouponId = null;
+		//是否配置了优惠券
+		SysConfig spotify_unLimit_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_unLimit_cid").last("limit 1"));
+		if (spotify_unLimit_cid != null && StrUtil.isNotBlank(spotify_unLimit_cid.getSysValue())) {
+			spotifyUnLimitCouponId = Long.parseLong(spotify_unLimit_cid.getSysValue());
+			Coupon coupon = couponMapper.selectById(spotifyUnLimitCouponId);
+			if (coupon != null) {
+				isFlag = couponCommonService.sendCouponToUser(spotifyUnLimitRecord.getUserId(), spotifyUnLimitCouponId, null, null, null, CouponUser.Channel.manual);
+			}
+		}
+		//14天内是否重复提交过该账号
+        SpotifyUnLimitRecord exists = spotifyUnLimitRecordMapper.selectOne(Wrappers.lambdaQuery(SpotifyUnLimitRecord.class)
+                .eq(SpotifyUnLimitRecord::getUserId, spotifyUnLimitRecord.getUserId())
+                .eq(SpotifyUnLimitRecord::getAccount, spotifyUnLimitRecord.getAccount())
+                .last("limit 1"));
+        if (exists != null && exists.getUpdateTime().after(DateUtil.offsetDay(DateTime.now(), -14))) {
+            throw BusinessRuntimeException.getInstance("您已提交过,请勿重复提交");
+        }
+        if (exists != null) {
+            spotifyUnLimitRecord.setId(exists.getId());
+            spotifyUnLimitRecord.setIsSend(false);
+            spotifyUnLimitRecordMapper.updateById(spotifyUnLimitRecord);
+        } else {
+            try {
+                spotifyUnLimitRecordMapper.insert(spotifyUnLimitRecord);
+            } catch (DuplicateKeyException e) {
+                log.info("用户:{}重复提交解除限制spotify账号:{}", spotifyUnLimitRecord.getUserId(), spotifyUnLimitRecord.getAccount());
+            }
+        }
+		return isFlag ? beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
+				.field(CouponUserView::getUserId, spotifyUnLimitRecord.getUserId()).op(Operator.Equal)
+				.field(CouponUserView::getCouponId, spotifyUnLimitCouponId).op(Operator.Equal)
+				.build()) : null;
+	}
+
+    @Override
+    public CouponUserView getAvailableSpotifyCoupon(long userId) {
+        SysConfig spotify_register_success_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_register_success_cid").last("limit 1"));
+        CouponUserView couponUserView = null;
+        if (spotify_register_success_cid != null && StrUtil.isNotBlank(spotify_register_success_cid.getSysValue())) {
+            DateTime now = DateTime.now();
+            Long spotifyRegisterSucCid = Long.parseLong(spotify_register_success_cid.getSysValue());
+            couponUserView = beanSearcher.searchFirst(CouponUserView.class, MapUtils.builder()
+                    .field(CouponUserView::getUserId, userId).op(Operator.Equal)
+                    .field(CouponUserView::getCouponId, spotifyRegisterSucCid).op(Operator.Equal)
+                    .field(CouponUserView::getCouponUserStatus, CouponUser.Status.unused.name()).op(Operator.Equal)
+                    .field(CouponUserView::getValidStartTime, now).op(Operator.LessThan)
+                    .field(Coupon::getValidEndTime, now).op(Operator.GreaterThan)
+                    .build());
+        }
+        return couponUserView;
+    }
+
     @Override
     public H5JsPayParams pay(Long orderId) throws Exception {
         log.info("注册 账号调起微信支付[start] orderId:{}", orderId);

+ 38 - 7
netflix-service/src/main/java/com/cyksj/service/register/impl/SpotifyServiceImpl.java

@@ -2,6 +2,7 @@ package com.cyksj.service.register.impl;
 
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpResponse;
 import cn.hutool.http.HttpUtil;
@@ -16,13 +17,15 @@ import com.cyksj.common.util.Jsons;
 import com.cyksj.dto.RedisKey;
 import com.cyksj.mapper.ProxysMapper;
 import com.cyksj.mapper.RegisterOrderDonMapper;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
 import com.cyksj.model.dto.WxMpTemplateData;
 import com.cyksj.model.dto.WxMpTemplateMessage;
-import com.cyksj.model.entity.Proxys;
-import com.cyksj.model.entity.RegisterOrderDon;
+import com.cyksj.model.entity.*;
 import com.cyksj.model.response.ProxyInfo;
 import com.cyksj.redis.RedisService;
+import com.cyksj.service.mange.coupon.CouponCommonService;
 import com.cyksj.service.register.SpotifyService;
+import com.cyksj.service.sys.SysConfigService;
 import com.cyksj.service.wechat.WeChatService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -53,6 +56,12 @@ public class SpotifyServiceImpl implements SpotifyService {
 
     private final WeChatService weChatService;
 
+    private final CouponMapper couponMapper;
+
+    private final CouponCommonService couponCommonService;
+
+    private final SysConfigService sysConfigService;
+
     private static final List<String> User_Agent_LIST =  List.of(
             "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
             "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
@@ -97,16 +106,20 @@ public class SpotifyServiceImpl implements SpotifyService {
 
         request.header("User-Agent", User_Agent_LIST.get(RandomUtil.randomInt(User_Agent_LIST.size())));
         request.header("Connection","close");
-        request.header( "Accept","*/*");
+        //request.header( "Accept","*/*");
         request.header( "Content-Type","application/x-www-form-urlencoded");
-        request.header( "Referer","https://www.spotify.com/us/");
-        request.header( "Accept-Encoding","gzip, deflate, br");
+        request.header( "Referer","https://www.spotify.com/");
         request.header( "Host","spclient.wg.spotify.com");
+        request.header(  "sec-fetch-site", "same-site");
+        request.header(  "sec-fetch-mode", "cors");
+        request.header(  "sec-fetch-dest", "empty");
+        request.header(  "authority", "spclient.wg.spotify.com");
+        request.header(  "origin", "https://www.spotify.com");
 
         Map<String,Object> jsonObject = new HashMap<>();
         jsonObject.put("key","a1e486e2729f46d6bb368d6b2bcda326");
         //creation_point=https://www.spotify.com/uk/
-        jsonObject.put("creation_point","https://www.spotify.com/us/");
+        jsonObject.put("creation_point","https%3A%2F%2Fwww.spotify.com%2Fus%2F");
         jsonObject.put("displayname",Optional.ofNullable(registerOrderDon.getNickname()).orElse(registerOrderDon.getEmail()));
         jsonObject.put("password_repeat",registerOrderDon.getPassword());
         jsonObject.put("password",registerOrderDon.getPassword());
@@ -116,6 +129,7 @@ public class SpotifyServiceImpl implements SpotifyService {
         jsonObject.put("birth_day",Optional.ofNullable(registerOrderDon.getBirthDay()).orElse("01"));
         jsonObject.put("gender",registerOrderDon.getGender());
         jsonObject.put("email",registerOrderDon.getEmail());
+        jsonObject.put("platform","www");
         jsonObject.put("iagree","1");
         request.setMethod(Method.POST);
         request.setConnectionTimeout(15 * 1000);
@@ -136,7 +150,7 @@ public class SpotifyServiceImpl implements SpotifyService {
                 ProxyInfo proxyInfo = (ProxyInfo)redisService.get(RedisKey.DOVEIP);
                 if (proxyInfo == null) {
 
-                    String result = HttpUtil.get("https://dvapi.doveip.com/cmapi.php?rq=distribute&user=1031494735&token=ckc1MUFURnk3bi80MXVxeUFTZUh0Zz09&auth=0&geo=ph&agreement=0&timeout=10&num=1&type_ip=1");
+                    String result = HttpUtil.get("https://dvapi.doveip.com/cmapi.php?rq=distribute&user=1031494735&token=ckc1MUFURnk3bi80MXVxeUFTZUh0Zz09&auth=0&geo=ir&agreement=0&timeout=10&num=1&type_ip=1&repeat=0");
                     log.info("付费ip 请求返回 res:{}",result);
                     /*
                      * {
@@ -209,6 +223,8 @@ public class SpotifyServiceImpl implements SpotifyService {
                         regFlag = true;
                         //注册成功发放模板消息
                         spotifyTemplateMsg(registerOrderDon, String.format("%s%s", envCommonService.getDomain(), "yinhe/web/sign/list"), true);
+                        //注册成功发放声破天优惠券
+                        sendSpotifyCoupon(registerOrderDon);
                         break;
                     } else {
                         if (errors != null && !errors.isEmpty()) {
@@ -244,6 +260,21 @@ public class SpotifyServiceImpl implements SpotifyService {
         }
     }
 
+    private void sendSpotifyCoupon(RegisterOrderDon registerOrderDon) {
+        //已经拥有声破天年费的不用发
+        Integer exists = registerOrderDonMapper.isPaySpotifyYearTicket(registerOrderDon.getUserId());
+        if (exists == null) {
+            SysConfig spotify_register_success_cid = sysConfigService.getOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "spotify_register_success_cid").last("limit 1"));
+            if (spotify_register_success_cid != null && StrUtil.isNotBlank(spotify_register_success_cid.getSysValue())) {
+                Long spotifyRegisterSucCid = Long.parseLong(spotify_register_success_cid.getSysValue());
+                Coupon coupon = couponMapper.selectById(spotifyRegisterSucCid);
+                if (coupon != null) {
+                    couponCommonService.sendCouponToUser(registerOrderDon.getUserId(), spotifyRegisterSucCid, null, null, null, CouponUser.Channel.manual);
+                }
+            }
+        }
+    }
+
     private void spotifyTemplateMsg(RegisterOrderDon registerOrderDon, String url, Boolean isSuccess) throws Exception {
         TemplateEnum templateEnum = null;
         if (EnvCommonService.active.equals(envCommonService.getEnv())) {

+ 21 - 0
netflix-service/src/main/java/com/cyksj/task/OrderCustomerHandleScheduler.java

@@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil;
 import com.cyksj.mapper.manage.CustomerServiceEveryMonthDetailMapper;
 import com.cyksj.mapper.manage.StatisticsCustomerServiceEveryMonthMapper;
 import com.cyksj.model.entity.CustomerServiceEveryMonthDetail;
+import com.cyksj.model.entity.CustomerServiceRenewOrderRecord;
 import com.cyksj.model.entity.RealGoodsOrderHandle;
 import com.cyksj.model.entity.StatisticsCustomerServiceEveryMonth;
 import com.ejlchina.searcher.BeanSearcher;
@@ -63,6 +64,14 @@ public class OrderCustomerHandleScheduler {
 		after.forEach((k, v) -> {
 			customerServiceRecord(k, v, 2, statistics.getId());
 		});
+		//续费业绩
+		List<CustomerServiceRenewOrderRecord> customerServiceRecords = beanSearcher.searchAll(CustomerServiceRenewOrderRecord.class, MapUtils.builder()
+				.field(RealGoodsOrderHandle::getCreatedTime, begin, now).op(Operator.Between)
+				.build());
+		Map<String, List<CustomerServiceRenewOrderRecord>> renews = customerServiceRecords.stream().collect(Collectors.groupingBy(CustomerServiceRenewOrderRecord::getCustomerService));
+		renews.forEach((k,v)->{
+			renewServiceOrderRecord(k, v, 3, statistics.getId());
+		});
 	}
 
 
@@ -78,4 +87,16 @@ public class OrderCustomerHandleScheduler {
 			detailMapper.insert(record);
 		}
 	}
+
+	public void renewServiceOrderRecord(String serviceName, List<CustomerServiceRenewOrderRecord> renewOrderRecords, Integer type, Long statisticsId) {
+		if (StrUtil.isNotBlank(serviceName)) {
+			CustomerServiceEveryMonthDetail record = new CustomerServiceEveryMonthDetail();
+			record.setType(type);
+			record.setCustomerServiceName(serviceName);
+			record.setStatisticsId(statisticsId);
+			record.setNumOfOrder(renewOrderRecords.size());
+			record.setMoney(BigDecimal.ZERO);
+			detailMapper.insert(record);
+		}
+	}
 }

+ 1 - 1
netflix-service/src/main/java/com/cyksj/task/Scheduler.java

@@ -183,7 +183,7 @@ public class Scheduler {
     /**
      * spotify 注册
      */
-    @Scheduled(cron = "0 0/5 * * * ?")
+    //@Scheduled(cron = "0 0/5 * * * ?")
     public void spotify() {
         synchronized (this){
             List<RegisterOrderDon> spotify = registerOrderDonMapper.selectList(Wrappers.lambdaQuery(RegisterOrderDon.class)

+ 3 - 1
netflix-web/src/main/java/com/cyksj/config/SaTokenConfigure.java

@@ -25,6 +25,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
 						"/manage/corp/**",
 						"/manage/coupon/corp/send",
 						"/manage/coupon",
-						"/manage/register/wx/refund/notify"));
+						"/manage/register/wx/refund/notify",
+						"/manage/register/unLimit"));
 	}
 }
+

+ 18 - 1
netflix-web/src/main/java/com/cyksj/web/GlobalExceptionHandler.java

@@ -1,5 +1,6 @@
 package com.cyksj.web;
 
+import cn.dev33.satoken.exception.NotLoginException;
 import com.cyksj.common.util.StringUtil;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayApiCode;
@@ -30,6 +31,8 @@ public class GlobalExceptionHandler {
     @Autowired
     protected HttpServletRequest request;
 
+    private final static List<String> token = List.of("token无效", "token已过期");
+
     @ExceptionHandler(value = {Exception.class})
     public Result<String> handleNoteException(Exception e) {
         StringBuilder builder = new StringBuilder(32);
@@ -48,8 +51,22 @@ public class GlobalExceptionHandler {
 
         String message = e.getMessage();
         String value = builder.toString();
+        GatewayApiCode apiCode = null;
+        if (e instanceof NotLoginException) {
+            //未登录
+            NotLoginException ee = (NotLoginException) e;
+            if (token.contains(ee.getMessage())) {
+                apiCode = GatewayApiCode.CMS_TOKEN_VALID;
+            } else if ("token已被踢下线".equals(ee.getMessage())) {
+                apiCode = GatewayApiCode.CMS_TOKEN_OUT_LINE;
+            } else {
+                apiCode = GatewayApiCode.CMS_USER_NO_LOGIN;
+            }
+        }
         log.error("全局错误处理: \n Cause: {} \n Value: {}", message, value);
-
+        if (apiCode != null) {
+            return GatewayResponse.FAIL.newBuilder().buildGatewayCode(apiCode).toResult();
+        }
         return GatewayResponse.FAIL.newBuilder().setMsg(message).toResult(value);
     }
 

+ 15 - 0
netflix-web/src/main/java/com/cyksj/web/controller/mail/MailService.java

@@ -0,0 +1,15 @@
+package com.cyksj.web.controller.mail;
+
+/*
+ *项目名: netflix
+ *文件名: MailService
+ *创建者: JavaZou
+ *创建时间:2023/1/3 15:31
+ */
+public interface MailService {
+
+	/**
+	 * 邮箱登录验证码
+	 */
+	String sendLoginCodeEmailToPeople(Integer digit, String toAddress);
+}

+ 64 - 0
netflix-web/src/main/java/com/cyksj/web/controller/mail/impl/MailServiceImpl.java

@@ -0,0 +1,64 @@
+package com.cyksj.web.controller.mail.impl;
+
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.redis.RedisService;
+import com.cyksj.web.controller.mail.MailService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.MailException;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.stereotype.Service;
+
+/*
+ *项目名: netflix
+ *文件名: MailServiceImpl
+ *创建者: JavaZou
+ *创建时间:2023/1/3 15:54
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class MailServiceImpl implements MailService {
+	@Value("${spring.mail.username}")
+	private String from;
+
+	private final JavaMailSender mailSender;
+
+	private final RedisService redisService;
+
+	@Override
+	public String sendLoginCodeEmailToPeople(Integer digit, String toAddress) {
+		// 生成验证码
+		String code = StringUtil.getRandomCodeStr(digit);
+		String subject = "【银河录像局】登录验证码";
+		String content = String.format("尊敬的用户,您的验证码为%s,此验证码仅用于银河录像。【银河录像局】", code);
+		boolean flag = sendMail(toAddress, subject, content);
+		if (flag) {
+			//删除之前的验证码缓存key
+			String key = RedisService.key.EMAIL_CODE_KEY.getNameFormat(toAddress);
+			redisService.del(key);
+			//将验证码存入redis
+			redisService.set(key, code, RedisService.key.EMAIL_CODE_KEY.getTimeout());
+			return code;
+		}
+		return null;
+	}
+
+
+	public boolean sendMail(String toAddress, String subject, String text) {
+		SimpleMailMessage msg = new SimpleMailMessage();
+		msg.setFrom(from);
+		msg.setTo(toAddress);
+		msg.setSubject(subject);
+		msg.setText(text);
+		try {
+			mailSender.send(msg);
+		} catch (MailException ex) {
+			log.error(ex.getMessage());
+			return false;
+		}
+		return true;
+	}
+}

+ 42 - 2
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsOrderController.java

@@ -13,6 +13,7 @@ import com.cyksj.model.entity.OrderDon;
 import com.cyksj.model.entity.OrderDonWaybill;
 import com.cyksj.model.entity.RealGoodsInterestUser;
 import com.cyksj.model.entity.RealGoodsOrderHandle;
+import com.cyksj.model.excel.ExcelOrderDonData;
 import com.cyksj.model.excel.ExcelRealGoodsUserInterestData;
 import com.cyksj.model.excel.ExcelRealGoodsUserInterestHasPaymentData;
 import com.cyksj.model.manage.request.ReqSetLogisticsPost;
@@ -72,7 +73,7 @@ public class CmsOrderController {
     public Result<SearchResult<OrderDonView>> get() {
         SearchResult<OrderDonView> search = beanSearcher.search(OrderDonView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
         search.getDataList().forEach(data -> {
-            if (data.getGoodsDonType() == 2) {
+	        if (data.getGoodsDonType() != null && data.getGoodsDonType() == 2) {
                 RealGoodsOrderHandle realGoodsOrderHandle = realGoodsOrderHandleMapper.selectOne(Wrappers.lambdaQuery(RealGoodsOrderHandle.class).eq(RealGoodsOrderHandle::getOrderId, data.getId()).last("limit 1"));
                 if (realGoodsOrderHandle != null) {
                     data.setBeforeServiceName(realGoodsOrderHandle.getBeforeServiceName());
@@ -87,7 +88,45 @@ public class CmsOrderController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);
     }
 
-    @PutMapping("/update/status")
+	/**
+	 * 订单导出
+	 */
+	@GetMapping("/export")
+	public void exportOrders(HttpServletResponse response) {
+		List<ExcelOrderDonData> list = beanSearcher.searchAll(ExcelOrderDonData.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		list.forEach(data -> {
+			data.setMoney(data.getMoney().divide(BigDecimal.valueOf(100)));
+			OrderDon.Type type = data.getType();
+			data.setTypeStr(type != null ? type.getDesc() : "");
+			OrderDon.Status status = data.getStatus();
+			data.setStatusStr(status != null ? status.getDesc() : "");
+			if (data.getChannelId() == null) {
+				data.setChannel("自然流量渠道");
+			}
+			if (data.getGoodsDonType() != null && data.getGoodsDonType() == 2) {
+				OrderDonTransportView transportView = beanSearcher.searchFirst(OrderDonTransportView.class, MapUtils.builder().field(OrderDonTransportView::getOrderId, data.getId()).build());
+				if (transportView != null) {
+					data.setTransportStr(String.format("%s%s%s%s", transportView.getProvince(), transportView.getCity(), transportView.getDistrict(), transportView.getAddress()));
+					data.setPhone(transportView.getPhone());
+					data.setReceiver(transportView.getReceiver());
+					data.setCodeCn(transportView.getCodeCn());
+					data.setTrackingNumber(transportView.getTrackingNumber());
+				}
+				RealGoodsOrderHandle realGoodsOrderHandle = realGoodsOrderHandleMapper.selectOne(Wrappers.lambdaQuery(RealGoodsOrderHandle.class).eq(RealGoodsOrderHandle::getOrderId, data.getId()).last("limit 1"));
+				if (realGoodsOrderHandle != null) {
+					data.setBeforeServiceName(realGoodsOrderHandle.getBeforeServiceName());
+					data.setAfterServiceName(realGoodsOrderHandle.getAfterServiceName());
+				}
+			}
+			if (data.getCouponUserId() != 0) {
+				data.setCouponName(couponMapper.getCouponNameByCouponUserId(data.getCouponUserId()));
+				data.setCouponMoney(data.getCouponMoney() == null ? BigDecimal.ZERO : data.getCouponMoney().divide(BigDecimal.valueOf(100)));
+			}
+		});
+		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelOrderDonData.class, list, "订单列表", "订单列表", ExcelTypeEnum.XLSX, null);
+	}
+
+	@PutMapping("/update/status")
     public Result<String> upStatus(@RequestBody OrderDon orderDon){
         cmsOrderDonService.update(Wrappers.lambdaUpdate(OrderDon.class).eq(OrderDon::getId,orderDon.getId()).set(OrderDon::getStatus,orderDon.getStatus()));
         return GatewayResponse.SUCCESS.newBuilder().toResult();
@@ -178,6 +217,7 @@ public class CmsOrderController {
 				data.setMoney(order.getMoney());
 				realGoodsInterestUserMapper.updateData(data.getId(), data.getSkuId(), data.getOrderCreatedTime(), data.getOrderId());
 			}
+			data.setMoney(data.getMoney().divide(BigDecimal.valueOf(100)));
 		});
 		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelRealGoodsUserInterestHasPaymentData.class, excelRealGoodsUserInterestData, "用户意愿列表", "用户意愿列表", ExcelTypeEnum.XLSX, null);
 	}

+ 83 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/account/CmsAccountController.java

@@ -3,7 +3,12 @@ package com.cyksj.web.controller.manage.account;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.excel.support.ExcelTypeEnum;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GroupsMapper;
@@ -11,20 +16,26 @@ import com.cyksj.mapper.HomeGroupMapper;
 import com.cyksj.model.entity.Account;
 import com.cyksj.model.entity.GroupsTrips;
 import com.cyksj.model.entity.HomeGroup;
+import com.cyksj.model.excel.ExcelAccountTemplateData;
+import com.cyksj.model.excel.ExcelManageAccountData;
 import com.cyksj.model.manage.request.ReqAccountIncrExpiryTime;
 import com.cyksj.model.manage.views.AccountView;
 import com.cyksj.service.mange.CmsAccountService;
+import com.cyksj.web.util.EasyExcelUtils;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapBuilder;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 import java.util.List;
 
 /**
@@ -77,6 +88,78 @@ public class CmsAccountController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);
     }
 
+    /**
+     * 导出账号
+     */
+    @GetMapping("/export/account")
+    public void exportAccount(Long userId, String nickName, String submitAccount, String expiryStartTime, String expiryEndTime, HttpServletResponse response) {
+        MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
+        if (StrUtil.isNotEmpty(expiryStartTime) || StrUtil.isNotEmpty(expiryEndTime)) {
+            mapBuilder.field(AccountView::getExpiryTime, expiryStartTime, expiryEndTime)
+                    .op(Operator.Between);
+        }
+
+        if (userId != null || StringUtils.isNotBlank(nickName) || StringUtils.isNotBlank(submitAccount)) {
+            List<Long> accountIds = homeGroupMapper.getAccountIdsByUserIdOrNickNameOrSubmitAccount(userId, nickName, submitAccount);
+            if (accountIds.isEmpty()) {
+                throw BusinessRuntimeException.getInstance("暂无数据!");
+            }
+
+            mapBuilder.field(AccountView::getId, accountIds).op(Operator.InList);
+        }
+        List<ExcelManageAccountData> list = beanSearcher.searchAll(ExcelManageAccountData.class, mapBuilder.build());
+
+        EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelManageAccountData.class, list, "账号列表", "账号列表", ExcelTypeEnum.XLSX, null);
+    }
+
+    /**
+     * 下载导入模板
+     */
+    @GetMapping("/get/account/template")
+    public void getAccountTemplate(HttpServletResponse response) {
+        EasyExcelUtils.createTemplateExcel(response, ExcelAccountTemplateData.class, "账号导入模板", "账号导入模板", ExcelTypeEnum.XLSX, null);
+    }
+
+	/**
+	 * 导入账号,修改密码
+	 */
+	@PostMapping("/import/account")
+	public Result<String> importAccount(Long goodsId, MultipartFile file) throws Exception {
+		EasyExcel.read(file.getInputStream(), ExcelAccountTemplateData.class, new AnalysisEventListener<ExcelAccountTemplateData>() {
+			@SneakyThrows
+			@Override
+			public void invoke(ExcelAccountTemplateData data, AnalysisContext analysisContext) {
+				if (StrUtil.isNotBlank(data.getAccount())) {
+					Account account = accountService.getOne(Wrappers.lambdaQuery(Account.class)
+							.eq(Account::getGoodsId, goodsId)
+							.eq(Account::getAccount, data.getAccount()));
+					Boolean flag = false;
+					if (account != null) {
+						if (data.getExpiryTime() != null) {
+							account.setExpiryTime(data.getExpiryTime());
+							flag = true;
+						}
+						if (StrUtil.isNotBlank(data.getPassword()) && !data.getPassword().equals(account.getPassword())) {
+							account.setPassword(data.getPassword());
+							flag = true;
+							//发放模板消息
+							accountService.sendAccountTemplateMsg(account);
+						}
+						if (flag) {
+							accountService.updateById(account);
+						}
+					}
+				}
+			}
+
+			@Override
+			public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+
+			}
+		}).sheet().doRead();
+		return GatewayResponse.SUCCESS.newBuilder().toResult("导入成功");
+	}
+
     @PostMapping("/post")
     public Result<String> add(@RequestBody Account account){
         accountService.save(account);

+ 9 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupController.java

@@ -7,6 +7,7 @@ import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.model.entity.Account;
 import com.cyksj.model.entity.GroupsTrips;
+import com.cyksj.model.views.GroupsTripsWaitingView;
 import com.cyksj.service.mange.CmsAccountService;
 import com.cyksj.service.mange.CmsGoodsDonSkuService;
 import com.cyksj.service.mange.CmsGroupService;
@@ -65,6 +66,14 @@ public class CmsGroupController {
         return GatewayResponse.SUCCESS.newBuilder().toResult(search);
     }
 
+    /**
+     * 各平台待发车详情
+     */
+    @GetMapping("/get/plat/waiting")
+    public Result<List<GroupsTripsWaitingView>> getPlatWaiting() {
+        return GatewayResponse.SUCCESS.newBuilder().toResult(beanSearcher.searchAll(GroupsTripsWaitingView.class, null));
+    }
+
     @PostMapping("/post")
     @NoSubmit
     public Result<GroupsTrips> add(@RequestBody GroupsTrips groups){

+ 33 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/register/CmsRegisterController.java

@@ -1,5 +1,6 @@
 package com.cyksj.web.controller.manage.register;
 
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.support.ExcelTypeEnum;
@@ -10,14 +11,17 @@ import com.cyksj.common.util.Xmls;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.AppleAutoRegisterDataMapper;
+import com.cyksj.mapper.SpotifyUnLimitRecordMapper;
 import com.cyksj.model.entity.AppleAutoRegisterData;
 import com.cyksj.model.entity.RegisterOrderDon;
+import com.cyksj.model.entity.SpotifyUnLimitRecord;
 import com.cyksj.model.excel.ExcelAppleIdTemplateData;
 import com.cyksj.model.excel.listener.ExcelAppleDataListener;
 import com.cyksj.model.request.WxOrderRefundReq;
 import com.cyksj.model.views.AppleIdCountryOrAreaDataView;
 import com.cyksj.model.views.AppleIdHeadDataView;
 import com.cyksj.model.views.AppleIdRegisterView;
+import com.cyksj.service.mange.SpotifySendMsgService;
 import com.cyksj.service.register.RegisterOrderDonService;
 import com.cyksj.web.util.EasyExcelUtils;
 import com.ejlchina.searcher.BeanSearcher;
@@ -54,6 +58,10 @@ public class CmsRegisterController {
 
     private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper;
 
+    private final SpotifyUnLimitRecordMapper spotifyUnLimitRecordMapper;
+
+    private final SpotifySendMsgService spotifySendMsgService;
+
     @GetMapping("/get")
     public Result<SearchResult<RegisterOrderDon>> get(Boolean appleIsAutoType) {
         MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
@@ -162,4 +170,29 @@ public class CmsRegisterController {
         }
     }
 
+    /**
+     * 获取需要解除限制的账号列表
+     */
+    @GetMapping("/unLimit")
+    public Result<SearchResult<SpotifyUnLimitRecord>> getUnLimit(){
+        SearchResult<SpotifyUnLimitRecord> search = beanSearcher.search(SpotifyUnLimitRecord.class, MapUtils.flatBuilder(request.getParameterMap())
+                .field(SpotifyUnLimitRecord::getStatus,"Processing").build());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+    }
+
+    /**
+     * 修改解除限制状态
+     */
+    @PutMapping("/unLimit")
+    public Result<String> upUnLimit(@RequestBody SpotifyUnLimitRecord spotifyUnLimitRecord) throws Exception {
+        spotifyUnLimitRecordMapper.update(null, Wrappers.lambdaUpdate(SpotifyUnLimitRecord.class)
+                .eq(SpotifyUnLimitRecord::getId, spotifyUnLimitRecord.getId())
+                .set(SpotifyUnLimitRecord::getStatus, spotifyUnLimitRecord.getStatus())
+                .set(SpotifyUnLimitRecord::getUpdateTime, DateTime.now())
+        );
+        if (spotifyUnLimitRecord.getStatus() == SpotifyUnLimitRecord.Status.Success) {
+            spotifySendMsgService.sendSpotifyUnLimitSuccessMsg(spotifyUnLimitRecord);
+        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
 }

+ 82 - 7
netflix-web/src/main/java/com/cyksj/web/controller/manage/statistics/StatisticsDataController.java

@@ -2,12 +2,14 @@ package com.cyksj.web.controller.manage.statistics;
 
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
 import com.alibaba.excel.support.ExcelTypeEnum;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.CustomerServiceRenewOrderRecordMapper;
 import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.mapper.UserMapper;
 import com.cyksj.mapper.manage.CustomerServiceEveryMonthDetailMapper;
@@ -18,6 +20,10 @@ import com.cyksj.mapper.manage.statistics.StatisticsUserDataMapper;
 import com.cyksj.mapper.stock.GoodsDonStockRecordMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.excel.ExcelAccountData;
+import com.cyksj.mapper.stock.GoodsDonStockRecordMapper;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.excel.ExcelAccountData;
+import com.cyksj.model.request.BatchServiceReq;
 import com.cyksj.model.request.RenewTimeUpReq;
 import com.cyksj.model.views.*;
 import com.cyksj.service.mange.statistics.StatisticsDataService;
@@ -26,6 +32,7 @@ import com.cyksj.web.util.EasyExcelUtils;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapBuilder;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import org.springframework.transaction.annotation.Transactional;
@@ -72,6 +79,8 @@ public class StatisticsDataController {
 
 	private final GoodsDonStockRecordMapper stockRecordMapper;
 
+	private final CustomerServiceRenewOrderRecordMapper customerServiceRenewOrderRecordMapper;
+
 	private final SignStatisticsDataMapper signStatisticsDataMapper;
 
 	/**
@@ -87,16 +96,16 @@ public class StatisticsDataController {
 	 * 有效子账号数列表
 	 */
 	@GetMapping("/expired/account")
-	public Result<IPage<ExpiredAccountDataView>> getExpiredAccountView(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, @RequestParam(required = false, defaultValue = "1") Long start, @RequestParam(required = false, defaultValue = "10") Long limit) {
-		return GatewayResponse.SUCCESS.newBuilder().toResult(statisticsDataService.getExpiredAccountView(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, start, limit));
+	public Result<IPage<ExpiredAccountDataView>> getExpiredAccountView(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, @RequestParam(required = false, defaultValue = "1") Long start, @RequestParam(required = false, defaultValue = "10") Long limit) {
+		return GatewayResponse.SUCCESS.newBuilder().toResult(statisticsDataService.getExpiredAccountView(isCorpWx, customerService, goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime, start, limit));
 	}
 
 	/**
 	 * 导出有效子账号数
 	 */
 	@GetMapping("/export/account")
-	public void exportAccount(Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, HttpServletResponse response) {
-		List<ExcelAccountData> expiredAccountView = statisticsDataService.getExpiredAccountViewNoPage(goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime);
+	public void exportAccount(Boolean isCorpWx, String customerService, Integer goodsId, String account, Boolean renewStatus, Boolean handleStatus, String cnAccount, String usAccount, Integer userId, Long startTime, Long endTime, HttpServletResponse response) {
+		List<ExcelAccountData> expiredAccountView = statisticsDataService.getExpiredAccountViewNoPage(isCorpWx, customerService, goodsId, account, renewStatus, handleStatus, cnAccount, usAccount, userId, startTime, endTime);
 		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, ExcelAccountData.class, expiredAccountView, "有效子账号", "有效子账号", ExcelTypeEnum.XLSX, null);
 	}
 
@@ -196,11 +205,16 @@ public class StatisticsDataController {
 		DateTime now = DateTime.now();
 		DateTime begin = DateUtil.beginOfMonth(now);
 		String statisticsDate = DateUtil.format(now, "yyyy-MM");
-		List<CustomerServiceHandleOrderToMonthView> toMonthDetail = realGoodsOrderHandleMapper.selectCustomerToMonthData(now, begin, OrderCustomerHandleScheduler.noOrderStatus, type);
 		StatisticsCustomerDataView customerDataView = new StatisticsCustomerDataView();
+		StatisticsCustomerServiceEveryMonthView toMonth = new StatisticsCustomerServiceEveryMonthView();
+		toMonth.setStatisticsData(statisticsDate);
+		List<CustomerServiceHandleOrderToMonthView> toMonthDetail;
+		if (type == 3) {
+			toMonthDetail = customerServiceRenewOrderRecordMapper.selectCustomerToMonthData(now, begin, type);
+		} else {
+			toMonthDetail = realGoodsOrderHandleMapper.selectCustomerToMonthData(now, begin, OrderCustomerHandleScheduler.noOrderStatus, type);
+		}
 		if (!toMonthDetail.isEmpty()) {
-			StatisticsCustomerServiceEveryMonthView toMonth = new StatisticsCustomerServiceEveryMonthView();
-			toMonth.setStatisticsData(statisticsDate);
 			toMonth.setDetails(toMonthDetail);
 			customerDataView.setToMonth(toMonth);
 		}
@@ -212,6 +226,45 @@ public class StatisticsDataController {
 		return GatewayResponse.SUCCESS.newBuilder().toResult(customerDataView);
 	}
 
+	/**
+	 * 客服业绩订单明细
+	 */
+	@GetMapping("/get/service/orders/detail")
+	public Result<CustomerServiceOrdersDetailView> getCustomerData(Integer type, String monthDate, String customerService, HttpServletRequest request) {
+		DateTime month = null;
+		if (StrUtil.isEmpty(monthDate)) {
+			month = DateTime.now();
+		} else {
+			month = DateUtil.parse(monthDate, "yyyy-MM-dd");
+		}
+		DateTime beginOfMonth = DateUtil.beginOfMonth(month);
+		DateTime endOfMonth = DateUtil.endOfMonth(month);
+		CustomerServiceOrdersDetailView view = new CustomerServiceOrdersDetailView();
+		if (type != 3) {
+			MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap())
+					.field(ServiceOrdersDetailView::getRecordCreatedTime, beginOfMonth, endOfMonth).op(Operator.Between);
+			if (type == 1) {
+				builder.field(ServiceOrdersDetailView::getBeforeServiceName, customerService).op(Operator.Equal);
+			} else if (type == 2) {
+				builder.field(ServiceOrdersDetailView::getAfterServiceName, customerService).op(Operator.Equal);
+			}
+			builder.field(ServiceOrdersDetailView::getStatus, OrderCustomerHandleScheduler.noOrderStatus).op(Operator.NotIn);
+			SearchResult<ServiceOrdersDetailView> search = beanSearcher.search(ServiceOrdersDetailView.class, builder.build());
+			Number AppleTV = beanSearcher.searchCount(ServiceOrdersDetailView.class, builder.field(ServiceOrdersDetailView::getSpecVal, "Apple TV").op(Operator.Contain).build());
+			Number Chromecast = beanSearcher.searchCount(ServiceOrdersDetailView.class, builder.field(ServiceOrdersDetailView::getSpecVal, "Chromecast").op(Operator.Contain).build());
+			view.setAppleTV(AppleTV.intValue());
+			view.setChromecast(Chromecast.intValue());
+			view.setOrders(search.getTotalCount().intValue());
+			view.setResult(search);
+		} else {
+			SearchResult<ServiceRenewOrdersDetailView> search = beanSearcher.search(ServiceRenewOrdersDetailView.class, MapUtils.flatBuilder(request.getParameterMap())
+					.field(ServiceRenewOrdersDetailView::getCustomerService, customerService).op(Operator.Equal)
+					.field(ServiceRenewOrdersDetailView::getRecordCreatedTime, beginOfMonth, endOfMonth).op(Operator.Between).build());
+			view.setResult(search);
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult(view);
+	}
+
 	@GetMapping("/get/stock")
 	public Result<List<StatisticsStockView>> getStock(HttpServletRequest request, @RequestParam(defaultValue = "0") Long startTime,@RequestParam(defaultValue = "0") Long endTime){
 		List<GoodsDonStock> goodsDonStocks = beanSearcher.searchAll(GoodsDonStock.class, MapUtils.flatBuilder(request.getParameterMap()).build());
@@ -282,6 +335,28 @@ public class StatisticsDataController {
 
 	}
 
+	/**
+	 * 批量分配客服
+	 */
+	@PostMapping("/batch/service")
+	public Result<String> batchService(@RequestBody List<BatchServiceReq> batchServices) {
+		batchServices.forEach(service -> {
+			if (StrUtil.isEmpty(service.getCustomerService())) {
+				throw BusinessRuntimeException.getInstance("所分配客服不能为空");
+			}
+			GroupsRelation relation = groupsRelationMapper.selectById(service.getRelationId());
+			if (relation == null) {
+				return;
+			}
+			if (StrUtil.isEmpty(relation.getCustomerService()) && relation.getUserId() != 0
+					&& GroupsRelation.Status.validity == relation.getStatus()) {
+				relation.setCustomerService(service.getCustomerService());
+				groupsRelationMapper.updateById(relation);
+			}
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult("分配客服成功");
+	}
+
 	/**
 	 * 签到以及积分数据统计
 	 */

+ 51 - 0
netflix-web/src/main/java/com/cyksj/web/controller/register/RegisterController.java

@@ -1,16 +1,24 @@
 package com.cyksj.web.controller.register;
 
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.common.annotation.NoSubmit;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.util.IoKit;
 import com.cyksj.common.util.Xmls;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.AppleAutoRegisterDataMapper;
+import com.cyksj.mapper.ProxysMapper;
+import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
 import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.AppleAutoRegisterData;
+import com.cyksj.model.entity.Proxys;
 import com.cyksj.model.entity.RegisterOrderDon;
+import com.cyksj.model.entity.SpotifyUnLimitRecord;
 import com.cyksj.model.manage.request.RegisterReq;
 import com.cyksj.model.views.AppleIdCountryOrAreaDataView;
+import com.cyksj.model.views.CouponUserView;
 import com.cyksj.service.register.RegisterOrderDonService;
 import com.cyksj.web.util.StpUserUtil;
 import com.ejlchina.searcher.BeanSearcher;
@@ -20,6 +28,7 @@ import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
@@ -45,6 +54,10 @@ public class RegisterController {
 
     private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper;
 
+	private final ProxysMapper proxysMapper;
+
+    private final CouponSkuMapper couponSkuMapper;
+
     @PostMapping
     public Result<RegisterOrderDon> register(@RequestBody RegisterReq req) throws Exception {
         long userId = StpUserUtil.getLoginIdAsLong();
@@ -158,4 +171,42 @@ public class RegisterController {
         registerOrderDonService.updateAppleAutoStatus(orderId);
         return GatewayResponse.SUCCESS.newBuilder().toResult("取消订单成功");
     }
+
+    /**
+     * 解除限制申请
+     */
+    @PostMapping("/spotify/unLimit")
+    @NoSubmit
+    public Result<CouponUserView> spotifyUnLimit(@RequestBody @Validated SpotifyUnLimitRecord spotifyUnLimitRecord) {
+        long userId = StpUserUtil.getLoginIdAsLong();
+        spotifyUnLimitRecord.setUserId(userId);
+        CouponUserView couponUserView = registerOrderDonService.spotifyUnLimit(spotifyUnLimitRecord);
+        if (couponUserView != null) {
+            String str = couponSkuMapper.selectGoodsSkuStr(couponUserView.getCouponId());
+            couponUserView.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult(couponUserView);
+    }
+
+    /**
+     * 查询是否有可用的spotify年付优惠券
+     */
+    @GetMapping("/get/available/spotifyCoupon")
+    public Result<CouponUserView> getAvailableSpotifyCoupon() {
+        CouponUserView couponUserView = registerOrderDonService.getAvailableSpotifyCoupon(StpUserUtil.getLoginIdAsLong());
+        if (couponUserView != null) {
+            String str = couponSkuMapper.selectGoodsSkuStr(couponUserView.getCouponId());
+            couponUserView.setGoodsSkuStr(str.replaceAll(",", "/").replaceAll(";", ""));
+        }
+        return GatewayResponse.SUCCESS.newBuilder().toResult(couponUserView);
+    }
+
+    /**
+     * 获取代理ip
+     */
+    @GetMapping("/get/proxys")
+    public Result<Page<Proxys>> getProxys(@RequestParam(defaultValue = "1") Integer start, @RequestParam(defaultValue = "10") Integer limit) {
+        return GatewayResponse.SUCCESS.newBuilder().toResult(proxysMapper.selectPage(new Page<>(start, limit), Wrappers.lambdaQuery(Proxys.class).orderByDesc(Proxys::getScore)));
+    }
+
 }

+ 73 - 31
netflix-web/src/main/java/com/cyksj/web/controller/user/AuthorizationController.java

@@ -6,14 +6,17 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Validator;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.EnvCommonService;
+import com.cyksj.common.annotation.NoSubmit;
 import com.cyksj.common.constant.TaskTypeEnum;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.snowflake.Sequence;
 import com.cyksj.common.util.Codec;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.SmsUtil;
+import com.cyksj.common.util.StringUtil;
 import com.cyksj.config.WeChatConfig;
 import com.cyksj.config.wxlogin.WxOpenPlatYHLXLoginConfig;
 import com.cyksj.dto.Result;
@@ -38,6 +41,7 @@ import com.cyksj.service.market.task.TaskService;
 import com.cyksj.service.order.OrderDonService;
 import com.cyksj.service.user.UserService;
 import com.cyksj.service.wechat.WeChatService;
+import com.cyksj.web.controller.mail.MailService;
 import com.cyksj.web.util.StpUserUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -52,7 +56,6 @@ import javax.servlet.http.HttpServletResponse;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.Date;
-import java.util.Random;
 
 /**
  * @author chan
@@ -96,6 +99,8 @@ public class AuthorizationController {
 
     private final TaskTypeMapper taskTypeMapper;
 
+    private final MailService mailService;
+
     @GetMapping(value = "/weChat")
     public void base(String url,Long sharedId, String authType, HttpServletResponse response) throws Exception {
 
@@ -263,25 +268,18 @@ public class AuthorizationController {
      * 获取手机验证码
      */
     @GetMapping("/get/phone/code")
+    @NoSubmit
     public Result<String> getPhoneCode(String phone, Integer digit) {
         if (StrUtil.isBlank(phone) || !Validator.isMobile(phone)) {
             throw BusinessRuntimeException.getInstance("请输入正确手机号");
         }
-        if (null == digit) {
-            digit = 6;
-        }
+
         PhoneCode phoneCode = phoneCodeMapper.selectOne(Wrappers.lambdaQuery(PhoneCode.class).eq(PhoneCode::getPhone, phone).last("limit 1"));
         if (phoneCode == null) {
             phoneCode = new PhoneCode();
             phoneCode.setPhone(phone);
         }
-        String code;
-        // 生成验证码
-        if (digit == 4) {
-            code = new Random().nextInt(8999) + 1000 + "";
-        } else {
-            code = new Random().nextInt(899999) + 100000 + "";
-        }
+        String code = StringUtil.getRandomCodeStr(digit);
         phoneCode.setCode(code);
         if (phoneCode.getId() == null) {
             phoneCodeMapper.insert(phoneCode);
@@ -293,35 +291,79 @@ public class AuthorizationController {
     }
 
     /**
-     * 手机号授权登录
+     * 发放邮箱验证码
      */
-    @PostMapping("/login/phone")
-    public Result<LoginPhoneRep> loginByPhone(@RequestBody LoginPhoneReq loginPhoneReq, HttpServletResponse response) throws Exception {
-        if (StrUtil.hasEmpty(loginPhoneReq.getPhone(), loginPhoneReq.getCode())) {
-            throw BusinessRuntimeException.getInstance("请输入对应信息");
+    @GetMapping("/get/email/code")
+    @NoSubmit
+    public Result<String> getEmailCode(String email, Integer digit) {
+        if (email == null || !Validator.isEmail(email)) {
+            throw BusinessRuntimeException.getInstance("请输入正确邮箱");
         }
-        DateTime now = DateTime.now();
-        PhoneCode phoneCode = phoneCodeMapper.selectOne(Wrappers.lambdaQuery(PhoneCode.class).eq(PhoneCode::getPhone, loginPhoneReq.getPhone()).last("limit 1"));
-        if (phoneCode == null || StrUtil.isEmpty(phoneCode.getCode())) {
-            throw BusinessRuntimeException.getInstance("请重新获取手机验证码");
+        String code = mailService.sendLoginCodeEmailToPeople(digit, email);
+        if (code == null) {
+            throw BusinessRuntimeException.getInstance("获取邮箱验证码错误");
         }
-        //3分钟失效
-        Date updateTime = phoneCode.getUpdateTime();
-        DateTime afterFiveMinutes = DateUtil.offsetMinute(updateTime, 3);
-        if (now.isAfter(afterFiveMinutes)) {
-            throw BusinessRuntimeException.getInstance("手机验证码已失效,请重新获取");
-        }
-        if (!StrUtil.equals(loginPhoneReq.getCode(), phoneCode.getCode())) {
-            throw BusinessRuntimeException.getInstance("手机验证码错误,请重新输入");
+        return GatewayResponse.SUCCESS.newBuilder().toResult("发送成功");
+    }
+
+    /**
+     * 手机号授权登录
+     */
+    @PostMapping("/login/phone")
+    @NoSubmit
+    public Result<LoginPhoneRep> loginByPhone(@RequestBody LoginPhoneReq loginPhoneReq) throws Exception {
+        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class).last("limit 1");
+        if (loginPhoneReq.getType() == 1) {
+            if (StrUtil.hasEmpty(loginPhoneReq.getPhone(), loginPhoneReq.getCode())) {
+                throw BusinessRuntimeException.getInstance("请输入对应信息");
+            }
+            DateTime now = DateTime.now();
+            PhoneCode phoneCode = phoneCodeMapper.selectOne(Wrappers.lambdaQuery(PhoneCode.class).eq(PhoneCode::getPhone, loginPhoneReq.getPhone()).last("limit 1"));
+            if (phoneCode == null || StrUtil.isEmpty(phoneCode.getCode())) {
+                throw BusinessRuntimeException.getInstance("请重新获取手机验证码");
+            }
+            //3分钟失效
+            Date updateTime = phoneCode.getUpdateTime();
+            DateTime afterFiveMinutes = DateUtil.offsetMinute(updateTime, 3);
+            if (now.isAfter(afterFiveMinutes)) {
+                throw BusinessRuntimeException.getInstance("手机验证码已失效,请重新获取");
+            }
+            if (!StrUtil.equals(loginPhoneReq.getCode(), phoneCode.getCode())) {
+                throw BusinessRuntimeException.getInstance("手机验证码错误,请重新输入");
+            }
+            wrapper.eq(User::getLoginPhone, loginPhoneReq.getPhone());
+        }else if (loginPhoneReq.getType() == 2){
+            if (StrUtil.isEmpty(loginPhoneReq.getEmail())) {
+                throw BusinessRuntimeException.getInstance("邮箱不能为空");
+            }
+            String code = redisService.getStr(RedisService.key.EMAIL_CODE_KEY.getNameFormat(loginPhoneReq.getEmail()));
+            if (StrUtil.isEmpty(code)) {
+                throw BusinessRuntimeException.getInstance("邮箱验证码已失效,请重新获取");
+            }
+            if (!StrUtil.equals(loginPhoneReq.getCode(), code)) {
+                throw BusinessRuntimeException.getInstance("邮箱验证码错误,请重新输入");
+            }
+            wrapper.eq(User::getEmail, loginPhoneReq.getEmail());
+            redisService.del(RedisService.key.EMAIL_CODE_KEY.getNameFormat(loginPhoneReq.getEmail()));
+        }else {
+            throw BusinessRuntimeException.getInstance("系统异常,请刷新页面重新操作");
         }
         //保存用户信息
-        User user = userService.getOne(Wrappers.lambdaQuery(User.class).eq(User::getLoginPhone, loginPhoneReq.getPhone()).last("limit 1"));
+        User user = userService.getOne(wrapper);
         UserDistributeShared userDistributeShared = null;
         Long sharedId = loginPhoneReq.getSharedId();
         if (user == null) {
             user = new User();
-            user.setLoginPhone(loginPhoneReq.getPhone());
-            user.setNickname(String.format("银河用户%s", Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(String.format("%s%s", user.getId(), user.getLoginPhone())).toHexString().substring(0, 6)));
+            if (loginPhoneReq.getType() == 1) {
+                user.setLoginPhone(loginPhoneReq.getPhone());
+            } else if (loginPhoneReq.getType() == 2) {
+                user.setEmail(loginPhoneReq.getEmail());
+            }
+            String name = "银河用户";
+            if (loginPhoneReq.getType() == 2) {
+                name += "@";
+            }
+            user.setNickname(String.format("%s%s", name, Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(String.format("%s%s", user.getId(), loginPhoneReq.getType() == 1 ? user.getLoginPhone() : user.getEmail())).toHexString().substring(0, 6)));
             //默认头像
             user.setHeadimgurl("https://cdn.sxfoundation.com/picture/f6ee11101c4f5f0bc48d88aca7a5dbfd-1666945673391.png");
             userService.save(user);

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
netflix-web/src/main/resources/application-dev.yml


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
netflix-web/src/main/resources/application-prd.yml


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác