From af8cab142a6b15c06e131a8474574dd5b00df982 Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期四, 04 十二月 2025 22:09:58 +0800
Subject: [PATCH] feat: 改造微信accesstoken存放在系统配置表中

---
 sql/wechat_subscribe_message_config_README.md                                               |  167 ++++++++++++++++++
 ruoyi-admin/src/main/resources/application.yml                                              |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java        |    1 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java   |   77 ++------
 sql/wechat_subscribe_message_config.sql                                                     |   18 ++
 ruoyi-system/src/main/java/com/ruoyi/system/service/IWechatAccessTokenService.java          |   27 +++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java |    2 
 app/pagesTask/components/StaffSelector.vue                                                  |   17 +
 app/pagesTask/edit-emergency.vue                                                            |   59 +++++-
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java    |   12 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatAccessTokenServiceImpl.java  |  123 +++++++++++++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/wechat/WechatController.java             |   11 
 app/pagesTask/detail.vue                                                                    |    8 
 13 files changed, 440 insertions(+), 84 deletions(-)

diff --git a/app/pagesTask/components/StaffSelector.vue b/app/pagesTask/components/StaffSelector.vue
index 9f750ed..972aae1 100644
--- a/app/pagesTask/components/StaffSelector.vue
+++ b/app/pagesTask/components/StaffSelector.vue
@@ -5,7 +5,7 @@
       <view class="staff-list">
         <view class="staff-item" v-for="(staff, index) in selectedStaff" :key="staff.userId">
           <view class="staff-info">
-            <text class="staff-name">{{ staff.nickName }}</text>
+            <text class="staff-name">{{ getStaffDisplayName(staff) }}</text>
           </view>
           <uni-icons 
             v-if="canRemove(index)"
@@ -454,6 +454,21 @@
     emitChange() {
       this.$emit('input', this.selectedStaff)
       this.$emit('change', this.selectedStaff)
+    },
+    
+    // 鑾峰彇浜哄憳鏄剧ず鍚嶇О锛堜紭鍏堟樉绀哄鍚嶏紝濡傛灉濮撳悕涓虹┖鍒欐樉绀烘墜鏈哄彿锛�
+    getStaffDisplayName(staff) {
+      if (!staff) {
+        return '鏈煡浜哄憳'
+      }
+      // 浼樺厛鏄剧ず nickName锛屽鏋滀负绌哄垯鏄剧ず鎵嬫満鍙凤紝閮戒负绌哄垯鏄剧ず userId
+      if (staff.nickName && staff.nickName.trim()) {
+        return staff.nickName
+      }
+      if (staff.phonenumber && staff.phonenumber.trim()) {
+        return staff.phonenumber
+      }
+      return `鐢ㄦ埛${staff.userId || ''}`
     }
   }
 }
diff --git a/app/pagesTask/detail.vue b/app/pagesTask/detail.vue
index 0e96658..bffcfea 100644
--- a/app/pagesTask/detail.vue
+++ b/app/pagesTask/detail.vue
@@ -804,6 +804,14 @@
       
       // 妫�鏌ヨ溅杈嗙姸鎬佸苟鍑哄彂
       checkVehicleAndDepart() {
+        // 妫�鏌ュ嚭鍙戞椂闂存槸鍚︿负绌烘垨1900骞达紙淇锛氶槻姝㈡棤鏁堟椂闂达級
+        if (!this.taskDetail.plannedStartTime || this.taskDetail.plannedStartTime.startsWith('1900')) {
+          this.$modal.confirm('浠诲姟鐨勮浆杩愭椂闂存湭璁剧疆鎴栨棤鏁堬紝闇�瑕佸厛淇敼浠诲姟琛ュ厖杞繍鏃堕棿鍚庢墠鑳藉嚭鍙戙�傛槸鍚︾幇鍦ㄥ幓淇敼锛�').then(() => {
+            this.handleEdit()
+          }).catch(() => {})
+          return
+        }
+        
         // 鑾峰彇浠诲姟杞﹁締ID
         const vehicleId = this.getVehicleId();
         if (!vehicleId) {
diff --git a/app/pagesTask/edit-emergency.vue b/app/pagesTask/edit-emergency.vue
index cb0d7dc..eefd5df 100644
--- a/app/pagesTask/edit-emergency.vue
+++ b/app/pagesTask/edit-emergency.vue
@@ -19,7 +19,8 @@
       />
       
       <view class="form-item">
-        <OrganizationSelector 
+        <OrganizationSelector
+          ref="organizationSelector"
           v-model="selectedOrganizationId"
           :required="true"
           :auto-select-user-dept="false"
@@ -53,6 +54,7 @@
         :required="false"
         :auto-add-current-user="false"
         :current-user-removable="true"
+        :branch-dept-ids="allOrganizationIds"
         @change="onStaffChange"
       />
       
@@ -224,6 +226,7 @@
       taskDetail: null,
       selectedVehicleId: null,
       selectedOrganizationId: null,
+      allOrganizationIds: [], // 鎵�鏈夊彲閫夋満鏋処D鏁扮粍
       selectedRegion: '',
       mapSelectorType: '',
       // 鎵╁睍 addressCoordinates 鏀寔澶氱閿悕
@@ -292,6 +295,11 @@
       }, 1500)
     }
   },
