集成钉钉

This commit is contained in:
2026-05-15 17:35:26 +08:00
parent f654180148
commit 42b1555c80
10 changed files with 288 additions and 8 deletions

View File

@@ -0,0 +1,110 @@
package com.rj.service.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rj.config.LbAssessmentDingTalkProperties;
import com.rj.service.DingTalkRobotService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class DingTalkRobotServiceImpl implements DingTalkRobotService {
private static final String SEND_URL = "https://oapi.dingtalk.com/robot/send";
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
private final LbAssessmentDingTalkProperties properties;
public DingTalkRobotServiceImpl(RestTemplate restTemplate,
ObjectMapper objectMapper,
LbAssessmentDingTalkProperties properties) {
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
this.properties = properties;
}
@Override
public void sendText(String content) {
if (!properties.isEnabled()) {
throw new IllegalStateException("钉钉机器人发送未启用,请在配置中设置 lb.assessment-apply.dingtalk.enabled=true");
}
if (!StringUtils.hasText(properties.getAccessToken())) {
throw new IllegalStateException("未配置钉钉机器人 access-tokenlb.assessment-apply.dingtalk.access-token");
}
if (!StringUtils.hasText(content)) {
throw new IllegalArgumentException("发送内容不能为空");
}
String url = buildWebhookUrl();
Map<String, Object> body = new HashMap<>();
body.put("msgtype", "text");
Map<String, String> text = new HashMap<>();
text.put("content", content);
body.put("text", text);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, entity, String.class);
parseResponse(response.getBody());
}
private String buildWebhookUrl() {
StringBuilder url = new StringBuilder(SEND_URL)
.append("?access_token=")
.append(properties.getAccessToken().trim());
if (StringUtils.hasText(properties.getSecret())) {
long timestamp = System.currentTimeMillis();
String sign = sign(timestamp, properties.getSecret().trim());
url.append("&timestamp=").append(timestamp).append("&sign=").append(sign);
}
return url.toString();
}
private static String sign(long timestamp, String secret) {
try {
String stringToSign = timestamp + "\n" + secret;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return URLEncoder.encode(Base64.getEncoder().encodeToString(signData), StandardCharsets.UTF_8);
} catch (Exception e) {
throw new IllegalStateException("钉钉加签失败:" + e.getMessage(), e);
}
}
private void parseResponse(String body) {
if (!StringUtils.hasText(body)) {
throw new IllegalStateException("钉钉接口返回为空");
}
try {
JsonNode root = objectMapper.readTree(body);
int errcode = root.path("errcode").asInt(-1);
if (errcode != 0) {
String errmsg = root.path("errmsg").asText("未知错误");
throw new IllegalStateException("钉钉发送失败:" + errmsg + "errcode=" + errcode + "");
}
} catch (IllegalStateException e) {
throw e;
} catch (Exception e) {
throw new IllegalStateException("解析钉钉响应失败:" + e.getMessage(), e);
}
}
}

View File

