Răsfoiți Sursa

feat(mind): add WechatAlertTemplateService for crisis alert push via WeChat template messages

Xiaogang Liao 2 luni în urmă
părinte
comite
c4b5f2da64

+ 144 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/WechatAlertTemplateService.java

@@ -0,0 +1,144 @@
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.entity.Child;
+import com.etotem.cfc.entity.User;
+import com.etotem.cfc.mapper.ChildMapper;
+import com.etotem.cfc.mapper.UserMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 微信危机告警模板消息服务 —— 当孩子产生情绪危机告警时,
+ * 通过微信模板消息通知同家庭的家长。
+ */
+@Slf4j
+@Service
+public class WechatAlertTemplateService {
+
+    @Resource
+    private ChildMapper childMapper;
+
+    @Resource
+    private UserMapper userMapper;
+
+    @Resource
+    private WechatService wechatService;
+
+    @Value("${wechat.alert-template-id:}")
+    private String alertTemplateId;
+
+    private static final Map<String, String> ALERT_TYPE_ZH = new HashMap<>();
+    private static final Map<String, String> SEVERITY_ZH = new HashMap<>();
+
+    static {
+        ALERT_TYPE_ZH.put("crisis_keyword", "危机关键词");
+        ALERT_TYPE_ZH.put("consecutive_low_mood", "持续情绪低落");
+        ALERT_TYPE_ZH.put("sharp_decline", "情绪锐降");
+
+        SEVERITY_ZH.put("high", "高风险");
+        SEVERITY_ZH.put("medium", "中风险");
+        SEVERITY_ZH.put("low", "低风险");
+    }
+
+    /**
+     * 向孩子的家长发送危机告警模板消息
+     *
+     * @param childId   孩子ID
+     * @param alertType 告警类型 (crisis_keyword / consecutive_low_mood / sharp_decline)
+     * @param severity  严重程度 (high / medium / low)
+     * @param title     告警标题
+     * @param message   告警内容(keyword4 会截断至20字符以内)
+     */
+    public void sendCrisisAlertToParents(Long childId, String alertType, String severity,
+                                          String title, String message) {
+        try {
+            // 测试模式:仅记录日志,不实际发送
+            if (wechatService.isTestMode()) {
+                log.info("测试模式:跳过危机告警模板消息, childId={}, alertType={}, severity={}",
+                        childId, alertType, severity);
+                return;
+            }
+
+            // 检查模板ID是否配置
+            if (alertTemplateId == null || alertTemplateId.isEmpty()) {
+                log.warn("微信告警模板ID未配置,跳过发送");
+                return;
+            }
+
+            // 1. 查找孩子信息
+            Child child = childMapper.selectById(childId);
+            if (child == null) {
+                log.warn("孩子不存在, childId={}", childId);
+                return;
+            }
+
+            String childNickname = child.getNickname() != null ? child.getNickname() : "孩子";
+            Long familyId = child.getFamilyId();
+            if (familyId == null) {
+                log.warn("孩子未关联家庭, childId={}", childId);
+                return;
+            }
+
+            // 2. 查找同家庭的家长
+            List<User> parents = userMapper.selectList(
+                    new LambdaQueryWrapper<User>()
+                            .eq(User::getFamilyId, familyId)
+                            .eq(User::getRole, "parent")
+            );
+
+            if (parents.isEmpty()) {
+                log.info("未找到同家庭家长, familyId={}", familyId);
+                return;
+            }
+
+            // 3. 构建模板关键词
+            String alertTypeZh = ALERT_TYPE_ZH.getOrDefault(alertType, alertType);
+            String severityZh = SEVERITY_ZH.getOrDefault(severity, severity);
+            String truncatedMessage = truncate(message, 20);
+
+            Map<String, Object> keywordMap = new HashMap<>();
+            keywordMap.put("keyword1", childNickname);
+            keywordMap.put("keyword2", alertTypeZh);
+            keywordMap.put("keyword3", severityZh);
+            keywordMap.put("keyword4", truncatedMessage);
+
+            // 4. 向每位有openid的家长发送模板消息
+            int sentCount = 0;
+            for (User parent : parents) {
+                String openid = parent.getOpenid();
+                if (openid != null && !openid.isEmpty()) {
+                    wechatService.sendTemplateMessage(openid, alertTemplateId, keywordMap);
+                    sentCount++;
+                }
+            }
+
+            log.info("危机告警模板消息发送完成, childId={}, parents={}, sent={}",
+                    childId, parents.size(), sentCount);
+
+        } catch (Exception e) {
+            // 告警创建不能因通知失败而中断,仅记录错误日志
+            log.error("发送危机告警模板消息异常, childId={}, alertType={}", childId, alertType, e);
+        }
+    }
+
+    /**
+     * 截断字符串至指定最大长度,超长时追加省略号
+     */
+    private String truncate(String str, int maxLen) {
+        if (str == null) {
+            return "";
+        }
+        if (str.length() <= maxLen) {
+            return str;
+        }
+        // 截断并保留1字符给省略号
+        return str.substring(0, maxLen - 1) + "…";
+    }
+}