ソースを参照

chong.afdian代充

zoujiajian 6 ヶ月 前
コミット
f1236c7836

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/response/recharge/ChongAfDianCardKeyResp.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.response.recharge;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: ChongAfDianCardKeyResp
+ * 创建者: JavaZou
+ * 创建时间:2026/2/4 9:44
+ */
+@Getter
+@Setter
+public class ChongAfDianCardKeyResp {
+	/**
+	 * 是否存在 true存在
+	 */
+	private Boolean exists;
+
+	/**
+	 * 是否有效 true有效
+	 */
+	private Boolean valid;
+
+	private String cardStatus;
+
+	private String message;
+}

+ 30 - 0
netflix-dao/src/main/java/com/cyksj/model/response/recharge/ChongAfDianCardKeySearchResp.java

@@ -0,0 +1,30 @@
+package com.cyksj.model.response.recharge;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: ChongAfDianCardKeySearchResp
+ * 创建者: JavaZou
+ * 创建时间:2026/2/4 10:00
+ */
+@Getter
+@Setter
+public class ChongAfDianCardKeySearchResp {
+
+	/**
+	 * key
+	 */
+	private String cardCode;
+
+	/**
+	 * 0未使用
+	 */
+	private String status;
+
+	/**
+	 * 卡密使用账号
+	 */
+	private String redeemEmail;
+}

+ 29 - 0
netflix-dao/src/main/java/com/cyksj/model/response/recharge/ChongAfDianTaskResp.java

@@ -0,0 +1,29 @@
+package com.cyksj.model.response.recharge;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: ChongAfDianTaskResp
+ * 创建者: JavaZou
+ * 创建时间:2026/2/4 10:13
+ */
+@Getter
+@Setter
+public class ChongAfDianTaskResp {
+	/**
+	 * 200 成功
+	 */
+	private Integer code;
+
+	/**
+	 * 任务id
+	 */
+	private String data;
+
+	/**
+	 * msg
+	 */
+	private String msg;
+}

+ 21 - 0
netflix-service/src/main/java/com/cyksj/service/recharge/ChongAfDianRechargeService.java

@@ -0,0 +1,21 @@
+package com.cyksj.service.recharge;
+
+import com.cyksj.model.entity.CmsUser;
+import com.cyksj.model.entity.GroupsRelation;
+import com.cyksj.model.response.recharge.ChongAfDianCardKeyResp;
+import com.cyksj.server.recharge.dto.TaskResult;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: ChongAfDianRechargeService
+ * 创建者: JavaZou
+ * 创建时间:2026/2/4 9:43
+ */
+public interface ChongAfDianRechargeService {
+
+	ChongAfDianCardKeyResp validateCardKey(String cardKey) throws Exception;
+
+	String confirmRecharge(GroupsRelation relation, String cardKey, CmsUser user);
+
+	TaskResult getTaskResult(GroupsRelation relation);
+}

+ 0 - 3
netflix-service/src/main/java/com/cyksj/service/recharge/impl/AfDianRechargeServiceImpl.java

@@ -14,7 +14,6 @@ import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.common.util.StringUtil;
 import com.cyksj.mapper.*;
-import com.cyksj.mapper.manage.cms.CmsUserMapper;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.response.AfDianCardKeySearchResp;
 import com.cyksj.model.response.recharge.AfDianCardKeyResp;
