From b79a402cdbbecfcab661f1c24028b51dd704b2e0 Mon Sep 17 00:00:00 2001
From: tjx <t2856754968@163.com>
Date: 星期三, 03 十二月 2025 20:34:38 +0800
Subject: [PATCH]  1 feat: 完善质量检测消息推送功能并添加测试    3 - 实现 MesQaDingtalkService.sendQaMsgSJ 方法,支持发送包含检验单号、物料编码、物料名称、车间、线体、工单号、检验结果等详细信息的钉钉消息    4 - 添加 MesQaDingtalkServiceTest 测试类,包含正常和异常情况的单元测试

---
 src/main/java/com/gs/dingtalk/task/ScheduledTasks.java                   |   10 
 src/main/java/com/gs/dingtalk/mapper/MesQaDingtalkMapper.java            |   18 +
 src/main/java/com/gs/dingtalk/service/impl/QaSjServiceImpl.java          |   22 ++
 src/test/java/com/gs/dingtalk/MesQaDingtalkServiceTest.java              |   36 +++
 README.md                                                                |  124 +++++++++++
 src/main/java/com/gs/dingtalk/dto/QaMsgDto.java                          |   13 +
 src/main/java/com/gs/dingtalk/service/SimpleExample.java                 |   15 +
 src/main/java/com/gs/dingtalk/entity/SendDingtalk.java                   |    6 
 src/main/java/com/gs/dingtalk/mapper/QaSjMapper.java                     |   18 +
 src/main/java/com/gs/dingtalk/service/impl/MesQaDingtalkServiceImpl.java |  158 ++++++++++++++
 src/main/java/com/gs/dingtalk/service/impl/SendDingtalkServiceImpl.java  |   10 
 src/main/java/com/gs/dingtalk/entity/QaSj.java                           |   57 +++++
 src/main/java/com/gs/dingtalk/service/SendDingtalkService.java           |    2 
 src/main/java/com/gs/dingtalk/entity/MesQaDingtalk.java                  |   65 +++++
 src/main/resources/mapper/QaSjMapper.xml                                 |   22 ++
 src/main/resources/mapper/MesQaDingtalkMapper.xml                        |    7 
 src/main/java/com/gs/dingtalk/service/QaSjService.java                   |   13 +
 src/main/java/com/gs/dingtalk/service/MesQaDingtalkService.java          |   16 +
 src/main/resources/application.yml                                       |    2 
 19 files changed, 613 insertions(+), 1 deletions(-)

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..530d030
--- /dev/null
+++ b/README.md
@@ -0,0 +1,124 @@
+# 閽夐拤娑堟伅鎺ㄩ�佺郴缁�
+
+## 椤圭洰姒傝堪
+
+杩欐槸涓�涓熀浜� Spring Boot 鐨勯拤閽夋秷鎭帹閫佺郴缁燂紝鍚嶄负 `dingtalk`銆傝绯荤粺閫氳繃閽夐拤寮�鏀惧钩鍙� API 瀹炵幇浜嗗畾鏃舵秷鎭帹閫併�佺敤鎴风鐞嗐�佷互鍙婅澶囨暟鎹彁閱掔瓑鍔熻兘銆�
+
+## 鏍稿績鎶�鏈爤
+
+- **妗嗘灦**: Spring Boot 2.6.13
+- **璇█**: Java 1.8
+- **鏁版嵁搴�**: Oracle锛屼娇鐢� Druid 杩炴帴姹�
+- **鎸佷箙灞�**: MyBatis-Plus
+- **閽夐拤闆嗘垚**: 閽夐拤寮�鏀惧钩鍙� SDK
+- **宸ュ叿搴�**: Lombok, Gson, OkHttp3, Fastjson, Hutool, Apache POI
+- **鍖呯鐞�**: Maven
+
+## 椤圭洰鏋舵瀯
+
+```
+src/
+鈹溾攢鈹� main/
+鈹�   鈹溾攢鈹� java/com/gs/dingtalk/
+鈹�   鈹�   鈹溾攢鈹� DingtalkApplication.java          # Spring Boot 鍚姩绫�
+鈹�   鈹�   鈹溾攢鈹� config/
+鈹�   鈹�   鈹�   鈹溾攢鈹� DataAcquisitionConfiguration.java # 閽夐拤搴旂敤閰嶇疆锛圓ppKey銆丄ppSecret绛夛級
+鈹�   鈹�   鈹�   鈹溾攢鈹� ResultMessage.java            # 缁熶竴鍝嶅簲娑堟伅鏍煎紡
+鈹�   鈹�   鈹�   鈹斺攢鈹� URLEncoder.java               # URL缂栫爜宸ュ叿绫�
+鈹�   鈹�   鈹溾攢鈹� controller/
+鈹�   鈹�   鈹�   鈹斺攢鈹� KMController.java             # 娑堟伅鍙戦�佹帶鍒跺眰
+鈹�   鈹�   鈹溾攢鈹� dto/
+鈹�   鈹�   鈹�   鈹溾攢鈹� ApiResponseCode.java          # API鍝嶅簲鐮�
+鈹�   鈹�   鈹�   鈹溾攢鈹� DingTalkMessage.java          # 閽夐拤娑堟伅 DTO
+鈹�   鈹�   鈹�   鈹溾攢鈹� DingTalkResponse.java         # 閽夐拤鍝嶅簲 DTO
+鈹�   鈹�   鈹�   鈹斺攢鈹� Result.java                   # 缁熶竴杩斿洖缁撴灉
+鈹�   鈹�   鈹溾攢鈹� entity/
+鈹�   鈹�   鈹�   鈹溾攢鈹� MesQaDingtalk.java            # 璐ㄩ噺妫�娴嬮拤閽夋秷鎭疄浣�
+鈹�   鈹�   鈹�   鈹溾攢鈹� SendDingtalk.java             # 閽夐拤鐢ㄦ埛瀹炰綋
+鈹�   鈹�   鈹�   鈹斺攢鈹� SendMessage.java              # 娑堟伅鍙戦�佸疄浣�
+鈹�   鈹�   鈹溾攢鈹� mapper/
+鈹�   鈹�   鈹�   鈹溾攢鈹� MesQaDingtalkMapper.java      # 璐ㄩ噺妫�娴嬮拤閽夋秷鎭暟鎹闂眰
+鈹�   鈹�   鈹�   鈹溾攢鈹� SendDingtalkMapper.java       # 閽夐拤鐢ㄦ埛鏁版嵁璁块棶灞�
+鈹�   鈹�   鈹�   鈹斺攢鈹� SendMessageMapper.java        # 娑堟伅鍙戦�佹暟鎹闂眰
+鈹�   鈹�   鈹溾攢鈹� service/
+鈹�   鈹�   鈹�   鈹溾攢鈹� MesQaDingtalkService.java     # 璐ㄩ噺妫�娴嬮拤閽夋湇鍔℃帴鍙�
+鈹�   鈹�   鈹�   鈹溾攢鈹� SendDingtalkService.java      # 閽夐拤鏈嶅姟鎺ュ彛
+鈹�   鈹�   鈹�   鈹溾攢鈹� SendMessageService.java       # 娑堟伅鏈嶅姟鎺ュ彛
+鈹�   鈹�   鈹�   鈹溾攢鈹� SimpleExample.java            # 閽夐拤娑堟伅鍙戦�佸疄鐜�
+鈹�   鈹�   鈹�   鈹斺攢鈹� impl/
+鈹�   鈹�   鈹�       鈹溾攢鈹� MesQaDingtalkServiceImpl.java # 璐ㄩ噺妫�娴嬮拤閽夋湇鍔″疄鐜�
+鈹�   鈹�   鈹�       鈹溾攢鈹� SendDingtalkServiceImpl.java # 閽夐拤鏈嶅姟瀹炵幇
+鈹�   鈹�   鈹�       鈹斺攢鈹� SendMessageServiceImpl.java  # 娑堟伅鏈嶅姟瀹炵幇
+鈹�   鈹�   鈹斺攢鈹� task/
+鈹�   鈹�       鈹斺攢鈹� ScheduledTasks.java           # 瀹氭椂浠诲姟
+鈹�   鈹斺攢鈹� resources/
+鈹�       鈹溾攢鈹� application.yml                   # 搴旂敤閰嶇疆鏂囦欢
+鈹�       鈹斺攢鈹� mapper/                           # MyBatis XML 鏄犲皠鏂囦欢
+鈹斺攢鈹� test/
+    鈹斺攢鈹� java/com/gs/dingtalk/DeviceReceivingApplicationTests.java # 娴嬭瘯绫�
+```
+
+## 鏁版嵁搴撻厤缃�
+
+- **鏁版嵁搴�**: Oracle
+- **URL**: jdbc:oracle:thin:@172.16.0.219:1521/orcl
+- **鐢ㄦ埛鍚�**: yc_dev
+- **瀵嗙爜**: ycdev
+- **杩炴帴姹�**: Druid锛屽垵濮嬭繛鎺ユ暟5锛屾渶澶ц繛鎺ユ暟20
+
+## 閽夐拤閰嶇疆
+
+鍦� `config/DataAcquisitionConfiguration.java` 涓厤缃細
+- **AppKey**: 閽夐拤搴旂敤鐨� AppKey
+- **AppSecret**: 閽夐拤搴旂敤鐨� AppSecret
+- **CorpId**: 閽夐拤浼佷笟 CorpId
+- **鏈哄櫒浜篢oken**: 閽夐拤缇よ亰鏈哄櫒浜� Token
+- **鏈哄櫒浜篠ecret**: 閽夐拤缇よ亰鏈哄櫒浜� Secret
+
+## 涓昏鍔熻兘妯″潡
+
+### 1. 鐢ㄦ埛绠$悊
+- `getDingTalkUserId()`: 閫氳繃鎵嬫満鍙疯幏鍙栭拤閽夌敤鎴稩D骞朵繚瀛樺埌鏁版嵁搴�
+- 浣跨敤閽夐拤 `OapiV2UserGetbymobileResponse` API 瀹炵幇
+
+### 2. 娑堟伅鎺ㄩ��
+- `chatSendMessage()`: 鍚戦拤閽夌兢鑱婃満鍣ㄤ汉鍙戦�佹秷鎭�
+- `sendDingTalkFiveMinute()`, `sendDingTalkFifteenMinute()`, `sendDingTalkthirtyMinute()`: 瀹氭椂鍚戠壒瀹氱敤鎴峰彂閫佹秷鎭�
+
+### 3. 瀹氭椂浠诲姟
+- `ScheduledTasks.java`: 瀹氫箟浜嗕笁涓畾鏃朵换鍔�
+  - 姣�2鍒嗛挓鎵ц涓�娆�5鍒嗛挓鎻愰啋妫�鏌�
+  - 姣�3鍒嗛挓鎵ц涓�娆�15鍒嗛挓鎻愰啋妫�鏌�
+  - 姣�4鍒嗛挓鎵ц涓�娆�30鍒嗛挓鎻愰啋妫�鏌�
+
+### 4. 璐ㄩ噺妫�娴�
+- `MesQaDingtalkServiceImpl`: 澶勭悊璐ㄩ噺妫�娴嬬浉鍏崇殑閽夐拤娑堟伅鎺ㄩ��
+
+### 5. 鏉冮檺鎺у埗
+- 閫氳繃 `SendDingtalk` 瀹炰綋涓殑 `purview` 瀛楁鎺у埗鐢ㄦ埛娑堟伅鎺ユ敹鏉冮檺
+- 鏍规嵁 `procNo`锛堝伐搴忕紪鍙凤級鍖归厤鐢ㄦ埛鏉冮檺
+
+## 鏋勫缓涓庤繍琛�
+
+### 鏋勫缓
+```bash
+mvn clean package
+```
+
+### 杩愯
+```bash
+java -jar dingtalk.jar
+```
+
+鎴栧湪 IDE 涓繍琛� `DingtalkApplication.java` 鐨� `main` 鏂规硶
+
+### 绔彛
+- 鏈嶅姟鍣ㄧ鍙�: 9096
+
+## 寮�鍙戠害瀹�
+
+- 浣跨敤 Lombok 娉ㄨВ绠�鍖栦唬鐮�
+- 浣跨敤 MyBatis-Plus 杩涜鏁版嵁搴撴搷浣�
+- 浣跨敤缁熶竴鐨勫搷搴旀牸寮� `ResultMessage`
+- 閰嶇疆鏂囦欢浣跨敤 YAML 鏍煎紡
+- 瀹氭椂浠诲姟浣跨敤 Spring 鐨� `@Scheduled` 娉ㄨВ
\ No newline at end of file
diff --git a/src/main/java/com/gs/dingtalk/dto/QaMsgDto.java b/src/main/java/com/gs/dingtalk/dto/QaMsgDto.java
new file mode 100644
index 0000000..806f691
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/dto/QaMsgDto.java
@@ -0,0 +1,13 @@
+package com.gs.dingtalk.dto;
+
+
+import lombok.Data;
+
+@Data
+public class QaMsgDto {
+
+    private String lineName;
+    private String workshopName;
+    private String qaType;
+    private String billNo;
+}
diff --git a/src/main/java/com/gs/dingtalk/entity/MesQaDingtalk.java b/src/main/java/com/gs/dingtalk/entity/MesQaDingtalk.java
new file mode 100644
index 0000000..e21c64f
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/entity/MesQaDingtalk.java
@@ -0,0 +1,65 @@
+package com.gs.dingtalk.entity;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import lombok.Data;
+
+/**
+ * 
+ * @TableName MES_QA_DINGTALK
+ */
+@TableName(value ="MES_QA_DINGTALK")
+@Data
+@KeySequence(value = "SEQ_SEND", dbType = DbType.ORACLE)
+public class MesQaDingtalk implements Serializable {
+    /**
+     * 
+     */
+    private Long id;
+
+    /**
+     * 绾夸綋缂栫爜
+     */
+    private String lineNo;
+
+    /**
+     * 绾夸綋鍚嶇О
+     */
+    private String lineName;
+
+    /**
+     * 杞﹂棿id
+     */
+    private Long departmentid;
+
+    /**
+     * 杞﹂棿鍚嶇О
+     */
+    private String departmentname;
+
+    /**
+     * 妫�楠岀被鍨�
+     */
+    private String qaType;
+
+    /**
+     * 鐢ㄦ埛鐨勯拤閽塱d
+     */
+    private String userId;
+
+    /**
+     * 鐢ㄦ埛涓枃
+     */
+    private String userName;
+
+    /**
+     * 鐢佃瘽
+     */
+    private String telephone;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+}
\ No newline at end of file
diff --git a/src/main/java/com/gs/dingtalk/entity/QaSj.java b/src/main/java/com/gs/dingtalk/entity/QaSj.java
new file mode 100644
index 0000000..b8c3ac0
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/entity/QaSj.java
@@ -0,0 +1,57 @@
+package com.gs.dingtalk.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import lombok.Data;
+
+/**
+ * 
+ * @TableName QA_SJ
+ */
+@TableName(value ="QA_SJ")
+@Data
+public class QaSj implements Serializable {
+    /**
+     * 妫�楠屽崟鍙�
+     */
+    private String billNo;
+
+    /**
+     * 鐗╂枡缂栫爜
+     */
+    private String itemNo;
+
+    /**
+     * 鐗╂枡鍚嶇О
+     */
+    private String itemName;
+
+    /**
+     * 绾夸綋
+     */
+    private String lineName;
+
+    /**
+     * 杞﹂棿
+     */
+    private String workshopName;
+
+    /**
+     * 鎻愪氦鏍囪瘑
+     */
+    private Long fsubmit;
+
+    /**
+     * 宸ュ崟鍙�
+     */
+    private String daa001;
+
+    /**
+     * 妫�楠岀粨鏋�
+     */
+    private String jyjg;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+}
\ No newline at end of file
diff --git a/src/main/java/com/gs/dingtalk/entity/SendDingtalk.java b/src/main/java/com/gs/dingtalk/entity/SendDingtalk.java
index 30df714..c3f319c 100644
--- a/src/main/java/com/gs/dingtalk/entity/SendDingtalk.java
+++ b/src/main/java/com/gs/dingtalk/entity/SendDingtalk.java
@@ -41,6 +41,12 @@
      */
     private Integer fiveMinuteReminder;
 