@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rj.entity.LbAssessmentApply;
import com.rj.mapper.LbAssessmentApplyMapper;
import com.rj.service.DingTalkRobotService;
import com.rj.service.ILbAssessmentApplyService;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
@@ -47,6 +48,9 @@ public class LbAssessmentApplyServiceImpl
@Autowired
private ObjectMapper objectMapper;
@Autowired
private DingTalkRobotService dingTalkRobotService;
@Value("${langchain4j.open-ai.chat-model.model-name:qwen-plus}")
private String dashScopeChatModel;
@@ -67,7 +71,7 @@ public class LbAssessmentApplyServiceImpl
moderatorName 主持人姓名;
meetingNumber 会议号;
applicationDatetime 申请日期时间,优先 ISO-8601如 2026-05-15T14:30:00也可 yyyy-MM-dd HH:mm:ss若仅有日期可设为当天 00:00:00。
不要输出 id、tenantId、createdAt、updatedAt。
不要输出 id、tenantId、createdAt、updatedAt、notificationText
""";
@Override
@@ -190,6 +194,7 @@ public class LbAssessmentApplyServiceImpl
entity.setCreatedAt(now);
}
entity.setUpdatedAt(now);
refreshNotificationText(entity);
boolean ok = this.save(entity);
result.put("success", ok);
@@ -216,6 +221,9 @@ public class LbAssessmentApplyServiceImpl
}
entity.setUpdatedAt(LocalDateTime.now());
boolean ok = this.updateById(entity);
if (ok) {
persistNotificationText(entity.getId());
}
result.put("success", ok);
result.put("message", ok ? "编辑成功" : "编辑失败");
if (ok) {
@@ -308,17 +316,14 @@ public class LbAssessmentApplyServiceImpl
return result;
}
LambdaQueryWrapper<LbAssessmentApply> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(LbAssessmentApply::getTenantId, tenantId.trim())
.eq(LbAssessmentApply::getId, id.trim());
LbAssessmentApply record = this.getOne(queryWrapper);
LbAssessmentApply record = getByTenantIdAndId(tenantId, id);
if (record == null) {
result.put("success", false);
result.put("message", "未找到对应的申请评估记录");
return result;
}
String text = formatAssessmentNotificationText(record);
String text = ensureAndSaveNotificationText(record);
result.put("success", true);
result.put("message", "生成成功");
result.put("data", text);
@@ -331,6 +336,91 @@ public class LbAssessmentApplyServiceImpl
}
}
@Override
public Map<String, Object> sendNotificationTextToDingTalk(String tenantId, String id) {
Map<String, Object> result = new HashMap<>();
try {
if (tenantId == null || tenantId.trim().isEmpty()) {
result.put("success", false);
result.put("message", "tenantId不能为空");
return result;
}
if (id == null || id.trim().isEmpty()) {
result.put("success", false);
result.put("message", "id不能为空");
return result;
}
LbAssessmentApply record = getByTenantIdAndId(tenantId, id);
if (record == null) {
result.put("success", false);
result.put("message", "未找到对应的申请评估记录");
return result;
}
String text = ensureAndSaveNotificationText(record);
dingTalkRobotService.sendText(text);
result.put("success", true);
result.put("message", "发送成功");
result.put("data", text);
return result;
} catch (IllegalArgumentException | IllegalStateException e) {
log.warn("发送评估会议通知到钉钉失败, tenantId={}, id={}, reason={}", tenantId, id, e.getMessage());
result.put("success", false);
result.put("message", e.getMessage());
return result;
} catch (Exception e) {
log.error("发送评估会议通知到钉钉异常, tenantId={}, id={}", tenantId, id, e);
result.put("success", false);
result.put("message", "发送失败:" + e.getMessage());
return result;
}
}
private LbAssessmentApply getByTenantIdAndId(String tenantId, String id) {
LambdaQueryWrapper<LbAssessmentApply> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(LbAssessmentApply::getTenantId, tenantId.trim())
.eq(LbAssessmentApply::getId, id.trim());
return this.getOne(queryWrapper);
}
/**
* 返回可用的通知文案:库中已有则直接返回,否则生成并落库。
*/
private String ensureAndSaveNotificationText(LbAssessmentApply record) {
String text = record.getNotificationText();
if (text != null && !text.trim().isEmpty()) {
return text;
}
text = formatAssessmentNotificationText(record);
LbAssessmentApply update = new LbAssessmentApply();
update.setId(record.getId());
update.setNotificationText(text);
update.setUpdatedAt(LocalDateTime.now());
if (!this.updateById(update)) {
throw new IllegalStateException("通知文案保存失败");
}
record.setNotificationText(text);
return text;
}
private void refreshNotificationText(LbAssessmentApply entity) {
entity.setNotificationText(formatAssessmentNotificationText(entity));
}
private void persistNotificationText(String id) {
LbAssessmentApply record = this.getById(id);
if (record == null) {
return;
}
LbAssessmentApply update = new LbAssessmentApply();
update.setId(id);
update.setNotificationText(formatAssessmentNotificationText(record));
update.setUpdatedAt(LocalDateTime.now());
this.updateById(update);
}
private static String formatAssessmentNotificationText(LbAssessmentApply record) {
String teacherName = nullToEmpty(record.getAssessmentTeacherName());
String applicantName = nullToEmpty(record.getApplicantName());