Ver código fonte

fix 奈飞月付续费其他规格更换车票

zoujiajian 3 anos atrás
pai
commit
d5e865ed8e

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

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

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

@@ -141,6 +141,16 @@ public class OrderDon extends BaseEntity implements Serializable {
      */
     private String subNode;
 
+    /**
+     * 续费后 是否需要更换车票
+     */
+    private Boolean isChangeTicket;
+
+    /**
+     * 之前的车票的规格id
+     */
+    private Long preSkuId;
+
     public enum Status {
         /**
          * 订单状态

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/UserRenewChangeTicketRecord.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: UserRenewChangeTicketRecord
+ *创建者: JavaZou
+ *创建时间:2023/3/20 14:39
+ */
+@Getter
+@Setter
+public class UserRenewChangeTicketRecord extends BaseEntity{
+	private Long userId;
+
+	private Long preRelationId;
+
+	private Long preSkuId;
+
+	private Date preStartTime;
+
+	private Date preExpiryTime;
+
+	private Long afterRelationId;
+
+	private Long afterSkuId;
+
+	private Date afterExpiryTime;
+
+	private Long renewOrderId;
+}

+ 7 - 1
netflix-dao/src/main/java/com/cyksj/model/excel/ExcelManageAccountData.java

@@ -8,6 +8,8 @@ import com.ejlchina.searcher.bean.SearchBean;
 import lombok.Getter;
 import lombok.Setter;
 
+import java.util.Date;
+
 /*
  *项目名: netflix
  *文件名: ExcelManageAccountData
@@ -45,9 +47,13 @@ public class ExcelManageAccountData {
 	@DbField("a.bank_card")
 	private String bankCard;
 
+	@DbField("a.expiry_time")
+	@ExcelIgnore
+	private Date expiryTime;
+
 	@ExcelProperty("到期时间")
 	@DbField("date_format(a.expiry_time,'%Y/%c/%e')")
-	private String expiryTime;
+	private String expiryTimeExcel;
 
 	@ExcelProperty("激活码")
 	@DbIgnore

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

@@ -33,6 +33,9 @@ public class ChannelPopularizeDetailView {
     @DbField("g.id")
     private Long goodsId;
 
+    @DbField("g.type")
+    private Integer goodsDonType;
+
     /**
      * 订单数
      */

+ 5 - 0
netflix-service/src/main/java/com/cyksj/service/exchange/ExchangeCodeService.java

@@ -38,4 +38,9 @@ public interface ExchangeCodeService extends IService<ExchangeCode> {
 	 * 获取车票
 	 */
 	GroupsRelation getGroupTripsTicket(GoodsDonSku sku, Long userId, OrderDon exCodeOrderDon, OrderDon.Type orderDonTye);
+
+	/**
+	 * 获取车票车位
+	 */
+	GroupsRelation getRelationNoOrder(GoodsDonSku sku, Long userId);
 }

+ 64 - 40
netflix-service/src/main/java/com/cyksj/service/exchange/impl/ExchangeCodeServiceImpl.java

@@ -227,46 +227,8 @@ public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, Exc
 		if (user == null) {
 			throw BusinessRuntimeException.getInstance("用户不存在");
 		}
-		GroupsRelation relation = null;
-		//寻找差不多过期时间规格的车位
-		relation = groupRelationFrontService.getSimilarExpiredGroupRelation(sku.getId(), sku.getMonths());
-		if (relation == null) {
-			GroupsTrips groups = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class).eq(GroupsTrips::getSkuId, sku.getId()).gt(GroupsTrips::getAvailableNum, 0).last("limit 1").orderByAsc(GroupsTrips::getAvailableNum));
-			if (groups == null) {
-				//新增车位 并关联
-				relation = createNewGroupTripsAndRelation(sku, groups, relation);
-			} else {
-				//寻找快满编车队进行占座
-				relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
-						.eq(GroupsRelation::getGroupsId, groups.getId())
-						.eq(GroupsRelation::getStatus, "none")
-						.eq(GroupsRelation::getUserId, 0)
-						.orderByAsc(GroupsRelation::getNum)
-						.last("limit 1")
-				);
-				//如果占位失败 新增车位并关联
-				if (relation == null) {
-					relation = createNewGroupTripsAndRelation(sku, groups, relation);
-				}
-			}
-		}
+		GroupsRelation relation = getRelationNoOrder(sku, userId);
 
