浏览代码

修改统计

YPZ 3 周之前
父节点
当前提交
63c174bd09

+ 8 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/controller/CustomerTradeController.java

@@ -15,6 +15,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.List;
+
 @RestController
 @RequestMapping("/customerTrade")
 @Api(tags = "客户: 客户管理")
@@ -59,4 +61,10 @@ public class CustomerTradeController {
     public Result<Object> delete(@RequestBody CustomerDTO dto) {
         return customerTradeService.delete(dto);
     }
+
+    @GetMapping("/source-distribution")
+    @ApiOperation(value = "客户来源分布", notes = "按 customer_source_dict_value 分组统计客户数与占比(占比保留 2 位小数);可传 idList 限定客户范围(如营销活动创建时已选客户的来源分布)")
+    public Result<Object> sourceDistribution(@RequestParam(required = false) List<Long> idList) {
+        return Result.result(customerTradeService.getSourceDistribution(idList));
+    }
 }

+ 10 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/CustomerTradeService.java

@@ -5,8 +5,11 @@ import com.storlead.framework.common.result.Result;
 import com.storlead.framework.mybatis.service.MyBaseService;
 import com.storlead.trade.dto.CustomerDTO;
 import com.storlead.trade.entity.CustomerEntity;
+import com.storlead.trade.vo.CustomerSourceDistributionVO;
 import com.storlead.trade.vo.CustomerVO;
 
+import java.util.List;
+
 public interface CustomerTradeService extends MyBaseService<CustomerEntity> {
 
     /**
@@ -33,4 +36,11 @@ public interface CustomerTradeService extends MyBaseService<CustomerEntity> {
      * 删除客户(单条/批量)
      */
     Result<Object> delete(CustomerDTO dto);
+
+    /**
+     * 客户来源分布(按 customer_source_dict_value 分组统计)
+     * @param idList 可选;传非空列表时只统计这些客户,传 null/空则统计全部客户
+     * @return 按客户数降序的分布列表
+     */
+    List<CustomerSourceDistributionVO> getSourceDistribution(List<Long> idList);
 }

+ 82 - 1
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/impl/CustomerTradeServiceImpl.java

@@ -2,12 +2,14 @@ package com.storlead.trade.service.impl;
 
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.storlead.framework.common.constant.CommonConstant;
 import com.storlead.framework.common.constant.DSConstants;
 import com.storlead.framework.common.result.Result;
 import com.storlead.framework.mybatis.service.impl.MyBaseServiceImpl;
+import com.storlead.trade.dto.SysDictDetailResponseDTO;
 import com.storlead.trade.dto.CustomerDTO;
 import com.storlead.trade.entity.CustomerCompanyEntity;
 import com.storlead.trade.entity.CustomerEntity;
@@ -18,6 +20,8 @@ import com.storlead.trade.service.CustomerCompanyTradeService;
 import com.storlead.trade.service.CustomerTradeService;
 import com.storlead.trade.service.CustomerAnalysisResultEntityService;
 import com.storlead.trade.service.LiaisonTradeService;
+import com.storlead.trade.service.SysDictService;
+import com.storlead.trade.vo.CustomerSourceDistributionVO;
 import com.storlead.trade.vo.CustomerVO;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
@@ -27,13 +31,16 @@ import org.springframework.util.ObjectUtils;
 import org.springframework.util.StringUtils;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
+import lombok.extern.slf4j.Slf4j;
 
+@Slf4j
 @Service
 @DS(DSConstants.DATASOURCE_TRADE)
 public class CustomerTradeServiceImpl
@@ -43,14 +50,17 @@ public class CustomerTradeServiceImpl
     private final CustomerCompanyTradeService customerCompanyTradeService;
     private final LiaisonTradeService liaisonTradeService;
     private final CustomerAnalysisResultEntityService customerAnalysisResultEntityService;
