فهرست منبع

Merge branch 'nf_recover_available'

zoujiajian 9 ماه پیش
والد
کامیت
f009ace177

+ 5 - 0
netflix-common/src/main/java/com/cyksj/common/constant/Constant.java

@@ -272,6 +272,11 @@ public interface Constant {
 	 */
 	Integer CORP_LINK_JOB_ID = 192;
 
+	/**
+	 * 用户奈飞账号恢复从备用账号替换job ID
+	 */
+	Integer NF_RECOVER_STANDBY_JOB_ID = 321;
+
 	String ACCOUNT_PWD_UPDATE_SYS_MSG_NOTIFY = "您购买的%s账号,密码已修改";
 
 	String ACCOUNT_SET_SYS_MSG_NOTIFY = "您购买的%s账号,账号已配置";

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

@@ -68,7 +68,8 @@ public class UserTicketClearedRecord extends BaseEntity{
 		ticket_replace("替换车票"),
 		vip_deduct("会员抵扣"),
 		codex_deduct("codex抵扣"),
-		cc_deduct("claude code抵扣")
+		cc_deduct("claude code抵扣"),
+		recover_replace("奈飞恢复直接替换")
 		;
 		String desc;
 

+ 11 - 0
netflix-service/src/main/java/com/cyksj/service/relation/CmsGroupsRelationService.java

@@ -0,0 +1,11 @@
+package com.cyksj.service.relation;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: CmsGroupsRelationService
+ * 创建者: JavaZou
+ * 创建时间:2025/11/11 17:45
+ */
+public interface CmsGroupsRelationService {
+	void recoverAvailableParkingSpace();
+}

+ 195 - 0
netflix-service/src/main/java/com/cyksj/service/relation/impl/CmsGroupsRelationServiceImpl.java

@@ -0,0 +1,195 @@
+package com.cyksj.service.relation.impl;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.mapper.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.relation.CmsGroupsRelationService;
+import com.cyksj.service.relation.GroupRelationClearService;
+import com.cyksj.service.user.UserBindRelationService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: CmsGroupsRelationServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2025/11/11 17:45
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class CmsGroupsRelationServiceImpl implements CmsGroupsRelationService {
+	private final NetflixUserAccountSubmitRecoverRecordMapper recoverRecordMapper;
+
+	private final GroupsRelationMapper groupsRelationMapper;
+
+	private final RedisService redisService;
+
+	private final GroupsMapper groupsMapper;
+
+	private final GoodsDonSkuMapper goodsDonSkuMapper;
+
+	private final UserBindRelationService userBindRelationService;
+
+	private final GroupRelationClearService clearService;
+
+	private final OrderDonMapper orderDonMapper;
+
+	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void recoverAvailableParkingSpace() {
+		List<NetflixUserAccountSubmitRecoverRecord> netflixUserAccountSubmitRecoverRecords = recoverRecordMapper.selectList(Wrappers.lambdaQuery(NetflixUserAccountSubmitRecoverRecord.class).eq(NetflixUserAccountSubmitRecoverRecord::getStatus, 0));
+		if (netflixUserAccountSubmitRecoverRecords.isEmpty()) {
+			return;
+		}
+		for (NetflixUserAccountSubmitRecoverRecord recoverRecord : netflixUserAccountSubmitRecoverRecords) {
+			Long recoverRecordId = recoverRecord.getId();
+			int update = recoverRecordMapper.update(null, Wrappers.lambdaUpdate(NetflixUserAccountSubmitRecoverRecord.class)
+					.set(NetflixUserAccountSubmitRecoverRecord::getStatus, 1)
+					.eq(NetflixUserAccountSubmitRecoverRecord::getId,recoverRecordId)
+					.eq(NetflixUserAccountSubmitRecoverRecord::getStatus, 0));
+			if (update > 0) {
+				//获取同规格的车票
+				Long skuId = recoverRecord.getSkuId();
+				GoodsDonSku sku = goodsDonSkuMapper.selectById(skuId);
+				DateTime newExpiryTime = DateUtil.offsetDay(DateTime.now(), recoverRecord.getRemainDays().intValue());
+				GroupsRelation newRelation = getRelation(sku, newExpiryTime);
+				if (newRelation == null) {
+					//恢复 待恢复状态
+					recoverStatusReset(recoverRecordId);
+					continue;
+				}
+				//清除车票
+				List<Long> userIdList;
+				try {
+					userIdList = userBindRelationService.getRelationUserIdList(recoverRecord.getUserId(), null);
+				} catch (Exception e) {
+					userIdList = List.of(recoverRecord.getUserId());
+				}
+				GroupsRelation oldRelation = groupsRelationMapper.selectOne(Wrappers.lambdaUpdate(GroupsRelation.class).eq(GroupsRelation::getId, recoverRecord.getRelationId()).in(GroupsRelation::getUserId, userIdList).last("limit 1"));
+				if (oldRelation != null) {
+					newRelation.setStartTime(oldRelation.getStartTime());
+					newRelation.setExpiryTime(newExpiryTime);
+					if (!resetUserTicket(oldRelation, newRelation, userIdList)) {
+						//恢复 待恢复状态
+						recoverStatusReset(recoverRecordId);
+					} else {
+						recoverRecordMapper.update(null, Wrappers.lambdaUpdate(NetflixUserAccountSubmitRecoverRecord.class)
+								.set(NetflixUserAccountSubmitRecoverRecord::getReplaceRelationId, newRelation.getId())
+								.eq(NetflixUserAccountSubmitRecoverRecord::getId, recoverRecordId));
+					}
+				}
+			}
+		}
+	}
+
+
+	public GroupsRelation getRelation(GoodsDonSku sku, DateTime expiryTime) {
+		GroupsRelation relation = null;
+		Integer maxNum = sku.getNum();
+		GroupsTrips groups = null;
+		if (groups == null) {
+			//寻找差不多过期时间规格的车位
+			relation = getSimilarExpiredGroupRelation(sku, expiryTime);
+			if (relation == null) {
+				DateTime filterAccountTime = null;
+				groups = groupsMapper.selectTicketGroups(sku.getId(), GroupsTrips.Status.validity, maxNum, null, filterAccountTime);
+				if (groups == null) {
+					return null;
+				}
+
+				//获取车位
+				if (relation == null) {
+					//寻找快满编车队进行占座
+					relation = groupsRelationMapper.selectRandomOneRelationByGroupsId(groups.getId());
+					if (relation == null) {
+						Long groupsId = groups.getId();
+						TASK_EXECUTOR.execute(() -> groupsMapper.updateAvailableNum(groupsId));
+						return null;
+					}
+				}
+			}
+		}
+		if (groups == null) {
+			groups = groupsMapper.selectById(relation.getGroupsId());
+		}
+		//车队目前可用停车数
+		relation.setGroupsAvailParkingNum(groups.getAvailableParkingNum());
+		relation.setMaxNum(maxNum);
+		return relation;
+	}
+
+	public GroupsRelation getSimilarExpiredGroupRelation(GoodsDonSku sku, DateTime expiryTime) {
+		Long skuId = sku.getId();
+		Integer maxNum = sku.getNum();
+		DateTime minExpiryTime = DateUtil.offsetDay(expiryTime, -5);
+		Long groupsId = groupsRelationMapper.selectSimilarExpiredRelation(skuId, minExpiryTime, maxNum, null);
+		if (groupsId == null) {
+			log.info("暂无相似过期时间车队");
+			return null;
+		}
+		GroupsRelation relation = groupsRelationMapper.selectOne(Wrappers.lambdaQuery(GroupsRelation.class)
+				.eq(GroupsRelation::getGroupsId, groupsId)
+				.eq(GroupsRelation::getStatus, GroupsRelation.Status.none)
+				.eq(GroupsRelation::getUserId, 0)
+				.orderByAsc(GroupsRelation::getNum)
+				.last("limit 1"));
+		if (relation != null) {
+			log.info("获取相似车队的车位relationId:{}", relation.getId());
+		}
+		return relation;
+	}
+
+	private Boolean resetUserTicket(GroupsRelation oldRelation, GroupsRelation newRelation, List<Long> userIdList) {
+		Long userId = oldRelation.getUserId();
+		Long oldRelationId = oldRelation.getId();
+		newRelation.setUserId(userId);
+
+		//校验座位
+		int update = groupsRelationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+				.set(GroupsRelation::getUserId, userId)
+				.eq(GroupsRelation::getUserId, 0)
+				.eq(GroupsRelation::getId, newRelation.getId()));
+		if (update == 0) {
+			return Boolean.FALSE;
+		}
+		groupsMapper.decrAvailableNum(newRelation.getGroupsId());
+		oldRelation.setNewRelationId(newRelation.getId());
+		clearService.clearTicket(oldRelation, UserTicketClearedRecord.Source.recover_replace);
+
+		//恢复状态
+		newRelation.setStatus(GroupsRelation.Status.validity);
+		groupsRelationMapper.updateById(newRelation);
+
+		//原订单座位id更新
+		OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+				.in(OrderDon::getUserId, userIdList)
+				.eq(OrderDon::getRelationId, oldRelationId)
+				.notIn(OrderDon::getStatus, Constant.noOrderStatus)
+				.orderByDesc(OrderDon::getId)
+				.last("limit 1"));
+		if (orderDon != null) {
+			orderDon.setRelationId(newRelation.getId());
+			orderDonMapper.updateById(orderDon);
+		}
+		return Boolean.TRUE;
+	}
+
+	public void recoverStatusReset(Long recoverId) {
+		recoverRecordMapper.update(null, Wrappers.lambdaUpdate(NetflixUserAccountSubmitRecoverRecord.class)
+				.set(NetflixUserAccountSubmitRecoverRecord::getStatus, 0)
+				.eq(NetflixUserAccountSubmitRecoverRecord::getId, recoverId)
+				.eq(NetflixUserAccountSubmitRecoverRecord::getStatus, 1));
+	}
+}