-		try {
-			//锁定座位
-			setRelation(relation.getId(), userId);
-		} catch (Exception e) {
-			//失败重新给他分配车位
-			getGroupTripsTicket(sku, userId, exCodeOrderDon, orderDonTye);
-			redisService.del(RedisKey.GROUPS_RELATION_NUM_KEY + relation.getId());
-		}
-		relation.setUserId(userId);
-		relation.setStatus(GroupsRelation.Status.validity);
-		//如果续费增加时间,如果首次则设置当前时间为初始时间
-		Date expiryTime = Optional.ofNullable(relation.getExpiryTime()).orElse(new Date());
-		Date newDate = DateUtil.offset(expiryTime, DateField.MONTH, sku.getMonths() * 1);
-		relation.setExpiryTime(newDate);
-		relation.setStartTime(relation.getStartTime() == null ? new Date() : null);
-		groupsRelationMapper.updateById(relation);
 		Long relationId = relation.getId();
 		OrderDon orderDon;
 		if (exCodeOrderDon != null) {
@@ -323,11 +285,73 @@ public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, Exc
 		return relation;
 	}
 
+	@Override
+	public GroupsRelation getRelationNoOrder(GoodsDonSku sku, Long userId) {
+		GroupsRelation relation = null;
+		Integer retryNum = 1;
+		relation = getFinalRelation(sku, userId, relation, retryNum);
+		return relation;
+	}
+
+	public GroupsRelation getFinalRelation(GoodsDonSku sku, Long userId, GroupsRelation relation, Integer retryNum) {
+		//寻找差不多过期时间规格的车位
+		relation = groupRelationFrontService.getSimilarExpiredGroupRelation(sku.getId(), sku.getMonths());
+		if (relation == null) {
+			GroupsTrips groups = groupsMapper.selectOne(Wrappers.lambdaQuery(GroupsTrips.class).eq(GroupsTrips::getSkuId, sku.getId()).gt(GroupsTrips::getAvailableNum, 0).last("limit 1").orderByAsc(GroupsTrips::getAvailableNum));
+			if (groups == null) {
+				//新增车位 并关联
+				relation = createNewGroupTripsAndRelation(sku, groups, relation);
+			} else {
+				//寻找快满编车队进行占座
+				relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+						.eq(GroupsRelation::getGroupsId, groups.getId())
+						.eq(GroupsRelation::getStatus, "none")
+						.eq(GroupsRelation::getUserId, 0)
+						.orderByAsc(GroupsRelation::getNum)
+						.last("limit 1")
+				);
+				//如果占位失败 新增车位并关联
+				if (relation == null) {
+					relation = createNewGroupTripsAndRelation(sku, groups, relation);
+				}
+			}
+		}
+
+		try {
+			//锁定座位
+			setRelation(relation.getId(), userId);
+			relation.setUserId(userId);
+			relation.setStatus(GroupsRelation.Status.validity);
+			//如果续费增加时间,如果首次则设置当前时间为初始时间
+			Date expiryTime = Optional.ofNullable(relation.getExpiryTime()).orElse(new Date());
+			Date newDate = DateUtil.offset(expiryTime, DateField.MONTH, sku.getMonths() * 1);
+			relation.setExpiryTime(newDate);
+			relation.setStartTime(relation.getStartTime() == null ? new Date() : null);
+			groupsRelationMapper.updateById(relation);
+		} catch (Exception e) {
+			Object value = redisService.get(RedisKey.GROUPS_RELATION_NUM_KEY + relation.getId());
+			if (value != null && userId.equals(Long.parseLong(value.toString()))) {
+				redisService.del(RedisKey.GROUPS_RELATION_NUM_KEY + relation.getId());
+			}
+			if (retryNum == 10) {
+				log.error("用户:{}获取车票:{}异常", userId, relation.getId());
+				throw BusinessRuntimeException.getInstance("获取车票异常,请联系客服");
+			}
+			//失败重新给他分配车位
+			getFinalRelation(sku, userId, relation, ++retryNum);
+		}
+		return relation;
+	}
+
 	private void setRelation(Long relationId, Long userId) {
 		if (redisService.get(RedisKey.GROUPS_RELATION_NUM_KEY + relationId) != null) {
+			log.info("=====>用户:{}获取了已有人座位:{}", userId, relationId);
 			throw new BusinessRuntimeException("客官手慢啦,该座位已经有人啦!");
 		} else {
-			redisService.set(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L);
+			if (!redisService.setNx(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L)) {
+				log.info("=====>用户:{}未抢到座位:{}", userId, relationId);
+				throw new BusinessRuntimeException("客官手慢啦,该座位已经有人啦!");
+			}
 			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
 			int count = groupsMapper.decrAvailableNum(relation.getGroupsId());
 			if (count == 0) {

+ 202 - 30
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -25,6 +25,7 @@ import com.cyksj.common.snowflake.Sequence;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
 import com.cyksj.common.util.Codec;
 import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
 import com.cyksj.common.util.Xmls;
 import com.cyksj.config.WeChatConfig;
 import com.cyksj.config.zfb.AliPayClientFactory;
@@ -48,11 +49,13 @@ import com.cyksj.model.dto.H5JsPayParams;
 import com.cyksj.model.entity.Address;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.AccountView;
+import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.manage.views.OrderDonView;
 import com.cyksj.model.request.*;
 import com.cyksj.model.response.AliPayRefundRep;
 import com.cyksj.model.views.CouponNewUserView;
 import com.cyksj.model.views.CouponUserView;
+import com.cyksj.model.views.GoodsDonSkuView;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.coupon.CouponFontService;
 import com.cyksj.service.distribute.equipment.EquipmentDistributeService;
@@ -62,6 +65,7 @@ import com.cyksj.service.market.MarketFrontService;
 import com.cyksj.service.market.task.TaskService;
 import com.cyksj.service.order.OrderDonService;
 import com.cyksj.service.order.UserRealOrderBenefitsService;
+import com.cyksj.service.relation.GroupRelationClearService;
 import com.cyksj.service.relation.GroupRelationFrontService;
 import com.cyksj.service.template.TemplateCommonService;
 import com.cyksj.service.wechat.WeChatService;
@@ -79,10 +83,7 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
 
 import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_ALREADY_BE_USER;
 import static com.cyksj.enums.GatewayApiCode.GROUP_INDEX_NOT_FIND;
@@ -184,6 +185,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final TransferAccountsRecordMapper transferAccountsRecordMapper;
 
+	private final GroupRelationClearService groupRelationClearService;
+
+	private final UserRenewChangeTicketRecordMapper userRenewChangeTicketRecordMapper;
+
 	private final List<String> noChekGoodsTemp = List.of("Apple One", "Youtube", "Spotify");
 
 	@Transactional(rollbackFor = Throwable.class)
@@ -488,8 +493,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (order.getIsNoLogin()) {
 			order.setStatus(OrderDon.Status.hasPayment);
 		}
+
 		if (!order.getIsNoLogin()) {
-			updateCarParkAndSendMsg(goodsDon, sku, order);
+			if (order.getOrderType() == 2 && order.getIsChangeTicket()) {
+				//将奈飞月付车票更换成其他规格的车票
+				changeTicketAndRecord(order.getPreSkuId(), order.getRelationId(), order.getId(), order.getNum(), sku, order.getUserId());
+			} else {
+				updateCarParkAndSendMsg(goodsDon, sku, order);
+			}
 			if (order.getMoney().compareTo(BigDecimal.ZERO) > 0 && order.getIsDistribute()) {
 				TASK_POOL.execute(() -> distributeWaitingSendPointsRecord(order));
 			}
@@ -590,29 +601,40 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 	public OrderDon renewal(OrderPayRequest payRequest) throws Exception {
 		Long userId = payRequest.getUserId();
 		User user = userMapper.selectById(userId);
+		Long renewSkuId = payRequest.getSkuId();
 		CouponUser couponUser = null;
 		if (StrUtil.isNotBlank(payRequest.getCouponExCode()) || payRequest.getCouponId() != null) {
-			checkCouponStatus(payRequest.getSkuId(), payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId(), true);
+			checkCouponStatus(renewSkuId, payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId(), true);
 			//记录优惠券使用状态
 			couponUser = couponStatusRecord(payRequest.getCouponExCode(), payRequest.getCouponId(), payRequest.getUserId());
 		}
 		Long relationId = payRequest.getRelationId();
-		if (relationId == null || relationId <= 0 || payRequest.getSkuId() == null || payRequest.getNum() == null) {
+		if (relationId == null || relationId <= 0 || renewSkuId == null || payRequest.getNum() == null) {
 			throw new BusinessRuntimeException("缺少必填参数");
 		}
+		//是否有其他规格的续费订单 若有请先关闭
+		Integer count = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class)
+				.eq(OrderDon::getRelationId, payRequest.getRelationId())
+				.eq(OrderDon::getStatus, OrderDon.Status.noPayment).eq(OrderDon::getOrderType, 2));
+		if (count > 0) {
+			throw BusinessRuntimeException.getInstance("您有该车票未支付的续费订单..");
+		}
 
-		GoodsDonSku sku = goodsDonSkuMapper.selectById(payRequest.getSkuId());
+		GoodsDonSku sku = goodsDonSkuMapper.selectById(renewSkuId);
 
         GroupsRelation relation = groupsRelationMapper.selectById(relationId);
         if (relation == null) {
             throw new BusinessRuntimeException("");
         }
 
+		//todo 多账号同一用户绑定 续费
 		if (!relation.getUserId().equals(user.getId())) {
 			throw new BusinessRuntimeException("");
 		}
         GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
-
+		if (groupsTrips == null) {
+			throw BusinessRuntimeException.getInstance("该车队不存在,请联系客服");
+		}
 		GoodsDon goodsDon = goodsDonMapper.selectById(sku.getGoodsId());
 
 		if (goodsDon == null || !goodsDon.getStatus()) {
@@ -622,12 +644,33 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (goodsDon.getType() == 2) {
 			throw new BusinessRuntimeException("实物不支持续费");
 		}
+		//用户车票规格
+		Long tripsSkuId = groupsTrips.getSkuId();
+		GoodsDonSkuView goodsDonSkuView = beanSearcher.searchFirst(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getGoodsId, goodsDon.getId())
+				.field(GoodsDonSkuView::getSkuId, tripsSkuId).build());
+		if (goodsDonSkuView == null) {
+			throw BusinessRuntimeException.getInstance("该续费规格不属于该车队");
+		}
 
 		if (GroupsTrips.Status.waiting.equals(groupsTrips.getStatus())) {
 			if (!noChekGoodsTemp.contains(goodsDon.getTitle())) {
 				throw new BusinessRuntimeException("亲,该车次还未发车,需等待客服发车才能续费哦.");
 			}
 		}
+		//netflix 续费逻辑 月付可续费所有规格 其他规格只能续费对应规格
+		//netflix月付 续费其他规格 更换车队车位
+		Boolean isChangeTicket = false;
+		if (goodsDon.getType() == 1 && goodsDon.getId() == 1) {
+			if (tripsSkuId != 4 && tripsSkuId != renewSkuId) {
+				if (tripsSkuId == 5) {
+					throw BusinessRuntimeException.getInstance("季付车票只能续费季付");
+				} else if (tripsSkuId == 6) {
+					throw BusinessRuntimeException.getInstance("年付车票只能续费年付");
+				} else throw BusinessRuntimeException.getInstance("请选择对应规格的车票续费");
+			} else if (tripsSkuId == 4 && tripsSkuId != renewSkuId) {
+				isChangeTicket = true;
+			}
+		}
 
 		String payOpenId = user.getOpenId();
 
@@ -637,12 +680,17 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDon.setOrderNo(donNo);
 		orderDon.setOpenId(payOpenId);
 		orderDon.setMoney(sku.getPrice().multiply(BigDecimal.valueOf(payRequest.getNum())));
-		orderDon.setSkuId(payRequest.getSkuId());
+		orderDon.setSkuId(renewSkuId);
 		orderDon.setRelationId(relation.getId());
 		//续费订单
 		orderDon.setOrderType(2);
 		orderDon.setStatus(OrderDon.Status.noPayment);
 		orderDon.setUserId(user.getId());
+		//续费后 是否需要更换车票
+		if (isChangeTicket) {
+			orderDon.setIsChangeTicket(isChangeTicket);
+			orderDon.setPreSkuId(tripsSkuId);
+		}
 		orderDon.setGoodsId(goodsDon.getId());
 		//虚拟商品订单,是否是分销订单
 		if (goodsDon.getType() == 1) {
@@ -766,8 +814,13 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			orderDon.setStatus(OrderDon.Status.hasPayment);
 		}
 		if (!orderDon.getIsNoLogin()) {
-			//修改车位状态并发送模板消息
-			updateCarParkAndSendMsg(goodsDon, sku, orderDon);
+			if (orderDon.getOrderType() == 2 && orderDon.getIsChangeTicket()) {
+				//将奈飞月付车票更换成其他规格的车票
+				changeTicketAndRecord(orderDon.getPreSkuId(), orderDon.getRelationId(), orderDon.getId(), orderDon.getNum(), sku, orderDon.getUserId());
+			} else {
+				//修改车位状态并发送模板消息
+				updateCarParkAndSendMsg(goodsDon, sku, orderDon);
+			}
 			if (orderDon.getMoney().compareTo(BigDecimal.ZERO) > 0 && orderDon.getIsDistribute()) {
 				TASK_POOL.execute(() -> distributeWaitingSendPointsRecord(orderDon));
 			}
@@ -959,29 +1012,56 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		if (goodsDon != null && goodsDon.getType() == 1) {
 			//清出车队,车位变为已过期
 			Long relationId = orderDon.getRelationId();
+			//更换车票前后的座位id集合
+			List<Long> relationIds = new ArrayList<>();
+			relationIds.add(relationId);
 			Integer months = sku.getMonths();
 			Integer num = orderDon.getNum();
 			DateTime now = DateTime.now();
-			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
-			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, Constant.noOrderAllStatus)
-						.orderByDesc(OrderDon::getId)
-						.last("limit 1"));
-				if (otherOrder != null) {
-					DateTime endTime = DateUtil.offsetMonth(otherOrder.getCreatedTime(), months * num);
-					if (now.isBefore(endTime)) {
-						isFlag = false;
-					}
+			GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+					.eq(GroupsRelation::getUserId, orderDon.getUserId())
+					.eq(GroupsRelation::getId, relationId)
+					.eq(GroupsRelation::getStatus, GroupsRelation.Status.validity));
+			//车票变更之后
+			if (relation == null || orderDon.getIsChangeTicket()) {
+				UserRenewChangeTicketRecord record = userRenewChangeTicketRecordMapper.selectOne(Wrappers.lambdaQuery(UserRenewChangeTicketRecord.class)
+						.eq(UserRenewChangeTicketRecord::getUserId, orderDon.getUserId())
+						.eq(UserRenewChangeTicketRecord::getPreRelationId, relationId)
+						.orderByDesc(UserRenewChangeTicketRecord::getId).last("limit 1"));
+				if (record != null && relationId.equals(record.getPreRelationId())) {
+					relation = groupsRelationMapper.selectById(record.getAfterRelationId());
+					Optional.ofNullable(relation).ifPresent((re) -> relationIds.add(re.getId()));
 				}
 			}
 			if (relation != null) {
+				Boolean isFlag = true;
 				Date expiryTime = relation.getExpiryTime();
+				if (orderDon.getOrderType() == 2 || relationIds.size() > 1) {
+					//是否有其他付款订单是否过期
+					OrderDon otherOrder = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+							.in(OrderDon::getRelationId, relationIds)
+							.eq(OrderDon::getUserId, orderDon.getUserId())
+							.ne(OrderDon::getId, orderDon.getId())
+							.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
+							.orderByDesc(OrderDon::getId)
+							.last("limit 1"));
+					if (otherOrder != null) {
+						Long otherSkuId = otherOrder.getSkuId();
+						GoodsDonSku otherGoodsSku = goodsDonSkuMapper.selectById(otherSkuId);
+						DateTime endTime = DateUtil.offsetMonth(otherOrder.getCreatedTime(), otherGoodsSku.getMonths() * num);
+						if (now.isBefore(endTime)) {
+							isFlag = false;
+						}
+					} else {
+						//续费更换后的座位 过期时间
+						if (expiryTime != null) {
+							DateTime refundExpiryTime = DateUtil.offsetMonth(expiryTime, -sku.getMonths() * num);
+							if (now.isBefore(refundExpiryTime)) {
+								isFlag = false;
+							}
+						}
+					}
+				}
 				if (isFlag) {
 					relation.setUserId(0l);
 					relation.setStatus(GroupsRelation.Status.none);
@@ -992,7 +1072,7 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 						relation.setRenewStatus(false);
 						relation.setAccount(null);
 						groupsRelationMapper.updateExpiryTime(relation, expiryTime);
-					}else {
+					} else {
 						groupsRelationMapper.updateById(relation);
 					}
 					delRelation(relation.getId());
@@ -1154,9 +1234,13 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		}
 
 		if (redisService.get(RedisKey.GROUPS_RELATION_NUM_KEY + relationId) != null) {
+			log.info("=====>用户:{}获取了已有人座位:{}", userId, relationId);
 			throw new BusinessRuntimeException("客官手慢啦,该座位已经有人啦!");
 		} else {
-			redisService.set(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L);
+			if (!redisService.setNx(RedisKey.GROUPS_RELATION_NUM_KEY + relationId, userId, 60 * 5L)) {
+				log.info("=====>用户:{}未抢到座位:{}", userId, relationId);
+				throw new BusinessRuntimeException("客官手慢啦,该座位已经有人啦!");
+			}
 			GroupsRelation relation = groupsRelationMapper.selectById(relationId);
 			int count = groupsMapper.decrAvailableNum(relation.getGroupsId());
 			if (count == 0) {
@@ -1751,4 +1835,92 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		Optional.ofNullable(transDate).ifPresent(transferTime -> record.setTransferTime(DateTime.of(transferTime, "yyyy-MM-dd HH:mm:ss")));
 		transferAccountsRecordMapper.insert(record);
 	}
+
+	/**
+	 *
+	 * @param preSkuId 续费之前的规格id
+	 * @param preRelationId 续费之前的座位id
+	 * @param orderId 订单id
+	 * @param afterSku 续费之后的规格
+	 */
+	private void changeTicketAndRecord(Long preSkuId, Long preRelationId, Long orderId, Integer num, GoodsDonSku afterSku, Long userId) {
+		//清理之前的车票
+		GroupsRelation relation = groupsRelationMapper.selectById(preRelationId);
+		if (relation != null) {
+			//记录更换车票记录
+			Date preStartTime = relation.getStartTime();
+			Date preExpiryTime = relation.getExpiryTime();
+
+			groupRelationClearService.clearRenewGroupRelation(relation);
+			//获取续费规格的车票
+			GroupsRelation afterRelation = exchangeCodeService.getRelationNoOrder(afterSku, userId);
+
+			DateTime afterExpiryTime = DateUtil.offsetMonth(preExpiryTime, num * afterSku.getMonths());
+
+			afterRelation.setStartTime(preStartTime);
+			afterRelation.setExpiryTime(afterExpiryTime);
+			groupsRelationMapper.updateById(afterRelation);
+
+			//保存更换车票记录
+			saveChangeTicketRecord(preStartTime, preExpiryTime, preRelationId, userId, preSkuId, orderId, num, afterSku, afterRelation.getId());
+
+			GroupsRelationView groupsRelationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getUserId, userId)
+					.field(GroupsRelationView::getId, afterRelation.getId()).build());
+			try {
+				//发放更换车票模板消息通知用户
+				log.info("发送更换用户:{}车票:{}通知消息start=====>", userId, afterRelation.getId());
+				templateCommonService.sendUpdateAccountMsgNotify(groupsRelationView);
+				log.info("发送更换用户车票通知消息end=====>");
+			} catch (Exception e) {
+				log.error("发送更换用户:{}车票:{}通知消息错误e:{}", userId, afterRelation.getId(), StringUtil.getErrorText(e));
+			}
+		} else {
+			//之前的车票已被清除  加续费时间加到新的车票上
+			GroupsRelationView relationView = beanSearcher.searchFirst(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getUserId, userId)
+					.field(GroupsRelationView::getGoodsId, afterSku.getGoodsId()).build());
+			Date preStartTime = relation.getStartTime();
+			Date preExpiryTime = relation.getExpiryTime();
+			DateTime afterExpiryTime = DateUtil.offsetMonth(relationView.getExpiryTime(), num * afterSku.getMonths());
+
+			log.info("用户:{}重复续费,将过期时间累加到新车票:{}上", userId, relationView.getId());
+			relationView.setExpiryTime(afterExpiryTime);
+			groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+					.set(GroupsRelation::getExpiryTime, relationView.getExpiryTime())
+					.eq(GroupsRelation::getId, relationView.getId()));
+
+			//保存更换车票记录
+			saveChangeTicketRecord(preStartTime, preExpiryTime, relationView.getId(), userId, preSkuId, orderId, num, afterSku, relationView.getId());
+		}
+	}
+
+	/**
+	 *
+	 * @param preStartTime 续费之前的开始时间
+	 * @param preExpiryTime 续费之前的过期时间
+	 * @param preRelationId 之前的座位id
+	 * @param userId 用户id
+	 * @param preSkuId 之前座位的规格id
+	 * @param orderId 订单id
+	 * @param num 订单数量
+	 * @param afterSku 续费之后的规格id
+	 * @param afterRelationId 续费之后的座位id
+	 */
+	public void saveChangeTicketRecord(Date preStartTime, Date preExpiryTime, Long preRelationId, Long userId, Long preSkuId, Long orderId, Integer num, GoodsDonSku afterSku, Long afterRelationId) {
+		Long afterSkuId = afterSku.getId();
+		//记录更换车票记录
+		UserRenewChangeTicketRecord record = new UserRenewChangeTicketRecord();
+		record.setUserId(userId);
+		record.setPreRelationId(preRelationId);
+		record.setPreSkuId(preSkuId);
+		record.setPreStartTime(preStartTime);
+		record.setPreExpiryTime(preExpiryTime);
+		record.setRenewOrderId(orderId);
+
+		record.setAfterSkuId(afterSkuId);
+		record.setAfterRelationId(afterRelationId);
+		DateTime afterExpiryTime = DateUtil.offsetMonth(preExpiryTime, num * afterSku.getMonths());
+		record.setAfterExpiryTime(afterExpiryTime);
+
+		userRenewChangeTicketRecordMapper.insert(record);
+	}
 }

