Bladeren bron

奖品池调整

liuchengsen 1 maand geleden
bovenliggende
commit
d6395f2122

+ 4 - 1
zhijiayun-activity/src/main/java/com/xuekairui/activity/service/ActivityInviteService.java

@@ -484,6 +484,9 @@ public class ActivityInviteService implements ActivityInviteGate {
             // 审计日志:记录奖励撤销
             String inviterName = getUserNickname(record.getInviterId());
             String inviteeName = getUserNickname(inviteeId);
+            HashMap<String, Object> revokeBefore = new HashMap<>();
+            revokeBefore.put("rewardDays", record.getRewardDays());
+            revokeBefore.put("rewardLevel", "PRO");
             auditLogService.logSuccess(
                     revokerId,
                     "运营(反作弊)",
@@ -491,7 +494,7 @@ public class ActivityInviteService implements ActivityInviteGate {
                     OperationType.INVITE_REWARD_GRANT,
                     "INVITE_REVOKE:" + inviteeId,
                     record.getInviterId(),
-                    Map.of("rewardDays", record.getRewardDays(), "rewardLevel", "PRO"),
+                    revokeBefore,
                     null,
                     String.format("撤销邀请奖励:邀请人%s(因被邀请人%s作弊,原因:%s)", inviterName, inviteeName, reason));
 

+ 46 - 17
zhijiayun-activity/src/main/java/com/xuekairui/activity/service/ActivityLotteryService.java

@@ -31,6 +31,7 @@ import com.xuekairui.user.enums.OperationType;
 import com.xuekairui.user.enums.OperatorRole;
 import com.xuekairui.user.mapper.BusinessLicenseMapper;
 import com.xuekairui.user.mapper.UserMapper;
+import com.xuekairui.user.service.CrawlerQuotaGrantService;
 import com.xuekairui.user.service.MembershipService;
 import com.xuekairui.user.service.OperationAuditLogService;
 import lombok.RequiredArgsConstructor;
@@ -41,8 +42,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ThreadLocalRandom;
 
@@ -65,6 +66,7 @@ public class ActivityLotteryService implements ActivityLotteryGate {
     private static final Long CONFIG_ID = 1L;
     private static final String SOURCE_COMPARE = "COMPARE";
     private static final String REWARD_TYPE_MEMBERSHIP = "MEMBERSHIP";
+    private static final String REWARD_TYPE_CRAWLER = "CRAWLER";
     private static final String REWARD_TYPE_EMPTY = "EMPTY";
     private static final String REWARD_TYPE_PHYSICAL = "PHYSICAL";
     private static final String REWARD_TYPE_OTHER = "OTHER";
@@ -78,6 +80,7 @@ public class ActivityLotteryService implements ActivityLotteryGate {
     private final MembershipService membershipService;
     private final ActivityEventRedisService activityEventRedisService;
     private final OperationAuditLogService operationAuditLogService;
+    private final CrawlerQuotaGrantService crawlerQuotaGrantService;
 
     // ==========================================
     // 配置管理
@@ -258,19 +261,26 @@ public class ActivityLotteryService implements ActivityLotteryGate {
                 if (stockAffected == 0) {
                     // 库存不足(并发竞争),兜底为谢谢惠顾
                     log.warn("奖品库存不足,兜底谢谢惠顾: prizeId={}, name={}", prize.getId(), prize.getName());
+                    HashMap<String, Object> stockFailBefore = new HashMap<>();
+                    stockFailBefore.put("action", "DECREMENT_STOCK");
+                    stockFailBefore.put("prizeName", prize.getName());
+                    stockFailBefore.put("result", "FAILED");
                     operationAuditLogService.logSuccess(
                             userId, OperatorRole.USER, OperationType.USER_UPDATE,
                             "LOTTERY_PRIZE:" + prize.getId(), prize.getId(),
-                            Map.of("action", "DECREMENT_STOCK", "prizeName", prize.getName(), "result", "FAILED"),
+                            stockFailBefore,
                             null, "奖品库存扣减失败(已发完),兜底谢谢惠顾");
                     prize = ActivityLotteryPrize.builder()
                             .name("谢谢惠顾").rewardType(REWARD_TYPE_EMPTY).rewardAmount(0).build();
                 } else {
+                    HashMap<String, Object> stockOkBefore = new HashMap<>();
+                    stockOkBefore.put("action", "DECREMENT_STOCK");
+                    stockOkBefore.put("prizeName", prize.getName());
+                    stockOkBefore.put("remainingAfter", prize.getRemainingStock() != null ? prize.getRemainingStock() - 1 : -1);
                     operationAuditLogService.logSuccess(
                             userId, OperatorRole.USER, OperationType.USER_UPDATE,
                             "LOTTERY_PRIZE:" + prize.getId(), prize.getId(),
-                            Map.of("action", "DECREMENT_STOCK", "prizeName", prize.getName(),
-                                    "remainingAfter", prize.getRemainingStock() != null ? prize.getRemainingStock() - 1 : -1),
+                            stockOkBefore,
                             null, "奖品库存扣减成功");
                 }
             }
@@ -453,12 +463,16 @@ public class ActivityLotteryService implements ActivityLotteryGate {
                 .sort(request.getSort() != null ? request.getSort() : 0)
                 .build();
         prizeMapper.insert(prize);
+        HashMap<String, Object> createAfter = new HashMap<>();
+        createAfter.put("name", prize.getName());
+        createAfter.put("rewardType", prize.getRewardType());
+        createAfter.put("stock", stock);
+        createAfter.put("rewardDays", prize.getRewardDays());
         operationAuditLogService.logSuccess(
                 0L, OperatorRole.ADMIN, OperationType.USER_UPDATE,
                 "LOTTERY_PRIZE:" + prize.getId(), prize.getId(),
                 null,
-                Map.of("name", prize.getName(), "rewardType", prize.getRewardType(),
-                        "stock", stock, "rewardDays", prize.getRewardDays()),
+                createAfter,
                 "创建抽奖奖品:" + prize.getName());
         return prize;
     }
@@ -492,13 +506,17 @@ public class ActivityLotteryService implements ActivityLotteryGate {
         Optional.ofNullable(request.getEnabled()).ifPresent(prize::setEnabled);
         Optional.ofNullable(request.getSort()).ifPresent(prize::setSort);
         prizeMapper.updateById(prize);
+        HashMap<String, Object> updateAfter = new HashMap<>();
+        updateAfter.put("name", prize.getName());
+        updateAfter.put("rewardType", prize.getRewardType());
+        updateAfter.put("stock", prize.getStock());
+        updateAfter.put("remainingStock", prize.getRemainingStock());
+        updateAfter.put("rewardDays", prize.getRewardDays());
         operationAuditLogService.logSuccess(
                 0L, OperatorRole.ADMIN, OperationType.USER_UPDATE,
                 "LOTTERY_PRIZE:" + prize.getId(), prize.getId(),
                 null,
-                Map.of("name", prize.getName(), "rewardType", prize.getRewardType(),
-                        "stock", prize.getStock(), "remainingStock", prize.getRemainingStock(),
-                        "rewardDays", prize.getRewardDays()),
+                updateAfter,
                 "更新抽奖奖品:" + prize.getName());
         return prize;
     }
@@ -507,11 +525,14 @@ public class ActivityLotteryService implements ActivityLotteryGate {
     public void deletePrize(Long id) {
         ActivityLotteryPrize prize = prizeMapper.selectById(id);
         if (prize != null) {
+            HashMap<String, Object> deleteBefore = new HashMap<>();
+            deleteBefore.put("name", prize.getName());
+            deleteBefore.put("stock", prize.getStock());
+            deleteBefore.put("remainingStock", prize.getRemainingStock());
             operationAuditLogService.logSuccess(
                     0L, OperatorRole.ADMIN, OperationType.USER_UPDATE,
                     "LOTTERY_PRIZE:" + id, id,
-                    Map.of("name", prize.getName(), "stock", prize.getStock(),
-                            "remainingStock", prize.getRemainingStock()),
+                    deleteBefore,
                     null, "删除抽奖奖品:" + prize.getName());
         }
         prizeMapper.deleteById(id);
@@ -683,6 +704,7 @@ public class ActivityLotteryService implements ActivityLotteryGate {
     /**
      * 根据奖品类型发放奖励
      * - MEMBERSHIP:会员时长即刻到账
+     * - CRAWLER:爬虫次数即刻到账(默认30天有效期)
      * - PHYSICAL:实物奖品,记录中奖,需客服后续核实(不自动发放)
      * - OTHER:其他奖品,记录中奖,需客服后续核实(不自动发放)
      * - EMPTY:无奖励
@@ -691,14 +713,21 @@ public class ActivityLotteryService implements ActivityLotteryGate {
         if (REWARD_TYPE_EMPTY.equalsIgnoreCase(prize.getRewardType())) {
             return;
         }
-        if (REWARD_TYPE_PHYSICAL.equalsIgnoreCase(prize.getRewardType())) {
-            // 实物奖品不自动发放,中奖记录已入库,需客服在活动结束后联系核实
-            log.info("实物奖品中奖,待客服核实: userId={}, prize={}", userId, prize.getName());
+        if (REWARD_TYPE_PHYSICAL.equalsIgnoreCase(prize.getRewardType())
+                || REWARD_TYPE_OTHER.equalsIgnoreCase(prize.getRewardType())) {
+            // 实物/其他奖品不自动发放,中奖记录已入库,需客服后续核实
+            log.info("{}奖品中奖,待客服核实: userId={}, prize={}",
+                    prize.getRewardType(), userId, prize.getName());
             return;
         }
-        if (REWARD_TYPE_OTHER.equalsIgnoreCase(prize.getRewardType())) {
-            // 其他奖品不自动发放,中奖记录已入库,需客服后续核实
-            log.info("其他奖品中奖,待客服核实: userId={}, prize={}", userId, prize.getName());
+        if (REWARD_TYPE_CRAWLER.equalsIgnoreCase(prize.getRewardType())) {
+            int quotaCount = prize.getRewardAmount() != null && prize.getRewardAmount() > 0
+                    ? prize.getRewardAmount() : 1;
+            LocalDateTime expireTime = LocalDateTime.now().plusDays(30);
+            crawlerQuotaGrantService.grantAdminQuota(userId, quotaCount, expireTime,
+                    "比价抽奖中奖:" + prize.getName());
+            log.info("爬虫次数奖励已发放: userId={}, quota={}, expire={}, prize={}",
+                    userId, quotaCount, expireTime, prize.getName());
             return;
         }
         if (REWARD_TYPE_MEMBERSHIP.equalsIgnoreCase(prize.getRewardType())) {

+ 50 - 22
zhijiayun-user/src/main/java/com/xuekairui/user/service/BusinessLicenseService.java

@@ -677,6 +677,15 @@ public class BusinessLicenseService {
                         result.getNewUser(),
                         result.getAccessToken() != null ? "***" : null);
 
+                HashMap<String, Object> syncOkAfter = new HashMap<>();
+                syncOkAfter.put("userId", userId);
+                syncOkAfter.put("userName", userName);
+                syncOkAfter.put("storeName", storeName);
+                syncOkAfter.put("phone", phone != null ? phone : "UNKNOWN");
+                syncOkAfter.put("trigger", trigger);
+                syncOkAfter.put("externalUserId", result.getUserId());
+                syncOkAfter.put("newUser", result.getNewUser());
+                syncOkAfter.put("licenseAfter", buildSnapshot(license));
                 auditLogService.logSuccess(
                         0L,
                         OperatorRole.SYSTEM,
@@ -684,11 +693,7 @@ public class BusinessLicenseService {
                         targetIdentifier,
                         license.getId(),
                         licenseBefore,
-                        Map.of("userId", userId, "userName", userName, "storeName", storeName,
-                                "phone", phone != null ? phone : "UNKNOWN",
-                                "trigger", trigger,
-                                "externalUserId", result.getUserId(), "newUser", result.getNewUser(),
-                                "licenseAfter", buildSnapshot(license)),
+                        syncOkAfter,
                         String.format("同步客户「%s」(手机号:%s,药店:%s)的入驻信息到第三方成功[%s],第三方用户ID:%s",
                                 userName, phone != null ? phone : "UNKNOWN", storeName, trigger, result.getUserId()),
                         OperationSource.SYSTEM_SYNC);
@@ -698,6 +703,14 @@ public class BusinessLicenseService {
                 log.warn("同步客户入驻信息到第三方未成功(第三方未返回user_id),标记为2: licenseId={}",
                         license.getId());
 
+                HashMap<String, Object> syncNoUserAfter = new HashMap<>();
+                syncNoUserAfter.put("userId", userId);
+                syncNoUserAfter.put("userName", userName);
+                syncNoUserAfter.put("storeName", storeName);
+                syncNoUserAfter.put("phone", phone != null ? phone : "UNKNOWN");
+                syncNoUserAfter.put("trigger", trigger);
+                syncNoUserAfter.put("result", "no_user_id");
+                syncNoUserAfter.put("licenseAfter", buildSnapshot(license));
                 auditLogService.logSuccess(
                         0L,
                         OperatorRole.SYSTEM,
@@ -705,10 +718,7 @@ public class BusinessLicenseService {
                         targetIdentifier,
                         license.getId(),
                         licenseBefore,
-                        Map.of("userId", userId, "userName", userName, "storeName", storeName,
-                                "phone", phone != null ? phone : "UNKNOWN",
-                                "trigger", trigger, "result", "no_user_id",
-                                "licenseAfter", buildSnapshot(license)),
+                        syncNoUserAfter,
                         String.format("同步客户「%s」(手机号:%s,药店:%s)的入驻信息到第三方未成功[%s]:第三方未返回用户ID",
                                 userName, phone != null ? phone : "UNKNOWN", storeName, trigger),
                         OperationSource.SYSTEM_SYNC);
@@ -719,6 +729,14 @@ public class BusinessLicenseService {
             log.warn("同步客户入驻信息到第三方失败,标记为2,不影响主流程: licenseId={}, error={}",
                     license.getId(), e.getMessage());
 
+            HashMap<String, Object> syncErrAfter = new HashMap<>();
+            syncErrAfter.put("userId", userId);
+            syncErrAfter.put("userName", userName);
+            syncErrAfter.put("storeName", storeName);
+            syncErrAfter.put("phone", phone != null ? phone : "UNKNOWN");
+            syncErrAfter.put("trigger", trigger);
+            syncErrAfter.put("error", e.getMessage());
+            syncErrAfter.put("licenseAfter", buildSnapshot(license));
             auditLogService.logSuccess(
                     0L,
                     OperatorRole.SYSTEM,
@@ -726,10 +744,7 @@ public class BusinessLicenseService {
                     targetIdentifier,
                     license.getId(),
                     licenseBefore,
-                    Map.of("userId", userId, "userName", userName, "storeName", storeName,
-                            "phone", phone != null ? phone : "UNKNOWN",
-                            "trigger", trigger, "error", e.getMessage(),
-                            "licenseAfter", buildSnapshot(license)),
+                    syncErrAfter,
                     String.format("同步客户「%s」(手机号:%s,药店:%s)的入驻信息到第三方失败[%s]:%s",
                             userName, phone != null ? phone : "UNKNOWN", storeName, trigger, e.getMessage()),
                     OperationSource.SYSTEM_SYNC);
@@ -856,6 +871,13 @@ public class BusinessLicenseService {
                 log.info("运营手动同步入驻信息到第三方成功: licenseId={}, externalUserId={}, newUser={}",
                         licenseId, result.getUserId(), result.getNewUser());
 
+                HashMap<String, Object> manualOkAfter = new HashMap<>();
+                manualOkAfter.put("userId", userId);
+                manualOkAfter.put("userName", userName);
+                manualOkAfter.put("storeName", storeName);
+                manualOkAfter.put("externalUserId", result.getUserId());
+                manualOkAfter.put("newUser", result.getNewUser());
+                manualOkAfter.put("licenseAfter", buildSnapshot(license));
                 auditLogService.logSuccess(
                         reviewerId != null ? reviewerId : 0L,
                         getAdminName(reviewerId),
@@ -864,9 +886,7 @@ public class BusinessLicenseService {
                         "LICENSE:" + license.getId(),
                         license.getId(),
                         licenseBefore,
-                        Map.of("userId", userId, "userName", userName, "storeName", storeName,
-                                "externalUserId", result.getUserId(), "newUser", result.getNewUser(),
-                                "licenseAfter", buildSnapshot(license)),
+                        manualOkAfter,
                         String.format("手动同步客户「%s」(药店:%s)的入驻信息到第三方,第三方用户ID:%s",
                                 userName, storeName, result.getUserId()),
                         OperationSource.ADMIN_CREATE);
@@ -876,6 +896,12 @@ public class BusinessLicenseService {
                 businessLicenseMapper.updateById(license);
                 log.warn("运营手动同步入驻信息到第三方未成功(未返回user_id): licenseId={}", licenseId);
 
+                HashMap<String, Object> manualNoUserAfter = new HashMap<>();
+                manualNoUserAfter.put("userId", userId);
+                manualNoUserAfter.put("userName", userName);
+                manualNoUserAfter.put("storeName", storeName);
+                manualNoUserAfter.put("result", "no_user_id");
+                manualNoUserAfter.put("licenseAfter", buildSnapshot(license));
                 auditLogService.logSuccess(
                         reviewerId != null ? reviewerId : 0L,
                         getAdminName(reviewerId),
@@ -884,9 +910,7 @@ public class BusinessLicenseService {
                         "LICENSE:" + license.getId(),
                         license.getId(),
                         licenseBefore,
-                        Map.of("userId", userId, "userName", userName, "storeName", storeName,
-                                "result", "no_user_id",
-                                "licenseAfter", buildSnapshot(license)),
+                        manualNoUserAfter,
                         String.format("手动同步客户「%s」(药店:%s)的入驻信息到第三方未成功:第三方未返回用户ID",
                                 userName, storeName),
                         OperationSource.ADMIN_CREATE);
@@ -897,6 +921,12 @@ public class BusinessLicenseService {
             businessLicenseMapper.updateById(license);
             log.warn("运营手动同步入驻信息到第三方失败: licenseId={}, error={}", licenseId, e.getMessage());
 
+            HashMap<String, Object> manualErrAfter = new HashMap<>();
+            manualErrAfter.put("userId", userId);
+            manualErrAfter.put("userName", userName);
+            manualErrAfter.put("storeName", storeName);
+            manualErrAfter.put("error", e.getMessage());
+            manualErrAfter.put("licenseAfter", buildSnapshot(license));
             auditLogService.logSuccess(
                     reviewerId != null ? reviewerId : 0L,
                     getAdminName(reviewerId),
@@ -905,9 +935,7 @@ public class BusinessLicenseService {
                     "LICENSE:" + license.getId(),
                     license.getId(),
                     licenseBefore,
-                    Map.of("userId", userId, "userName", userName, "storeName", storeName,
-                            "error", e.getMessage(),
-                            "licenseAfter", buildSnapshot(license)),
+                    manualErrAfter,
                     String.format("手动同步客户「%s」(药店:%s)的入驻信息到第三方失败:%s",
                             userName, storeName, e.getMessage()),
                     OperationSource.ADMIN_CREATE);