+    private final SysDictService sysDictService;
 
     public CustomerTradeServiceImpl(
             CustomerCompanyTradeService customerCompanyTradeService,
             LiaisonTradeService liaisonTradeService,
-            CustomerAnalysisResultEntityService customerAnalysisResultEntityService) {
+            CustomerAnalysisResultEntityService customerAnalysisResultEntityService,
+            SysDictService sysDictService) {
         this.customerCompanyTradeService = customerCompanyTradeService;
         this.liaisonTradeService = liaisonTradeService;
         this.customerAnalysisResultEntityService = customerAnalysisResultEntityService;
+        this.sysDictService = sysDictService;
     }
 
     @Override
@@ -308,4 +318,75 @@ public class CustomerTradeServiceImpl
         }
         return Result.ok();
     }
+
+    @Override
+    public List<CustomerSourceDistributionVO> getSourceDistribution(List<Long> idList) {
+        // Step1: 按 customer_source_dict_value 分组统计
+        QueryWrapper<CustomerEntity> qw = new QueryWrapper<>();
+        qw.select("customer_source_dict_value AS source, count(*) AS cnt");
+        qw.eq("is_delete", CommonConstant.DEL_FLAG_0);
+        // 仅在 idList 非空时限定客户范围
+        if (!CollectionUtils.isEmpty(idList)) {
+            qw.in("id", idList);
+        }
+        qw.groupBy("customer_source_dict_value");
+        List<Map<String, Object>> rows = this.listMaps(qw);
+
+        // Step2: 计算总数
+        long total = 0L;
+        if (rows != null) {
+            for (Map<String, Object> r : rows) {
+                Object cnt = r.get("cnt");
+                if (cnt instanceof Number) {
+                    total += ((Number) cnt).longValue();
+                }
+            }
+        }
+
+        // Step3: sys_dict_detail 翻译 source -> label
+        Map<String, String> labelMap = new HashMap<>();
+        try {
+            List<SysDictDetailResponseDTO> details = sysDictService.getListByCode("customer_source");
+            if (!CollectionUtils.isEmpty(details)) {
+                for (SysDictDetailResponseDTO d : details) {
+                    if (d != null && d.getValue() != null) {
+                        labelMap.put(d.getValue(), d.getLabel());
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            log.warn("查询 customer_source 字典明细失败,跳过 label 翻译: {}", ex.getMessage());
+        }
+
+        // Step4: 组装 VO(保留 source 为 null 的桶,label 标"未分类")
+        List<CustomerSourceDistributionVO> result = new ArrayList<>();
+        if (rows != null) {
+            for (Map<String, Object> r : rows) {
+                Object srcObj = r.get("source");
+                Object cntObj = r.get("cnt");
+                String source = srcObj == null ? null : srcObj.toString();
+                long cnt = cntObj instanceof Number ? ((Number) cntObj).longValue() : 0L;
+                double ratio = total > 0 ? Math.round(cnt * 10000.0 / total) / 100.0 : 0.0;
+
+                CustomerSourceDistributionVO vo = new CustomerSourceDistributionVO();
+                vo.setSource(source);
+                vo.setCount(cnt);
+                vo.setRatio(ratio);
+                if (source == null || source.isEmpty()) {
+                    vo.setSourceLabel("未分类");
+                } else {
+                    vo.setSourceLabel(labelMap.getOrDefault(source, source));
+                }
+                result.add(vo);
+            }
+        }
+
+        // Step5: 按 count 降序
+        result.sort((a, b) -> {
+            long ac = a.getCount() == null ? 0L : a.getCount();
+            long bc = b.getCount() == null ? 0L : b.getCount();
+            return Long.compare(bc, ac);
+        });
+        return result;
+    }
 }

+ 22 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/vo/CustomerSourceDistributionVO.java

@@ -0,0 +1,22 @@
+package com.storlead.trade.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(value = "CustomerSourceDistributionVO", description = "客户来源分布项")
+public class CustomerSourceDistributionVO {
+
+    @ApiModelProperty(value = "来源字典值(customerSourceDictValue)")
+    private String source;
+
+    @ApiModelProperty(value = "来源显示名(来自 customer_source 字典,未匹配则为空)")
+    private String sourceLabel;
+
+    @ApiModelProperty(value = "该来源下的客户数")
+    private Long count;
+
+    @ApiModelProperty(value = "占比(百分比,保留 2 位小数,如 35.71 表示 35.71%)")
+    private Double ratio;
+}