+    //Fifteen_MINUTE_REMINDER
+    /**
+     * 鍗佷簲鍒嗛挓璇ユ彁閱掔殑浜�  0琛ㄧず涓嶆彁閱�  1 琛ㄧず鎻愰啋
+     */
+    private Integer FifteenMinuteReminder;
+
     /**
      * 涓夊崄鍒嗛挓璇ユ彁閱掔殑浜� 0琛ㄧず涓嶆彁閱�  1 琛ㄧず鎻愰啋
      */
diff --git a/src/main/java/com/gs/dingtalk/mapper/MesQaDingtalkMapper.java b/src/main/java/com/gs/dingtalk/mapper/MesQaDingtalkMapper.java
new file mode 100644
index 0000000..7514bee
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/mapper/MesQaDingtalkMapper.java
@@ -0,0 +1,18 @@
+package com.gs.dingtalk.mapper;
+
+import com.gs.dingtalk.entity.MesQaDingtalk;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author Administrator
+* @description 閽堝琛ㄣ�怣ES_QA_DINGTALK銆戠殑鏁版嵁搴撴搷浣淢apper
+* @createDate 2025-12-03 18:53:07
+* @Entity com.gs.dingtalk.entity.MesQaDingtalk
+*/
+public interface MesQaDingtalkMapper extends BaseMapper<MesQaDingtalk> {
+
+}
+
+
+
+
diff --git a/src/main/java/com/gs/dingtalk/mapper/QaSjMapper.java b/src/main/java/com/gs/dingtalk/mapper/QaSjMapper.java
new file mode 100644
index 0000000..eb4d01f
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/mapper/QaSjMapper.java
@@ -0,0 +1,18 @@
+package com.gs.dingtalk.mapper;
+
+import com.gs.dingtalk.entity.QaSj;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+* @author Administrator
+* @description 閽堝琛ㄣ�怮A_SJ銆戠殑鏁版嵁搴撴搷浣淢apper
+* @createDate 2025-12-03 19:48:50
+* @Entity com.gs.dingtalk.entity.QaSj
+*/
+public interface QaSjMapper extends BaseMapper<QaSj> {
+
+}
+
+
+
+
diff --git a/src/main/java/com/gs/dingtalk/service/MesQaDingtalkService.java b/src/main/java/com/gs/dingtalk/service/MesQaDingtalkService.java
new file mode 100644
index 0000000..e0ae4b6
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/service/MesQaDingtalkService.java
@@ -0,0 +1,16 @@
+package com.gs.dingtalk.service;
+
+import com.gs.dingtalk.dto.QaMsgDto;
+import com.gs.dingtalk.entity.MesQaDingtalk;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author Administrator
+* @description 閽堝琛ㄣ�怣ES_QA_DINGTALK銆戠殑鏁版嵁搴撴搷浣淪ervice
+* @createDate 2025-12-03 18:53:07
+*/
+public interface MesQaDingtalkService extends IService<MesQaDingtalk> {
+
+    void sendQaMsgSJ(QaMsgDto dto);
+
+}
diff --git a/src/main/java/com/gs/dingtalk/service/QaSjService.java b/src/main/java/com/gs/dingtalk/service/QaSjService.java
new file mode 100644
index 0000000..978e596
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/service/QaSjService.java
@@ -0,0 +1,13 @@
+package com.gs.dingtalk.service;
+
+import com.gs.dingtalk.entity.QaSj;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author Administrator
+* @description 閽堝琛ㄣ�怮A_SJ銆戠殑鏁版嵁搴撴搷浣淪ervice
+* @createDate 2025-12-03 19:48:50
+*/
+public interface QaSjService extends IService<QaSj> {
+
+}
diff --git a/src/main/java/com/gs/dingtalk/service/SendDingtalkService.java b/src/main/java/com/gs/dingtalk/service/SendDingtalkService.java
index e454b7e..861b38e 100644
--- a/src/main/java/com/gs/dingtalk/service/SendDingtalkService.java
+++ b/src/main/java/com/gs/dingtalk/service/SendDingtalkService.java
@@ -10,6 +10,8 @@
 
     void sendDingTalkFiveMinute() throws Exception;
 