+ 33 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/account/NetflixErrorRecoverController.java

@@ -1,14 +1,21 @@
 package com.cyksj.web.controller.manage.account;
 
+import cn.hutool.core.util.StrUtil;
+import com.cyksj.common.EnvCommonService;
+import com.cyksj.common.constant.Constant;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.model.views.NetflixErrorRecoverView;
+import com.cyksj.service.relation.CmsGroupsRelationService;
+import com.cyksj.xxljob.XxlJobInfo;
+import com.cyksj.xxljob.XxlJobUtil;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
@@ -29,6 +36,10 @@ public class NetflixErrorRecoverController {
 
 	private final HttpServletRequest request;
 
+	private final CmsGroupsRelationService cmsGroupsRelationService;
+
+	private final EnvCommonService envCommonService;
+
 	/**
 	 * 奈飞恢复列表
 	 */
@@ -39,4 +50,26 @@ public class NetflixErrorRecoverController {
 				.build());
 		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
 	}
+
+
+	/**
+	 * 从已有可用车位中直接恢复
+	 */
+	@PostMapping("/recover/available/parking/space")
+	public Result<String> recoverAvailableParkingSpace() {
+		cmsGroupsRelationService.recoverAvailableParkingSpace();
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 奈飞从备用账号中恢复
+	 */
+	@PostMapping("/recover/standby")
+	public Result<String> recoverStandby() throws Exception {
+		if (envCommonService.isPrdEnv()) {
+			//立即执行客服值班定时任务
+			XxlJobUtil.executeMarkDynamicRuleTagJob(XxlJobInfo.getExecuteTaskInstance(Constant.NF_RECOVER_STANDBY_JOB_ID, StrUtil.EMPTY).toString());
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
 }