Ver Fonte

添加sop

YPZ há 2 dias atrás
pai
commit
4ce27fa88b

+ 12 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/controller/MarketEmailsController.java

@@ -63,6 +63,18 @@ public class MarketEmailsController {
         return marketEmailsService.getReplyList(id);
     }
 
+    @GetMapping("/intervention-list")
+    @ApiOperation("营销: 人工干预列表(客户保护白名单/营销暂停)")
+    public Result<Object> interventionList(Long id, String tabType) {
+        return marketEmailsService.getInterventionList(id, tabType);
+    }
+
+    @PostMapping("/remove-protect")
+    @ApiOperation("营销: 移除保护(按邮件主键 id,has_protect 置 0)")
+    public Result<Object> removeProtect(@RequestBody MarketEmailsDTO marketEmailsDTO) {
+        return marketEmailsService.removeProtect(marketEmailsDTO.getId());
+    }
+
     @PostMapping("/add")
     @ApiOperation("营销: 营销邮件添加")
     public Result<?> add(@RequestBody MarketEmailsDTO marketEmailsDTO) {

+ 4 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/dto/MarketEmailsDTO.java

@@ -61,6 +61,10 @@ public class MarketEmailsDTO extends Page {
     @TableField("has_suspend")
     private Integer hasSuspend;
 
+    @ApiModelProperty(value = "保护原因(仅添加/更新保护时使用)")
+    @TableField("protect_reason")
+    private String protectReason;
+
     @ApiModelProperty(value = "发件人邮箱")
     @TableField("recipient")
     private String recipient;

+ 4 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/entity/MarketEmailsEntity.java

@@ -92,4 +92,8 @@ public class MarketEmailsEntity extends SysBaseField {
     @TableField("first_reply_theme")
     private String firstReplyTheme;
 
+    @ApiModelProperty(value = "保护原因")
+    @TableField("protect_reason")
+    private String protectReason;
+
 }

+ 11 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/service/MarketEmailsService.java

@@ -46,6 +46,17 @@ public interface MarketEmailsService extends MyBaseService<MarketEmailsEntity> {
      */
     Result<Object> getReplyList(Long marketingCampaignId);
 
+    /**
+     * 人工干预列表(客户保护白名单 / 营销暂停,按 marketingCampaignId + tabType 过滤)
+     * tabType: protect(保护白名单) / suspend(营销暂停)
+     */
+    Result<Object> getInterventionList(Long marketingCampaignId, String tabType);
+
+    /**
+     * 移除保护(按邮件主键 id 把 has_protect 置 0)
+     */
+    Result<Object> removeProtect(Long id);
+
     /**
      * 邮件管理弹窗(按营销活动 id 查询,拼装一个营销活动下所有邮件卡片)
      *

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

@@ -276,6 +276,14 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
         wrapper.in(MarketEmailsEntity::getId, ids);
         wrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
         wrapper.set(MarketEmailsEntity::getHasProtect, dto.getHasProtect());
+        // 如果是开启保护 且 前端传了保护原因 -> 一并写入
+        if (Integer.valueOf(1).equals(dto.getHasProtect()) && !ObjectUtils.isEmpty(dto.getProtectReason())) {
+            wrapper.set(MarketEmailsEntity::getProtectReason, dto.getProtectReason());
+        }
+        // 如果是移除保护 -> 清空保护原因
+        if (Integer.valueOf(0).equals(dto.getHasProtect())) {
+            wrapper.set(MarketEmailsEntity::getProtectReason, null);
+        }
         boolean ok = this.update(wrapper);
         if (!ok) {
             return Result.error("保护失败");
@@ -483,6 +491,141 @@ public class MarketEmailsServiceImpl extends MyBaseServiceImpl<MarketEmailsMappe
         return Result.ok(voList);
     }
 
+    /**
+     * 人工干预列表:tabType = protect / suspend
+     * 策略:单活动下保护/暂停的邮件数通常几百~几千,先查全集在内存里按 recipient 分组
+     */
+    @Override
+    public Result<Object> getInterventionList(Long marketingCampaignId, String tabType) {
+        if (ObjectUtils.isEmpty(marketingCampaignId)) {
+            return Result.error("marketingCampaignId不能为空");
+        }
+        if (!"protect".equalsIgnoreCase(tabType) && !"suspend".equalsIgnoreCase(tabType)) {
+            return Result.error("tabType 必须是 protect 或 suspend");
+        }
+
+        LambdaQueryWrapper<MarketEmailsEntity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
+        wrapper.eq(MarketEmailsEntity::getMarketingCampaignId, marketingCampaignId);
+        wrapper.isNotNull(MarketEmailsEntity::getRecipient);
+        wrapper.ne(MarketEmailsEntity::getRecipient, "");
+        if ("protect".equalsIgnoreCase(tabType)) {
+            wrapper.eq(MarketEmailsEntity::getHasProtect, 1);
+        } else {
+            wrapper.eq(MarketEmailsEntity::getHasSuspend, 1);
+        }
+        wrapper.select(MarketEmailsEntity::getId,
+                MarketEmailsEntity::getRecipient,
+                MarketEmailsEntity::getCustomerId,
+                MarketEmailsEntity::getHasProtect,
+                MarketEmailsEntity::getHasSuspend,
+                MarketEmailsEntity::getProtectReason,
+                MarketEmailsEntity::getUpdateTime);
+        List<MarketEmailsEntity> all = this.list(wrapper);
+
+        // 按 recipient 聚合(同一客户多个保护/暂停记录合并为一张卡片)
+        Map<String, com.storlead.trade.vo.MarketEmailsProtectVO> aggMap = new HashMap<>();
+        if (!CollectionUtils.isEmpty(all)) {
+            for (MarketEmailsEntity e : all) {
+                String r = e.getRecipient();
+                if (r == null || r.isEmpty()) continue;
+                com.storlead.trade.vo.MarketEmailsProtectVO vo = aggMap.computeIfAbsent(r, k -> {
+                    com.storlead.trade.vo.MarketEmailsProtectVO v = new com.storlead.trade.vo.MarketEmailsProtectVO();
+                    v.setRecipient(k);
+                    v.setHasProtect(0);
+                    v.setHasSuspend(0);
+                    return v;
+                });
+                // 记下第一条(最早设置保护/暂停的那一条)
+                if (vo.getId() == null) {
+                    vo.setId(e.getId());
+                }
+                if (vo.getCustomerId() == null && e.getCustomerId() != null) {
+                    vo.setCustomerId(e.getCustomerId());
+                }
+                if (e.getHasProtect() != null && e.getHasProtect() == 1) {
+                    vo.setHasProtect(1);
+                }
+                if (e.getHasSuspend() != null && e.getHasSuspend() == 1) {
+                    vo.setHasSuspend(1);
+                }
+                // 取最早的 updateTime 作为保护/暂停时间
+                if (e.getUpdateTime() != null) {
+                    if (vo.getProtectTime() == null || e.getUpdateTime().before(vo.getProtectTime())) {
+                        vo.setProtectTime(e.getUpdateTime());
+                    }
+                }
+                // 保护原因:取该 recipient 下任意一条非空的(多条一致时取最先写入的)
+                if (vo.getProtectReason() == null && e.getProtectReason() != null && !e.getProtectReason().isEmpty()) {
+                    vo.setProtectReason(e.getProtectReason());
+                }
+            }
+        }
+        // 保护时间 DESC 排序
+        List<com.storlead.trade.vo.MarketEmailsProtectVO> voList = new java.util.ArrayList<>(aggMap.values());
+        voList.sort((a, b) -> {
+            if (a.getProtectTime() == null && b.getProtectTime() == null) return 0;
+            if (a.getProtectTime() == null) return 1;
+            if (b.getProtectTime() == null) return -1;
+            return b.getProtectTime().compareTo(a.getProtectTime());
+        });
+
+        // 批量回填客户名
+        if (!voList.isEmpty()) {
+            java.util.Set<Long> customerIdSet = new java.util.LinkedHashSet<>();
+            for (com.storlead.trade.vo.MarketEmailsProtectVO v : voList) {
+                if (v.getCustomerId() != null) customerIdSet.add(v.getCustomerId());
+            }
+            if (!customerIdSet.isEmpty()) {
+                try {
+                    java.util.List<com.storlead.trade.entity.CustomerEntity> customers =
+                            customerTradeService.listByIds(new java.util.ArrayList<>(customerIdSet));
+                    java.util.Map<Long, com.storlead.trade.entity.CustomerEntity> customerMap = new java.util.HashMap<>();
+                    if (!CollectionUtils.isEmpty(customers)) {
+                        for (com.storlead.trade.entity.CustomerEntity c : customers) {
+                            if (c.getId() != null) customerMap.put(c.getId(), c);
+                        }
+                    }
+                    for (com.storlead.trade.vo.MarketEmailsProtectVO v : voList) {
+                        com.storlead.trade.entity.CustomerEntity c = customerMap.get(v.getCustomerId());
+                        if (c != null) {
+                            v.setCustomerName(c.getCustomerName());
+                        }
+                    }
+                } catch (Exception ex) {
+                    log.warn("回填客户名失败,customerIds={} err={}", customerIdSet, ex.getMessage());
+                }
+            }
+        }
+        return Result.ok(voList);
+    }
+
+    /**
+     * 移除保护:按邮件主键 id 把 has_protect 置 0(保留 has_suspend)
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Object> removeProtect(Long id) {
+        if (ObjectUtils.isEmpty(id)) {
+            return Result.error("id不能为空");
+        }
+        MarketEmailsEntity exist = this.getById(id);
+        if (exist == null || CommonConstant.DEL_FLAG_1.equals(exist.getIsDelete())) {
+            return Result.error("邮件不存在或已删除");
+        }
+        LambdaUpdateWrapper<MarketEmailsEntity> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.eq(MarketEmailsEntity::getId, id);
+        wrapper.eq(MarketEmailsEntity::getIsDelete, CommonConstant.DEL_FLAG_0);
+        wrapper.set(MarketEmailsEntity::getHasProtect, 0);
+        // 同步清空保护原因
+        wrapper.set(MarketEmailsEntity::getProtectReason, null);
+        boolean ok = this.update(wrapper);
+        if (!ok) {
+            return Result.error("移除保护失败");
+        }
+        return Result.ok();
+    }
+
 
     @Override
     public Result<Object> getManagementDetail(Long marketingCampaignId) {

+ 53 - 0
java/storlead-sasa/storlead-trade/src/main/java/com/storlead/trade/vo/MarketEmailsProtectVO.java

@@ -0,0 +1,53 @@
+package com.storlead.trade.vo;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 人工干预列表响应 VO(客户保护白名单 / 营销暂停)
+ * <p>
+ * 数据源:market_emails.has_protect=1 或 has_suspend=1 的记录
+ * 聚合:按 recipient 聚合(一个客户多个保护记录合并)
+ * 排序:保护时间 DESC(最近保护排前)
+ * </p>
+ *
+ * @author Generated
+ */
+@Data
+@ApiModel(value = "MarketEmailsProtectVO", description = "人工干预列表(按 recipient 聚合)")
+public class MarketEmailsProtectVO implements Serializable {
+
+    @ApiModelProperty(value = "邮件主键 id(用于移除保护接口)")
+    private Long id;
+
+    @ApiModelProperty(value = "发件人邮箱(客户邮箱)")
+    private String recipient;
+
+    @ApiModelProperty(value = "客户id")
+    private Long customerId;
+
+    @ApiModelProperty(value = "客户名称")
+    private String customerName;
+
+    @ApiModelProperty(value = "保护原因")
+    private String protectReason;
+
+    @ApiModelProperty(value = "保护时间(取该 recipient 下最早一次保护的时间)")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
+    private Date protectTime;
+
+    @ApiModelProperty(value = "是否保护(0未保护、1保护)")
+    private Integer hasProtect;
+
+    @ApiModelProperty(value = "是否暂停(0未暂停、1暂停)")
+    private Integer hasSuspend;
+}