+    void sendDingTalkFifteenMinute() throws Exception;
+
     void sendDingTalkthirtyMinute() throws Exception;
 
     void chatSendMessage() throws Exception;
diff --git a/src/main/java/com/gs/dingtalk/service/SimpleExample.java b/src/main/java/com/gs/dingtalk/service/SimpleExample.java
index 95b6e6b..e1fea38 100644
--- a/src/main/java/com/gs/dingtalk/service/SimpleExample.java
+++ b/src/main/java/com/gs/dingtalk/service/SimpleExample.java
@@ -222,4 +222,19 @@
         }
         return rsp;
     }
+
+    public OapiV2UserGetbymobileResponse getOapiV2UserGetbymobileResponse(String mobile, String accessToken) {
+        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getbymobile");
+        OapiV2UserGetbymobileRequest req = new OapiV2UserGetbymobileRequest();
+        req.setMobile(mobile);
+        req.setSupportExclusiveAccountSearch(true);
+        OapiV2UserGetbymobileResponse rsp = null;
+
+        try {
+            rsp = client.execute(req, accessToken);
+        } catch (ApiException e) {
+            throw new RuntimeException(e);
+        }
+        return rsp;
+    }
 }
diff --git a/src/main/java/com/gs/dingtalk/service/impl/MesQaDingtalkServiceImpl.java b/src/main/java/com/gs/dingtalk/service/impl/MesQaDingtalkServiceImpl.java
new file mode 100644
index 0000000..d388f14
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/service/impl/MesQaDingtalkServiceImpl.java
@@ -0,0 +1,158 @@
+package com.gs.dingtalk.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.gs.dingtalk.dto.DingTalkMessage;
+import com.gs.dingtalk.dto.QaMsgDto;
+import com.gs.dingtalk.entity.MesQaDingtalk;
+import com.gs.dingtalk.entity.QaSj;
+import com.gs.dingtalk.service.MesQaDingtalkService;
+import com.gs.dingtalk.mapper.MesQaDingtalkMapper;
+import com.gs.dingtalk.service.QaSjService;
+import com.gs.dingtalk.service.SimpleExample;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Administrator
+ * @description 閽堝琛ㄣ�怣ES_QA_DINGTALK銆戠殑鏁版嵁搴撴搷浣淪ervice瀹炵幇
+ * @createDate 2025-12-03 18:53:07
+ */
+@Service
+@RequiredArgsConstructor
+@Transactional(rollbackFor = Exception.class)
+public class MesQaDingtalkServiceImpl extends ServiceImpl<MesQaDingtalkMapper, MesQaDingtalk>
+        implements MesQaDingtalkService {
+
+
+    private final SimpleExample simpleExample;
+
+    private final QaSjService qaSjService;
+
+    @Override
+    public void sendQaMsgSJ(QaMsgDto dto) {
+        LambdaQueryWrapper<MesQaDingtalk> wrapper = new LambdaQueryWrapper<>();
+
+        wrapper.eq(MesQaDingtalk::getLineName, dto.getLineName())
+                .eq(MesQaDingtalk::getDepartmentname, dto.getWorkshopName())
+                .eq(MesQaDingtalk::getQaType, dto.getQaType());
+
+        //鑾峰彇鍒扮敤鎴穒d锛屽綋鐢ㄦ埛娌℃湁id鏃堕噸鏂拌幏鍙栧苟鏇存柊MesQaDingtalk琛�
+        List<MesQaDingtalk> list = list(wrapper);
+
+        if (list.isEmpty()) {
+            // 濡傛灉娌℃湁鎵惧埌瀵瑰簲鐨勭敤鎴凤紝鐩存帴杩斿洖
+            return;
+        }
+
+        LambdaQueryWrapper<QaSj> qaSjQueryWrapper = new LambdaQueryWrapper<>();
+        qaSjQueryWrapper.eq(QaSj::getBillNo, dto.getBillNo());
+        QaSj qaSj = qaSjService.getOne(qaSjQueryWrapper, false);
+
+        if (qaSj == null) {
+            // 濡傛灉娌℃湁鎵惧埌瀵瑰簲鐨勬楠屼俊鎭紝鐩存帴杩斿洖
+            return;
+        }
+
+        String message = String.format("棣栦欢[妫�楠屽崟鍙�:%s, 鐗╂枡缂栫爜:%s, 鐗╂枡鍚嶇О:%s, 杞﹂棿:%s, 绾夸綋:%s, 宸ュ崟鍙�:%s, 妫�楠岀粨鏋�:%s]锛岃杩涜鍚庣画鎿嶄綔",
+                qaSj.getBillNo(), qaSj.getItemNo(), qaSj.getItemName(), qaSj.getWorkshopName(),
+                qaSj.getLineName(), qaSj.getDaa001(), qaSj.getJyjg());
+
+        // 鎶奓ist<MesQaDingtalk>涓殑userId浣跨敤,鎷兼帴璧锋潵鍙樻垚userIdList杩欎釜鍙橀噺
+
+        List<String> sidList = list.stream()
+                .map(MesQaDingtalk::getTelephone)
+                .collect(Collectors.toList());
+
+        List<String> userIdList = getDingtalkUserIdListByPhones(sidList);
+
+        if (userIdList == null || userIdList.isEmpty()) {
+            log.warn("娌℃湁鏈夋晥鐨勯拤閽夌敤鎴稩D");
+            return;
+        }
+
+        String userIdListStr = String.join(",", userIdList);
+
+
+        DingTalkMessage dingTalkMessage = null;
+        try {
+            dingTalkMessage = simpleExample.sendMessage(userIdListStr, message);
+        } catch (Exception e) {
+            throw new RuntimeException("鍙戦�侀拤閽夋秷鎭け璐�", e);
+        }
+
+        if (dingTalkMessage != null && dingTalkMessage.getErrcode() == 0) {
+            System.out.println("鍙戦�佹垚鍔�");
+        } else {
+            System.out.println("鍙戦�佸け璐�");
+        }
+    }
+
+    private List<String> getDingtalkUserIdListByPhones(List<String> phoneList) {
+        try {
+            if (phoneList == null || phoneList.isEmpty()) {
+                return new ArrayList<>();
+            }
+
+            // 鍘婚噸
+            phoneList = phoneList.stream().distinct().collect(Collectors.toList());
+
+            // 鏍规嵁sid鏌ヨDingtalkInfo
+            List<MesQaDingtalk> list = baseMapper.selectList(
+                    new LambdaQueryWrapper<MesQaDingtalk>().in(MesQaDingtalk::getTelephone, phoneList)
+            );
+
+            if (list == null || list.isEmpty()) {
+                return new ArrayList<>();
+            }
+
+            // 浣跨敤stream娴佽繃婊ゅ嚭list涓璬ingtalkId涓虹┖鐨勬暟鎹�
+            List<MesQaDingtalk> emptyDingtalkIdList = list.stream()
+                    .filter(info -> !StringUtils.hasText(info.getUserId()))
+                    .collect(Collectors.toList());
+
+            // 濡傛灉瀛樺湪涓虹┖鐨勬暟鎹氨閫氳繃閽夐拤鐨勬帴鍙h幏鍙栵紝涓篸ingtalkId璧嬪�硷紝骞朵笖鏇存柊鏁版嵁搴�
+            if (!emptyDingtalkIdList.isEmpty()) {
+                String accessToken = simpleExample.getAccessToken();
+
+                for (MesQaDingtalk info : emptyDingtalkIdList) {
+                    if (StringUtils.hasText(info.getTelephone())) {
+                        try {
+                            // 閫氳繃鎵嬫満鍙疯幏鍙栭拤閽夌敤鎴稩D
+                            com.dingtalk.api.response.OapiV2UserGetbymobileResponse response =
+                                    simpleExample.getOapiV2UserGetbymobileResponse(info.getTelephone(), accessToken);
+
+                            if (response != null && response.getResult() != null) {
+                                info.setUserId(response.getResult().getUserid());
+                                // 鏇存柊鏁版嵁搴�
+                                updateById(info);
+                            }
+                        } catch (Exception e) {
+                            log.error("鑾峰彇閽夐拤鐢ㄦ埛ID澶辫触锛屾墜鏈哄彿锛�" + info.getTelephone());
+                        }
+                    }
+                }
+            }
+
+            // 涓嶅瓨鍦ㄤ负绌虹殑鏁版嵁鎴栬�呭鐞嗗畬绌烘暟鎹悗锛岃繑鍥炴墍鏈夋湁鏁堢殑dingtalkId鍒楄〃
+            return list.stream()
+                    .map(MesQaDingtalk::getUserId)
+                    .filter(StringUtils::hasText)
+                    .distinct()
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("鑾峰彇閽夐拤鐢ㄦ埛鍒楄〃澶辫触", e);
+            return new ArrayList<>();
+        }
+    }
+}
+
+
+
+
diff --git a/src/main/java/com/gs/dingtalk/service/impl/QaSjServiceImpl.java b/src/main/java/com/gs/dingtalk/service/impl/QaSjServiceImpl.java
new file mode 100644
index 0000000..af672e9
--- /dev/null
+++ b/src/main/java/com/gs/dingtalk/service/impl/QaSjServiceImpl.java
@@ -0,0 +1,22 @@
+package com.gs.dingtalk.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.gs.dingtalk.entity.QaSj;
+import com.gs.dingtalk.service.QaSjService;
+import com.gs.dingtalk.mapper.QaSjMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author Administrator
+* @description 閽堝琛ㄣ�怮A_SJ銆戠殑鏁版嵁搴撴搷浣淪ervice瀹炵幇
+* @createDate 2025-12-03 19:48:50
+*/
+@Service
+public class QaSjServiceImpl extends ServiceImpl<QaSjMapper, QaSj>
+    implements QaSjService{
+
+}
+
+
+
+
diff --git a/src/main/java/com/gs/dingtalk/service/impl/SendDingtalkServiceImpl.java b/src/main/java/com/gs/dingtalk/service/impl/SendDingtalkServiceImpl.java
index c267e80..0659d43 100644
--- a/src/main/java/com/gs/dingtalk/service/impl/SendDingtalkServiceImpl.java
+++ b/src/main/java/com/gs/dingtalk/service/impl/SendDingtalkServiceImpl.java
@@ -83,6 +83,16 @@
     }
 
     @Override
