chore: sync local latest state and repository cleanup

This commit is contained in:
Your Name
2026-03-23 13:02:36 +08:00
parent f1ff3d629f
commit 2ef0f17961
493 changed files with 46912 additions and 7977 deletions

View File

@@ -0,0 +1,120 @@
package com.mosquito.project.job;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.UUID;
/**
* 内部奖励发放适配器
* 对接内部账户系统API支持超时、重试、幂等键
*/
@Component
public class InternalRewardDistributor implements RewardJobProcessor.RewardDistributor {
private static final Logger log = LoggerFactory.getLogger(InternalRewardDistributor.class);
@Value("${app.internal-account.api-url:}")
private String apiUrl;
@Value("${app.internal-account.api-key:}")
private String apiKey;
@Value("${app.internal-account.timeout:5000}")
private int timeout;
/**
* 是否允许模拟成功模式(仅开发/测试环境使用)
* 生产环境必须设置为false否则可能造成"假成功发放"
*/
@Value("${app.internal-account.allow-mock-success:false}")
private boolean allowMockSuccess;
private final RestTemplate restTemplate;
public InternalRewardDistributor(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public boolean distribute(Long userId, Long activityId, String trackingId, int points, String rewardType) {
// 生成幂等键,确保重试不会重复发放
String idempotencyKey = generateIdempotencyKey(userId, activityId, trackingId);
log.info("发放奖励: userId={}, activityId={}, trackingId={}, points={}, type={}, idempotencyKey={}, allowMockSuccess={}",
userId, activityId, trackingId, points, rewardType, idempotencyKey, allowMockSuccess);
// 如果未配置API地址
if (apiUrl == null || apiUrl.isBlank()) {
// 生产环境(Fail-Closed)不允许模拟成功返回false进入重试队列
if (!allowMockSuccess) {
log.error("内部账户系统API地址未配置且不允许模拟成功奖励发放失败请检查配置");
return false;
}
// 开发/测试环境:允许模拟成功
log.warn("内部账户系统API地址未配置模拟奖励发放成功仅开发环境");
return true;
}
try {
return callInternalAccountApi(userId, points, rewardType, idempotencyKey);
} catch (Exception e) {
log.error("奖励发放失败: userId={}, error={}", userId, e.getMessage());
return false;
}
}
/**
* 调用内部账户系统API
*/
private boolean callInternalAccountApi(Long userId, int points, String rewardType, String idempotencyKey) {
// 构建请求体
String requestBody = String.format(
"{\"userId\":%d,\"points\":%d,\"type\":\"%s\",\"idempotencyKey\":\"%s\"}",
userId, points, rewardType, idempotencyKey
);
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if (apiKey != null && !apiKey.isBlank()) {
headers.set("X-API-Key", apiKey);
}
headers.set("X-Idempotency-Key", idempotencyKey);
HttpEntity<String> request = new HttpEntity<>(requestBody, headers);
try {
ResponseEntity<String> response = restTemplate.exchange(
apiUrl + "/api/v1/points/grant",
HttpMethod.POST,
request,
String.class
);
if (response.getStatusCode() == HttpStatus.OK || response.getStatusCode() == HttpStatus.CREATED) {
log.info("奖励发放成功: userId={}, response={}", userId, response.getBody());
return true;
} else {
log.error("奖励发放失败: userId={}, status={}", userId, response.getStatusCode());
return false;
}
} catch (Exception e) {
log.error("调用内部账户系统API异常: userId={}, error={}", userId, e.getMessage());
throw e;
}
}
/**
* 生成幂等键
*/
private String generateIdempotencyKey(Long userId, Long activityId, String trackingId) {
return UUID.nameUUIDFromBytes(
String.format("%d-%d-%s", userId, activityId, trackingId).getBytes()
).toString();
}
}