+  
+  mounted() {
+    // 椤甸潰鎸傝浇鍚庡姞杞芥墍鏈夋満鏋処D
+    this.loadAllOrganizationIds()
+  },
   methods: {
     // 鍔犺浇浠诲姟璇︽儏
     loadTaskDetail() {
@@ -320,8 +328,9 @@
           const info = this.taskDetail.emergencyInfo
           console.log('杞繍浠诲姟淇℃伅:', info)
           
-          // 杞繍鏃堕棿
-          this.taskForm.transferTime = this.taskDetail.plannedStartTime || ''
+          // 杞繍鏃堕棿锛堜慨澶嶏細1900骞寸殑鏃ユ湡鏄剧ず涓虹┖锛�
+          const transferTime = this.taskDetail.plannedStartTime || ''
+          this.taskForm.transferTime = transferTime && transferTime.startsWith('1900') ? '' : transferTime
           
           // 鎮h�呬俊鎭�
           this.taskForm.patient.contact = info.patientContact || ''
@@ -376,7 +385,8 @@
         } else {
           console.warn('浠诲姟璇︽儏涓病鏈塭mergencyInfo瀛楁锛屽皾璇曚粠涓诲璞¤幏鍙栨暟鎹�')
           // 鍏煎澶勭悊锛氬鏋渆mergencyInfo涓嶅瓨鍦紝灏濊瘯浠庝富瀵硅薄鑾峰彇
-          this.taskForm.transferTime = this.taskDetail.plannedStartTime || ''
+          const transferTime = this.taskDetail.plannedStartTime || ''
+          this.taskForm.transferTime = transferTime && transferTime.startsWith('1900') ? '' : transferTime
           this.taskForm.transferDistance = this.taskDetail.estimatedDistance ? String(this.taskDetail.estimatedDistance) : ''
         }
         
@@ -421,16 +431,24 @@
           console.log('璁剧疆鐩爣鍦板潗鏍�:', this.taskDetail.destinationLongitude, this.taskDetail.destinationLatitude)
         }
         
-        // 璁剧疆鎵ц浜哄憳锛堜慨澶嶏細纭繚 assignees 涓嶄负 null锛�
+        // 璁剧疆鎵ц浜哄憳锛堜慨澶嶏細纭繚 assignees 涓嶄负 null锛屽苟姝g‘鏄犲皠瀛楁锛�
         if (this.taskDetail.assignees && Array.isArray(this.taskDetail.assignees) && this.taskDetail.assignees.length > 0) {
           console.log('鍘熷鎵ц浜哄憳鏁版嵁:', this.taskDetail.assignees)
-          this.selectedStaff = this.taskDetail.assignees.map(assignee => ({
-            userId: assignee.userId,
-            nickName: assignee.userName,
-            type: assignee.userType || 'driver',
-            phonenumber: '',
-            deptName: ''
-          }))
+          this.selectedStaff = this.taskDetail.assignees.map(assignee => {
+            console.log('澶勭悊鎵ц浜哄憳:', assignee)
+            console.log('  - userName:', assignee.userName)
+            console.log('  - nickName:', assignee.nickName)
+            console.log('  - phonenumber:', assignee.phonenumber)
+            console.log('  - phone:', assignee.phone)
+            
+            return {
+              userId: assignee.userId,
+              nickName: assignee.userName || assignee.nickName || '',
+              type: assignee.userType || 'driver',
+              phonenumber: assignee.phonenumber || assignee.phone || '',
+              deptName: assignee.deptName || ''
+            }
+          })
           console.log('澶勭悊鍚庣殑鎵ц浜哄憳鍒楄〃:', this.selectedStaff)
         } else {
           console.warn('浠诲姟娌℃湁鍒嗛厤鎵ц浜哄憳鎴朼ssignees涓虹┖')
@@ -455,6 +473,23 @@
       console.log('閫変腑杞﹁締:', vehicle)
     },
     
