|
|
@@ -7,27 +7,39 @@ import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.TypeReference;
|
|
|
import com.ylx.common.core.domain.R;
|
|
|
import com.ylx.common.exception.ServiceException;
|
|
|
+import com.ylx.lottery.service.LotteryCountService;
|
|
|
+import com.ylx.massage.domain.TWxUser;
|
|
|
+import com.ylx.massage.service.TWxUserService;
|
|
|
import com.ylx.usercenter.domain.dto.UnifiedUserCenterDTO;
|
|
|
import com.ylx.usercenter.domain.vo.UnifiedUserCenterResponseVO;
|
|
|
import com.ylx.usercenter.domain.vo.UnifiedUserClientListVO;
|
|
|
import com.ylx.usercenter.service.UnifiedUserCenterService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Propagation;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
|
@Slf4j
|
|
|
@Service
|
|
|
public class UnifiedUserCenterServiceImpl implements UnifiedUserCenterService {
|
|
|
|
|
|
@Value("${remote.user-center.base-url}")
|
|
|
- private String baseUrl;
|
|
|
- @Value("${remote.user-center.client-id}")
|
|
|
+ private String userCenterBaseUrl;
|
|
|
+ @Value("${remote.local-live.client-id}")
|
|
|
private String clientId;
|
|
|
|
|
|
// 定义接口路径常量
|
|
|
private static final String QUERY_BIND_PATH = "/userApp/queryBind";
|
|
|
private static final String BIND_PATH = "/userApp/bind";
|
|
|
private static final String UNBIND_PATH = "/userApp/unbind";
|
|
|
+ @Resource
|
|
|
+ private TWxUserService wxUserService;
|
|
|
+ @Resource
|
|
|
+ private LotteryCountService lotteryCountService;
|
|
|
|
|
|
@Override
|
|
|
public R<?> queryBind(UnifiedUserCenterDTO dto) {
|
|
|
@@ -39,8 +51,42 @@ public class UnifiedUserCenterServiceImpl implements UnifiedUserCenterService {
|
|
|
|
|
|
@Override
|
|
|
public R<?> bind(UnifiedUserCenterDTO dto) {
|
|
|
- validateDto(dto);
|
|
|
- return executePost(BIND_PATH, dto, "添加用户绑定信息");
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 1.检验参数信息
|
|
|
+ validateDto(dto);
|
|
|
+
|
|
|
+ // 2.获取用户信息
|
|
|
+ TWxUser wxUser = this.wxUserService.getById(dto.getSourceUserId());
|
|
|
+ if (ObjUtil.isEmpty(wxUser)) {
|
|
|
+ log.error("用户信息不存在, userId: {}", dto.getSourceUserId());
|
|
|
+ throw new ServiceException("用户信息不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3.添加用户绑定信息
|
|
|
+ R<?> remoteResult = executePost(BIND_PATH, dto, "添加用户绑定信息");
|
|
|
+
|
|
|
+ // 4. 判断绑定成功
|
|
|
+ if (!remoteResult.getData().equals(Boolean.TRUE)) {
|
|
|
+ log.error("远程绑定失败, userId: {}, msg: {}", dto.getSourceUserId(), remoteResult.getMsg());
|
|
|
+ return R.fail("用户绑定失败:" + remoteResult.getMsg());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 更新本地数据库(独立事务)
|
|
|
+ updateUserBindStatus(dto);
|
|
|
+
|
|
|
+ // 6. 同步抽奖次数(异步处理)
|
|
|
+ syncLotteryCount(dto);
|
|
|
+
|
|
|
+ return R.ok(remoteResult.getData(), "用户绑定成功");
|
|
|
+
|
|
|
+ } catch (ServiceException e) {
|
|
|
+ log.error("用户绑定业务异常, userId:{}", dto.getSourceUserId(), e);
|
|
|
+ throw e;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("用户绑定系统异常, userId:{}", dto.getSourceUserId(), e);
|
|
|
+ throw new ServiceException("系统异常,请稍后重试");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -55,11 +101,10 @@ public class UnifiedUserCenterServiceImpl implements UnifiedUserCenterService {
|
|
|
private R<?> executePost(String path, UnifiedUserCenterDTO dto, String logDesc) {
|
|
|
try {
|
|
|
// 1. 构建 URL
|
|
|
- String url = baseUrl + path;
|
|
|
+ String url = userCenterBaseUrl + path;
|
|
|
|
|
|
// 2. 转换参数
|
|
|
String jsonParam = JSON.toJSONString(dto);
|
|
|
- log.info("调用远程接口: {}, 参数: {}", url, jsonParam);
|
|
|
|
|
|
// 3. 发送请求
|
|
|
String result = HttpUtil.post(url, jsonParam);
|
|
|
@@ -78,7 +123,7 @@ public class UnifiedUserCenterServiceImpl implements UnifiedUserCenterService {
|
|
|
|
|
|
// 4. 处理业务状态
|
|
|
if (!response.isSuccess()) {
|
|
|
- log.warn("查询绑定信息失败: data={},code={}, msg={}", response.getData(), response.getCode(), response.getMessage());
|
|
|
+ log.warn("{}失败: data={},code={}, msg={}", logDesc, response.getData(), response.getCode(), response.getMessage());
|
|
|
return R.fail(response.getData(), response.getMessage());
|
|
|
}
|
|
|
|
|
|
@@ -109,8 +154,91 @@ public class UnifiedUserCenterServiceImpl implements UnifiedUserCenterService {
|
|
|
* 参数校验提取
|
|
|
*/
|
|
|
private void validateDto(UnifiedUserCenterDTO dto) {
|
|
|
- if (ObjUtil.isNull(dto)) {
|
|
|
- throw new IllegalArgumentException("请求参数不能为空");
|
|
|
+
|
|
|
+ if (StrUtil.isEmpty(dto.getSourceUserId())) {
|
|
|
+ throw new IllegalArgumentException("源用户ID不能为空");
|
|
|
+ }
|
|
|
+ if (StrUtil.isEmpty(dto.getTargetUserId())) {
|
|
|
+ throw new IllegalArgumentException("目标用户ID不能为空");
|
|
|
+ }
|
|
|
+ if (StrUtil.isEmpty(dto.getClientId())) {
|
|
|
+ throw new IllegalArgumentException("客户端ID不能为空");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 独立事务方法
|
|
|
+ @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
|
|
|
+ public void updateUserBindStatus(UnifiedUserCenterDTO dto) {
|
|
|
+ try {
|
|
|
+ TWxUser wxUser = this.wxUserService.getById(dto.getSourceUserId());
|
|
|
+ wxUser.setIsBind(1);
|
|
|
+ boolean wxUserBool = this.wxUserService.updateById(wxUser);
|
|
|
+ if (!wxUserBool) {
|
|
|
+ log.error("更新用户绑定状态失败, userId: {}", dto.getSourceUserId());
|
|
|
+ throw new ServiceException("更新用户绑定状态失败");
|
|
|
+ }
|
|
|
+ log.info("更新用户绑定一账通信息成功, userId:{}", dto.getSourceUserId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("更新用户绑定状态异常, userId: {}", dto.getSourceUserId(), e);
|
|
|
+ // 如果本地更新失败,异步调用远程解绑接口进行补偿
|
|
|
+ asyncCompensateUnbind(dto);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 异步同步抽奖次数
|
|
|
+ @Async
|
|
|
+ public void syncLotteryCount(UnifiedUserCenterDTO dto) {
|
|
|
+ try {
|
|
|
+ boolean lotteryCountBool = this.lotteryCountService.batchAdd(dto);
|
|
|
+ if (lotteryCountBool) {
|
|
|
+ log.info("同步用户抽奖次数到本地生活服务成功, userId:{}", dto.getTargetUserId());
|
|
|
+ } else {
|
|
|
+ log.warn("同步用户抽奖次数到本地生活服务失败, userId:{}", dto.getTargetUserId());
|
|
|
+ // 同步失败,记录告警日志
|
|
|
+ log.error("【告警】同步用户抽奖次数到本地生活服务失败,需要人工处理, userId:{}", dto.getTargetUserId());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("同步用户抽奖次数到本地生活服务异常, userId:{}", dto.getTargetUserId(), e);
|
|
|
+ // 异常情况,记录告警日志
|
|
|
+ log.error("【告警】同步用户抽奖次数到本地生活服务异常,需要人工处理, userId:{}", dto.getTargetUserId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 异步补偿:解除用户绑定
|
|
|
+ * 带重试机制,最多重试3次
|
|
|
+ */
|
|
|
+ @Async
|
|
|
+ public void asyncCompensateUnbind(UnifiedUserCenterDTO dto) {
|
|
|
+ final int maxRetries = 3;
|
|
|
+ int retryCount = 0;
|
|
|
+ boolean success = false;
|
|
|
+
|
|
|
+ while (retryCount < maxRetries && !success) {
|
|
|
+ retryCount++;
|
|
|
+ try {
|
|
|
+ log.info("异步补偿:尝试解除用户绑定, userId: {}, 重试次数: {}", dto.getSourceUserId(), retryCount);
|
|
|
+ executePost(UNBIND_PATH, dto, "补偿:解除用户绑定信息");
|
|
|
+ log.info("异步补偿:成功解除用户绑定, userId: {}", dto.getSourceUserId());
|
|
|
+ success = true;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("异步补偿:解除用户绑定失败, userId: {}, 重试次数: {}", dto.getSourceUserId(), retryCount, e);
|
|
|
+ if (retryCount < maxRetries) {
|
|
|
+ try {
|
|
|
+ // 等待一段时间后重试
|
|
|
+ Thread.sleep(1000 * retryCount);
|
|
|
+ } catch (InterruptedException ie) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ log.error("异步补偿:线程被中断, userId: {}", dto.getSourceUserId(), ie);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 重试次数用完,记录告警日志
|
|
|
+ log.error("【告警】异步补偿:解除用户绑定失败,已达最大重试次数,需要人工处理, userId: {}", dto.getSourceUserId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|