Quellcode durchsuchen

Merge remote-tracking branch 'origin/master'

18118724055@163.com vor 1 Woche
Ursprung
Commit
fbdd1ca022

+ 32 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/MarketEmailsServiceImpl.java

@@ -26,8 +26,11 @@ import com.storlead.trade.mapper.MarketEmailsMapper;
 import com.storlead.trade.service.*;
 import com.storlead.trade.vo.*;
 import org.apache.commons.lang.time.DateUtils;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -85,6 +88,10 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
     @Resource
     private LiaisonTradeService liaisonTradeService;
 
+    @Autowired
+    @Lazy
+    private RedissonClient redissonClient;
+
 
     @Override
     public Result<Object> getList(MarketEmailsDTO dto) {
@@ -1142,6 +1149,28 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
 
     @Override
     public void sendAgain() {
+        String lockKey = "market-emails:send-again:lock";
+        RLock lock = redissonClient.getLock(lockKey);
+        boolean acquired = false;
+        try {
+            // 尝试获取锁,最多等 0 秒(抢不到直接放弃,避免当前执行实例被阻塞)
+            acquired = lock.tryLock(0, 2, java.util.concurrent.TimeUnit.MINUTES);
+            if (!acquired) {
+                log.info("[sendAgain] 锁未获取到,当前有其他实例在执行,跳过本次");
+                return;
+            }
+            doSendAgain(lockKey);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            log.warn("[sendAgain] 获取锁被中断: {}", e.getMessage());
+        } finally {
+            if (acquired && lock.isHeldByCurrentThread()) {
+                lock.unlock();
+            }
+        }
+    }
+
+    private void doSendAgain(String lockKey) {
         LambdaQueryWrapper<MarketEmailsEntity> marketEmailHasFirstQueryWrapper = new LambdaQueryWrapper<>();
         marketEmailHasFirstQueryWrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
         marketEmailHasFirstQueryWrapper.eq(MarketEmailsEntity::getHasFirst, 2);
@@ -1206,6 +1235,9 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
                 Date expectSendTime = DateUtils.addMinutes(new Date(), 30);
                 newMarketEmail.setExpectSendTime(expectSendTime);
                 newMarketEmail.setHasFirst(2);
+                newMarketEmail.setHasProtect(0);
+                newMarketEmail.setHasReader(0);
+                newMarketEmail.setHasReply(0);
 
                 newMarketEmailList.add(newMarketEmail);
             }

+ 17 - 6
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/MarketingCampaignServiceImpl.java

@@ -144,9 +144,9 @@ public class MarketingCampaignServiceImpl
             Map<Long, Long> firstSentMap = countEmailsByCampaign(marketingCampaignIdList,
                     "count(*) as cnt", qw -> qw.eq("status", 1).eq("has_first", 1));
             Map<Long, Long> openedEmailMap = countEmailsByCampaign(marketingCampaignIdList,
-                    "count(*) as cnt", qw -> qw.eq("has_reader", 1));
+                    "count(*) as cnt", qw -> qw.eq("status", 1).eq("has_reader", 1));
             Map<Long, Long> repliedEmailMap = countEmailsByCampaign(marketingCampaignIdList,
-                    "count(*) as cnt", qw -> qw.eq("has_reply", 1));
+                    "count(*) as cnt", qw -> qw.eq("status", 1).eq("has_reply", 1));
             Map<Long, Long> cancelledMap = countEmailsByCampaign(marketingCampaignIdList,
                     "count(*) as cnt", qw -> qw.eq("status", -3));
             Map<Long, Long> totalEmailMap = countEmailsByCampaign(marketingCampaignIdList,
@@ -326,6 +326,7 @@ public class MarketingCampaignServiceImpl
 //        Integer sendVolume = campaign.getSendVolume();
 //        stats.setSendVolume(sendVolume);
 
+        // emailCount:计划发送总数(hasFirst=1 的邮件数,用于展示)
         Integer emailCountLong = marketEmailsService.lambdaQuery()
                 .eq(MarketEmailsEntity::getMarketingCampaignId, id)
                 .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
@@ -334,20 +335,28 @@ public class MarketingCampaignServiceImpl
         Integer emailCount = emailCountLong == null ? 0 : emailCountLong.intValue();
         stats.setEmailCount(emailCount);
 
+        // 已发送数(status=1,所有首封邮件,与 getList/getDashboardStats 口径一致)
         Integer sentCountLong = marketEmailsService.lambdaQuery()
                 .eq(MarketEmailsEntity::getMarketingCampaignId, id)
                 .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
                 .eq(MarketEmailsEntity::getStatus, 1)
-                .eq(MarketEmailsEntity::getHasFirst, 1)
                 .count();
         Integer sentCount = sentCountLong == null ? 0 : sentCountLong.intValue();
         stats.setSentCount(sentCount);
 
+        // emailCountAll:全部未删邮件总数(用于 achievementRate 分母,与 sentCount 口径对齐)
+        Integer emailCountAllLong = marketEmailsService.lambdaQuery()
+                .eq(MarketEmailsEntity::getMarketingCampaignId, id)
+                .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .count();
+        int emailCountAll = emailCountAllLong == null ? 0 : emailCountAllLong.intValue();
+
 
 
         Integer openCountLong = marketEmailsService.lambdaQuery()
                 .eq(MarketEmailsEntity::getMarketingCampaignId, id)
                 .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .eq(MarketEmailsEntity::getStatus, 1)
                 .eq(MarketEmailsEntity::getHasReader, 1)
                 .count();
         Integer openCount = openCountLong == null ? 0 : openCountLong.intValue();
@@ -356,6 +365,7 @@ public class MarketingCampaignServiceImpl
         Integer replyCountLong = marketEmailsService.lambdaQuery()
                 .eq(MarketEmailsEntity::getMarketingCampaignId, id)
                 .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .eq(MarketEmailsEntity::getStatus, 1)
                 .eq(MarketEmailsEntity::getHasReply, 1)
                 .count();
         Integer replyCount = replyCountLong == null ? 0 : replyCountLong.intValue();
@@ -365,13 +375,14 @@ public class MarketingCampaignServiceImpl
         stats.setOpenRate(sentCount > 0 ? Math.round(openCount * 100f / sentCount) : 0);
         // 回复率 = replyCount / sentCount
         stats.setReplyRate(sentCount > 0 ? Math.round(replyCount * 100f / sentCount) : 0);
-        // 达成率 = openCountLong / emailCount
-        stats.setAchievementRate(sentCount > 0 ? Math.round(sentCount * 100f / emailCount) : 0);
+        // 达成率 = 已发送数 / 全部未删邮件数(与 getList sentRate 口径一致)
+        stats.setAchievementRate(emailCountAll > 0 ? Math.round(sentCount * 100f / emailCountAll) : 0);
 
         // 已打开邮件的客户数(distinct customer_id where hasReader=1)
         List<MarketEmailsEntity> openedEmailList = marketEmailsService.lambdaQuery()
                 .eq(MarketEmailsEntity::getMarketingCampaignId, id)
                 .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .eq(MarketEmailsEntity::getStatus, 1)
                 .eq(MarketEmailsEntity::getHasReader, 1)
                 .isNotNull(MarketEmailsEntity::getCustomerId)
                 .select(MarketEmailsEntity::getCustomerId)
@@ -708,7 +719,7 @@ public class MarketingCampaignServiceImpl
                 .eq(MarketEmailsEntity::getHasReader, 1)
                 .count();
 
-        // 本月回复数
+        // 本月回复数(仅已发送邮件中有回复的)
         Integer replyCount = marketEmailsService.lambdaQuery()
                 .eq(MarketEmailsEntity::getStatus, 1)
                 .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)

+ 1 - 1
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/task/MarketStartOrEndTask.java

@@ -18,7 +18,7 @@ public class MarketStartOrEndTask {
     @Resource
     private MarketingCampaignService marketingCampaignService;
 
-    @Scheduled(cron = "0 0 9 * * ?")
+    @Scheduled(cron = "0 50 8 * * ?")
     public void dispatch() {
         long start = System.currentTimeMillis();
         log.info("营销活动开始/结束状态同步任务开始");