From 2b0b64182263d922b946ec898070e59b602382dc Mon Sep 17 00:00:00 2001
From: Bang Hu <hu_bang@hotmail.com>
Date: 星期四, 11 九月 2025 18:38:09 +0800
Subject: [PATCH] 交易审批多节点问题处理完善

---
 src/main/java/com/webmanage/service/impl/PointsFlowServiceImpl.java |  513 +++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 424 insertions(+), 89 deletions(-)

diff --git a/src/main/java/com/webmanage/service/impl/PointsFlowServiceImpl.java b/src/main/java/com/webmanage/service/impl/PointsFlowServiceImpl.java
index ac0d99d..c09eb66 100644
--- a/src/main/java/com/webmanage/service/impl/PointsFlowServiceImpl.java
+++ b/src/main/java/com/webmanage/service/impl/PointsFlowServiceImpl.java
@@ -7,12 +7,17 @@
 import com.webmanage.common.BusinessException;
 import com.webmanage.common.PageResult;
 import com.webmanage.dto.AddPointsFlowDTO;
+import com.webmanage.dto.DeductUserPointsDTO;
 import com.webmanage.dto.PointsFlowQueryDTO;
+import com.webmanage.emun.RuleTypeEnum;
 import com.webmanage.entity.PointsFlow;
 import com.webmanage.entity.PointsRule;
 import com.webmanage.entity.UserPoints;
 import com.webmanage.mapper.PointsFlowMapper;
 import com.webmanage.mapper.UserPointsMapper;
+import com.webmanage.mapper.PointsTransactionMapper;
+import com.webmanage.entity.PointsTransaction;
+import com.webmanage.service.OrderInfoService;
 import com.webmanage.service.PointsFlowService;
 import com.webmanage.service.PointsRuleService;
 import lombok.extern.slf4j.Slf4j;
@@ -24,6 +29,7 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * 绉垎娴佹按Service瀹炵幇绫�
@@ -38,17 +44,20 @@
     @Resource
     private PointsRuleService pointsRuleService;
 
