Ver código fonte

修改负责人为多个

YPZ 1 mês atrás
pai
commit
9c95facdf7

+ 1 - 9
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/controller/MarketingController.java

@@ -16,7 +16,6 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import java.util.HashMap;
 import java.util.List;
 
 @RestController
@@ -69,14 +68,7 @@ public class MarketingController {
     @GetMapping("/dashboard/stats")
     @ApiOperation("营销: 仪表盘统计数据")
     public Result<Object> dashboardStats() {
-        return Result.result(new HashMap<String, Object>() {{
-            put("monthlySent", "2.8K");
-            put("avgOpenRate", "45%");
-            put("clickRate", "12%");
-            put("replyRate", "8%");
-            put("protectedCustomers", 156);
-            put("pendingReview", 23);
-        }});
+        return marketingService.getDashboardStats();
     }
 
     @GetMapping("/detail/{id}")

+ 5 - 2
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/dto/MarketingCampaignDTO.java

@@ -48,9 +48,12 @@ public class MarketingCampaignDTO extends Page {
     @TableField("end_date")
     private LocalDate endDate;
 
-    @ApiModelProperty(value = "负责人id")
+    @ApiModelProperty(value = "负责人id(逗号分隔字符串,如1,2,3)")
     @TableField("supervisor")
-    private Long supervisor;
+    private String supervisor;
+
+    @ApiModelProperty(value = "负责人id列表(前端传参用)")
+    private List<Long> supervisorList;
 
     @ApiModelProperty(value = "关联商机")
     @TableField("link_to_opportunity")

+ 2 - 2
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/entity/MarketingCampaignEntity.java

@@ -46,9 +46,9 @@ public class MarketingCampaignEntity extends SysBaseField {
     @TableField("end_date")
     private LocalDate endDate;
 
-    @ApiModelProperty(value = "负责人id")
+    @ApiModelProperty(value = "负责人id列表(逗号分隔字符串)")
     @TableField("supervisor")
-    private Long supervisor;
+    private String supervisor;
 
     @ApiModelProperty(value = "关联商机")
     @TableField("link_to_opportunity")

+ 2 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/MarketingCampaignService.java

@@ -22,4 +22,6 @@ public interface MarketingCampaignService extends MyBaseService<MarketingCampaig
 
     Result<Object> updateStatus(Long id, Integer status);
 
+    Result<Object> getDashboardStats();
+
 }

+ 140 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/MarketingCampaignServiceImpl.java

@@ -14,6 +14,7 @@ import com.storlead.trade.mapper.MarketingCampaignEntityMapper;
 import com.storlead.trade.service.*;
 import com.storlead.trade.vo.CampaignStatsVO;
 import com.storlead.trade.vo.MarketingCampaignDetailVO;
+import com.storlead.user.service.IUserService;
 import com.storlead.trade.vo.MarketingCampaignVO;
 import com.storlead.framework.common.constant.CommonConstant;
 import com.storlead.framework.common.result.Result;
@@ -27,7 +28,11 @@ import org.springframework.util.ObjectUtils;
 
 import javax.annotation.Resource;
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 @Service
@@ -50,6 +55,8 @@ public class MarketingCampaignServiceImpl
     private MarketEmailsService marketEmailsService;
     @Resource
     private CustomerTradeService customerTradeService;
+    @Resource
+    private IUserService userService;
 
     @Override
     public Result<Object> getList(MarketingCampaignDTO marketingCampaignDTO) {
@@ -166,6 +173,13 @@ public class MarketingCampaignServiceImpl
         detailVO.setSopDetailList(relationList);
         detailVO.setCustomerList(customerEntityList);
 
+        // 3.1 supervisor 联动:解析为id列表 + 查user表
+        List<Long> supervisorIdList = parseSupervisorList(campaign.getSupervisor());
+        detailVO.setSupervisorList(supervisorIdList);
+        if (!CollectionUtils.isEmpty(supervisorIdList)) {
+            detailVO.setSupervisorUserList(userService.getUserListByIds(supervisorIdList));
+        }
+
         // 4. 计算达成率统计(从 market_emails 表取实际数据)
         CampaignStatsVO stats = new CampaignStatsVO();
         Integer sendVolume = campaign.getSendVolume();
@@ -214,6 +228,8 @@ public class MarketingCampaignServiceImpl
     public Result<Object> addMarket(MarketingCampaignDTO marketingCampaignDTO) {
         MarketingCampaignEntity marketingCampaignEntity = new MarketingCampaignEntity();
         BeanUtils.copyProperties(marketingCampaignDTO, marketingCampaignEntity);
+        // supervisorList 转为逗号分隔字符串
+        marketingCampaignEntity.setSupervisor(joinSupervisorList(marketingCampaignDTO));
         // 过滤 AI 最优时间占位值,避免写入 DATETIME 列报错
         if ("optimal".equalsIgnoreCase(marketingCampaignEntity.getEmailSentTime())) {
             marketingCampaignEntity.setEmailSentTime(null);
@@ -278,6 +294,8 @@ public class MarketingCampaignServiceImpl
         MarketingCampaignEntity marketingCampaignOld = this.getById(marketingCampaignDTO.getId());
         MarketingCampaignEntity marketingCampaignEntity = new MarketingCampaignEntity();
         BeanUtils.copyProperties(marketingCampaignDTO, marketingCampaignEntity);
+        // supervisorList 转为逗号分隔字符串
+        marketingCampaignEntity.setSupervisor(joinSupervisorList(marketingCampaignDTO));
         // 过滤 AI 最优时间占位值,避免写入 DATETIME 列报错
         if ("optimal".equalsIgnoreCase(marketingCampaignEntity.getEmailSentTime())) {
             marketingCampaignEntity.setEmailSentTime(null);
@@ -357,4 +375,126 @@ public class MarketingCampaignServiceImpl
         this.updateById(entity);
         return Result.ok();
     }
+
+    @Override
+    public Result<Object> getDashboardStats() {
+        // 计算本月起止时间
+        Calendar cal = Calendar.getInstance();
+        cal.set(Calendar.DAY_OF_MONTH, 1);
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        Date monthStart = cal.getTime();
+
+        // 本月已发送数(market_emails.status=1)
+        Integer sentCount = marketEmailsService.lambdaQuery()
+                .eq(MarketEmailsEntity::getStatus, 1)
+                .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .ge(MarketEmailsEntity::getCreateTime, monthStart)
+                .count();
+
+        // 本月打开数
+        Integer openCount = marketEmailsService.lambdaQuery()
+                .eq(MarketEmailsEntity::getStatus, 1)
+                .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .ge(MarketEmailsEntity::getCreateTime, monthStart)
+                .eq(MarketEmailsEntity::getHasReader, 1)
+                .count();
+
+        // 本月回复数
+        Integer replyCount = marketEmailsService.lambdaQuery()
+                .eq(MarketEmailsEntity::getStatus, 1)
+                .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .ge(MarketEmailsEntity::getCreateTime, monthStart)
+                .eq(MarketEmailsEntity::getHasReply, 1)
+                .count();
+
+        int sent = sentCount == null ? 0 : sentCount.intValue();
+        int open = openCount == null ? 0 : openCount.intValue();
+        int reply = replyCount == null ? 0 : replyCount.intValue();
+
+        int avgOpenRate = sent > 0 ? Math.round(open * 100f / sent) : 0;
+        int replyRate = sent > 0 ? Math.round(reply * 100f / sent) : 0;
+        // TODO: market_emails 表未加 has_clicked 字段,点击率暂返回 0,添加后替换为 hasClick=1 统计
+        int clickRate = 0;
+
+        // 受保护客户数(distinct customer_id where has_protect=1)
+        List<MarketEmailsEntity> protectedList = marketEmailsService.lambdaQuery()
+                .eq(MarketEmailsEntity::getHasProtect, 1)
+                .eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .isNotNull(MarketEmailsEntity::getCustomerId)
+                .select(MarketEmailsEntity::getCustomerId)
+                .list();
+        long protectedCustomers = protectedList.stream()
+                .map(MarketEmailsEntity::getCustomerId)
+                .filter(java.util.Objects::nonNull)
+                .distinct()
+                .count();
+
+        // 待审核营销活动数(status=0 且未软删)
+        Integer pendingCount = this.lambdaQuery()
+                .eq(MarketingCampaignEntity::getStatus, 0)
+                .eq(MarketingCampaignEntity::getIsDelete, CommonConstant.DEL_FLAG_0)
+                .count();
+
+        Map<String, Object> stats = new HashMap<>();
+        stats.put("monthlySent", formatCount(sent));
+        stats.put("avgOpenRate", avgOpenRate + "%");
+        // TODO: market_emails 表未加 has_clicked 字段,点击率暂返回 0%,添加后替换为 hasClick=1 统计
+        stats.put("clickRate", clickRate + "%");
+        stats.put("replyRate", replyRate + "%");
+        stats.put("protectedCustomers", String.valueOf((int) protectedCustomers));
+        stats.put("pendingReview", String.valueOf(pendingCount == null ? 0 : pendingCount.intValue()));
+
+        return Result.result(stats);
+    }
+
+    /**
+     * 格式化计数显示:<1000 原样, 1000-9999 转为 "X.XK", >=10000 转为 "X.XW"
+     */
+    private String formatCount(int n) {
+        if (n < 1000) {
+            return String.valueOf(n);
+        }
+        if (n < 10000) {
+            return String.format("%.1fK", n / 1000.0);
+        }
+        return String.format("%.1fW", n / 10000.0);
+    }
+
+    /**
+     * supervisorList 转逗号分隔字符串。如未传 supervisorList 则取原 supervisor 字符串。
+     */
+    private String joinSupervisorList(MarketingCampaignDTO dto) {
+        if (dto == null) {
+            return null;
+        }
+        if (!CollectionUtils.isEmpty(dto.getSupervisorList())) {
+            return dto.getSupervisorList().stream()
+                    .filter(java.util.Objects::nonNull)
+                    .map(String::valueOf)
+                    .collect(Collectors.joining(","));
+        }
+        return dto.getSupervisor();
+    }
+
+    /**
+     * 解析逗号分隔的 supervisor 字符串为 id 列表。
+     */
+    private List<Long> parseSupervisorList(String supervisor) {
+        if (supervisor == null || supervisor.trim().isEmpty()) {
+            return new ArrayList<>();
+        }
+        List<Long> ids = new ArrayList<>();
+        for (String s : supervisor.split(",")) {
+            if (!s.trim().isEmpty()) {
+                try {
+                    ids.add(Long.parseLong(s.trim()));
+                } catch (NumberFormatException ignore) {
+                }
+            }
+        }
+        return ids;
+    }
 }

+ 7 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/vo/MarketingCampaignDetailVO.java

@@ -1,6 +1,7 @@
 package com.storlead.trade.vo;
 
 import com.storlead.trade.entity.*;
+import com.storlead.user.pojo.entity.UserEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -25,4 +26,10 @@ public class MarketingCampaignDetailVO {
 
     @ApiModelProperty(value = "达成率统计(计划/已发送/打开/回复 + 三个率)")
     private CampaignStatsVO stats;
+
+    @ApiModelProperty(value = "负责人id列表")
+    private List<Long> supervisorList;
+
+    @ApiModelProperty(value = "负责人用户详情列表")
+    private List<UserEntity> supervisorUserList;
 }