|
|
@@ -0,0 +1,153 @@
|
|
|
+package com.txz.cif.service.impl;
|
|
|
+
|
|
|
+import cn.hutool.http.HttpUtil;
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.txz.cif.configurer.TecrmConf;
|
|
|
+import com.txz.cif.service.TecrmService;
|
|
|
+import lombok.AllArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.security.MessageDigest;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Random;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author: MTD®️
|
|
|
+ * @date: 2025/11/17
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+@AllArgsConstructor
|
|
|
+public class TecrmServiceImpl implements TecrmService {
|
|
|
+
|
|
|
+ private static final String smsUrl = "/backend/msg-channel/v1/external/send-otp";
|
|
|
+
|
|
|
+ private final TecrmConf tecrmConf;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean sendSms(String mobile, String code) {
|
|
|
+ log.info("开始发送短信--->" + mobile + ",--->" + code);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 生成签名参数
|
|
|
+ Map<String, String> signParams = generateSignature(tecrmConf.getAppId(), tecrmConf.getAppKey());
|
|
|
+
|
|
|
+ // 构建请求URL
|
|
|
+ String url = tecrmConf.getDomain() + smsUrl;
|
|
|
+
|
|
|
+ // 构建请求体
|
|
|
+ JSONObject requestBody = new JSONObject();
|
|
|
+ requestBody.put("cid", tecrmConf.getCid());
|
|
|
+ requestBody.put("phone_number", mobile);
|
|
|
+ requestBody.put("otp", code);
|
|
|
+ requestBody.put("template_code", tecrmConf.getTemplateCode());
|
|
|
+
|
|
|
+ log.info("tecrm-sms-body:{}", JSONUtil.toJsonStr(requestBody));
|
|
|
+
|
|
|
+ // 构建完整URL(包含查询参数)
|
|
|
+ String fullUrl = String.format("%s?timestamp=%s&nonce=%s&appid=%s&signature=%s",
|
|
|
+ url,
|
|
|
+ signParams.get("timestamp"),
|
|
|
+ signParams.get("nonce"),
|
|
|
+ signParams.get("appid"),
|
|
|
+ signParams.get("signature"));
|
|
|
+
|
|
|
+ // 发送HTTP请求
|
|
|
+ String result = HttpUtil.createPost(fullUrl)
|
|
|
+ .body(JSONUtil.toJsonStr(requestBody))
|
|
|
+ .contentType("application/json")
|
|
|
+ .execute()
|
|
|
+ .body();
|
|
|
+
|
|
|
+ log.info("tecrm-sms-result:{}", result);
|
|
|
+
|
|
|
+ // 解析响应结果
|
|
|
+ JSONObject resultJson = JSONUtil.parseObj(result);
|
|
|
+
|
|
|
+ // 根据API响应格式判断成功条件
|
|
|
+ // 成功条件:code为0且data.ok为true
|
|
|
+ if (resultJson.getInt("code", -1) == 0) {
|
|
|
+ JSONObject data = resultJson.getJSONObject("data");
|
|
|
+ if (data != null && data.getBool("ok", false)) {
|
|
|
+ log.info("发送短信成功");
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 记录失败原因
|
|
|
+ String message = resultJson.getStr("message", "未知错误");
|
|
|
+ log.error("发送短信失败,code: {}, message: {}", resultJson.getInt("code"), message);
|
|
|
+ return false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("发送短信异常", e);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成签名
|
|
|
+ *
|
|
|
+ * @param appid 应用ID
|
|
|
+ * @param appkey 应用密钥
|
|
|
+ *
|
|
|
+ * @return 包含签名信息的Map
|
|
|
+ */
|
|
|
+ private Map<String, String> generateSignature(String appid, String appkey) {
|
|
|
+ long timestamp = System.currentTimeMillis() / 1000;
|
|
|
+ String nonce = generateNonce(6);
|
|
|
+
|
|
|
+ // 构建签名字符串: timestamp:nonce:appkey
|
|
|
+ String signString = timestamp + ":" + nonce + ":" + appkey;
|
|
|
+
|
|
|
+ // SHA1加密
|
|
|
+ String signature = sha1(signString);
|
|
|
+
|
|
|
+ Map<String, String> signMap = new HashMap<>();
|
|
|
+ signMap.put("timestamp", String.valueOf(timestamp));
|
|
|
+ signMap.put("nonce", nonce);
|
|
|
+ signMap.put("appid", appid);
|
|
|
+ signMap.put("signature", signature);
|
|
|
+
|
|
|
+ return signMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * SHA1加密
|
|
|
+ */
|
|
|
+ private String sha1(String input) {
|
|
|
+ try {
|
|
|
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
|
|
|
+ byte[] digest = md.digest(input.getBytes("UTF-8"));
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (byte b : digest) {
|
|
|
+ sb.append(String.format("%02x", b));
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("SHA-1算法不可用", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成指定长度的随机字符串(大小写字母)
|
|
|
+ */
|
|
|
+ private String generateNonce(int length) {
|
|
|
+ Random random = new Random(System.currentTimeMillis());
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+
|
|
|
+ for (int i = 0; i < length; i++) {
|
|
|
+ int num = random.nextInt(57) + 65; // 65-122范围
|
|
|
+ // 跳过91-96之间的字符(大写字母Z和小写字母a之间的符号)
|
|
|
+ while (num > 90 && num < 97) {
|
|
|
+ num = random.nextInt(57) + 65;
|
|
|
+ }
|
|
|
+ sb.append((char) num);
|
|
|
+ }
|
|
|
+
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+}
|