+    // 鍔犺浇鎵�鏈夋満鏋処D
+    loadAllOrganizationIds() {
+      // 閫氳繃 OrganizationSelector 缁勪欢鑾峰彇鎵�鏈夋満鏋�
+      const orgSelector = this.$refs.organizationSelector
+      if (orgSelector) {
+        orgSelector.reload().then(organizations => {
+          this.allOrganizationIds = organizations.map(org => org.deptId)
+          console.log('鎵�鏈夋満鏋処D:', this.allOrganizationIds)
+        })
+      } else {
+        // 濡傛灉缁勪欢杩樻湭鎸傝浇,绋嶅悗閲嶈瘯
+        setTimeout(() => {
+          this.loadAllOrganizationIds()
+        }, 100)
+      }
+    },
+    
     // 褰掑睘鏈烘瀯閫夋嫨鍙樺寲
     onOrganizationChange(orgData) {
       // orgData 鍖呭惈锛歞eptId, deptName, serviceOrderClass, region, departureAddress, departureLongitude, departureLatitude
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java
index 0480114..d9545fe 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/task/SysTaskAttachmentController.java
@@ -14,6 +14,7 @@
 import com.ruoyi.system.service.ISysTaskEmergencyService;
 import com.ruoyi.system.service.ISysUserService;
 import com.ruoyi.system.service.ITaskAttachmentSyncService;
+import com.ruoyi.system.service.IWechatAccessTokenService;
 import com.ruoyi.system.task.ITaskAttachmentService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -62,6 +63,9 @@
 
     @Autowired
     private ISysUserService userService;
+
+    @Autowired
+    private IWechatAccessTokenService wechatAccessTokenService;
     /**
      * 鏌ヨ浠诲姟闄勪欢鍒楄〃
      */
@@ -135,6 +139,7 @@
     
     /**
      * 浠庡井淇ediaId涓婁紶闄勪欢锛堝井淇″皬绋嬪簭涓撶敤锛�
+     * 浣跨敤搴旂敤绾х紦瀛樼殑AccessToken
      */
     @Log(title = "浠诲姟闄勪欢", businessType = BusinessType.INSERT)
     @PostMapping("/uploadFromWechat/{taskId}")
@@ -142,11 +147,8 @@
                                        @RequestParam("mediaId") String mediaId,
                                        @RequestParam(value = "category", required = false) String category) {
         try {
-            // 鑾峰彇寰俊AccessToken
-            String accessToken = WechatUtils.getAccessToken(
-                wechatConfig.getAppId(), 
-                wechatConfig.getAppSecret()
-            );
+            // 鑾峰彇寰俊AccessToken锛堜娇鐢ㄥ簲鐢ㄧ骇缂撳瓨锛�
+            String accessToken = wechatAccessTokenService.getAppAccessToken();
             if (accessToken == null || accessToken.isEmpty()) {
                 return error("鑾峰彇寰俊AccessToken澶辫触");
             }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wechat/WechatController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wechat/WechatController.java
index 82258c3..91796b1 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wechat/WechatController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wechat/WechatController.java
@@ -20,6 +20,7 @@
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.system.service.IWechatTaskNotifyService;
+import com.ruoyi.system.service.IWechatAccessTokenService;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -49,17 +50,17 @@
 
     @Autowired
     private IWechatTaskNotifyService wechatTaskNotifyService;
+
+    @Autowired
+    private IWechatAccessTokenService wechatAccessTokenService;
     
     /**
-     * 鑾峰彇寰俊AccessToken
+     * 鑾峰彇寰俊AccessToken锛堜娇鐢ㄥ簲鐢ㄧ骇缂撳瓨锛�
      */
     @GetMapping("/accessToken")
     public AjaxResult getAccessToken() {
         try {
-            String accessToken = WechatUtils.getAccessToken(
-                wechatConfig.getAppId(), 
-                wechatConfig.getAppSecret()
-            );
+            String accessToken = wechatAccessTokenService.getAppAccessToken();
             if (accessToken == null || accessToken.isEmpty()) {
                 return error("鑾峰彇寰俊AccessToken澶辫触");
             }
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 8ab857f..b039b84 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -58,7 +58,7 @@
     basename: i18n/messages
   profiles:
     # 鐜 dev|test|prod
-    active: dev
+    active: prod
   # 鏂囦欢涓婁紶
   servlet:
     multipart:
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/IWechatAccessTokenService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/IWechatAccessTokenService.java
new file mode 100644
index 0000000..0abe1c4
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/IWechatAccessTokenService.java
@@ -0,0 +1,27 @@
+package com.ruoyi.system.service;
+
+/**
+ * 寰俊AccessToken鏈嶅姟鎺ュ彛
+ * 鎻愪緵搴旂敤绾ccessToken鐨勭粺涓�绠$悊锛屼娇鐢╯ys_config琛ㄧ紦瀛�
+ * 
+ * @author ruoyi
+ * @date 2025-12-04
+ */
+public interface IWechatAccessTokenService {
+    
+    /**
+     * 鑾峰彇搴旂敤绾у井淇ccessToken锛堝甫缂撳瓨锛�
+     * 浼樺厛浠巗ys_config璇诲彇骞跺垽鏂湁鏁堟湡锛涜繃鏈熷垯閲嶆柊鑾峰彇骞跺啓鍥瀞ys_config
+     * 
+     * @return Access Token锛屽け璐ヨ繑鍥瀗ull
+     */
+    String getAppAccessToken();
+    
+    /**
+     * 寮哄埗鍒锋柊AccessToken
+     * 蹇界暐缂撳瓨锛岀洿鎺ヤ粠寰俊鑾峰彇鏂癟oken骞舵洿鏂扮紦瀛�
+     * 
+     * @return 鏂扮殑Access Token锛屽け璐ヨ繑鍥瀗ull
+     */
+    String refreshAppAccessToken();
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
index e764723..c2b98b1 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/LegacyTransferSyncServiceImpl.java
@@ -672,7 +672,7 @@
                             if (sysUser != null) {
                                 TaskCreateVO.AssigneeInfo assigneeInfo = new TaskCreateVO.AssigneeInfo();
                                 assigneeInfo.setUserId(sysUser.getUserId()); // 浣跨敤绯荤粺鐢ㄦ埛ID
-                                assigneeInfo.setUserName(sysUser.getUserName());
+                                assigneeInfo.setUserName(sysUser.getNickName());
                                 // 鏍规嵁EntourageState纭畾瑙掕壊绫诲瀷
                                 // 1,2 鍙告満锛�3,5 鍖荤敓锛�4,6 鎶ゅ+
                                 if ("1".equals(entourageState) || "2".equals(entourageState)) {
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
index 2a8e338..ca8b487 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/PaymentSyncServiceImpl.java
@@ -73,7 +73,6 @@
      * 灏嗘柊绯荤粺鏀粯璁板綍鍚屾鍒版棫绯荤粺PaidMoney琛�
      */
     @Override
-    @Transactional    
     public boolean syncPaymentToLegacy(SysTaskPayment payment) {
         Long paymentId = payment.getId();
         try {
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatAccessTokenServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatAccessTokenServiceImpl.java
new file mode 100644
index 0000000..2d43bdb
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatAccessTokenServiceImpl.java
@@ -0,0 +1,123 @@
+package com.ruoyi.system.service.impl;
+
+import com.ruoyi.common.config.WechatConfig;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.WechatUtils;
+import com.ruoyi.system.domain.SysConfig;
+import com.ruoyi.system.mapper.SysConfigMapper;
+import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.IWechatAccessTokenService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 寰俊AccessToken鏈嶅姟瀹炵幇
+ * 鎻愪緵搴旂敤绾ccessToken鐨勭粺涓�绠$悊锛屼娇鐢╯ys_config琛ㄧ紦瀛�
+ * 
+ * @author ruoyi
+ * @date 2025-12-04
+ */
+@Service
+public class WechatAccessTokenServiceImpl implements IWechatAccessTokenService {
+    
+    private static final Logger log = LoggerFactory.getLogger(WechatAccessTokenServiceImpl.class);
+    
+    @Autowired
+    private WechatConfig wechatConfig;
+    
+    @Autowired
+    private ISysConfigService configService;
+    
+    @Autowired
+    private SysConfigMapper configMapper;
+    
+    /**
+     * 鑾峰彇搴旂敤绾у井淇ccessToken锛堝甫缂撳瓨锛�
+     * 浼樺厛浠巗ys_config璇诲彇骞跺垽鏂湁鏁堟湡锛涜繃鏈熷垯閲嶆柊鑾峰彇骞跺啓鍥瀞ys_config
+     */
+    @Override
+    public String getAppAccessToken() {
+        try {
+            String appId = wechatConfig.getAppId();
+            String tokenKey = "weixin.access_token." + appId;
+            String expireKey = "weixin.access_token_expires." + appId;
+            
+            String cachedToken = configService.selectConfigByKey(tokenKey);
+            String cachedExpireStr = configService.selectConfigByKey(expireKey);
+            long now = System.currentTimeMillis();
+            long expireTs = 0L;
+            if (StringUtils.isNotEmpty(cachedExpireStr)) {
+                try {
+                    expireTs = Long.parseLong(cachedExpireStr);
+                } catch (NumberFormatException e) {
+                    expireTs = 0L;
+                }
+            }
+            
+            // 缂撳瓨鏈夋晥涓旀湭杩囨湡锛堥鐣�60绉掑畨鍏ㄨ竟鐣岋級
+            if (StringUtils.isNotEmpty(cachedToken) && expireTs > now + 60000L) {
+                log.debug("浣跨敤缂撳瓨鐨凙ccessToken锛屽墿浣欐湁鏁堟湡锛歿}绉�", (expireTs - now) / 1000);
+                return cachedToken;
+            }
+            
+            // 閲嶆柊鑾峰彇锛屽苟鍐欏叆sys_config
+            log.info("AccessToken宸茶繃鏈熸垨涓嶅瓨鍦紝閲嶆柊鑾峰彇");
+            return refreshAppAccessToken();
+        } catch (Exception e) {
+            log.error("鑾峰彇搴旂敤绾у井淇ccessToken澶辫触", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 寮哄埗鍒锋柊AccessToken
+     * 蹇界暐缂撳瓨锛岀洿鎺ヤ粠寰俊鑾峰彇鏂癟oken骞舵洿鏂扮紦瀛�
+     */
+    @Override
+    public String refreshAppAccessToken() {
+        try {
+            String appId = wechatConfig.getAppId();
+            String appSecret = wechatConfig.getAppSecret();
+            String tokenKey = "weixin.access_token." + appId;
+            String expireKey = "weixin.access_token_expires." + appId;
+            
+            String newToken = WechatUtils.getAccessToken(appId, appSecret);
+            if (StringUtils.isEmpty(newToken)) {
+                log.error("浠庡井淇¤幏鍙朅ccessToken澶辫触");
+                return null;
+            }
+            
+            long now = System.currentTimeMillis();
+            long newExpireTs = now + 7200L * 1000L; // 7200绉�
+            
+            upsertConfig(tokenKey, newToken);
+            upsertConfig(expireKey, String.valueOf(newExpireTs));
+            
+            log.info("AccessToken鍒锋柊鎴愬姛锛屾湁鏁堟湡锛�7200绉�");
+            return newToken;
+        } catch (Exception e) {
+            log.error("鍒锋柊搴旂敤绾у井淇ccessToken澶辫触", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 鏍规嵁configKey鍐欏叆鎴栨洿鏂皊ys_config
+     */
+    private void upsertConfig(String key, String value) {
+        SysConfig exist = configMapper.checkConfigKeyUnique(key);
+        if (exist != null && exist.getConfigId() != null) {
+            exist.setConfigValue(value);
+            configMapper.updateConfig(exist);
+        } else {
+            SysConfig cfg = new SysConfig();
+            cfg.setConfigKey(key);
+            cfg.setConfigName(key);
+            cfg.setConfigValue(value);
+            cfg.setConfigType("Y"); // 鍐呯疆鍙傛暟
+            configMapper.insertConfig(cfg);
+        }
+    }
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java
index 6b91981..5b7273a 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java
@@ -13,9 +13,8 @@
 import com.ruoyi.system.domain.SysTaskEmergency;
 import com.ruoyi.system.mapper.SysUserMapper;
 import com.ruoyi.system.service.IWechatTaskNotifyService;
+import com.ruoyi.system.service.IWechatAccessTokenService;
 import com.ruoyi.system.service.ISysConfigService;
-import com.ruoyi.system.mapper.SysConfigMapper;
-import com.ruoyi.system.domain.SysConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -50,69 +49,26 @@
     private WechatConfig wechatConfig;
     
     @Autowired
-    private ISysConfigService configService;
+    private IWechatAccessTokenService wechatAccessTokenService;
     
     @Autowired
-    private SysConfigMapper configMapper;
+    private ISysConfigService configService;
     
+    
+
     
     /**
-     * 鑾峰彇搴旂敤绾у井淇ccessToken锛堝甫缂撳瓨锛�
-     * 浼樺厛浠巗ys_config璇诲彇骞跺垽鏂湁鏁堟湡锛涜繃鏈熷垯閲嶆柊鑾峰彇骞跺啓鍥瀞ys_config
+     * 妫�鏌ユ槸鍚﹀惎鐢ㄨ闃呮秷鎭彂閫�
+     * 
+     * @return true=鍚敤锛宖alse=绂佺敤
      */
-    private String getAppAccessToken() {
+    private boolean isSubscribeMessageEnabled() {
         try {
-            String appId = wechatConfig.getAppId();
-            String tokenKey = "weixin.access_token." + appId;
-            String expireKey = "weixin.access_token_expires." + appId;
-            
-            String cachedToken = configService.selectConfigByKey(tokenKey);
-            String cachedExpireStr = configService.selectConfigByKey(expireKey);
-            long now = System.currentTimeMillis();
-            long expireTs = 0L;
-            if (StringUtils.isNotEmpty(cachedExpireStr)) {
-                try {
-                    expireTs = Long.parseLong(cachedExpireStr);
-                } catch (NumberFormatException e) {
-                    expireTs = 0L;
-                }
-            }
-            
-            // 缂撳瓨鏈夋晥涓旀湭杩囨湡锛堥鐣�60绉掑畨鍏ㄨ竟鐣岋級
-            if (StringUtils.isNotEmpty(cachedToken) && expireTs > now + 60000L) {
-                return cachedToken;
-            }
-            
-            // 閲嶆柊鑾峰彇锛屽苟鍐欏叆sys_config
-            String newToken = WechatUtils.getAccessToken(wechatConfig.getAppId(), wechatConfig.getAppSecret());
-            if (StringUtils.isEmpty(newToken)) {
-                return null;
-            }
-            long newExpireTs = now + 7200L * 1000L; // 7200绉�
-            upsertConfig(tokenKey, newToken);
-            upsertConfig(expireKey, String.valueOf(newExpireTs));
-            return newToken;
+            String enabled = configService.selectConfigByKey("wechat.subscribe.message.enabled");
+            return "true".equalsIgnoreCase(enabled);
         } catch (Exception e) {
-            log.error("鑾峰彇搴旂敤绾у井淇ccessToken澶辫触", e);
-            return null;
-        }
-    }
-    
-    /**
-     * 鏍规嵁configKey鍐欏叆鎴栨洿鏂皊ys_config
-     */
-    private void upsertConfig(String key, String value) {
-        SysConfig exist = configMapper.checkConfigKeyUnique(key);
-        if (exist != null && exist.getConfigId() != null) {
-            exist.setConfigValue(value);
-            configMapper.updateConfig(exist);
-        } else {
-            SysConfig cfg = new SysConfig();
-            cfg.setConfigKey(key);
-            cfg.setConfigName(key);
-            cfg.setConfigValue(value);
-            cfg.setConfigType("Y"); // 鍐呯疆鍙傛暟
-            configMapper.insertConfig(cfg);
+            log.warn("鑾峰彇璁㈤槄娑堟伅寮�鍏抽厤缃け璐ワ紝榛樿鍚敤", e);
+            return true; // 榛樿鍚敤
         }
     }
     
@@ -126,6 +82,11 @@
      */
     @Override
     public int sendTaskNotifyMessage(Long taskId, List<Long> userIds, Long excludeUserId) {
+        // 妫�鏌ヨ闃呮秷鎭紑鍏�
+        if (!isSubscribeMessageEnabled()) {
+            log.info("璁㈤槄娑堟伅鍙戦�佸凡鍏抽棴锛岃烦杩囧彂閫侊紝taskId={}", taskId);
+            return 0;
+        }
         if (taskId == null || userIds == null || userIds.isEmpty()) {
             log.warn("鍙戦�佸井淇′换鍔¢�氱煡鍙傛暟涓嶅畬鏁达紝taskId={}, userIds={}", taskId, userIds);
             return 0;
@@ -142,7 +103,7 @@
         SysTaskEmergency emergency = sysTaskEmergencyMapper.selectSysTaskEmergencyByTaskId(taskId);
         
         // 鑾峰彇寰俊AccessToken锛堣蛋搴旂敤绾х紦瀛橈級
-        String accessToken = getAppAccessToken();
+        String accessToken = wechatAccessTokenService.getAppAccessToken();
         if (StringUtils.isEmpty(accessToken)) {
             log.error("鑾峰彇寰俊AccessToken澶辫触锛屾棤娉曞彂閫佷换鍔¢�氱煡");
             return 0;
diff --git a/sql/wechat_subscribe_message_config.sql b/sql/wechat_subscribe_message_config.sql
new file mode 100644
index 0000000..0785aaa
--- /dev/null
+++ b/sql/wechat_subscribe_message_config.sql
@@ -0,0 +1,18 @@
+-- 寰俊璁㈤槄娑堟伅鍙戦�佸紑鍏抽厤缃�
+-- 鐢ㄤ簬鎺у埗绯荤粺鏄惁鍚敤寰俊璁㈤槄娑堟伅鎺ㄩ�佸姛鑳�
+
+-- 鎻掑叆璁㈤槄娑堟伅寮�鍏抽厤缃紙榛樿寮�鍚級
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark) 
+VALUES 
+('寰俊璁㈤槄娑堟伅寮�鍏�', 'wechat.subscribe.message.enabled', 'true', 'N', 'admin', NOW(), 'admin', NOW(), 
+ '鎺у埗鏄惁鍚敤寰俊璁㈤槄娑堟伅鎺ㄩ�佸姛鑳姐�倀rue=鍚敤锛宖alse=绂佺敤銆傚叧闂悗绯荤粺灏嗕笉鍐嶅彂閫佷换浣曡闃呮秷鎭��');
+
+-- 璇存槑锛�
+-- 1. wechat.subscribe.message.enabled 鎺у埗鍏ㄥ眬璁㈤槄娑堟伅鍙戦�佸紑鍏�
+-- 2. 璁剧疆涓� true 鏃讹紝绯荤粺姝e父鍙戦�佽闃呮秷鎭�
+-- 3. 璁剧疆涓� false 鏃讹紝鎵�鏈夎闃呮秷鎭彂閫佸皢琚烦杩�
+-- 4. 閫傜敤鍦烘櫙锛�
+--    - 鐢熶骇鐜涓存椂鍏抽棴娑堟伅鎺ㄩ��
+--    - 娴嬭瘯鐜閬垮厤璇彂娑堟伅
+--    - 绯荤粺缁存姢鏈熼棿鏆傚仠閫氱煡
+-- 5. 鍙湪绯荤粺绠$悊->鍙傛暟璁剧疆涓姩鎬佷慨鏀癸紝鏃犻渶閲嶅惎鏈嶅姟
diff --git a/sql/wechat_subscribe_message_config_README.md b/sql/wechat_subscribe_message_config_README.md
new file mode 100644
index 0000000..9982a96
--- /dev/null
+++ b/sql/wechat_subscribe_message_config_README.md
@@ -0,0 +1,167 @@
+# 寰俊璁㈤槄娑堟伅寮�鍏充娇鐢ㄨ鏄�
+
+## 涓�銆侀厤缃」璇存槑
+
+### 閰嶇疆閿細`wechat.subscribe.message.enabled`
+
+| 灞炴�� | 鍊� |
+|------|-----|
+| 閰嶇疆鍚嶇О | 寰俊璁㈤槄娑堟伅寮�鍏� |
+| 閰嶇疆閿� | wechat.subscribe.message.enabled |
+| 閰嶇疆绫诲瀷 | N (鏅�氬弬鏁�) |
+| 榛樿鍊� | true |
+| 鍙�夊�� | true / false |
+
+### 閰嶇疆璇存槑
+
+- **true**: 鍚敤寰俊璁㈤槄娑堟伅鎺ㄩ�侊紙榛樿锛�
+- **false**: 绂佺敤寰俊璁㈤槄娑堟伅鎺ㄩ��
+
+## 浜屻�丼QL鑴氭湰
+
+鎵ц浠ヤ笅SQL鑴氭湰娣诲姞閰嶇疆锛�
+
+```sql
+INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark) 
+VALUES 
+('寰俊璁㈤槄娑堟伅寮�鍏�', 'wechat.subscribe.message.enabled', 'true', 'N', 'admin', NOW(), 'admin', NOW(), 
+ '鎺у埗鏄惁鍚敤寰俊璁㈤槄娑堟伅鎺ㄩ�佸姛鑳姐�倀rue=鍚敤锛宖alse=绂佺敤銆傚叧闂悗绯荤粺灏嗕笉鍐嶅彂閫佷换浣曡闃呮秷鎭��');
+```
+
+## 涓夈�佸姛鑳借鏄�
+
+### 1. 寮�鍏虫帶鍒惰寖鍥�
+
+璇ュ紑鍏虫帶鍒朵互涓嬪満鏅殑璁㈤槄娑堟伅鍙戦�侊細
+
+- 鉁� 鏂颁换鍔″垱寤烘椂閫氱煡鎵ц浜�
+- 鉁� 浠诲姟鏇存柊鏃堕�氱煡鐩稿叧浜哄憳
+- 鉁� 鏃х郴缁熷悓姝ヤ换鍔℃椂鐨勯�氱煡
+- 鉁� 鎵嬪姩瑙﹀彂鐨勪换鍔¢�氱煡
+- 鉁� 鎵�鏈夐�氳繃 `IWechatTaskNotifyService` 鍙戦�佺殑娑堟伅
+
+### 2. 寮�鍏冲叧闂悗鐨勮〃鐜�
+
+褰撳紑鍏宠缃负 `false` 鏃讹細
+- 绯荤粺灏嗚烦杩囨墍鏈夎闃呮秷鎭彂閫�
+- 鏃ュ織璁板綍锛歚璁㈤槄娑堟伅鍙戦�佸凡鍏抽棴锛岃烦杩囧彂閫侊紝taskId=xxx`
+- 涓嶅奖鍝嶅叾浠栦笟鍔¢�昏緫
+- 杩斿洖鍊间负 0锛堝彂閫佹垚鍔熸暟閲忎负0锛�
+
+### 3. 寮傚父澶勭悊
+
+- 濡傛灉閰嶇疆璇诲彇澶辫触锛岄粯璁や负 **鍚敤** 鐘舵��
+- 纭繚绯荤粺姝e父杩愯锛岄伩鍏嶅洜閰嶇疆寮傚父瀵艰嚧鍔熻兘涓柇
+
+## 鍥涖�佷娇鐢ㄥ満鏅�
+
+### 1. 娴嬭瘯鐜
+
+鍦ㄦ祴璇曠幆澧冧腑鍏抽棴娑堟伅鎺ㄩ�侊紝閬垮厤娴嬭瘯鏁版嵁瑙﹀彂鐪熷疄娑堟伅锛�
+
+```sql
+UPDATE sys_config 
+SET config_value = 'false' 
+WHERE config_key = 'wechat.subscribe.message.enabled';
+```
+
+### 2. 鐢熶骇缁存姢
+
+鍦ㄧ郴缁熺淮鎶ゆ湡闂翠复鏃跺叧闂秷鎭帹閫侊細
+
+```sql
+-- 鍏抽棴娑堟伅鎺ㄩ��
+UPDATE sys_config 
+SET config_value = 'false' 
+WHERE config_key = 'wechat.subscribe.message.enabled';
+
+-- 缁存姢瀹屾垚鍚庢仮澶�
+UPDATE sys_config 
+SET config_value = 'true' 
+WHERE config_key = 'wechat.subscribe.message.enabled';
+```
+
+### 3. 鏁呴殰鎺掓煡
+
+褰撳井淇℃帴鍙e紓甯告椂锛屼复鏃跺叧闂秷鎭帹閫侊紝閬垮厤澶ч噺閿欒鏃ュ織锛�
+
+```sql
+UPDATE sys_config 
+SET config_value = 'false' 
+WHERE config_key = 'wechat.subscribe.message.enabled';
+```
+
+## 浜斻�佺鐞嗙晫闈㈡搷浣�
+
+### 閫氳繃鍚庡彴绠$悊鐣岄潰淇敼
+
+1. 鐧诲綍绯荤粺绠$悊鍚庡彴
+2. 杩涘叆 **绯荤粺绠$悊 > 鍙傛暟璁剧疆**
+3. 鎼滅储 **寰俊璁㈤槄娑堟伅寮�鍏�**
+4. 淇敼鍙傛暟鍊间负 `true` 鎴� `false`
+5. 淇濆瓨鍚� **绔嬪嵆鐢熸晥**锛屾棤闇�閲嶅惎鏈嶅姟
+
+## 鍏�佹妧鏈疄鐜�
+
+### 浠g爜浣嶇疆
+
+- **閰嶇疆SQL**: `/sql/wechat_subscribe_message_config.sql`
+- **鏈嶅姟瀹炵幇**: `/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/WechatTaskNotifyServiceImpl.java`
+
+### 鏍稿績浠g爜
+
+```java
+/**
+ * 妫�鏌ユ槸鍚﹀惎鐢ㄨ闃呮秷鎭彂閫�
+ */
+private boolean isSubscribeMessageEnabled() {
+    try {
+        String enabled = configService.selectConfigByKey("wechat.subscribe.message.enabled");
+        return "true".equalsIgnoreCase(enabled);
+    } catch (Exception e) {
+        log.warn("鑾峰彇璁㈤槄娑堟伅寮�鍏抽厤缃け璐ワ紝榛樿鍚敤", e);
+        return true; // 榛樿鍚敤
+    }
+}
+```
+
+## 涓冦�佹敞鎰忎簨椤�
+
+1. 鈿狅笍 淇敼閰嶇疆鍚庣珛鍗崇敓鏁堬紝鏃犻渶閲嶅惎鏈嶅姟
+2. 鈿狅笍 鍏抽棴寮�鍏充細褰卞搷鎵�鏈夎闃呮秷鎭彂閫�
+3. 鈿狅笍 榛樿鍊间负 `true`锛堝惎鐢級锛岀‘淇濇甯镐笟鍔′笉鍙楀奖鍝�
+4. 鈿狅笍 寤鸿鍦ㄧ敓浜х幆澧冧繚鎸佸紑鍚姸鎬�
+5. 鈿狅笍 娴嬭瘯鐜寤鸿鍏抽棴锛岄伩鍏嶈鍙戞秷鎭粰鐪熷疄鐢ㄦ埛
+
+## 鍏�侀獙璇佹柟娉�
+
+### 1. 楠岃瘉閰嶇疆宸叉坊鍔�
+
+```sql
+SELECT * FROM sys_config WHERE config_key = 'wechat.subscribe.message.enabled';
+```
+
+### 2. 楠岃瘉寮�鍏崇敓鏁�
+
+**鍏抽棴寮�鍏筹細**
+```sql
+UPDATE sys_config SET config_value = 'false' WHERE config_key = 'wechat.subscribe.message.enabled';
+```
+
+**鍒涘缓娴嬭瘯浠诲姟锛�**
+- 瑙傚療鏃ュ織鏄惁杈撳嚭锛歚璁㈤槄娑堟伅鍙戦�佸凡鍏抽棴锛岃烦杩囧彂閫乣
+- 纭娌℃湁瀹為檯鍙戦�佸井淇℃秷鎭�
+
+**鎭㈠寮�鍏筹細**
+```sql
+UPDATE sys_config SET config_value = 'true' WHERE config_key = 'wechat.subscribe.message.enabled';
+```
+
+## 涔濄�佺浉鍏抽厤缃�
+
+璇ュ姛鑳介厤鍚堜互涓嬮厤缃娇鐢細
+
+- `weixin.access_token.{appId}` - 寰俊AccessToken缂撳瓨
+- `weixin.access_token_expires.{appId}` - AccessToken杩囨湡鏃堕棿
+- `wechat.task.notify.template.id` - 浠诲姟閫氱煡妯℃澘ID (application.yml)
+- `wechat.task.detail.page` - 浠诲姟璇︽儏椤佃矾寰� (application.yml)

--
Gitblit v1.9.1