From d699da385dda6257677ee93bef47388c0635f64e Mon Sep 17 00:00:00 2001
From: “mitao” <“2763622819@qq.com”>
Date: 星期五, 17 四月 2026 08:57:41 +0800
Subject: [PATCH] feat:APP个人中心

---
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/points/service/PointsSettlementService.java |   52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 52 insertions(+), 0 deletions(-)

diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/points/service/PointsSettlementService.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/points/service/PointsSettlementService.java
index 0f27862..a24ee43 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/points/service/PointsSettlementService.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/points/service/PointsSettlementService.java
@@ -17,6 +17,8 @@
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
@@ -29,6 +31,9 @@
 public class PointsSettlementService {
 
     private static final DateTimeFormatter ORDER_NO_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+    private static final String SETTLEMENT_CLEAR_SOURCE_PREFIX = "config-clear-";
+    private static final String SETTLEMENT_CLEAR_REFERENCE_PREFIX = "POINTS_CONFIG-";
+    private static final Logger log = LoggerFactory.getLogger(PointsSettlementService.class);
 
     private final PointsConfigMapper pointsConfigMapper;
     private final UserPointsAccountMapper userPointsAccountMapper;
@@ -62,6 +67,9 @@
                 .eq(PointsConfig::getSettlementStatus, 0)
                 .le(PointsConfig::getActivityEndAt, now)
                 .orderByAsc(PointsConfig::getId));
+        if (!configs.isEmpty()) {
+            log.info("points settlement scan found {} expired pending configs", configs.size());
+        }
         for (PointsConfig config : configs) {
             settleConfig(config.getId());
         }
@@ -80,9 +88,13 @@
             return;
         }
 
+        log.info("points settlement start, configId={}, activityEndAt={}, requiredPoints={}",
+                config.getId(), config.getActivityEndAt(), config.getRequiredPoints());
         List<PointsSettlementCandidateRow> candidates = userPointsAccountMapper.selectSettlementCandidates();
         List<PointsExchangeOrderItem> createdItems = new ArrayList<PointsExchangeOrderItem>();
         LocalDateTime now = LocalDateTime.now();
+        int qualifiedCount = 0;
+        int clearedOnlyCount = 0;
         for (PointsSettlementCandidateRow candidate : candidates) {
             int balance = candidate.getPointsBalance() == null ? 0 : candidate.getPointsBalance();
             if (balance <= 0) {
@@ -92,6 +104,28 @@
                 PointsExchangeOrderItem item = buildOrderItem(config, candidate, now);
                 pointsExchangeOrderItemMapper.insert(item);
                 createdItems.add(item);
+                qualifiedCount++;
+            } else {
+                pointsLedgerAppService.createSettlementClearLedger(
+                        buildSettlementClearSourceId(config.getId(), candidate.getAppUserId()),
+                        candidate.getAppUserId(),
+                        candidate.getUserNickname(),
+                        candidate.getUserPhone(),
+                        candidate.getClinicId(),
+                        candidate.getClinicName(),
+                        candidate.getProvinceCode(),
+                        candidate.getProvinceName(),
+                        candidate.getCityCode(),
+                        candidate.getCityName(),
+                        candidate.getDistrictCode(),
+                        candidate.getDistrictName(),
+                        config.getGiftName(),
+                        config.getId(),
+                        buildSettlementReferenceNo(config.getId()),
+                        balance,
+                        now
+                );
+                clearedOnlyCount++;
             }
             UserPointsAccount account = userPointsAccountMapper.selectById(candidate.getAccountId());
             if (account != null) {
@@ -105,6 +139,8 @@
         config.setSettlementStatus(1);
         config.setSettledAt(now);
         pointsConfigMapper.updateById(config);
+        log.info("points settlement completed, configId={}, qualifiedCount={}, clearedOnlyCount={}, createdItemCount={}, settledAt={}",
+                config.getId(), qualifiedCount, clearedOnlyCount, createdItems.size(), now);
     }
 
     private PointsExchangeOrderItem buildOrderItem(PointsConfig config, PointsSettlementCandidateRow candidate, LocalDateTime now) {
@@ -275,4 +311,20 @@
         }
         return "-";
     }
+
+    /**
+     * 构建活动结束清零流水的唯一来源标识,确保重复调度不会重复入账。
+     */
+    private Long buildSettlementClearSourceId(Long configId, Long appUserId) {
+        String compositeId = SETTLEMENT_CLEAR_SOURCE_PREFIX + (configId == null ? "0" : configId)
+                + "-" + (appUserId == null ? "0" : appUserId);
+        return Long.valueOf(Math.abs((long) compositeId.hashCode()));
+    }
+
+    /**
+     * 构建活动结算流水引用编号,便于后台追溯到积分活动。
+     */
+    private String buildSettlementReferenceNo(Long configId) {
+        return SETTLEMENT_CLEAR_REFERENCE_PREFIX + (configId == null ? 0L : configId.longValue());
+    }
 }

--
Gitblit v1.7.1