+ 5 - 0
netflix-service/src/main/java/com/cyksj/service/relation/GroupRelationClearService.java

@@ -15,4 +15,9 @@ public interface GroupRelationClearService {
 	 * 清除车票
 	 */
 	void clearTicket(GroupsRelation relation, UserTicketClearedRecord.Source source);
+
+	/**
+	 * 续费换车票,清理之前的车票
+	 */
+	void clearRenewGroupRelation(GroupsRelation relation);
 }

+ 20 - 0
netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationClearServiceImpl.java

@@ -73,4 +73,24 @@ public class GroupRelationClearServiceImpl implements GroupRelationClearService
 			}
 		}
 	}
+
+	@Override
+	public void clearRenewGroupRelation(GroupsRelation relation) {
+		Date expireTime = relation.getExpiryTime();
+		relation.setStartTime(null);
+		relation.setExpiryTime(null);
+		relation.setUserId(0l);
+		relation.setStatus(GroupsRelation.Status.none);
+		relation.setIsHandle(false);
+		relation.setRenewStatus(false);
+		relation.setAccount(null);
+		Integer success = groupsRelationMapper.updateExpiryTime(relation, expireTime);
+		if (success == 1) {
+			log.info("续费后更换车票,座位号:{}", relation.getId());
+			int count = groupsMapper.incrAvailableNum(relation.getGroupsId());
+			if (count == 0) {
+				log.error("续费更换车位异常 groupId:{}", relation.getGroupsId());
+			}
+		}
+	}
 }

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/template/TemplateCommonService.java