@@ -44,8 +43,6 @@ import java.util.List;
 public class AfDianRechargeServiceImpl implements AfDianRechargeService {
 	private final GptRechargeCardKeyChannelService gptRechargeCardKeyChannelService;
 
-	private final CmsUserMapper cmsUserMapper;
-
 	private final GroupsRelationMapper relationMapper;
 
 	private final GroupsMapper groupsMapper;

+ 310 - 0
netflix-service/src/main/java/com/cyksj/service/recharge/impl/ChongAfDianRechargeServiceImpl.java

@@ -0,0 +1,310 @@
+package com.cyksj.service.recharge.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpStatus;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.mapper.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.response.recharge.AfDianTaskStatusResp;
+import com.cyksj.model.response.recharge.ChongAfDianCardKeyResp;
+import com.cyksj.model.response.recharge.ChongAfDianCardKeySearchResp;
+import com.cyksj.model.response.recharge.ChongAfDianTaskResp;
+import com.cyksj.redis.RedisService;
+import com.cyksj.server.recharge.dto.TaskResult;
+import com.cyksj.service.chatgpt.GptUserRechargeRecordService;
+import com.cyksj.service.recharge.ChongAfDianRechargeService;
+import com.cyksj.service.recharge.GptRechargeCardKeyChannelService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 项目名: yhlxj11111111
+ * 文件名: ChongAfDianRechargeServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2026/2/4 9:43
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class ChongAfDianRechargeServiceImpl implements ChongAfDianRechargeService {
+	private final GptRechargeCardKeyChannelService gptRechargeCardKeyChannelService;
+
+	private final GroupsRelationMapper relationMapper;
+
+	private final GroupsMapper groupsMapper;
+
+	private final GoodsDonSkuMapper skuMapper;
+
+	private final RedisService redisService;
+
+	private final OrderDonMapper orderDonMapper;
+
+	private final GptRechargeCardKeyMapper gptRechargeCardKeyMapper;
+
+	private final GptUserRechargeRecordService gptUserRechargeRecordService;
+
+	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+	@Override
+	public ChongAfDianCardKeyResp validateCardKey(String cardKey) throws Exception {
+		String BASE_URL = gptRechargeCardKeyChannelService.getCardKeyDomain(cardKey);
+		String url = BASE_URL + "/redeem/verify";
+
+		JSONObject params = new JSONObject();
+		params.putOpt("cardCode", cardKey);
+		HttpResponse response = HttpRequest.post(url)
+				.body(params.toString())
+				.execute();
+		JSONObject resp = Jsons.parseObject(response.body(), JSONObject.class);
+		ChongAfDianCardKeyResp data = Jsons.parseObject(resp.getObj("data"), ChongAfDianCardKeyResp.class);
+		return data;
+	}
+
+	@Override
+	public String confirmRecharge(GroupsRelation relation, String gptCardKey, CmsUser cmsUser) {
+		Long relationId = relation.getId();
+		//校验卡密是否可用
+		ChongAfDianCardKeyResp chongAfDianCardKeyResp = null;
+		try {
+			chongAfDianCardKeyResp = validateCardKey(gptCardKey);
+		} catch (Exception e) {
+			if (cmsUser == null) {
+				throw BusinessRuntimeException.getInstance("系统繁忙,请重试");
+			}
+			throw BusinessRuntimeException.getInstance("卡密:{0}错误", gptCardKey);
+		}
+		if (!chongAfDianCardKeyResp.getExists() || !chongAfDianCardKeyResp.getValid()) {
+			log.error("卡密:{}已被使用", gptCardKey);
+			if (cmsUser == null) {
+				throw BusinessRuntimeException.getInstance("系统繁忙,请重试");
+			}
+			throw BusinessRuntimeException.getInstance("卡密:{0}已被使用", gptCardKey);
+		}
+		String accessToken = relation.getGptToken();
+		String rechargeAccount = relation.getAccount();
+		//提交任务
+		String taskId = null;
+		try {
+			taskId = submitTask(gptCardKey, accessToken);
+		} catch (Exception e) {
+			setRechargeRelationStatusAutoError(relation);
+			if (cmsUser == null) {
+				throw BusinessRuntimeException.getInstance("充值失败,请联系客服开通");
+			}
+			throw BusinessRuntimeException.getInstance("充值失败,{0}", StringUtil.getErrorText(e));
+		}
+		String operator = null;
+		if (cmsUser != null) {
+			operator = cmsUser.getNickname();
+		}
+		String password = relation.getPassword();
+		relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+				//记录代充账号
+				.set(GroupsRelation::getAccount, rechargeAccount)
+				//记录操作人员
+				.set(GroupsRelation::getOperator, operator)
+				//密码
+				.set(StrUtil.isNotBlank(password), GroupsRelation::getPassword, password)
+				.set(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharging)
+				.set(GroupsRelation::getCardKey, gptCardKey)
+				.set(GroupsRelation::getGptToken, accessToken)
+				.set(GroupsRelation::getGptTaskId, taskId)
+				.eq(GroupsRelation::getId, relationId));
+		return taskId;
+	}
+
+	private String submitTask(String cardKey, String accessToken) {
+		log.info("开始提交chong.afdian任务 - 卡密: {}", cardKey);
+		try {
+			String BASE_URL = gptRechargeCardKeyChannelService.getCardKeyDomain(cardKey);
+			String url = BASE_URL + "/redeem/submit";
+			JSONObject requestBody = new JSONObject();
+			requestBody.put("allowOverwrite", false);
+			requestBody.put("cardCode", cardKey);
+			requestBody.put("tokenContent", accessToken);
+
+			HttpResponse response = HttpRequest.post(url)
+					.setConnectionTimeout(60000)
+					.body(requestBody.toString())
+					.execute();
+			String body = response.body();
+			log.info("chong.afdian任务提交响应: {}", body);
+
+			ChongAfDianTaskResp chongAfDianTaskResp = Jsons.parseObject(body, ChongAfDianTaskResp.class);
+			if (chongAfDianTaskResp.getCode() != HttpStatus.HTTP_OK) {
+				throw BusinessRuntimeException.getInstance(chongAfDianTaskResp.getMsg());
+			}
+			return chongAfDianTaskResp.getData();
+		} catch (Exception e) {
+			log.error("提交任务失败: {}", e.getMessage(), e);
+			throw new RuntimeException("提交任务失败: " + e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * 设置代充自动充值异常
+	 */
+	public void setRechargeRelationStatusAutoError(GroupsRelation relation) {
+		TASK_EXECUTOR.execute(()->{
+			relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class)
+					.set(GroupsRelation::getAccount, relation.getAccount())
+					.set(GroupsRelation::getGptToken, relation.getGptToken())
+					.set(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharge_error)
+					.eq(GroupsRelation::getId, relation.getId()));
+		});
+	}
+
+	@Override
+	public TaskResult getTaskResult(GroupsRelation relation) {
+		try {
+			String taskId = relation.getGptTaskId();
+			String cardKey = relation.getCardKey();
+			AfDianTaskStatusResp taskStatusResp;
+			if (taskId.contains("Gateway time-out")) {
+				taskStatusResp = getChongCardKeyUseStatus(cardKey, relation.getAccount());
+			} else {
+				String BASE_URL = gptRechargeCardKeyChannelService.getCardKeyDomain(cardKey);
+				String url = BASE_URL + "/stocks/public/outstock/" + taskId;
+				HttpResponse response = null;
+				try {
+					response = HttpRequest.get(url).setConnectionTimeout(10000).execute();
+					String body = response.body();
+					log.info("任务查询响应: {}", body);
+					taskStatusResp = Jsons.parseObject(body, AfDianTaskStatusResp.class);
+					//任务不存在
+					if (StrUtil.isEmpty(taskStatusResp.getTask_id())) {
+						return null;
+					}
+				} catch (Exception e) {
+					//异常任务id
+					//直接获取卡密的使用情况
+					taskStatusResp = getChongCardKeyUseStatus(cardKey, relation.getAccount());
+				}
+			}
+			Boolean pending = taskStatusResp.getPending();
+			Boolean status = taskStatusResp.getSuccess();
+
+			//充值中
+			TaskResult taskResult = new TaskResult();
+			if (pending && status) {
+				taskResult.setStatus("recharging");
+				return taskResult;
+			}
+
+			String resultResult = taskStatusResp.getMessage();
+			//更新代充状态
+			resetGroupsRelationRechargeStatus(relation, pending, status, resultResult);
+
+			if (status && !pending) {
+				taskResult.setStatus("completed");
+			} else taskResult.setStatus("failed");
+			return taskResult;
+		} catch (Exception e) {
+			log.error("获取任务结果失败: {}", e.getMessage(), e);
+			return null;
+		}
+	}
+
+	/**
+	 * 更新代充状态
+	 */
+	private void resetGroupsRelationRechargeStatus(GroupsRelation relation, Boolean pending, Boolean status, String result) {
+		GroupsRelation.RechargeStatus rechargeStatus;
+		if (status && !pending) {
+			rechargeStatus = GroupsRelation.RechargeStatus.complete;
+		} else {
+			rechargeStatus = GroupsRelation.RechargeStatus.recharge_error;
+		}
+		String taskId = relation.getGptTaskId();
+		if (relation != null && relation.getRechargeStatus() == GroupsRelation.RechargeStatus.recharging) {
+			Integer rechargeRemainNum = relation.getRechargeRemainNum();
+			String cardKey = relation.getCardKey();
+			GptRechargeCardKey gptRechargeCardKey = gptRechargeCardKeyMapper.selectOne(Wrappers.lambdaQuery(GptRechargeCardKey.class).eq(GptRechargeCardKey::getCardKey, cardKey).last("limit 1"));
+			if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error) {
+				if (gptRechargeCardKey != null) {
+					gptRechargeCardKeyMapper.update(null, Wrappers.lambdaUpdate(GptRechargeCardKey.class).set(GptRechargeCardKey::getStatus, Boolean.FALSE).set(GptRechargeCardKey::getOrderId, null).set(GptRechargeCardKey::getOrderNo, null).eq(GptRechargeCardKey::getId, gptRechargeCardKey.getId()).eq(GptRechargeCardKey::getStatus, Boolean.TRUE));
+				}
+			}
+			if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error) {
+				if (!redisService.setNx(RedisService.key.GPT_RECHARGE_RECOVER_KEY.getName() + taskId, taskId, RedisService.key.GPT_RECHARGE_RECOVER_KEY.getTimeout())) {
+					return;
+				}
+			}
+			Date startTime = relation.getStartTime();
+			DateTime expiryTime = null;
+			if (rechargeStatus == GroupsRelation.RechargeStatus.complete && startTime == null) {
+				GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+				if (groupsTrips != null) {
+					startTime = DateTime.now();
+					GoodsDonSku sku = skuMapper.selectById(groupsTrips.getSkuId());
+					if (sku.getDays() != null && sku.getDays() > 0) {
+						expiryTime = DateUtil.offsetDay(startTime, sku.getDays());
+					} else {
+						expiryTime = DateUtil.offsetMonth(startTime, sku.getMonths());
+					}
+				}
+			}
+			String rechargeErrorInfo = null;
+			if (rechargeStatus == GroupsRelation.RechargeStatus.recharge_error) {
+				rechargeErrorInfo = result;
+			}
+			relationMapper.update(null, Wrappers.lambdaUpdate(GroupsRelation.class).set(rechargeStatus == GroupsRelation.RechargeStatus.recharge_error, GroupsRelation::getRechargeRemainNum, rechargeRemainNum + 1).set(GroupsRelation::getRechargeStatus, rechargeStatus).set(expiryTime != null, GroupsRelation::getStartTime, startTime).set(expiryTime != null, GroupsRelation::getExpiryTime, expiryTime).set(StrUtil.isNotEmpty(rechargeErrorInfo), GroupsRelation::getRechargeErrorInfo, rechargeErrorInfo).eq(GroupsRelation::getId, relation.getId()).le(GroupsRelation::getRechargeRemainNum, relation.getRechargeNum()).eq(GroupsRelation::getRechargeStatus, GroupsRelation.RechargeStatus.recharging));
+			//记录代充记录
+			if (rechargeStatus == GroupsRelation.RechargeStatus.complete) {
+				String remark = "自动代充";
+				if (StrUtil.isNotBlank(relation.getOperator())) {
+					remark = "后台自动代充";
+				}
+				gptUserRechargeRecordService.recordGptUserRechargeNum(relation.getUserId(), relation.getId(), relation.getAccount(), -1, relation.getOperator(), remark);
+				//修改对应订单状态
+				if (gptRechargeCardKey != null) {
+					Long orderId = gptRechargeCardKey.getOrderId();
+					OrderDon orderDon = orderDonMapper.selectById(orderId);
+					if (orderDon != null && orderDon.getStatus() == OrderDon.Status.hasPayment) {
+						orderDon.setStatus(OrderDon.Status.complete);
+						orderDonMapper.updateById(orderDon);
+					}
+				}
+			}
+		}
+	}
+
+	private AfDianTaskStatusResp getChongCardKeyUseStatus(String cardKey, String rechargeAccount) throws Exception {
+		String searchStatusByCardKeyUrl = "https://chong.afdian.org/api/card/batchQuery";
+
+		JSONObject params = new JSONObject();
+		params.putOpt("cardCodes", List.of(cardKey));
+
+		HttpResponse searchResp = HttpRequest.post(searchStatusByCardKeyUrl).body(params.toString()).setConnectionTimeout(10000).execute();
+		JSONObject resp = Jsons.parseObject(searchResp.body(), JSONObject.class);
+		List<ChongAfDianCardKeySearchResp> afDianCardKeySearchResps = Jsons.parseList(resp.getObj("data"), ChongAfDianCardKeySearchResp.class);
+		if (CollUtil.isNotEmpty(afDianCardKeySearchResps)) {
+			ChongAfDianCardKeySearchResp afDianCardKeySearchResp = afDianCardKeySearchResps.get(0);
+			AfDianTaskStatusResp taskStatusResp = new AfDianTaskStatusResp();
+			if ("1".equals(afDianCardKeySearchResp.getStatus()) && (afDianCardKeySearchResp.getRedeemEmail() == null || afDianCardKeySearchResp.getRedeemEmail().equals(rechargeAccount))) {
+				taskStatusResp.setSuccess(true);
+				taskStatusResp.setPending(false);
+			} else {
+				//失败
+				taskStatusResp.setSuccess(false);
+				taskStatusResp.setPending(false);
+			}
+			return taskStatusResp;
+		}
+		return null;
+	}
+}