+    public void sendDingTalkFifteenMinute() throws Exception {
+        LambdaQueryWrapper<SendDingtalk> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SendDingtalk::getFifteenMinuteReminder, 1);
+
+        List<SendDingtalk> list = list(wrapper);
+
+        getMessage(list, 15);
+    }
+
+    @Override
     public void sendDingTalkthirtyMinute() throws Exception {
         LambdaQueryWrapper<SendDingtalk> wrapper = new LambdaQueryWrapper<>();
         wrapper.eq(SendDingtalk::getThirtyMinuteReminder, 1);
diff --git a/src/main/java/com/gs/dingtalk/task/ScheduledTasks.java b/src/main/java/com/gs/dingtalk/task/ScheduledTasks.java
index 418703e..84636de 100644
--- a/src/main/java/com/gs/dingtalk/task/ScheduledTasks.java
+++ b/src/main/java/com/gs/dingtalk/task/ScheduledTasks.java
@@ -34,6 +34,16 @@
     }
 
     @Scheduled(cron = "0 0/3 * * * ?")
+    public void sendDingTalkFifteenMinute() {
+        try {
+            sendDingtalkService.sendDingTalkFifteenMinute();
+            log.info("瀹氭椂浠诲姟 sendDingTalkFifteenMinute 鎵ц鎴愬姛");
+        } catch (Exception e) {
+            log.error("瀹氭椂浠诲姟 sendDingTalkFifteenMinute 鎵ц澶辫触: ", e);
+        }
+    }
+
+    @Scheduled(cron = "0 0/4 * * * ?")
     public void sendDingTalkThirtyMinute() {
         try {
             sendDingtalkService.sendDingTalkthirtyMinute();
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index ef0a82f..93b2947 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -5,7 +5,7 @@
   datasource:
     type: com.alibaba.druid.pool.DruidDataSource
     driver-class-name: oracle.jdbc.OracleDriver
-    url: jdbc:oracle:thin:@192.168.0.100:1521/orcl
+    url: jdbc:oracle:thin:@172.16.0.219:1521/orcl
     username: yc_dev
     password: ycdev
     druid:
diff --git a/src/main/resources/mapper/MesQaDingtalkMapper.xml b/src/main/resources/mapper/MesQaDingtalkMapper.xml
new file mode 100644
index 0000000..0eb10a6
--- /dev/null
+++ b/src/main/resources/mapper/MesQaDingtalkMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.gs.dingtalk.mapper.MesQaDingtalkMapper">
+
+</mapper>
diff --git a/src/main/resources/mapper/QaSjMapper.xml b/src/main/resources/mapper/QaSjMapper.xml
new file mode 100644
index 0000000..64f5bd9
--- /dev/null
+++ b/src/main/resources/mapper/QaSjMapper.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.gs.dingtalk.mapper.QaSjMapper">
+
+    <resultMap id="BaseResultMap" type="com.gs.dingtalk.entity.QaSj">
+            <result property="billNo" column="BILL_NO" />
+            <result property="itemNo" column="ITEM_NO" />
+            <result property="itemName" column="ITEM_NAME" />
+            <result property="lineName" column="LINE_NAME" />
+            <result property="workshopName" column="WORKSHOP_NAME" />
+            <result property="fsubmit" column="FSUBMIT" />
+            <result property="daa001" column="DAA001" />
+            <result property="jyjg" column="JYJG" />
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        BILL_NO,ITEM_NO,ITEM_NAME,LINE_NAME,WORKSHOP_NAME,FSUBMIT,
+        DAA001,JYJG
+    </sql>
+</mapper>
diff --git a/src/test/java/com/gs/dingtalk/MesQaDingtalkServiceTest.java b/src/test/java/com/gs/dingtalk/MesQaDingtalkServiceTest.java
new file mode 100644
index 0000000..7c0f29e
--- /dev/null
+++ b/src/test/java/com/gs/dingtalk/MesQaDingtalkServiceTest.java
@@ -0,0 +1,36 @@
+package com.gs.dingtalk;
+
+import com.gs.dingtalk.dto.QaMsgDto;
+import com.gs.dingtalk.entity.MesQaDingtalk;
+import com.gs.dingtalk.entity.QaSj;
+import com.gs.dingtalk.service.MesQaDingtalkService;
+import com.gs.dingtalk.service.QaSjService;
+import com.gs.dingtalk.service.SimpleExample;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.*;
+
+@SpringBootTest
+class MesQaDingtalkServiceTest {
+
+    @Autowired
+    private MesQaDingtalkService mesQaDingtalkService;
+
+
+    @Test
+    void testSendQaMsgSJ() throws Exception {
+        // 鍑嗗娴嬭瘯鏁版嵁
+        QaMsgDto dto = new QaMsgDto();
+        dto.setLineName("娉ㄥ3鍙锋満");
+        dto.setWorkshopName("娉ㄥ杞﹂棿");
+        dto.setQaType("棣栦欢棣栨瀹屾垚");
+        dto.setBillNo("SJ202509150022");
+
+        mesQaDingtalkService.sendQaMsgSJ(dto);
+    }
+}

--
Gitblit v1.9.3