|
|
@@ -52,6 +52,7 @@ public class BusinessLicenseService {
|
|
|
private final AdminMapper adminMapper;
|
|
|
private final ShopResourceSyncService shopResourceSyncService;
|
|
|
private final com.xuekairui.user.mapper.LicenseSyncDiffMapper licenseSyncDiffMapper;
|
|
|
+ private final com.xuekairui.user.mapper.LoginLogMapper loginLogMapper;
|
|
|
|
|
|
// ======================== 查询方法 ========================
|
|
|
|
|
|
@@ -188,6 +189,33 @@ public class BusinessLicenseService {
|
|
|
return businessLicenseMapper.markRewardGranted(licenseId);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 登录时检查并提供奖励(入驻信息审核通过,但目前无PC/SMS登录记录 → 触发补发)
|
|
|
+ * <p>
|
|
|
+ * 场景:入驻信息审核通过但用户未PC/SMS登录 → rewardGranted 保持 0 不标记,
|
|
|
+ * 待用户首次 PC/SMS 登录时补发 PRO 会员。
|
|
|
+ *
|
|
|
+ * @param userId 用户ID
|
|
|
+ */
|
|
|
+ @Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW)
|
|
|
+ public void tryGrantPendingLicenseReward(Long userId) {
|
|
|
+ BusinessLicense license = businessLicenseMapper.selectApprovedUnrewarded(userId);
|
|
|
+ if (license == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 原子标记 + 发放(与 approve() 中的逻辑一致)
|
|
|
+ int claimed = businessLicenseMapper.markRewardGranted(license.getId());
|
|
|
+ if (claimed > 0) {
|
|
|
+ membershipService.grantOrExtendMembership(
|
|
|
+ userId,
|
|
|
+ MembershipLevel.PRO,
|
|
|
+ 30,
|
|
|
+ "LICENSE",
|
|
|
+ "入驻信息审核通过赠送30天高级会员");
|
|
|
+ log.info("用户PC/SMS登录后补发高级会员奖励: userId={}, licenseId={}", userId, license.getId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/** 获取管理员用户名(用于审计日志操作人) */
|
|
|
private String getAdminName(Long adminId) {
|
|
|
if (adminId == null) {
|
|
|
@@ -577,35 +605,44 @@ public class BusinessLicenseService {
|
|
|
license.setShowVerifiedBadge(true);
|
|
|
businessLicenseMapper.updateById(license);
|
|
|
|
|
|
- // 原子标记奖励已发放(CAS:WHERE reward_granted = 0),
|
|
|
- // 杜绝并发场景下 rewardGranted 检查与更新的竞态条件,确保全局仅发放一次
|
|
|
- int claimed = businessLicenseMapper.markRewardGranted(licenseId);
|
|
|
- if (claimed > 0) {
|
|
|
- // 首次标记成功 → 赠送 30 天高级会员
|
|
|
- membershipService.grantOrExtendMembership(
|
|
|
- userId,
|
|
|
- MembershipLevel.PRO,
|
|
|
- 30,
|
|
|
- "LICENSE",
|
|
|
- "入驻信息审核通过赠送30天高级会员");
|
|
|
- log.info("入驻信息审核通过,赠送用户{} 30天高级会员", userId);
|
|
|
+ // 校验用户是否至少有过一次 PC/SMS 登录(小程序登录不算),
|
|
|
+ // 有登录记录才标记 reward_granted,无登录则不标记,等登录时补发
|
|
|
+ boolean hasPcOrSmsLogin = loginLogMapper.hasPcOrSmsLogin(userId);
|
|
|
+ if (!hasPcOrSmsLogin) {
|
|
|
+ log.info("用户{}未有PC/SMS登录记录,暂不发放高级会员奖励,待登录后补发: licenseId={}", userId, licenseId);
|
|
|
+ } else {
|
|
|
+ // 原子标记奖励已发放(CAS:WHERE reward_granted = 0),
|
|
|
+ // 杜绝并发场景下 rewardGranted 检查与更新的竞态条件,确保全局仅发放一次
|
|
|
+ int claimed = businessLicenseMapper.markRewardGranted(licenseId);
|
|
|
+ if (claimed > 0) {
|
|
|
+ // 首次标记成功 → 赠送 30 天高级会员
|
|
|
+ membershipService.grantOrExtendMembership(
|
|
|
+ userId,
|
|
|
+ MembershipLevel.PRO,
|
|
|
+ 30,
|
|
|
+ "LICENSE",
|
|
|
+ "入驻信息审核通过赠送30天高级会员");
|
|
|
+ log.info("入驻信息审核通过,赠送用户{} 30天高级会员", userId);
|
|
|
+ } else {
|
|
|
+ log.info("入驻信息重新审核通过,奖励已发放(CAS防并发),跳过重复奖励: licenseId={}, userId={}",
|
|
|
+ licenseId, userId);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 事务提交后推送 Redis 事件,供活动模块消费(邀请有礼活动)
|
|
|
- // 活动模块通过 uk_invitee 唯一索引保证同一被邀请人仅奖励一次
|
|
|
- TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
- @Override
|
|
|
- public void afterCommit() {
|
|
|
+ // 事务提交后推送 Redis 事件和同步(仅成功发放时推送事件)
|
|
|
+ final boolean finalHasLogin = hasPcOrSmsLogin;
|
|
|
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
|
|
+ @Override
|
|
|
+ public void afterCommit() {
|
|
|
+ if (finalHasLogin) {
|
|
|
try {
|
|
|
activityEventRedisService.pushLicenseApproved(userId, license.getId(), "BUSINESS_LICENSE");
|
|
|
} catch (Exception e) {
|
|
|
log.warn("存入入驻审核通过事件失败,不影响主流程: userId={}", userId, e);
|
|
|
}
|
|
|
}
|
|
|
- });
|
|
|
- } else {
|
|
|
- log.info("入驻信息重新审核通过,奖励已发放(CAS防并发),跳过重复奖励: licenseId={}, userId={}",
|
|
|
- licenseId, userId);
|
|
|
- }
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
// 事务提交后同步客户入驻信息到第三方(无论是否首次通过,每次审核状态变更都同步)
|
|
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|