+    @Resource
+    private PointsTransactionMapper pointsTransactionMapper;
+
+
     @Override
     public PageResult<PointsFlow> getPersonalPointsFlowPage(PointsFlowQueryDTO queryDTO) {
-        if (queryDTO.getUserId() == null) {
+        if (!StringUtils.hasText(queryDTO.getUserId())) {
             throw new BusinessException("鐢ㄦ埛ID涓嶈兘涓虹┖");
         }
 
         Page<PointsFlow> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
         
         QueryWrapper<PointsFlow> wrapper = new QueryWrapper<>();
-        wrapper.eq("deleted", 0)
-               .eq("user_id", queryDTO.getUserId());
+        wrapper.eq("user_id", queryDTO.getUserId());
         
         buildQueryWrapper(wrapper, queryDTO);
         
@@ -72,8 +81,7 @@
         Page<PointsFlow> page = new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize());
         
         QueryWrapper<PointsFlow> wrapper = new QueryWrapper<>();
-        wrapper.eq("deleted", 0)
-               .eq("unit_id", queryDTO.getUnitId());
+        wrapper.eq("unit_id", queryDTO.getUnitId());
         
         buildQueryWrapper(wrapper, queryDTO);
         
@@ -118,97 +126,124 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean recordPointsFlow(Long userId, Long unitId, String flowType, String pointsSource, 
-                                  Integer pointsValue, String orderId, String description) {
-        if (userId == null || unitId == null || !StringUtils.hasText(flowType) || 
-            !StringUtils.hasText(pointsSource) || pointsValue == null) {
-            throw new BusinessException("鍙傛暟涓嶅畬鏁�");
-        }
-
-        // 鍒涘缓绉垎娴佹按璁板綍
-        PointsFlow pointsFlow = new PointsFlow();
-        pointsFlow.setUserId(userId);
-        pointsFlow.setUnitId(unitId);
-        pointsFlow.setDataType(flowType);
-        pointsFlow.setDataCategory(pointsSource);
-        pointsFlow.setPoints(pointsValue);
-        pointsFlow.setName(description);
-        pointsFlow.setFlowTime(LocalDateTime.now());
-
-        boolean saved = save(pointsFlow);
-        if (!saved) {
-            throw new BusinessException("淇濆瓨绉垎娴佹按澶辫触");
-        }
-
-        // 鏇存柊鐢ㄦ埛绉垎
-        updateUserPoints(userId, unitId, pointsValue);
-
-        return true;
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
     public boolean addPointsFlowByRule(AddPointsFlowDTO addPointsFlowDTO) {
         if (addPointsFlowDTO == null) {
             throw new BusinessException("鍙傛暟涓嶈兘涓虹┖");
         }
 
-        Long userId = addPointsFlowDTO.getUserId();
-        Long unitId = addPointsFlowDTO.getUnitId();
-        String ruleType = addPointsFlowDTO.getRuleType();
-        String ruleName = addPointsFlowDTO.getRuleName();
+        String userId = addPointsFlowDTO.getUserId();
+        String providerId = addPointsFlowDTO.getProviderId();
+        String unitId = addPointsFlowDTO.getUnitId();
+        Integer ruleType = addPointsFlowDTO.getRuleType();
+        String category = addPointsFlowDTO.getCategory();
+        String ruleNameCode = addPointsFlowDTO.getRuleNameCode();
         Integer count = addPointsFlowDTO.getCount() != null ? addPointsFlowDTO.getCount() : 1;
 
-        // 鏌ヨ绉垎瑙勫垯
-        PointsRule pointsRule = pointsRuleService.getRuleByTriggerCondition(ruleName);
-        if (pointsRule == null) {
-            throw new BusinessException("绉垎瑙勫垯涓嶅瓨鍦ㄦ垨鏈惎鐢�: " + ruleName);
+        boolean applyUser = StringUtils.hasText(userId) ;
+        boolean applyProvider = StringUtils.hasText(providerId) ;;
+        if (!applyUser && !applyProvider) {
+            throw new BusinessException("userId 涓� providerId 涓嶈兘鍚屾椂涓虹┖");
         }
 
-        // 楠岃瘉瑙勫垯绫诲瀷鏄惁鍖归厤
-        if (!ruleType.equals(pointsRule.getRuleType())) {
-            throw new BusinessException("瑙勫垯绫诲瀷涓嶅尮閰嶏紝鏈熸湜: " + pointsRule.getRuleType() + "锛屽疄闄�: " + ruleType);
+        // 鍒嗗埆鑾峰彇鐢ㄦ埛渚�/鎻愪緵鑰呬晶鏈�鏂扮敓鏁堣鍒欙紙鎸� points_winner 杩囨护锛�1鐢ㄦ埛銆�0鎻愪緵鑰咃級
+        PointsRule userRule = null;
+        PointsRule providerRule = null;
+        if (applyUser) {
+            userRule = getLatestEffectiveRule(ruleType, ruleNameCode, category, 1);
+            if (userRule == null) {
+                throw new BusinessException("鏈壘鍒扮敤鎴蜂晶鐢熸晥瑙勫垯: ruleType=" + ruleType + ", ruleNameCode=" + ruleNameCode + ", category=" + category + ", points_winner=1");
+            }
+            if (!ruleType.equals(userRule.getRuleType())) {
+                throw new BusinessException("鐢ㄦ埛渚ц鍒欑被鍨嬩笉鍖归厤锛屾湡鏈�: " + userRule.getRuleType() + "锛屽疄闄�: " + ruleType);
+            }
+        }
+        if (applyProvider) {
+            providerRule = getLatestEffectiveRule(ruleType, ruleNameCode, category, 0);
+            if (providerRule == null) {
+                throw new BusinessException("鏈壘鍒版彁渚涜�呬晶鐢熸晥瑙勫垯: ruleType=" + ruleType + ", ruleNameCode=" + ruleNameCode + ", category=" + category + ", points_winner=0");
+            }
+            if (!ruleType.equals(providerRule.getRuleType())) {
+                throw new BusinessException("鎻愪緵鑰呬晶瑙勫垯绫诲瀷涓嶅尮閰嶏紝鏈熸湜: " + providerRule.getRuleType() + "锛屽疄闄�: " + ruleType);
+            }
         }
 
-        // 璁$畻绉垎鍊�
-        Integer basePoints = pointsRule.getPointsValue() != null ? pointsRule.getPointsValue() : 0;
-        Integer totalPoints = basePoints * count;
-
-        // 濡傛灉鏄秷璐圭被鍨嬶紝绉垎涓鸿礋鏁�
-        if ("娑堣垂".equals(ruleType)) {
-            totalPoints = -totalPoints;
+        // 璁$畻涓や晶绉垎鍙樺寲锛堝悇鑷寜瑙勫垯璁$畻锛�
+        int userPointsChange = 0;
+        int providerPointsChange = 0;
+        if (applyUser) {
+            int base = userRule.getPointsValue() != null ? userRule.getPointsValue() : 0;
+            int amount = base * count;
+            userPointsChange = (ruleType == RuleTypeEnum.CONSUME.getCode()) ? -Math.abs(amount) : Math.abs(amount);
+        }
+        if (applyProvider) {
+            int base = providerRule.getPointsValue() != null ? providerRule.getPointsValue() : 0;
+            int amount = base * count;
+            providerPointsChange = (ruleType == RuleTypeEnum.CONSUME.getCode()) ? -Math.abs(amount) : Math.abs(amount);
         }
 
-        // 妫�鏌ユ瘡鏃ョН鍒嗕笂闄�
-        if (basePoints > 0 && pointsRule.getPriority() != null && pointsRule.getPriority() > 0) {
-            checkDailyLimit(userId, unitId, ruleName, totalPoints, pointsRule.getPriority());
+        // 姣忔棩涓婇檺鏍¢獙锛堝垎鍒牎楠岋級
+        if (applyUser && userRule.getIsLimit() != null && userRule.getIsLimit() == 1) {
+            checkDailyLimitByRule(userId, unitId, userRule, userPointsChange);
+        }
+        if (applyProvider && providerRule.getIsLimit() != null && providerRule.getIsLimit() == 1) {
+            checkDailyLimitByRule(providerId, null, providerRule, providerPointsChange);
         }
 
-        // 鍒涘缓绉垎娴佹按璁板綍
-        PointsFlow pointsFlow = new PointsFlow();
-        pointsFlow.setUserId(userId);
-        pointsFlow.setUnitId(unitId);
-        pointsFlow.setDataType(ruleType);
-        pointsFlow.setDataCategory(ruleName);
-        pointsFlow.setPoints(totalPoints);
-        pointsFlow.setName(addPointsFlowDTO.getDescription() != null ? addPointsFlowDTO.getDescription() : pointsRule.getRuleDescription());
-        pointsFlow.setFlowTime(LocalDateTime.now());
-
-        boolean saved = save(pointsFlow);
-        if (!saved) {
-            throw new BusinessException("淇濆瓨绉垎娴佹按澶辫触");
+        // 浣欓鏍¢獙锛堜粎鍦ㄦ墸鍑忔椂锛�
+        if (applyUser && userPointsChange < 0) {
+            checkBalanceSufficient(userId, unitId, Math.abs(userPointsChange));
+        }
+        if (applyProvider && providerPointsChange < 0) {
+            checkBalanceSufficient(providerId, providerId, Math.abs(providerPointsChange));
         }
 
-        // 鏇存柊鐢ㄦ埛绉垎璐︽埛
-        updateUserPointsByRule(userId, unitId, totalPoints, ruleType);
+        // 鐢熸垚鐢ㄦ埛渚ф祦姘村苟鏇存柊璐︽埛
+        if (applyUser && userPointsChange != 0) {
+            PointsFlow pointsFlow = new PointsFlow();
+            pointsFlow.setUserId(userId);
+            pointsFlow.setUnitId(unitId);
+            pointsFlow.setDataType(ruleType);
+            pointsFlow.setDataCategory(addPointsFlowDTO.getCategory());
+            pointsFlow.setPoints(userPointsChange);
+            pointsFlow.setName(addPointsFlowDTO.getDescription() != null ? addPointsFlowDTO.getDescription() : userRule.getRuleDescription());
+            pointsFlow.setFlowTime(LocalDateTime.now());
+            pointsFlow.setRlueId(userRule.getId());
+
+            boolean saved = save(pointsFlow);
+            if (!saved) {
+                throw new BusinessException("淇濆瓨绉垎娴佹按澶辫触");
+            }
+
+            updateUserPointsByRule(userId, unitId, userPointsChange);
+        }
+
+        // 鐢熸垚鎻愪緵鑰呬晶娴佹按骞舵洿鏂拌处鎴�
+        if (applyProvider && providerPointsChange != 0) {
+            PointsFlow providerFlow = new PointsFlow();
+            providerFlow.setUserId(providerId);
+            providerFlow.setUnitId(null);
+            providerFlow.setDataType(ruleType);
+            providerFlow.setDataCategory(addPointsFlowDTO.getCategory());
+            providerFlow.setPoints(providerPointsChange);
+            providerFlow.setName(addPointsFlowDTO.getDescription() != null ? addPointsFlowDTO.getDescription() : providerRule.getRuleDescription());
+            providerFlow.setFlowTime(LocalDateTime.now());
+            providerFlow.setRlueId(providerRule.getId());
+
+            boolean providerSaved = save(providerFlow);
+            if (!providerSaved) {
+                throw new BusinessException("淇濆瓨鎻愪緵鑰呯Н鍒嗘祦姘村け璐�");
+            }
+            updateUserPointsByRule(providerId, null, userPointsChange);
+            // updateProviderUnitPoints(providerId, providerPointsChange);
+        }
 
         return true;
     }
 
     @Override
-    public Integer getUserPointsTotal(Long userId) {
-        if (userId == null) {
-            return 0;
+    public UserPoints getUserPointsTotal(String userId) {
+        if (!StringUtils.hasText(userId)) {
+            throw new BusinessException("鐢ㄦ埛ID涓嶈兘涓簄ull");
         }
         
         QueryWrapper<UserPoints> wrapper = new QueryWrapper<>();
@@ -216,13 +251,13 @@
                .eq("user_id", userId);
         
         UserPoints userPoints = userPointsMapper.selectOne(wrapper);
-        return userPoints != null ? userPoints.getBalance() : 0;
+        return userPoints ;
     }
 
     @Override
-    public Integer getUnitPointsTotal(Long unitId) {
+    public UserPoints getUnitPointsTotal(Long unitId) {
         if (unitId == null) {
-            return 0;
+            throw new BusinessException("鐢ㄦ埛ID涓嶈兘涓簄ull");
         }
         
         QueryWrapper<UserPoints> wrapper = new QueryWrapper<>();
@@ -230,15 +265,37 @@
                .eq("unit_id", unitId);
         
         UserPoints userPoints = userPointsMapper.selectOne(wrapper);
-        return userPoints != null ? userPoints.getBalance() : 0;
+        return userPoints;
+    }
+
+    @Override
+    public List<String> getPointsFlowCategories() {
+        QueryWrapper<PointsFlow> wrapper = new QueryWrapper<>();
+        wrapper.select("DISTINCT data_category")
+               .isNotNull("data_category")
+               .ne("data_category", "")
+               .ne("data_category", "null")
+               .eq("deleted", 0)
+               .orderByAsc("data_category");
+        
+        List<PointsFlow> flows = list(wrapper);
+        return flows.stream()
+                .map(PointsFlow::getDataCategory)
+                .filter(category -> category != null && !category.trim().isEmpty())
+                .distinct()
+                .sorted()
+                .collect(Collectors.toList());
     }
 
     /**
      * 鏋勫缓鏌ヨ鏉′欢
      */
     private void buildQueryWrapper(QueryWrapper<PointsFlow> wrapper, PointsFlowQueryDTO queryDTO) {
-        if (StringUtils.hasText(queryDTO.getFlowType())) {
-            wrapper.eq("flow_type", queryDTO.getFlowType());
+        if(StringUtils.hasText(queryDTO.getDataCategory())){
+            wrapper.eq("data_category", queryDTO.getDataCategory());
+        }
+        if (queryDTO.getDataType()!=null) {
+            wrapper.eq("data_type", queryDTO.getDataType());
         }
         if (StringUtils.hasText(queryDTO.getPointsSource())) {
             wrapper.eq("points_source", queryDTO.getPointsSource());
@@ -246,18 +303,110 @@
         if (StringUtils.hasText(queryDTO.getOrderId())) {
             wrapper.eq("order_id", queryDTO.getOrderId());
         }
-        if (queryDTO.getStartTime() != null) {
-            wrapper.ge("flow_time", queryDTO.getStartTime());
+        if (queryDTO.getFlowEndTime() != null) {
+            wrapper.ge("flow_time", queryDTO.getFlowStartTime());
         }
-        if (queryDTO.getEndTime() != null) {
-            wrapper.le("flow_time", queryDTO.getEndTime());
+        if (queryDTO.getFlowEndTime() != null) {
+            wrapper.le("flow_time", queryDTO.getFlowEndTime());
         }
         
         wrapper.orderByDesc("flow_time");
     }
 
     /**
-     * 妫�鏌ユ瘡鏃ョН鍒嗕笂闄�
+     * 鏍规嵁ruleType銆乺uleNameCode銆乧ategory鏌ヨ鐢熸晥鏃堕棿鏈�鏂扮殑绉垎瑙勫垯
+     */
+    private PointsRule getLatestEffectiveRule(Integer ruleType, String ruleNameCode, String category) {
+        QueryWrapper<PointsRule> wrapper = new QueryWrapper<>();
+        wrapper.eq("deleted", 0)
+               .eq("is_enabled", 0) // 0琛ㄧず鍚敤
+               .eq("rule_type", ruleType)
+               .eq("rule_name_code", ruleNameCode)
+               .eq("category", category)
+               .orderByDesc("created_at") // 鎸夊垱寤烘椂闂村�掑簭锛岃幏鍙栨渶鏂扮殑瑙勫垯
+               .last("LIMIT 1");
+        
+        return pointsRuleService.getOne(wrapper);
+    }
+
+    /**
+     * 鏍规嵁ruleType銆乺uleNameCode銆乧ategory銆乸oints_winner鏌ヨ鐢熸晥鏃堕棿鏈�鏂扮殑绉垎瑙勫垯
+     */
+    private PointsRule getLatestEffectiveRule(Integer ruleType, String ruleNameCode, String category, Integer pointsWinner) {
+        QueryWrapper<PointsRule> wrapper = new QueryWrapper<>();
+        wrapper.eq("is_enabled", 0)
+               .eq("rule_type", ruleType)
+               .eq("rule_name_code", ruleNameCode)
+               .eq("category", category)
+               .eq(pointsWinner != null, "points_winner", pointsWinner)
+               .orderByDesc("created_at")
+               .last("LIMIT 1");
+        return pointsRuleService.getOne(wrapper);
+    }
+
+    /**
+     * 妫�鏌ユ瘡鏃ョН鍒嗕笂闄愶紙鍩轰簬瑙勫垯锛�
+     */
+    private void checkDailyLimitByRule(String userId, String unitId, PointsRule pointsRule, Integer currentPoints) {
+        // 鑾峰彇浠婃棩寮�濮嬪拰缁撴潫鏃堕棿
+        LocalDate today = LocalDate.now();
+        LocalDateTime startOfDay = today.atStartOfDay();
+        LocalDateTime endOfDay = today.atTime(23, 59, 59);
+
+        // 鏌ヨ浠婃棩璇ヨ鍒欑殑绉垎娴佹按
+        QueryWrapper<PointsFlow> wrapper = new QueryWrapper<>();
+        wrapper.eq("deleted", 0)
+               .eq("user_id", userId)
+//               .eq("data_category", pointsRule.getRuleName())
+                .eq("rule_id",pointsRule.getId())
+                // .eq("data_type", 0)
+               .ge("flow_time", startOfDay)
+               .le("flow_time", endOfDay);
+        if(StringUtils.hasText(unitId)){
+            wrapper.eq("unit_id", unitId);
+        }
+
+        List<PointsFlow> todayFlows = list(wrapper);
+        
+        // 璁$畻浠婃棩绱绉垎
+        int todayTotal = todayFlows.stream()
+                .mapToInt(flow -> flow.getPoints() != null ? flow.getPoints() : 0)
+                .sum();
+
+        // 鑾峰彇瑙勫垯鐨勬瘡鏃ョН鍒嗕笂闄�
+        Integer dailyLimit = pointsRule.getDailyLimit();
+        if (dailyLimit != null && dailyLimit > 0) {
+            // 濡傛灉浠婃棩绱绉垎瓒呰繃姣忔棩涓婇檺锛屽垯鎶涘嚭寮傚父
+            if (Math.abs(todayTotal) > dailyLimit) {
+                throw new BusinessException("浠婃棩璇ヨ鍒欑Н鍒嗗凡杈句笂闄�: " + dailyLimit);
+            }
+            
+            // 濡傛灉鍔犱笂褰撳墠绉垎浼氳秴杩囨瘡鏃ヤ笂闄愶紝鍒欐姏鍑哄紓甯�
+//            if (Math.abs(todayTotal + currentPoints) > dailyLimit) {
+//                throw new BusinessException("鏈绉垎鎿嶄綔灏嗚秴杩囨瘡鏃ヤ笂闄�: " + dailyLimit + "锛屽綋鍓嶅凡绱: " + Math.abs(todayTotal));
+//            }
+        }
+    }
+
+    /**
+     * 妫�鏌ョН鍒嗕綑棰濇槸鍚﹁冻澶�
+     */
+    private void checkBalanceSufficient(String userId, String unitId, Integer requiredPoints) {
+        // 浠呮鏌ヤ釜浜虹Н鍒嗕綑棰濓紙绉婚櫎鍗曚綅浣欓鍒ゅ畾锛�
+        QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>();
+        userWrapper.eq("deleted", 0)
+                  .eq("user_id", userId);
+        
+        UserPoints userPoints = userPointsMapper.selectOne(userWrapper);
+        if (userPoints == null || userPoints.getBalance() < requiredPoints) {
+            throw new BusinessException("涓汉绉垎浣欓涓嶈冻锛屽綋鍓嶄綑棰�: " + (userPoints != null ? userPoints.getBalance() : 0) + "锛岄渶瑕佹墸闄�: " + requiredPoints);
+        }
+
+        // 鍘熷崟浣嶄綑棰濇牎楠屽凡绉婚櫎
+    }
+
+    /**
+     * 妫�鏌ユ瘡鏃ョН鍒嗕笂闄愶紙鏃ф柟娉曪紝淇濈暀鍏煎鎬э級
      */
     private void checkDailyLimit(Long userId, Long unitId, String ruleName, Integer currentPoints, Integer priority) {
         // 鑾峰彇浠婃棩寮�濮嬪拰缁撴潫鏃堕棿
@@ -290,7 +439,7 @@
     /**
      * 鏇存柊鐢ㄦ埛绉垎
      */
-    private void updateUserPoints(Long userId, Long unitId, Integer pointsValue) {
+    private void updateUserPoints(String userId, String unitId, Integer pointsValue) {
         // 鏇存柊涓汉绉垎
         QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>();
         userWrapper.eq("deleted", 0)
@@ -331,7 +480,7 @@
     /**
      * 鏍规嵁瑙勫垯鏇存柊鐢ㄦ埛绉垎璐︽埛
      */
-    private void updateUserPointsByRule(Long userId, Long unitId, Integer pointsValue, String ruleType) {
+    private void updateUserPointsByRule(String userId, String unitId, Integer pointsValue) {
         // 鏇存柊涓汉绉垎璐︽埛
         QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>();
         userWrapper.eq("deleted", 0)
@@ -339,14 +488,26 @@
         
         UserPoints userPoints = userPointsMapper.selectOne(userWrapper);
         if (userPoints == null) {
+            // 濡傛灉鏄柊鐢ㄦ埛涓旀槸鎵gН鍒嗘搷浣滐紝浣欓涓嶈冻
+            if (pointsValue < 0) {
+                throw new BusinessException("绉垎浣欓涓嶈冻锛屾棤娉曟墸闄ょН鍒�");
+            }
+            
             userPoints = new UserPoints();
             userPoints.setUserId(userId);
-            userPoints.setUnitId(unitId);
+            if(StringUtils.hasText(unitId)){
+                userPoints.setUnitId(unitId);
+            }
             userPoints.setBalance(pointsValue);
             userPoints.setTotalEarned(pointsValue > 0 ? pointsValue : 0);
             userPoints.setTotalConsumed(pointsValue < 0 ? Math.abs(pointsValue) : 0);
             userPointsMapper.insert(userPoints);
         } else {
+            // 妫�鏌ユ墸绉垎鏃朵綑棰濇槸鍚﹁冻澶�
+            if (pointsValue < 0 && userPoints.getBalance() + pointsValue < 0) {
+                throw new BusinessException("绉垎浣欓涓嶈冻锛屽綋鍓嶄綑棰�: " + userPoints.getBalance() + "锛岄渶瑕佹墸闄�: " + Math.abs(pointsValue));
+            }
+            
             userPoints.setBalance(userPoints.getBalance() + pointsValue);
             
             // 鏇存柊绱鑾峰彇绉垎
@@ -366,12 +527,17 @@
         }
 
         // 鏇存柊鍗曚綅绉垎璐︽埛
-        QueryWrapper<UserPoints> unitWrapper = new QueryWrapper<>();
+         /* QueryWrapper<UserPoints> unitWrapper = new QueryWrapper<>();
         unitWrapper.eq("deleted", 0)
                   .eq("unit_id", unitId);
         
         UserPoints unitPoints = userPointsMapper.selectOne(unitWrapper);
         if (unitPoints == null) {
+            // 濡傛灉鏄柊鍗曚綅涓旀槸鎵gН鍒嗘搷浣滐紝浣欓涓嶈冻
+            if (pointsValue < 0) {
+                throw new BusinessException("鍗曚綅绉垎浣欓涓嶈冻锛屾棤娉曟墸闄ょН鍒�");
+            }
+            
             unitPoints = new UserPoints();
             unitPoints.setUserId(userId);
             unitPoints.setUnitId(unitId);
@@ -380,6 +546,11 @@
             unitPoints.setTotalConsumed(pointsValue < 0 ? Math.abs(pointsValue) : 0);
             userPointsMapper.insert(unitPoints);
         } else {
+            // 妫�鏌ユ墸绉垎鏃朵綑棰濇槸鍚﹁冻澶�
+            if (pointsValue < 0 && unitPoints.getBalance() + pointsValue < 0) {
+                throw new BusinessException("鍗曚綅绉垎浣欓涓嶈冻锛屽綋鍓嶄綑棰�: " + unitPoints.getBalance() + "锛岄渶瑕佹墸闄�: " + Math.abs(pointsValue));
+            }
+            
             unitPoints.setBalance(unitPoints.getBalance() + pointsValue);
             
             // 鏇存柊绱鑾峰彇绉垎
@@ -396,6 +567,170 @@
             
             unitPoints.setUpdateTime(LocalDateTime.now());
             userPointsMapper.updateById(unitPoints);
+        } */
+    }
+
+    /**
+     * 鎵e噺鐢ㄦ埛绉垎
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deductUserPoints(DeductUserPointsDTO deductDTO) {
+        try {
+            String userId = deductDTO.getUserId();
+            String providerId = deductDTO.getProviderId();
+            String unitId = deductDTO.getUnitId();
+            Integer points = deductDTO.getPoints();
+            String orderId = deductDTO.getOrderId();
+            String remark = deductDTO.getRemark();
+            String dataCategory = deductDTO.getDataCategory();
+            Integer dataType = deductDTO.getDataType();
+
+            if (!StringUtils.hasText(userId)) {
+                throw new BusinessException("鐢ㄦ埛ID涓嶈兘涓虹┖");
+            }
+            if (points == null || points <= 0) {
+                throw new BusinessException("鎵e噺绉垎鏁伴噺蹇呴』澶т簬0");
+            }
+
+            // 妫�鏌ョ敤鎴风Н鍒嗕綑棰濇槸鍚﹀厖瓒�
+            checkBalanceSufficient(userId, unitId, points);
+
+            // 鍒涘缓绉垎娴佹按璁板綍
+            PointsFlow pointsFlow = new PointsFlow();
+            pointsFlow.setUserId(userId);
+            pointsFlow.setUnitId(unitId);
+            pointsFlow.setDataCategory(dataCategory != null ? dataCategory : "绉垎浜ゆ槗");
+            pointsFlow.setDataType(dataType != null ? dataType : 1);
+            pointsFlow.setPoints(-points); // 璐熸暟琛ㄧず鎵e噺
+            pointsFlow.setName(remark != null ? remark : "绉垎鎵e噺"); // name瀛楁瀛樺偍remark鍐呭
+            pointsFlow.setFlowTime(LocalDateTime.now());
+            pointsFlow.setRlueId(null); // 鐩存帴鎵e噺锛屼笉鍏宠仈瑙勫垯
+
+            boolean saved = save(pointsFlow);
+            if (!saved) {
+                throw new BusinessException("淇濆瓨绉垎娴佹按澶辫触");
+            }
+
+            // 鏇存柊鐢ㄦ埛绉垎璐︽埛
+            updateUserPointsOnly(userId, -points);
+
+            // 鏂板绉垎浜ゆ槗璁板綍锛堜笌娴佹按鍚屼簨鍔★級
+            PointsTransaction trans = new PointsTransaction();
+            // tb_points_transaction 鐨� chk_data_category 浠呭厑璁� '鐢ㄦ埛鍙備笌'銆�'鍏朵粬'
+            // 杩欓噷灏嗕笟鍔$被鐩槧灏勪负鏁版嵁搴撳厑璁哥殑鍊�
+            trans.setDataCategory("鐢ㄦ埛鍙備笌");
+            trans.setTransactionName(pointsFlow.getName());
+            trans.setTransactionTime(LocalDateTime.now());
+            trans.setPointsChange(-points);
+            trans.setRuleType("娑堣��");
+            try {
+                trans.setUserId(Long.valueOf(userId));
+            } catch (Exception ignore) {}
+            try {
+                if (StringUtils.hasText(unitId)) trans.setUnitId(Long.valueOf(unitId));
+            } catch (Exception ignore) {}
+            trans.setUserType("涓汉鐢ㄦ埛");
+            trans.setRuleId(null);
+            trans.setDetailId(null);
+            trans.setCreatedAt(LocalDateTime.now());
+            trans.setDeleted(0);
+            int inserted = pointsTransactionMapper.insert(trans);
+            log.info("Points transaction inserted rows={}, id={}", inserted, trans.getId());
+            if (inserted <= 0 || trans.getId() == null) {
+                throw new BusinessException("淇濆瓨绉垎浜ゆ槗璁板綍澶辫触");
+            }
+            // 涓烘彁渚涜�呮柊澧炵Н鍒�
+            AddPointsFlowDTO addPointsFlowDTO = new AddPointsFlowDTO();
+            addPointsFlowDTO.setCategory("璧勬簮浜ゆ槗");
+            addPointsFlowDTO.setCount(1);
+            addPointsFlowDTO.setProviderId(providerId);
+            addPointsFlowDTO.setRuleType(0);
+            addPointsFlowDTO.setRuleNameCode("digital_product_transaction");
+            addPointsFlowDTO.setDescription("浜у搧浜ゆ槗");
+            addPointsFlowByRule(addPointsFlowDTO);
+            return true;
+        } catch (Exception e) {
+            log.error("鎵e噺鐢ㄦ埛绉垎澶辫触", e);
+            throw new BusinessException("鎵e噺鐢ㄦ埛绉垎澶辫触锛�" + e.getMessage());
+        }
+    }
+
+    /**
+     * 浠呮洿鏂版彁渚涜�咃紙鍗曚綅锛夌Н鍒嗚处鎴�
+     */
+    private void updateProviderUnitPoints(String providerUnitId, Integer pointsValue) {
+        QueryWrapper<UserPoints> unitWrapper = new QueryWrapper<>();
+        unitWrapper.eq("deleted", 0)
+                  .eq("unit_id", providerUnitId);
+
+        UserPoints unitPoints = userPointsMapper.selectOne(unitWrapper);
+        if (unitPoints == null) {
+            if (pointsValue < 0) {
+                throw new BusinessException("鎻愪緵鑰呯Н鍒嗕綑棰濅笉瓒筹紝鏃犳硶鎵i櫎绉垎");
+            }
+
+            unitPoints = new UserPoints();
+            unitPoints.setUserId(null);
+            unitPoints.setUnitId(providerUnitId);
+            unitPoints.setBalance(pointsValue);
+            unitPoints.setTotalEarned(pointsValue > 0 ? pointsValue : 0);
+            unitPoints.setTotalConsumed(pointsValue < 0 ? Math.abs(pointsValue) : 0);
+            userPointsMapper.insert(unitPoints);
+        } else {
+            if (pointsValue < 0 && unitPoints.getBalance() + pointsValue < 0) {
+                throw new BusinessException("鎻愪緵鑰呯Н鍒嗕綑棰濅笉瓒筹紝褰撳墠浣欓: " + unitPoints.getBalance() + "锛岄渶瑕佹墸闄�: " + Math.abs(pointsValue));
+            }
+
+            unitPoints.setBalance(unitPoints.getBalance() + pointsValue);
+
+            if (pointsValue > 0) {
+                unitPoints.setTotalEarned(unitPoints.getTotalEarned() != null ?
+                        unitPoints.getTotalEarned() + pointsValue : pointsValue);
+            }
+            if (pointsValue < 0) {
+                unitPoints.setTotalConsumed(unitPoints.getTotalConsumed() != null ?
+                        unitPoints.getTotalConsumed() + Math.abs(pointsValue) : Math.abs(pointsValue));
+            }
+
+            unitPoints.setUpdateTime(LocalDateTime.now());
+            userPointsMapper.updateById(unitPoints);
+        }
+    }
+
+    /**
+     * 浠呮洿鏂颁釜浜虹Н鍒嗚处鎴凤紙涓嶆搷浣滃崟浣嶈处鎴凤級
+     */
+    private void updateUserPointsOnly(String userId, Integer pointsValue) {
+        // 鏇存柊涓汉绉垎璐︽埛
+        QueryWrapper<UserPoints> userWrapper = new QueryWrapper<>();
+        userWrapper.eq("deleted", 0)
+                  .eq("user_id", userId);
+        UserPoints userPoints = userPointsMapper.selectOne(userWrapper);
+        if (userPoints == null) {
+            if (pointsValue < 0) {
+                throw new BusinessException("涓汉绉垎浣欓涓嶈冻锛屾棤娉曟墸闄ょН鍒�");
+            }
+            userPoints = new UserPoints();
+            userPoints.setUserId(userId);
+            userPoints.setUnitId(null);
+            userPoints.setBalance(pointsValue);
+            userPoints.setTotalEarned(pointsValue > 0 ? pointsValue : 0);
+            userPoints.setTotalConsumed(pointsValue < 0 ? Math.abs(pointsValue) : 0);
+            userPointsMapper.insert(userPoints);
+        } else {
+            if (pointsValue < 0 && userPoints.getBalance() + pointsValue < 0) {
+                throw new BusinessException("涓汉绉垎浣欓涓嶈冻锛屽綋鍓嶄綑棰�: " + userPoints.getBalance() + "锛岄渶瑕佹墸闄�: " + Math.abs(pointsValue));
+            }
+            userPoints.setBalance(userPoints.getBalance() + pointsValue);
+            if (pointsValue > 0) {
+                userPoints.setTotalEarned(userPoints.getTotalEarned() != null ? userPoints.getTotalEarned() + pointsValue : pointsValue);
+            }
+            if (pointsValue < 0) {
+                userPoints.setTotalConsumed(userPoints.getTotalConsumed() != null ? userPoints.getTotalConsumed() + Math.abs(pointsValue) : Math.abs(pointsValue));
+            }
+            userPoints.setUpdateTime(LocalDateTime.now());
+            userPointsMapper.updateById(userPoints);
         }
     }
 }

--
Gitblit v1.8.0