@@ -2,6 +2,7 @@ package com.cyksj.service.template;
 
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.request.ReqSetLogisticsPost;
+import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.views.WxAppTemplateView;
 
 /*
@@ -41,6 +42,11 @@ public interface TemplateCommonService {
 	 */
 	void sendAccountTemplateMsg(Account account) throws Exception;
 
+	/**
+	 * 修改用户车票账号密码通知
+	 */
+	void sendUpdateAccountMsgNotify(GroupsRelationView relation) throws Exception;
+
 	/**
 	 * Spotify账号到期通知模板
 	 */

+ 31 - 20
netflix-service/src/main/java/com/cyksj/service/template/impl/TemplateCommonServiceImpl.java

@@ -218,28 +218,39 @@ public class TemplateCommonServiceImpl implements TemplateCommonService {
 					.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) {
-				Long userId = relation.getUserId();
-				WxAppTemplateView wxAppTemplateView = this.getWxAppByUserId(userId, TemplateEnum.PLAT_ACCOUNT_UPDATE_TEMPLATE.getDesc());
-				if (wxAppTemplateView != null) {
-					User user = userMapper.selectById(relation.getUserId());
-					if (user != null) {
-						String openId = user.getOpenId();
-						WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
-								.setToUser(Optional.ofNullable(wxAppTemplateView.getOpenId()).orElse(openId))
-								.setTemplateId(wxAppTemplateView.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(wxAppTemplateView.getAppId()), Jsons.toJson(templateMessage));
-					}
-				}
+				sendUpdateAccountMsg(relation);
+			}
+		}
+	}
+
+	@Override
+	public void sendUpdateAccountMsgNotify(GroupsRelationView relation) throws Exception {
+		if (!EnvCommonService.active.equals(envCommonService.getEnv())) {
+			sendUpdateAccountMsg(relation);
+		}
+	}
+
+	public void sendUpdateAccountMsg(GroupsRelationView relation) throws Exception {
+		String url = String.format("%sauth/weChat?url=%syinhe/web/ticket", envCommonService.getHost(), envCommonService.getDomain());
+		Long userId = relation.getUserId();
+		WxAppTemplateView wxAppTemplateView = this.getWxAppByUserId(userId, TemplateEnum.PLAT_ACCOUNT_UPDATE_TEMPLATE.getDesc());
+		if (wxAppTemplateView != null) {
+			User user = userMapper.selectById(relation.getUserId());
+			if (user != null) {
+				String openId = user.getOpenId();
+				WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
+						.setToUser(Optional.ofNullable(wxAppTemplateView.getOpenId()).orElse(openId))
+						.setTemplateId(wxAppTemplateView.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(wxAppTemplateView.getAppId()), Jsons.toJson(templateMessage));
 			}
 		}
 	}

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

@@ -115,7 +115,7 @@ public class CmsAccountController {
     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)
+            mapBuilder.field(ExcelManageAccountData::getExpiryTime, expiryStartTime, expiryEndTime)
                     .op(Operator.Between);
         }
 
@@ -125,7 +125,7 @@ public class CmsAccountController {
                 throw BusinessRuntimeException.getInstance("暂无数据!");
             }
 
-            mapBuilder.field(AccountView::getId, accountIds).op(Operator.InList);
+            mapBuilder.field(ExcelManageAccountData::getId, accountIds).op(Operator.InList);
         }
         List<ExcelManageAccountData> list = beanSearcher.searchAll(ExcelManageAccountData.class, mapBuilder.build());