|
|
@@ -133,6 +133,18 @@ public class BusinessLicenseService {
|
|
|
return hasOtherApprovedLicense(userId, excludeLicenseId);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 原子标记入驻奖励已发放(CAS:仅当 reward_granted=0 时更新为1)。
|
|
|
+ * <p>供 AuthService 第三方同步场景使用,与 approve() 方法使用同一 CAS 机制,
|
|
|
+ * 确保全局仅发放一次高级会员权益。
|
|
|
+ *
|
|
|
+ * @param licenseId 入驻信息 ID
|
|
|
+ * @return 受影响行数(1=首次标记成功,0=已被其他请求标记,调用方据此决定是否发放奖励)
|
|
|
+ */
|
|
|
+ public int markRewardGrantedAtomic(Long licenseId) {
|
|
|
+ return businessLicenseMapper.markRewardGranted(licenseId);
|
|
|
+ }
|
|
|
+
|
|
|
// ======================== 用户侧:提交入驻信息 ========================
|
|
|
|
|
|
/**
|
|
|
@@ -471,8 +483,11 @@ public class BusinessLicenseService {
|
|
|
license.setShowVerifiedBadge(true);
|
|
|
businessLicenseMapper.updateById(license);
|
|
|
|
|
|
- // 首次审核通过,赠送 30 天高级会员(rewardGranted 保证同一入驻记录仅发一次奖励)
|
|
|
- if (!Boolean.TRUE.equals(license.getRewardGranted())) {
|
|
|
+ // 原子标记奖励已发放(CAS:WHERE reward_granted = 0),
|
|
|
+ // 杜绝并发场景下 rewardGranted 检查与更新的竞态条件,确保全局仅发放一次
|
|
|
+ int claimed = businessLicenseMapper.markRewardGranted(licenseId);
|
|
|
+ if (claimed > 0) {
|
|
|
+ // 首次标记成功 → 赠送 30 天高级会员
|
|
|
membershipService.grantOrExtendMembership(
|
|
|
userId,
|
|
|
MembershipLevel.PRO,
|
|
|
@@ -481,10 +496,6 @@ public class BusinessLicenseService {
|
|
|
"入驻信息审核通过赠送30天高级会员");
|
|
|
log.info("入驻信息审核通过,赠送用户{} 30天高级会员", userId);
|
|
|
|
|
|
- // 标记奖励已发放
|
|
|
- license.setRewardGranted(true);
|
|
|
- businessLicenseMapper.updateById(license);
|
|
|
-
|
|
|
// 事务提交后推送 Redis 事件,供活动模块消费(邀请有礼活动)
|
|
|
// 活动模块通过 uk_invitee 唯一索引保证同一被邀请人仅奖励一次
|
|
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
@@ -497,19 +508,19 @@ public class BusinessLicenseService {
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ // 事务提交后同步客户入驻信息到第三方系统(智价云药店版),仅首次审核通过时同步
|
|
|
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
+ @Override
|
|
|
+ public void afterCommit() {
|
|
|
+ syncShopResourceToThirdParty(submitter, license, "APPROVE");
|
|
|
+ }
|
|
|
+ });
|
|
|
} else {
|
|
|
- log.info("入驻信息重新审核通过,奖励已发放,跳过重复奖励: licenseId={}, userId={}",
|
|
|
+ log.info("入驻信息重新审核通过,奖励已发放(CAS防并发),跳过重复奖励及第三方同步: licenseId={}, userId={}",
|
|
|
licenseId, userId);
|
|
|
}
|
|
|
|
|
|
- // 事务提交后同步客户入驻信息到第三方系统(智价云药店版),失败不阻塞主流程
|
|
|
- TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
- @Override
|
|
|
- public void afterCommit() {
|
|
|
- syncShopResourceToThirdParty(submitter, license, "APPROVE");
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
// 审计日志:审核通过,记录客户信息
|
|
|
String userName = submitter.getNickname() != null
|
|
|
? submitter.getNickname() : String.valueOf(userId);
|
|
|
@@ -918,9 +929,9 @@ public class BusinessLicenseService {
|
|
|
if (license == null) {
|
|
|
throw new BusinessException(ErrorCode.NOT_FOUND, "入驻信息记录不存在");
|
|
|
}
|
|
|
-// if (!LicenseStatus.PENDING.name().equals(license.getReviewStatus())) {
|
|
|
-// throw new BusinessException(ErrorCode.BAD_REQUEST, "该入驻信息已审核,无法重复操作");
|
|
|
-// }
|
|
|
+ if (!LicenseStatus.PENDING.name().equals(license.getReviewStatus())) {
|
|
|
+ throw new BusinessException(ErrorCode.BAD_REQUEST, "该入驻信息已审核,无法重复操作");
|
|
|
+ }
|
|
|
return license;
|
|
|
}
|
|
|
|