Merge branch 'main' of http://47.112.118.149:10082/xgxt_sd/zhxg_java
This commit is contained in:
@@ -9,7 +9,10 @@ import com.srs.common.core.controller.BaseController;
|
||||
// OkHttp 显式导入
|
||||
import com.srs.common.exception.ServiceException;
|
||||
import com.srs.common.utils.SecurityUtils;
|
||||
import com.srs.common.utils.ServletUtils;
|
||||
import com.srs.framework.web.service.TokenService;
|
||||
import com.srs.teacher.domain.dto.ConversationDTO;
|
||||
import com.srs.web.core.config.DifyConfig;
|
||||
import okhttp3.*;
|
||||
|
||||
// Spring 显式导入(不要用 *)
|
||||
@@ -41,60 +44,8 @@ import java.util.concurrent.CompletableFuture;
|
||||
@RestController
|
||||
@RequestMapping("/aitutor/aichat")
|
||||
public class AiChatController extends BaseController {
|
||||
|
||||
/**
|
||||
* Dify API的访问密钥
|
||||
* 用于身份验证,授权访问Dify服务
|
||||
*/
|
||||
private static final String DIFY_API_KEY = "app-BfPtBZDNBuHOS9K1PaZrxQYE";
|
||||
|
||||
/**
|
||||
* Dify API的URL地址
|
||||
* 用于发送聊天消息请求到Dify服务
|
||||
*/
|
||||
|
||||
private static final String DIFY_API_URL = "http://172.16.129.101/v1:6100/v1/chat-messages";
|
||||
|
||||
/**
|
||||
* Dify反馈API的基础URL
|
||||
* 用于提交消息反馈(点赞、点踩等)
|
||||
*/
|
||||
private static final String DIFY_FEEDBACK_BASE_URL = "http://172.16.129.101/v1:6100/v1/messages";
|
||||
|
||||
/**
|
||||
* Dify获取反馈API的基础URL
|
||||
* 用于获取消息反馈(点赞、点踩等)
|
||||
*/
|
||||
private static final String DIFY_API_FEEDBACK_URL = "http://172.16.129.101/v1:6100/v1/app/feedbacks?page=";
|
||||
|
||||
/**
|
||||
* Dify消息历史记录API的基础URL
|
||||
* 用于获取消息历史记录
|
||||
*/
|
||||
private static final String DIFY_API_HISTORY_URL = "http://172.16.129.101/v1:6100/v1/messages";
|
||||
|
||||
/**
|
||||
* Dify会话API的基础URL
|
||||
* 用于获取会话列表
|
||||
*/
|
||||
private static final String DIFY_CONVERSATIONS_URL = "http://172.16.129.101/v1:6100/v1/conversations";
|
||||
|
||||
/**
|
||||
* Dify文件上传API的URL地址
|
||||
* 用于上传文件到Dify服务,支持图文多模态理解
|
||||
*/
|
||||
private static final String DIFY_FILES_URL = "http://172.16.129.101/v1:6100/v1/files/upload";
|
||||
|
||||
/**
|
||||
* Redis中会话ID的key前缀
|
||||
*/
|
||||
private static final String CONVERSATION_ID_CACHE_PREFIX = "dify:conversation:id:";
|
||||
|
||||
/**
|
||||
* Redis中会话ID的过期时间(小时)
|
||||
*/
|
||||
private static final int CONVERSATION_ID_CACHE_EXPIRE_HOURS = 1;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
/**
|
||||
* HTTP客户端实例
|
||||
* 配置了5分钟的读取超时时间,用于与Dify API进行通信
|
||||
@@ -112,6 +63,13 @@ public class AiChatController extends BaseController {
|
||||
@Autowired
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* Dify配置类
|
||||
* 用于获取Dify的API密钥和其他配置参数
|
||||
*/
|
||||
@Autowired
|
||||
private DifyConfig difyConfig;
|
||||
|
||||
/** 为本次请求设置 “总超时”(含连接/读写),避免无限挂起 */
|
||||
private Response execWithTimeouts(Request req, int callSecs, int readSecs, int writeSecs) throws IOException {
|
||||
OkHttpClient shortClient = client.newBuilder()
|
||||
@@ -162,11 +120,13 @@ public class AiChatController extends BaseController {
|
||||
|
||||
// 在主线程中获取当前用户名,避免在异步线程中获取安全上下文
|
||||
String currentUsername = SecurityUtils.getLoginUser().getUsername();
|
||||
|
||||
// 获取JWT token
|
||||
String currentUserToken = tokenService.getToken(ServletUtils.getRequest());
|
||||
// String currentUserToken = SecurityUtils.getLoginUser().getToken();
|
||||
// 异步执行请求处理,避免阻塞主线程
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
sendToDifyAndStream(requestData, emitter, currentUsername);
|
||||
sendToDifyAndStream(requestData, emitter, currentUsername, currentUserToken);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
@@ -192,7 +152,8 @@ public class AiChatController extends BaseController {
|
||||
* @param currentUsername
|
||||
* @throws IOException 当网络请求或IO操作失败时抛出
|
||||
*/
|
||||
private void sendToDifyAndStream(Map<String, Object> requestData, SseEmitter emitter, String currentUsername)
|
||||
private void sendToDifyAndStream(Map<String, Object> requestData, SseEmitter emitter, String currentUsername,
|
||||
String currentUserToken)
|
||||
throws IOException {
|
||||
|
||||
// 构建请求体参数
|
||||
@@ -229,9 +190,9 @@ public class AiChatController extends BaseController {
|
||||
inputs.put("user_name", userName);
|
||||
}
|
||||
|
||||
Object userToken = requestData.get("user_token");
|
||||
if (userToken != null) {
|
||||
inputs.put("user_token", userToken);
|
||||
// Object userToken = requestData.get("user_token");
|
||||
if (currentUserToken != null) {
|
||||
inputs.put("user_token", currentUserToken);
|
||||
}
|
||||
|
||||
Object userRole = requestData.get("user_role");
|
||||
@@ -239,6 +200,11 @@ public class AiChatController extends BaseController {
|
||||
inputs.put("user_role", userRole);
|
||||
}
|
||||
|
||||
Object userPlatform = requestData.get("user_platform");
|
||||
if (userPlatform != null) {
|
||||
inputs.put("user_platform", userPlatform);
|
||||
}
|
||||
|
||||
bodyMap.put("inputs", inputs);
|
||||
|
||||
// 自动为对话生成名称
|
||||
@@ -252,8 +218,8 @@ public class AiChatController extends BaseController {
|
||||
|
||||
// 构建HTTP请求
|
||||
Request httpRequest = new Request.Builder()
|
||||
.url(DIFY_API_URL) // 设置请求URL
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY) // 添加认证头
|
||||
.url(difyConfig.getApiUrl()) // 设置请求URL
|
||||
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey()) // 添加认证头
|
||||
.addHeader("Content-Type", "application/json") // 设置内容类型
|
||||
.post(body) // 设置为POST请求
|
||||
.build();
|
||||
@@ -383,8 +349,8 @@ public class AiChatController extends BaseController {
|
||||
|
||||
// 调用 Dify API
|
||||
Request request = new Request.Builder()
|
||||
.url(DIFY_FEEDBACK_BASE_URL + "/" + messageId + "/feedbacks")
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.url(difyConfig.getFeedbackBaseUrl() + "/" + messageId + "/feedbacks")
|
||||
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.post(body)
|
||||
.build();
|
||||
@@ -430,8 +396,8 @@ public class AiChatController extends BaseController {
|
||||
|
||||
// 构建请求
|
||||
Request request = new Request.Builder()
|
||||
.url(DIFY_API_FEEDBACK_URL + page + "&limit=" + limitValue)
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.url(difyConfig.getApiFeedbackUrl() + page + "&limit=" + limitValue)
|
||||
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||
.addHeader("Content-Type", "application/json")
|
||||
.get()
|
||||
.build();
|
||||
@@ -504,9 +470,9 @@ public class AiChatController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('cph:teacher:list')")
|
||||
@GetMapping("/getMessagesToAdmin")
|
||||
public AjaxResult getMessagesToAdmin(@RequestParam String user,
|
||||
@RequestParam(required = false) String firstId,
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
@RequestParam(required = false) String firstId,
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
|
||||
try {
|
||||
// 首先尝试从Redis缓存中获取会话ID
|
||||
@@ -532,7 +498,7 @@ public class AiChatController extends BaseController {
|
||||
return successResult;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error("网络请求失败,请稍后重试");
|
||||
return AjaxResult.error("网络请求失败,请稍后重试getMessagesToAdmin");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error("请求处理失败: " + e.getMessage());
|
||||
@@ -542,8 +508,8 @@ public class AiChatController extends BaseController {
|
||||
// 权限标识为学生
|
||||
@GetMapping("/getMessagesToUser")
|
||||
public AjaxResult getMessagesToUser(@RequestParam(required = false) String firstId,
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
@RequestParam(defaultValue = "20") int limit,
|
||||
@RequestParam(defaultValue = "-updated_at") String sortBy) {
|
||||
|
||||
try {
|
||||
String user = SecurityUtils.getLoginUser().getUsername();
|
||||
@@ -571,7 +537,7 @@ public class AiChatController extends BaseController {
|
||||
return successResult;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error("网络请求失败,请稍后重试");
|
||||
return AjaxResult.error("网络请求失败,请稍后重试getMessagesToUser");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return AjaxResult.error("请求处理失败: " + e.getMessage());
|
||||
@@ -592,7 +558,7 @@ public class AiChatController extends BaseController {
|
||||
}
|
||||
|
||||
user = user.trim();
|
||||
String cacheKey = CONVERSATION_ID_CACHE_PREFIX + user;
|
||||
String cacheKey = difyConfig.getConversationCachePrefix() + user;
|
||||
|
||||
// 从Redis中获取会话ID
|
||||
String conversationId = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||
@@ -606,7 +572,7 @@ public class AiChatController extends BaseController {
|
||||
/**
|
||||
* 将会话ID与用户绑定并存储到Redis中
|
||||
*
|
||||
* @param user 用户名
|
||||
* @param user 用户名
|
||||
* @param conversationId 会话ID
|
||||
*/
|
||||
private void cacheConversationId(String user, String conversationId) {
|
||||
@@ -624,11 +590,11 @@ public class AiChatController extends BaseController {
|
||||
conversationId = conversationId.trim();
|
||||
|
||||
// 将用户与会话ID绑定存储到Redis中
|
||||
String cacheKey = CONVERSATION_ID_CACHE_PREFIX + user;
|
||||
String cacheKey = difyConfig.getConversationCachePrefix() + user;
|
||||
stringRedisTemplate.opsForValue().set(
|
||||
cacheKey,
|
||||
conversationId,
|
||||
Duration.ofHours(CONVERSATION_ID_CACHE_EXPIRE_HOURS));
|
||||
Duration.ofHours(difyConfig.getConversationCacheExpireHours()));
|
||||
} catch (Exception e) {
|
||||
System.out.println("绑定会话ID时发生错误: " + e.getMessage());
|
||||
}
|
||||
@@ -640,7 +606,7 @@ public class AiChatController extends BaseController {
|
||||
public List<ConversationDTO> getConversations(String user, String lastId, int limit, String sortBy)
|
||||
throws IOException {
|
||||
// 构建带查询参数的 URL
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_CONVERSATIONS_URL).newBuilder();
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(difyConfig.getConversationsUrl()).newBuilder();
|
||||
urlBuilder.addQueryParameter("user", user);
|
||||
if (lastId != null && !lastId.trim().isEmpty()) {
|
||||
urlBuilder.addQueryParameter("last_id", lastId);
|
||||
@@ -651,7 +617,7 @@ public class AiChatController extends BaseController {
|
||||
// 构建请求
|
||||
Request request = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||
.get()
|
||||
.build();
|
||||
|
||||
@@ -723,7 +689,7 @@ public class AiChatController extends BaseController {
|
||||
int finalLimit = limit != null && limit > 0 ? Math.min(limit, 100) : 20;
|
||||
|
||||
// 构建请求 URL
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(DIFY_API_HISTORY_URL).newBuilder();
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(difyConfig.getApiHistoryUrl()).newBuilder();
|
||||
urlBuilder.addQueryParameter("conversation_id", conversationId);
|
||||
urlBuilder.addQueryParameter("user", user);
|
||||
if (firstId != null && !firstId.trim().isEmpty()) {
|
||||
@@ -733,7 +699,7 @@ public class AiChatController extends BaseController {
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(urlBuilder.build())
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||
.addHeader("Accept", "application/json")
|
||||
.get()
|
||||
.build();
|
||||
@@ -821,8 +787,8 @@ public class AiChatController extends BaseController {
|
||||
|
||||
// 构建请求
|
||||
Request request = new Request.Builder()
|
||||
.url(DIFY_FILES_URL)
|
||||
.addHeader("Authorization", "Bearer " + DIFY_API_KEY)
|
||||
.url(difyConfig.getFilesUrl())
|
||||
.addHeader("Authorization", "Bearer " + difyConfig.getApiKey())
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
|
@@ -21,6 +21,7 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Dify心理问题发送企业微信 知无涯
|
||||
@@ -57,18 +58,37 @@ public class WeChatMentalAlertController extends BaseController {
|
||||
return AjaxResult.error("Token无效或已过期");
|
||||
}
|
||||
|
||||
String studentId = request.getUserId();
|
||||
|
||||
// 查询辅导员信息
|
||||
SysUser teacher = sysUserMapper.selectTeacherByStuNo(request.getUserId());
|
||||
if (teacher == null || !StringUtils.hasText(teacher.getUserName())) {
|
||||
log.error("辅导员信息不完整,学号: {}", request.getUserId());
|
||||
return AjaxResult.error("未分配辅导员或信息不完整");
|
||||
}
|
||||
|
||||
// 获取今天日期
|
||||
LocalDate today = LocalDate.now();
|
||||
|
||||
// 查询该学生今天已触发的心理预警次数
|
||||
int todayCount = studentMentalRatingMapper.countTodayByStudentId(studentId, today);
|
||||
|
||||
/* 保存学生心理问题评级 */
|
||||
StudentMentalRating record = new StudentMentalRating();
|
||||
record.setStudentId(request.getUserId());
|
||||
record.setRating(request.getRating());
|
||||
studentMentalRatingMapper.insert(record);
|
||||
|
||||
// 只有评级为"一级风险"时才发送企业微信通知 陈冠元
|
||||
if (!"一级风险".equals(request.getRating())) {
|
||||
return AjaxResult.success("预警已记录,因评级非一级风险未发送通知");
|
||||
}
|
||||
|
||||
// === 判断是否超过当日发送上限(3次)=== 陈冠元
|
||||
if (todayCount > 3) {
|
||||
return AjaxResult.success("预警已记录,因当日已达上限未发送通知");
|
||||
}
|
||||
|
||||
// 构建并发送消息
|
||||
try {
|
||||
String content = buildContent(request, teacher);
|
||||
@@ -88,7 +108,7 @@ public class WeChatMentalAlertController extends BaseController {
|
||||
? teacher.getNickName()
|
||||
: teacher.getUserName();
|
||||
return String.format(
|
||||
"【心理预警通知】\n" +
|
||||
"【智水AI心理预警通知】\n" +
|
||||
"辅导员:%s(%s)\n" +
|
||||
"学生姓名:%s\n" +
|
||||
"学号:%s\n" +
|
||||
|
105
srs-admin/src/main/java/com/srs/web/core/config/DifyConfig.java
Normal file
105
srs-admin/src/main/java/com/srs/web/core/config/DifyConfig.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.srs.web.core.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Component // 让 Spring 扫描到
|
||||
@ConfigurationProperties(prefix = "dify")
|
||||
public class DifyConfig {
|
||||
|
||||
/**
|
||||
* Dify 服务的基础 URL,例如:http://47.112.118.149:8100
|
||||
*/
|
||||
private String baseUrl;
|
||||
|
||||
/**
|
||||
* Dify API 密钥
|
||||
*/
|
||||
private String apiKey;
|
||||
|
||||
/**
|
||||
* 超时时间配置(可选)
|
||||
*/
|
||||
private Duration timeout = Duration.ofSeconds(30);
|
||||
|
||||
// ================== API URLs ==================
|
||||
// 这些 URL 基于 baseUrl 自动生成,不直接暴露在配置文件中
|
||||
private String apiUrl;
|
||||
private String feedbackBaseUrl;
|
||||
private String apiFeedbackUrl;
|
||||
private String apiHistoryUrl;
|
||||
private String conversationsUrl;
|
||||
private String filesUrl;
|
||||
|
||||
// ================== Redis 缓存配置 ==================
|
||||
private String conversationCachePrefix = "dify:conversation:id:";
|
||||
private int conversationCacheExpireHours = 1;
|
||||
|
||||
// -------------------- Getters and Setters --------------------
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public Duration getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(Duration timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public String getApiUrl() {
|
||||
return baseUrl + "/v1/chat-messages";
|
||||
}
|
||||
|
||||
public String getFeedbackBaseUrl() {
|
||||
return baseUrl + "/v1/messages";
|
||||
}
|
||||
|
||||
public String getApiFeedbackUrl() {
|
||||
return baseUrl + "/v1/app/feedbacks?page=";
|
||||
}
|
||||
|
||||
public String getApiHistoryUrl() {
|
||||
return baseUrl + "/v1/messages";
|
||||
}
|
||||
|
||||
public String getConversationsUrl() {
|
||||
return baseUrl + "/v1/conversations";
|
||||
}
|
||||
|
||||
public String getFilesUrl() {
|
||||
return baseUrl + "/v1/files/upload";
|
||||
}
|
||||
|
||||
public String getConversationCachePrefix() {
|
||||
return conversationCachePrefix;
|
||||
}
|
||||
|
||||
public void setConversationCachePrefix(String conversationCachePrefix) {
|
||||
this.conversationCachePrefix = conversationCachePrefix;
|
||||
}
|
||||
|
||||
public int getConversationCacheExpireHours() {
|
||||
return conversationCacheExpireHours;
|
||||
}
|
||||
|
||||
public void setConversationCacheExpireHours(int conversationCacheExpireHours) {
|
||||
this.conversationCacheExpireHours = conversationCacheExpireHours;
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ srs:
|
||||
# 开发环境配置
|
||||
server:
|
||||
# 服务器的HTTP端口,默认为8080 正式:8085 测试:8088
|
||||
port: 8085
|
||||
port: 8088
|
||||
servlet:
|
||||
# 应用的访问路径
|
||||
context-path: /
|
||||
@@ -42,6 +42,11 @@ logging:
|
||||
com.srs: debug
|
||||
org.springframework: warn
|
||||
|
||||
#dify配置
|
||||
dify:
|
||||
base-url: http://47.112.118.149:8100 #http://172.16.129.101:6100
|
||||
api-key: app-2wjqcYI9n6igHTVHdH8qXlnh #app-BfPtBZDNBuHOS9K1PaZrxQYE
|
||||
|
||||
# 用户配置
|
||||
user:
|
||||
password:
|
||||
|
@@ -157,7 +157,7 @@ public class WeChatUtil {
|
||||
String url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + accessToken;
|
||||
|
||||
JSONObject msg = new JSONObject();
|
||||
msg.put("touser", "2023429229");
|
||||
msg.put("touser", toUser);
|
||||
msg.put("msgtype", "text");
|
||||
msg.put("agentid", weChatConfig.getAgentId());
|
||||
|
||||
|
@@ -126,4 +126,15 @@ public class SrsIdentifytableController extends BaseController {
|
||||
{
|
||||
return toAjax(srsIdentifytableService.deleteSrsIdentifytableByIds(ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* 班级信息
|
||||
*/
|
||||
@Log(title = "学生鉴定信息", businessType = BusinessType.DELETE)
|
||||
@GetMapping("/deptdata")
|
||||
@ApiOperation("获取对应人的班级信息")
|
||||
public AjaxResult deptData()
|
||||
{
|
||||
return success(srsIdentifytableService.deptDataLsit());
|
||||
}
|
||||
}
|
||||
|
@@ -55,6 +55,14 @@ public interface CphMsgMapper extends BaseMapper<CphMsg>
|
||||
*/
|
||||
public int deleteCphMsgById(Long id);
|
||||
|
||||
/**
|
||||
* 根据接收人和内容中的标识来查找流程消息 (使用LIKE查询) 知无涯
|
||||
*
|
||||
* @param cphMsg 包含 receiver 和 content (作为模糊查询的标识) 的查询对象
|
||||
* @return 匹配的消息列表
|
||||
*/
|
||||
public List<CphMsg> selectCphMsgListForFlowable(CphMsg cphMsg);
|
||||
|
||||
/**
|
||||
* 批量删除消息
|
||||
*
|
||||
|
@@ -2,6 +2,8 @@ package com.srs.comprehensive.mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.srs.common.core.domain.entity.SysDept;
|
||||
import com.srs.comprehensive.domain.SrsClass;
|
||||
import com.srs.comprehensive.domain.SrsIdentifytable;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
@@ -107,4 +109,13 @@ public interface SrsIdentifytableMapper extends BaseMapper<SrsIdentifytable> {
|
||||
* @return 结果
|
||||
*/
|
||||
int deleteSrsGraduataByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 获取对应人的班级信息
|
||||
*
|
||||
* @param userName 用户名称
|
||||
* @return 结果
|
||||
*/
|
||||
List<SrsClass> selectDeptByDeptCodes(String userName);
|
||||
|
||||
}
|
||||
|
@@ -59,6 +59,14 @@ public interface ICphMsgService
|
||||
*/
|
||||
public int deleteCphMsgById(Long id);
|
||||
|
||||
/**
|
||||
* 删除消息信息 精准匹配版
|
||||
*
|
||||
* @param cphMsg 消息
|
||||
* @return 列表
|
||||
*/
|
||||
List<CphMsg> selectCphMsgListForFlowable(CphMsg cphMsg);
|
||||
|
||||
/**
|
||||
* 根据学号查询用户ID
|
||||
*
|
||||
|
@@ -3,6 +3,8 @@ package com.srs.comprehensive.service;
|
||||
import java.util.List;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.srs.comprehensive.domain.CascaderEntity;
|
||||
import com.srs.comprehensive.domain.SrsClass;
|
||||
import com.srs.comprehensive.domain.SrsIdentifytable;
|
||||
|
||||
/**
|
||||
@@ -59,4 +61,12 @@ public interface ISrsIdentifytableService extends IService<SrsIdentifytable> {
|
||||
* @return 结果
|
||||
*/
|
||||
int deleteSrsIdentifytableById(Long id);
|
||||
|
||||
/**
|
||||
* 获取对应人的班级信息
|
||||
*
|
||||
* @param
|
||||
* @return 集合结果
|
||||
*/
|
||||
List<SrsClass> deptDataLsit();
|
||||
}
|
||||
|
@@ -95,6 +95,16 @@ public class CphMsgServiceImpl extends ServiceImpl<CphMsgMapper,CphMsg> implemen
|
||||
{
|
||||
return cphMsgMapper.deleteCphMsgById(id);
|
||||
}
|
||||
/**
|
||||
* 删除消息信息
|
||||
*
|
||||
* @param cphMsg 消息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public List<CphMsg> selectCphMsgListForFlowable(CphMsg cphMsg) {
|
||||
return cphMsgMapper.selectCphMsgListForFlowable(cphMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学号查询用户ID
|
||||
|
@@ -1,17 +1,20 @@
|
||||
package com.srs.comprehensive.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.srs.common.core.domain.entity.SysDept;
|
||||
import com.srs.common.core.domain.model.LoginUser;
|
||||
import com.srs.common.utils.DateUtils;
|
||||
import com.srs.comprehensive.domain.GraduateStudentNews;
|
||||
import com.srs.comprehensive.domain.SaveStudent;
|
||||
import com.srs.comprehensive.mapper.GraduateTextMapper;
|
||||
import com.srs.comprehensive.mapper.SrsGraduateStudentMapper;
|
||||
import com.srs.common.utils.SecurityUtils;
|
||||
import com.srs.comprehensive.domain.*;
|
||||
import com.srs.comprehensive.mapper.*;
|
||||
import com.srs.comprehensive.service.ICascaderDataStudentService;
|
||||
import com.srs.comprehensive.util.FileUploadUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.srs.comprehensive.mapper.SrsIdentifytableMapper;
|
||||
import com.srs.comprehensive.domain.SrsIdentifytable;
|
||||
import com.srs.comprehensive.service.ISrsIdentifytableService;
|
||||
|
||||
/**
|
||||
@@ -156,4 +159,23 @@ public class SrsIdentifytableServiceImpl extends ServiceImpl<SrsIdentifytableMap
|
||||
public int deleteSrsIdentifytableById(Long id) {
|
||||
return srsIdentifytableMapper.deleteSrsIdentifytableById(id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取对应人的班级信息
|
||||
*
|
||||
* @param
|
||||
* @return 集合结果
|
||||
*/
|
||||
@Override
|
||||
public List<SrsClass> deptDataLsit() {
|
||||
//获取登录人的信息
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
List<SrsClass> depts = srsIdentifytableMapper.selectDeptByDeptCodes(loginUser.getUsername());
|
||||
if(depts != null && depts.size() > 0) {
|
||||
return depts;
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -781,6 +781,12 @@
|
||||
from sys_performance as a
|
||||
where (a.shstatus IS NOT NULL and a.ksstatus IS NULL) -- 科室复核待办
|
||||
or (a.ksstatus IS NOT NULL and a.xgstatus IS NULL) -- 学工处长待办
|
||||
-- 邵政文-(宿舍管理-住宿费用-辅导员确认待办)
|
||||
union
|
||||
select concat('zsfy-',count(a.id)) as `all`
|
||||
from dms_new_record as a
|
||||
left join view_dms_record as d on a.stu_no = d.stu_no
|
||||
where d.employee_id = #{tNo} and a.apply_status = 6
|
||||
</select>
|
||||
|
||||
|
||||
|
@@ -84,4 +84,16 @@
|
||||
<select id="getUserIdByStuNo" parameterType="String" resultType="Long">
|
||||
select user_id from sys_user where user_name = #{stuNo}
|
||||
</select>
|
||||
|
||||
|
||||
<select id="selectCphMsgListForFlowable" parameterType="CphMsg" resultMap="CphMsgResult">
|
||||
<include refid="selectCphMsgVo"/>
|
||||
<where>
|
||||
<if test="receiver != null "> and receiver = #{receiver}</if>
|
||||
<if test="content != null and content != ''">
|
||||
and content LIKE CONCAT('%', #{content}, '%')
|
||||
</if>
|
||||
</where>
|
||||
order by id desc
|
||||
</select>
|
||||
</mapper>
|
||||
|
@@ -54,6 +54,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<!-- 班级信息 -->
|
||||
<select id="selectDeptByDeptCodes" parameterType="String" resultType="com.srs.comprehensive.domain.SrsClass">
|
||||
SELECT DISTINCT v.class_id,v.class_name
|
||||
FROM srs_dev.view_stu_info v
|
||||
JOIN srs_class c ON v.teacher_id = c.teacher_id
|
||||
WHERE v.t_no = #{userName}
|
||||
</select>
|
||||
|
||||
|
||||
<!-- <insert id="insertSrsIdentifytable" parameterType="SrsIdentifytable" useGeneratedKeys="true" keyProperty="id">-->
|
||||
<!-- insert into srs_identifytable-->
|
||||
<!-- <trim prefix="(" suffix=")" suffixOverrides=",">-->
|
||||
|
@@ -0,0 +1,55 @@
|
||||
package com.srs.flowable.listener.disciplinary;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.DisciplinaryMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 执行监听器:在“学生教育管理科审批归档”节点开始时, 知无涯
|
||||
* 向负责归档的角色发送企业微信通知。
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ArchivingNotifyListener implements ExecutionListener {
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
log.info("流程实例 [{}]: 已进入学生教育管理科审批归档节点,准备发送通知。", delegateExecution.getProcessInstanceId());
|
||||
|
||||
try {
|
||||
DisciplinaryMapper disciplinaryMapper = SpringUtils.getBean(DisciplinaryMapper.class);
|
||||
|
||||
// 1. 【核心】定义负责归档的角色的 role_key。
|
||||
final String ARCHIVING_ROLE_KEY = "xsjyglkspgd";
|
||||
|
||||
// 2. 根据角色Key查询用户ID列表
|
||||
List<Long> userIdList = disciplinaryMapper.getApprovalByRoleKey(ARCHIVING_ROLE_KEY);
|
||||
|
||||
if (userIdList == null || userIdList.isEmpty()) {
|
||||
log.warn("根据角色Key '{}' 未找到任何归档人员,无法发送通知。", ARCHIVING_ROLE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 批量查询 userName 并发送通知
|
||||
List<String> userNameList = disciplinaryMapper.getUserNamesByUserIdList(userIdList);
|
||||
if (!userNameList.isEmpty()) {
|
||||
String toUser = String.join("|", userNameList);
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
String content = "您有一条新的学生违纪归档任务待处理,<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>请点击前往处理</a>。";
|
||||
|
||||
weChatUtil.sendTextMessage(toUser, content);
|
||||
log.info("已成功向归档角色 ({}) 发送通知。接收人: {}", ARCHIVING_ROLE_KEY, toUser);
|
||||
} else {
|
||||
log.warn("角色 '{}' 中找到了 {} 个用户,但无人配置企业微信账号,未发送任何通知。", ARCHIVING_ROLE_KEY, userIdList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("向归档角色发送企业微信通知时出现异常。流程将继续。错误详情: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,7 +9,7 @@ import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 根据辅导员部门id获取二级学院书记
|
||||
* 根据辅导员部门id获取二级学院书记 知无涯
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
|
@@ -0,0 +1,128 @@
|
||||
package com.srs.flowable.listener.disciplinary; // 您可以根据需要调整包路径
|
||||
|
||||
import com.srs.common.utils.SecurityUtils;
|
||||
import com.srs.comprehensive.domain.CphMsg;
|
||||
import com.srs.comprehensive.service.ICphMsgService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 【通用线上监听器】为即将到来的任务创建内部“我的消息”记录。 知无涯1
|
||||
* <p>
|
||||
* 该监听器被设计为在SequenceFlow的'take'事件上触发。
|
||||
* 它会智能地从流程变量中读取单个接收人(变量名: approval)或多个接收人(变量名: userList),
|
||||
* 然后为每一位接收人创建一条内部消息。
|
||||
* </p>
|
||||
*/
|
||||
@Component("genericMessageListener")
|
||||
@Slf4j
|
||||
public class GenericMessageListener implements ExecutionListener {
|
||||
|
||||
@Autowired
|
||||
private ICphMsgService cphMsgService;
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution execution) {
|
||||
log.info("流程实例 [{}]: 触发通用消息监听器...", execution.getProcessInstanceId());
|
||||
|
||||
List<Long> receiverIdList = getReceiverIds(execution);
|
||||
if (receiverIdList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// --- 关键改动:获取下一个即将创建的任务ID ---
|
||||
// 注意:在线上监听器中,任务尚未创建,所以我们无法直接获取任务ID。
|
||||
// 但我们可以获取到这个“活动”的定义ID,它在流程定义中是唯一的。
|
||||
String activityId = execution.getCurrentActivityId();
|
||||
|
||||
String taskName = execution.getCurrentFlowElement() != null ? execution.getCurrentFlowElement().getName() : "新";
|
||||
|
||||
// --- 关键改动:构建带有隐藏标识的消息内容 ---
|
||||
String messageText = "您有一条新的【" + taskName + "】待办任务需要处理,";
|
||||
// 我们嵌入一个隐藏的span,它包含了流程实例ID和活动ID作为唯一标识
|
||||
String hiddenIdentifier = String.format("<span id='flow-identifier' data-proc-inst-id='%s' data-activity-id='%s' style='display:none;'></span>",
|
||||
execution.getProcessInstanceId(),
|
||||
activityId);
|
||||
|
||||
String finalContent = messageText + hiddenIdentifier;
|
||||
|
||||
Long senderId = getSenderId();
|
||||
|
||||
for (Long receiverId : receiverIdList) {
|
||||
try {
|
||||
CphMsg cphMsg = new CphMsg();
|
||||
cphMsg.setReceiver(receiverId);
|
||||
cphMsg.setSender(senderId);
|
||||
cphMsg.setContent(finalContent); // 使用包含隐藏标识的内容
|
||||
|
||||
cphMsgService.insertCphMsg(cphMsg);
|
||||
log.info("已成功为用户 [{}] 创建内部消息 (关联 Activity ID: {})。", receiverId, activityId);
|
||||
} catch (Exception e) {
|
||||
log.error("为用户 [{}] 创建内部消息时发生异常: {}", receiverId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能地从流程变量中解析出接收人ID列表。
|
||||
* 策略是:
|
||||
* 1. 优先检查 'userList' 变量,这通常用于多实例任务。
|
||||
* 2. 如果 'userList' 不存在或无效,则检查 'approval' 变量,这通常用于单实例任务。
|
||||
*
|
||||
* @param execution 当前的执行实例
|
||||
* @return 包含一个或多个用户ID的列表,如果都找不到则返回空列表。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Long> getReceiverIds(DelegateExecution execution) {
|
||||
// 优先检查 'userList' (用于多实例)
|
||||
Object userListObj = execution.getVariable("userList");
|
||||
if (userListObj instanceof List && !((List<?>) userListObj).isEmpty()) {
|
||||
try {
|
||||
// 确保列表内容是Long类型
|
||||
return (List<Long>) userListObj;
|
||||
} catch (ClassCastException e) {
|
||||
log.error("流程变量 'userList' 不是 Long 类型的列表。", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 其次检查 'approval' (用于单实例)
|
||||
Object approvalObj = execution.getVariable("approval");
|
||||
if (approvalObj instanceof Number) {
|
||||
return Collections.singletonList(((Number) approvalObj).longValue());
|
||||
}
|
||||
// 兼容字符串形式的ID
|
||||
if (approvalObj instanceof String) {
|
||||
try {
|
||||
return Collections.singletonList(Long.parseLong((String) approvalObj));
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("流程变量 'approval' 的值 '{}' 无法转换为Long类型。", approvalObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果都找不到,返回一个不可变的空列表
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全地获取当前操作的发送人ID。
|
||||
* 在异步执行的监听器中,可能无法获取到安全上下文,因此提供了默认值。
|
||||
*
|
||||
* @return 发送人用户ID,默认为1L (系统管理员)。
|
||||
*/
|
||||
private Long getSenderId() {
|
||||
try {
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
// 如果SecurityUtils返回null,也使用默认值
|
||||
return userId != null ? userId : 1L;
|
||||
} catch (Exception e) {
|
||||
log.warn("在监听器中获取发送人ID时发生异常,将使用默认系统管理员ID(1L)。异常信息: {}", e.getMessage());
|
||||
return 1L; // 默认为系统管理员
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
package com.srs.flowable.listener.disciplinary;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.DisciplinaryMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 执行监听器:在“申请人(辅导员)接收”节点开始时, 知无涯
|
||||
* 向流程发起人发送企业微信通知,告知其结果已出。
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class InitiatorResultListener implements ExecutionListener {
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
log.info("流程实例 [{}]: 已进入辅导员接收结果节点,准备向发起人发送通知。", delegateExecution.getProcessInstanceId());
|
||||
|
||||
try {
|
||||
DisciplinaryMapper disciplinaryMapper = SpringUtils.getBean(DisciplinaryMapper.class);
|
||||
|
||||
// 1. 从流程变量中获取发起人的ID。Flowable会自动将发起人ID存入名为 "INITIATOR" 的变量中。
|
||||
// 这个变量通常是 String 类型。
|
||||
Object initiatorObj = delegateExecution.getVariable("INITIATOR");
|
||||
if (initiatorObj == null) {
|
||||
log.warn("在流程实例 [{}] 中未找到发起人(INITIATOR)变量,无法发送通知。", delegateExecution.getProcessInstanceId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 将发起人ID转换为 Long 类型,以便查询数据库
|
||||
Long initiatorUserId;
|
||||
try {
|
||||
initiatorUserId = Long.parseLong(initiatorObj.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("发起人(INITIATOR)变量 '{}' 无法转换为Long类型,无法发送通知。", initiatorObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 使用 initiatorUserId 查询对应的企业微信 userName
|
||||
String initiatorUserName = disciplinaryMapper.getUserNameByUserId(initiatorUserId);
|
||||
|
||||
if (initiatorUserName != null && !initiatorUserName.isEmpty()) {
|
||||
// 4. 发送通知
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
String content = "您提交的学生违纪处分申请已有最终处理结果,<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>请点击前往处理</a>。\"";
|
||||
|
||||
weChatUtil.sendTextMessage(initiatorUserName, content);
|
||||
log.info("已成功向流程发起人 ({}) 发送结果通知。", initiatorUserName);
|
||||
} else {
|
||||
log.warn("找到了发起人(userId:{}),但其对应的企业微信账号(userName)为空,未发送通知。", initiatorUserId);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("向流程发起人发送结果通知时出现异常。流程将继续。错误详情: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
package com.srs.flowable.listener.disciplinary; // 您可以根据需要调整包路径
|
||||
|
||||
import com.srs.comprehensive.domain.CphMsg;
|
||||
import com.srs.comprehensive.service.ICphMsgService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.task.service.delegate.DelegateTask;
|
||||
import org.flowable.task.service.delegate.TaskListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 【任务监听器】在任务完成后,删除内部消息。
|
||||
* <p>
|
||||
* 该监听器被设计为在UserTask的'complete'事件上触发。
|
||||
* 它通过解析消息内容中的隐藏HTML标识来找到与当前已完成任务关联的消息,
|
||||
* 并将其删除。
|
||||
* </p>
|
||||
*/
|
||||
@Component("messageProcessorListener") // 定义一个清晰的Bean名称
|
||||
@Slf4j
|
||||
public class MessageProcessorListener implements TaskListener {
|
||||
|
||||
@Autowired
|
||||
private ICphMsgService cphMsgService;
|
||||
|
||||
/**
|
||||
* 依赖检查方法,确保关键服务已注入。
|
||||
*/
|
||||
@PostConstruct
|
||||
public void checkDependencies() {
|
||||
Assert.notNull(cphMsgService, "ICphMsgService dependency not injected for MessageProcessorListener!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notify(DelegateTask delegateTask) {
|
||||
// 步骤 1: 获取当前任务的办理人ID
|
||||
String assigneeIdStr = delegateTask.getAssignee();
|
||||
if (assigneeIdStr == null || assigneeIdStr.isEmpty()) {
|
||||
log.warn("任务 [ID: {}] 没有办理人,无法处理消息。", delegateTask.getId());
|
||||
return;
|
||||
}
|
||||
Long receiverId;
|
||||
try {
|
||||
receiverId = Long.parseLong(assigneeIdStr);
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("任务办理人ID '{}' 无法转换为Long类型。", assigneeIdStr);
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤 2: 获取用于查找消息的唯一标识
|
||||
String processInstanceId = delegateTask.getProcessInstanceId();
|
||||
// taskDefinitionKey 对应流程图中节点的ID,即我之前在 GenericMessageListener 中存入的 activityId
|
||||
String activityId = delegateTask.getTaskDefinitionKey();
|
||||
|
||||
log.info("任务 [ID: {}, Name: {}] (办理人: {}) 已完成。开始查找并处理关联的内部消息...",
|
||||
delegateTask.getId(), delegateTask.getName(), receiverId);
|
||||
log.debug("用于查找的关联标识 -> ProcInst ID: [{}], Activity ID: [{}]", processInstanceId, activityId);
|
||||
|
||||
try {
|
||||
// 步骤 3: 构造用于模糊查询的、唯一的标识字符串
|
||||
// 这个字符串必须与 GenericMessageListener 中生成该标识的逻辑完全一致
|
||||
String searchIdentifier = String.format("data-proc-inst-id='%s' data-activity-id='%s'",
|
||||
processInstanceId,
|
||||
activityId);
|
||||
|
||||
// 步骤 4: 创建查询参数并调用服务
|
||||
CphMsg queryParam = new CphMsg();
|
||||
queryParam.setReceiver(receiverId);
|
||||
queryParam.setContent(searchIdentifier); // 将标识作为模糊查询的内容
|
||||
|
||||
// 注意:这一步要求 CphMsgMapper.xml 中的 selectCphMsgListForFlowable 方法支持对 content 字段的 LIKE 查询
|
||||
List<CphMsg> messagesToProcess = cphMsgService.selectCphMsgListForFlowable(queryParam);
|
||||
|
||||
if (messagesToProcess.isEmpty()) {
|
||||
log.info("未找到需要为用户 [{}] 处理的、与当前任务关联的消息。", receiverId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 步骤 5: 遍历所有找到的消息,并进行处理(删除)
|
||||
for (CphMsg msg : messagesToProcess) {
|
||||
try {
|
||||
cphMsgService.deleteCphMsgById(msg.getId());
|
||||
log.info("已成功删除与任务关联的消息 [ID: {}]。", msg.getId());
|
||||
} catch (Exception e) {
|
||||
// 捕获单个消息删除失败的异常,确保不影响其他消息的处理
|
||||
log.error("删除消息 [ID: {}] 时发生异常: {}", msg.getId(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// 捕获整个处理过程中的异常,确保不影响主流程的流转
|
||||
log.error("处理用户 [{}] 的内部消息时发生严重异常: {}", receiverId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package com.srs.flowable.listener.disciplinary;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.DisciplinaryMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 执行监听器:在“校领导审批”节点开始时,向校领导发送企业微信通知。 知无涯
|
||||
* (简化版:因为流程能到达此节点,即表示前序节点已全票通过)
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SchoolLeaderApprovalListener implements ExecutionListener {
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
log.info("流程实例 [{}]: 已进入校领导审批节点,准备发送通知。", delegateExecution.getProcessInstanceId());
|
||||
|
||||
try {
|
||||
DisciplinaryMapper disciplinaryMapper = SpringUtils.getBean(DisciplinaryMapper.class);
|
||||
|
||||
// 1. 【核心】定义“校领导”这个角色的 role_key
|
||||
final String LEADER_ROLE_KEY = "xldsp";
|
||||
|
||||
// 2. 根据角色Key查询用户ID列表
|
||||
List<Long> userIdList = disciplinaryMapper.getApprovalByRoleKey(LEADER_ROLE_KEY);
|
||||
|
||||
if (userIdList == null || userIdList.isEmpty()) {
|
||||
log.warn("根据角色Key '{}' 未找到任何校领导用户,无法发送通知。", LEADER_ROLE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 批量查询 userName 并发送通知
|
||||
List<String> userNameList = disciplinaryMapper.getUserNamesByUserIdList(userIdList);
|
||||
if (!userNameList.isEmpty()) {
|
||||
String toUser = String.join("|", userNameList);
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
String content = "校领导您有一条新的学生违纪审批任务待处理,<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>请点击前往处理</a>。";
|
||||
|
||||
weChatUtil.sendTextMessage(toUser, content);
|
||||
log.info("已成功向校领导 (角色: {}) 发送通知。接收人: {}", LEADER_ROLE_KEY, toUser);
|
||||
} else {
|
||||
log.warn("角色 '{}' 中找到了 {} 个用户,但无人配置企业微信账号,未发送任何通知。", LEADER_ROLE_KEY, userIdList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("向校领导发送企业微信通知时出现异常。流程将继续。错误详情: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +1,17 @@
|
||||
package com.srs.flowable.listener.disciplinary;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.DisciplinaryMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 设置处分学生审批参数
|
||||
* 流程结束监听器。
|
||||
* 在流程的最终节点,向当事学生发送处理结果的通知。 知无涯
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
@@ -14,10 +19,41 @@ public class StuInfoListener implements ExecutionListener {
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
Long stuId = (Long)delegateExecution.getVariable("stuId");
|
||||
// 1. 从流程变量中获取学生ID
|
||||
Object stuIdObj = delegateExecution.getVariable("stuId");
|
||||
|
||||
log.info("学生信息:{}",stuId);
|
||||
delegateExecution.setVariable("approval",stuId);
|
||||
// todo 企业微信推送消息
|
||||
// 安全检查:确保stuId存在且类型正确
|
||||
if (!(stuIdObj instanceof Long)) {
|
||||
log.error("流程实例 [{}] 在结束时未能获取到有效的 'stuId' 变量,无法发送最终通知。", delegateExecution.getProcessInstanceId());
|
||||
return;
|
||||
}
|
||||
Long stuId = (Long) stuIdObj;
|
||||
log.info("流程实例 [{}] 即将结束,准备向学生ID: {} 发送最终通知。", delegateExecution.getProcessInstanceId(), stuId);
|
||||
|
||||
// 2. 向该学生发送企业微信通知
|
||||
try {
|
||||
DisciplinaryMapper disciplinaryMapper = SpringUtils.getBean(DisciplinaryMapper.class);
|
||||
|
||||
// 根据学生ID查询其对应的企业微信账号(userName)
|
||||
String userName = disciplinaryMapper.getUserNameByUserId(stuId);
|
||||
|
||||
// 检查是否找到了对应的企业微信账号
|
||||
if (!StringUtils.hasText(userName)) {
|
||||
log.warn("流程实例 [{}]: 根据学生ID '{}' 未找到对应的企业微信账号,无法发送最终通知。", delegateExecution.getProcessInstanceId(), stuId);
|
||||
return; // 中止发送逻辑
|
||||
}
|
||||
|
||||
// 3. 构建并发送通知内容
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
String content = "您好,关于您的违纪处理流程已办结,最终结果已生成。<a href='http://zhxg.gxsdxy.cn/web/#/pages/Disciplinary/MyDisciplinary'>请点击登录系统查看详情</a>。";
|
||||
|
||||
weChatUtil.sendTextMessage(userName, content);
|
||||
log.info("流程实例 [{}]: 已成功向学生ID '{}' (企业微信账号: {}) 发送了流程办结通知。", delegateExecution.getProcessInstanceId(), stuId, userName);
|
||||
|
||||
} catch (Exception e) {
|
||||
// 即使通知失败,也不应影响流程的正常结束
|
||||
log.error("流程实例 [{}]: 在向学生ID '{}' 发送最终通知时出现异常。错误详情: {}",
|
||||
delegateExecution.getProcessInstanceId(), stuId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
package com.srs.flowable.listener.disciplinary;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.DisciplinaryMapper;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class XGLDSHListener implements ExecutionListener {
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
DisciplinaryMapper disciplinaryMapper = SpringUtils.getBean(DisciplinaryMapper.class);
|
||||
|
||||
// 固定角色 Key
|
||||
final String TARGET_ROLE_KEY = "xgldsp";
|
||||
|
||||
// 步骤 1: 查询角色下的所有审批人 userId
|
||||
List<Long> userIdList = disciplinaryMapper.getApprovalByRoleKey(TARGET_ROLE_KEY);
|
||||
|
||||
if (userIdList == null || userIdList.isEmpty()) {
|
||||
throw new RuntimeException("未找到角色 '" + TARGET_ROLE_KEY + "' 的审批人员,无法发送通知。");
|
||||
}
|
||||
|
||||
try {
|
||||
// 步骤 2: 查询 userName 列表
|
||||
List<String> userNameList = disciplinaryMapper.getUserNamesByUserIdList(userIdList);
|
||||
|
||||
if (userNameList != null && !userNameList.isEmpty()) {
|
||||
String toUser = String.join("|", userNameList);
|
||||
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
|
||||
String content = "您有一条新的学生违纪审批任务待处理," +
|
||||
"<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>请点击前往处理</a>。";
|
||||
|
||||
// 步骤 3: 发送企业微信通知
|
||||
weChatUtil.sendTextMessage(toUser, content);
|
||||
// log.info("已成功向角色 '{}' 的成员发送企业微信审批通知。接收人: {}", TARGET_ROLE_KEY, toUser);
|
||||
|
||||
} else {
|
||||
// log.warn("角色 '{}' 下找到 {} 个用户,但无对应的企业微信账号,无法发送通知。", TARGET_ROLE_KEY, userIdList.size());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// log.error("向角色 '{}' 发送企业微信通知时出现异常,但流程将继续。错误详情: {}", TARGET_ROLE_KEY, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,7 @@ import org.springframework.stereotype.Component;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生教育管理科审核节点的执行监听器。
|
||||
* 学生教育管理科审核节点的执行监听器。 知无涯
|
||||
* 向“学生教育管理科”角色发送企业微信通知。
|
||||
*/
|
||||
@Component
|
||||
|
@@ -15,7 +15,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
/**
|
||||
* 根据辅导员的部门id,查询该部门的学无干事人员
|
||||
* 根据辅导员的部门id,查询该部门的学无干事人员 知无涯
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
|
@@ -1,55 +1,74 @@
|
||||
package com.srs.flowable.listener.disciplinary;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.DisciplinaryMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 学院违纪处理委员会审批
|
||||
* 【线上监听器】为“学院违纪处理委员会”多实例任务准备数据并发送通知 知无涯
|
||||
*/
|
||||
@Component
|
||||
@MapperScan("com.srs.flowable.mapper")
|
||||
@Component("xywjclwyhListener")
|
||||
@Slf4j
|
||||
public class XYWJCLWYHListener implements ExecutionListener {
|
||||
|
||||
// 推荐:使用依赖注入,而不是SpringUtils
|
||||
@Autowired
|
||||
private DisciplinaryMapper disciplinaryMapper;
|
||||
|
||||
@Autowired
|
||||
private WeChatUtil weChatUtil;
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
DisciplinaryMapper disciplinaryMapper = SpringUtils.getBean("disciplinaryMapper");
|
||||
// 步骤 1: 根据角色Key获取审批人ID列表 (第1次DB查询)
|
||||
public void notify(DelegateExecution execution) {
|
||||
log.info("流程实例 [{}]: 在进入'学院违纪处理委员会'节点前,开始准备审批数据...", execution.getProcessInstanceId());
|
||||
|
||||
// 步骤 1: 单一数据库查询,获取所有需要的审批人信息 (ID和企微账号)
|
||||
// List<DisciplinaryMapper.UserInfo> userInfos = disciplinaryMapper.getUserInfoByRoleKey("xywjclwyh");
|
||||
// 注意:如果您不方便修改Mapper,这里可以保持原来的两次查询,逻辑依然正确。
|
||||
// 我们先用原始的两次查询来演示,更具普适性。
|
||||
|
||||
List<Long> userIdList = disciplinaryMapper.getApprovalByRoleKey("xywjclwyh");
|
||||
if (userIdList == null || userIdList.isEmpty()){
|
||||
log.error("未找到角色相关信息");
|
||||
|
||||
if (userIdList == null || userIdList.isEmpty()) {
|
||||
log.error("角色 'xywjclwyh' 未配置或未找到任何有效用户,流程无法继续。");
|
||||
throw new RuntimeException("学院违纪处理委员会审批人员未设置");
|
||||
}else{
|
||||
delegateExecution.setVariable("userList",userIdList);
|
||||
// todo 企业微信推送消息
|
||||
// --- 企业微信推送消息模块 ---
|
||||
try {
|
||||
// 步骤 2: 一次性批量查询所有有效的userName (第2次DB查询,性能最优)
|
||||
List<String> userNameList = disciplinaryMapper.getUserNamesByUserIdList(userIdList);
|
||||
|
||||
if (!userNameList.isEmpty()) {
|
||||
// 步骤 3: 将 userName 列表用 "|" 连接成一个字符串
|
||||
String toUser = String.join("|", userNameList);
|
||||
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
String content = "您有一条新的学生违纪审批任务待处理,<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>请点击前往处理</a>。";
|
||||
|
||||
weChatUtil.sendTextMessage(toUser, content);
|
||||
log.info("已成功向学院违纪处理委员会发送群组通知。接收人: {}", toUser);
|
||||
} else {
|
||||
log.warn("角色 'xywjclwyh' 存在审批人,但无人配置企业微信账号,未发送任何通知。");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 保证即使通知失败,流程也能继续
|
||||
log.error("向学院违纪处理委员会发送企业微信通知时出现异常。流程将继续。错误详情: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤 2: 设置多实例任务需要的流程变量 (这是“线上”为“节点”准备的核心数据)
|
||||
execution.setVariable("userList", userIdList);
|
||||
log.info("流程实例 [{}]: 已成功设置'userList'变量,包含 {} 个审批人。", execution.getProcessInstanceId(), userIdList.size());
|
||||
|
||||
// 步骤 3: 发送企业微信通知
|
||||
sendWeChatNotification(userIdList, execution.getProcessInstanceId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装并执行企业微信通知逻辑
|
||||
*/
|
||||
private void sendWeChatNotification(List<Long> userIdList, String processInstanceId) {
|
||||
try {
|
||||
List<String> userNameList = disciplinaryMapper.getUserNamesByUserIdList(userIdList);
|
||||
if (userNameList == null || userNameList.isEmpty()) {
|
||||
log.warn("流程实例 [{}]: 角色'xywjclwyh'存在审批人ID,但无人配置企微账号,未发送通知。", processInstanceId);
|
||||
return;
|
||||
}
|
||||
|
||||
String toUser = String.join("|", userNameList);
|
||||
String content = "您有一条新的学生违纪审批任务待处理,<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>请点击前往处理</a>。";
|
||||
|
||||
weChatUtil.sendTextMessage(toUser, content);
|
||||
log.info("流程实例 [{}]: 已成功向学院违纪处理委员会发送通知。接收人: {}", processInstanceId, toUser);
|
||||
} catch (Exception e) {
|
||||
log.error("流程实例 [{}]: 发送企微通知时出现异常,但不影响主流程。错误: {}", processInstanceId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,11 +1,13 @@
|
||||
package com.srs.flowable.listener.relieve;
|
||||
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.common.utils.spring.SpringUtils;
|
||||
import com.srs.flowable.mapper.LeaveMapper;
|
||||
import com.srs.flowable.mapper.RelieveMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.delegate.DelegateExecution;
|
||||
import org.flowable.engine.delegate.ExecutionListener;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -17,6 +19,9 @@ import java.util.Map;
|
||||
@Slf4j
|
||||
public class StuCounselorListener implements ExecutionListener {
|
||||
|
||||
@Autowired
|
||||
private WeChatUtil weChatUtil;
|
||||
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
RelieveMapper relieveMapper = (RelieveMapper) SpringUtils.getBean("relieveMapper");
|
||||
@@ -31,6 +36,36 @@ public class StuCounselorListener implements ExecutionListener {
|
||||
delegateExecution.setVariable("deptId",map.get("deptId"));
|
||||
|
||||
// todo 企业微信推送消息
|
||||
/**
|
||||
* 庞世斌
|
||||
*/
|
||||
try {
|
||||
Long userId = map.get("userId");
|
||||
|
||||
// 使用 userId 查询对应的企业微信账号
|
||||
String userName = relieveMapper.getUserNameByUserId(userId);
|
||||
|
||||
if (userName != null && !userName.isEmpty()) {
|
||||
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||
|
||||
// 构造包含超链接的消息内容
|
||||
String content = "您有一条新的学生违纪审批任务待处理," +
|
||||
"<a href='http://zhxg.gxsdxy.cn/web/#/pages/Approval/index'>点此查看</a>";
|
||||
|
||||
// 发送企业微信消息
|
||||
weChatUtil.sendTextMessage(userName, content);
|
||||
log.info("✅ 已成功向学务干事(userName:{})发送企业微信审批通知。", userName);
|
||||
|
||||
} else {
|
||||
// 如果找不到 userName,记录警告日志,但流程继续
|
||||
log.warn("⚠ 找到了审批人(userId:{}), 但其对应的企业微信账号(userName)为空,无法发送通知。", userId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Long userId = map.get("userId"); // 确保日志里带上 userId
|
||||
log.error("❌ 向学务干事(userId:{})发送企业微信通知时出现异常,但流程将继续。错误详情:", userId, e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}else {
|
||||
throw new RuntimeException("获取辅导员信息失败");
|
||||
|
@@ -23,7 +23,7 @@ public interface DisciplinaryMapper {
|
||||
*/
|
||||
String getUserNameByUserId(@Param("userId") Long userId);
|
||||
/**
|
||||
* 知无涯新增:根据用户ID列表批量查询用户名列表
|
||||
* 知无涯 根据用户ID列表批量查询用户名列表
|
||||
* @param userIdList 用户ID列表
|
||||
* @return 用户的username列表
|
||||
*/
|
||||
|
@@ -11,6 +11,10 @@ public interface RelieveMapper {
|
||||
|
||||
Map<String,Long> getCounselorInfo(Long stuUserId);
|
||||
|
||||
|
||||
|
||||
String getUserNameByUserId ( long userId);
|
||||
|
||||
/**
|
||||
* 查询学生解除处分申请
|
||||
*
|
||||
|
@@ -113,6 +113,12 @@
|
||||
<include refid="selectRtStuDisciplinaryRelieveVo"/>
|
||||
where relieve_id = #{relieveId}
|
||||
</select>
|
||||
<!-- 根据userid获取到userName-->
|
||||
<select id="getUserNameByUserId" parameterType="long" resultType="string">
|
||||
SELECT user_name
|
||||
FROM sys_user
|
||||
WHERE user_id = #{userId}
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
@@ -209,7 +209,7 @@ public class TokenService
|
||||
* @param request
|
||||
* @return token
|
||||
*/
|
||||
private String getToken(HttpServletRequest request)
|
||||
public String getToken(HttpServletRequest request)
|
||||
{
|
||||
String token = request.getHeader(header);
|
||||
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
|
||||
|
@@ -222,6 +222,14 @@ public class RtStuDisciplinaryApplication extends BaseEntity {
|
||||
@Excel(name = "籍贯")
|
||||
private String jg;
|
||||
|
||||
/**
|
||||
* 政治面貌
|
||||
*/
|
||||
@ApiModelProperty("政治面貌")
|
||||
@TableField("political_status")
|
||||
@Excel(name = "政治面貌")
|
||||
private String politicalStatus;
|
||||
|
||||
/**
|
||||
* 市/县
|
||||
*/
|
||||
|
@@ -214,6 +214,14 @@ public class RtStuDisciplinaryRelieve extends BaseEntity {
|
||||
@Excel(name = "籍贯")
|
||||
private String jg;
|
||||
|
||||
/**
|
||||
* 政治面貌
|
||||
*/
|
||||
@ApiModelProperty("政治面貌")
|
||||
@TableField("political_status")
|
||||
@Excel(name = "政治面貌")
|
||||
private String politicalStatus;
|
||||
|
||||
/**
|
||||
* 市/县
|
||||
*/
|
||||
|
@@ -265,7 +265,7 @@ public class RtStuDisciplinaryApplicationServiceImpl extends ServiceImpl<RtStuDi
|
||||
|
||||
// todo 企业微信推送消息
|
||||
|
||||
AjaxResult ajaxResult = flowDefinitionService.startProcessInstanceById("flow_n27gxm4k:8:610039", variables);
|
||||
AjaxResult ajaxResult = flowDefinitionService.startProcessInstanceById("flow_n27gxm4k:22:720004", variables);
|
||||
String code = ajaxResult.get("code").toString();
|
||||
if (code.equals("200")) {
|
||||
|
||||
|
@@ -3,16 +3,20 @@ package com.srs.routine.service.impl;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.srs.common.core.domain.AjaxResult;
|
||||
import com.srs.common.core.domain.entity.SysUser;
|
||||
import com.srs.common.doman.dto.ProcessResultDto;
|
||||
import com.srs.common.exception.ServiceException;
|
||||
import com.srs.common.utils.DateUtils;
|
||||
import com.srs.common.utils.SecurityUtils;
|
||||
import com.srs.common.utils.WeChatUtil;
|
||||
import com.srs.flowable.service.IFlowDefinitionService;
|
||||
import com.srs.routine.domain.RtStuDisciplinaryApplication;
|
||||
import com.srs.routine.mapper.RtStuDisciplinaryApplicationMapper;
|
||||
import com.srs.system.mapper.SysUserMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.IdentityService;
|
||||
import org.flowable.engine.TaskService;
|
||||
@@ -40,6 +44,12 @@ public class RtStuDisciplinaryRelieveServiceImpl extends ServiceImpl<RtStuDiscip
|
||||
@Autowired
|
||||
RtStuDisciplinaryApplicationMapper rtStuDisciplinaryApplicationMapper;
|
||||
|
||||
@Autowired
|
||||
private SysUserMapper sysUserMapper;
|
||||
|
||||
@Autowired
|
||||
public WeChatUtil weChatUtil;
|
||||
|
||||
/**
|
||||
* 查询学生解除处分申请
|
||||
*
|
||||
@@ -208,12 +218,49 @@ public class RtStuDisciplinaryRelieveServiceImpl extends ServiceImpl<RtStuDiscip
|
||||
// 保存审核结果到任务变量中
|
||||
variables.put("approved", true);
|
||||
|
||||
// 标记是否有任务被完成
|
||||
boolean taskCompleted = false;
|
||||
|
||||
// 完成待办任务列表
|
||||
for (Task task : tasks) {
|
||||
String taskId = task.getId();
|
||||
String applicationId = taskService.getVariable(taskId, "relieveId").toString();
|
||||
if (applicationId.equals(rtStuDisciplinaryRelieve.getRelieveId().toString())) {
|
||||
taskService.complete(task.getId(), variables);
|
||||
taskCompleted = true;
|
||||
}
|
||||
}
|
||||
// 所有相关任务完成后,发送企业微信消息
|
||||
if (taskCompleted) {
|
||||
try {
|
||||
// 获取申请人信息
|
||||
String applicantName = rtStuDisciplinaryRelieve.getStuName();
|
||||
String politicalStatus = rtStuDisciplinaryRelieve.getPoliticalStatus();
|
||||
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setRoleId(118L);
|
||||
List<SysUser> sysUsers = sysUserMapper.selectAllocatedList(sysUser);
|
||||
|
||||
// 提取二级学院的书记的账号
|
||||
List<String> userNames = sysUsers.stream()
|
||||
.map(SysUser::getUserName)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 只有政治面貌是团员时才发送企业微信消息
|
||||
if ("团员".equals(politicalStatus)) {
|
||||
// 消息内容
|
||||
String messageContent = "【解除处分】" + applicantName + "的解除处分申请已通过审核。";
|
||||
int batchSize = 10;
|
||||
for (int i = 0; i < userNames.size(); i += batchSize) {
|
||||
List<String> batch = userNames.subList(i, Math.min(i + batchSize, userNames.size()));
|
||||
// 拼接成"user1|user2|user3"格式
|
||||
String toUser = String.join("|", batch);
|
||||
// 调用企业微信发送消息方法
|
||||
weChatUtil.sendTextMessage(toUser, messageContent);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("发送企业微信消息失败:", e);
|
||||
}
|
||||
}
|
||||
return dto;
|
||||
|
@@ -117,7 +117,7 @@ public class RtStuMultiLevelReviewServiceImpl extends ServiceImpl<RtStuMultiLeve
|
||||
if (result > 0) {
|
||||
String messageContent = rtStuMultiLevelReview.getNotes();
|
||||
if (messageContent == null || messageContent.trim().isEmpty()) {
|
||||
messageContent = "你申请办理的学生证制作完成,长堽校区前往xxx领取,里建校区前往xxx领取";
|
||||
messageContent = "你申请办理的学生证制作完成,长堽校区前往经管楼学工处1-1办公室领取,里建校区前往“一站式”学生社区大厅领取";
|
||||
}
|
||||
|
||||
weChatUtil.sendTextMessage(rtStuMultiLevelReview.getStuNo(), messageContent);
|
||||
|
@@ -34,6 +34,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="mz" column="mz" />
|
||||
<result property="birthday" column="birthday" />
|
||||
<result property="jg" column="jg" />
|
||||
<result property="politicalStatus" column="political_status" />
|
||||
<result property="hksz2" column="hksz2" />
|
||||
<result property="dispositionService" column="disposition_service" />
|
||||
<result property="letterService" column="letter_service" />
|
||||
@@ -44,7 +45,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
select application_id, applicant_id, applicant_name, penalty_number, stu_no, stu_name, stu_id, penalty_type,
|
||||
penalty_status, violation_date, expiration_date, evidence_upload, penalty_recommendation, violation_regulations,
|
||||
submission_status, process_instance_id, deploy_id, create_by, create_time, update_by, update_time, remark, gender,
|
||||
department_Name, grade_name, class_name, mz, birthday, jg, hksz2,disposition_service,letter_service,disciplinary_date from rt_stu_disciplinary_application
|
||||
department_Name, grade_name, class_name, mz, birthday, jg,political_status,hksz2,disposition_service,letter_service,disciplinary_date from rt_stu_disciplinary_application
|
||||
</sql>
|
||||
|
||||
<select id="selectRtStuDisciplinaryApplicationList" parameterType="RtStuDisciplinaryApplication" resultMap="RtStuDisciplinaryApplicationResult">
|
||||
@@ -73,6 +74,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="mz != null and mz != ''"> and mz = #{mz}</if>
|
||||
<if test="birthday != null and birthday != ''"> and birthday = #{birthday}</if>
|
||||
<if test="jg != null and jg != ''"> and jg = #{jg}</if>
|
||||
<if test="politicalStatus != null and politicalStatus != ''"> and political_status = #{politicalStatus}</if>
|
||||
<if test="hksz2 != null and hksz2 != ''"> and hksz2 = #{hksz2}</if>
|
||||
</where>
|
||||
ORDER BY create_time desc
|
||||
@@ -116,7 +118,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
select application_id, applicant_id, applicant_name, penalty_number, stu_no, stu_name, stu_id, penalty_type,penalty_status,
|
||||
violation_date, expiration_date, evidence_upload, penalty_recommendation, violation_regulations,submission_status,
|
||||
process_instance_id, deploy_id, a.create_by, a.create_time, a.update_by, a.update_time, a.remark, a.gender,a.department_Name,
|
||||
a.grade_name, a.class_name, a.mz, a.birthday, a.jg, a.hksz2,a.disposition_service,a.letter_service,a.disciplinary_date from rt_stu_disciplinary_application a
|
||||
a.grade_name, a.class_name, a.mz, a.birthday, a.jg,a.political_status,a.hksz2,a.disposition_service,a.letter_service,a.disciplinary_date from rt_stu_disciplinary_application a
|
||||
LEFT JOIN sys_user b ON a.applicant_id = b.user_id
|
||||
LEFT JOIN cph_teacher c ON b.user_name = c.employee_id
|
||||
<where>
|
||||
@@ -175,6 +177,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="mz != null">mz,</if>
|
||||
<if test="birthday != null">birthday,</if>
|
||||
<if test="jg != null">jg,</if>
|
||||
<if test="politicalStatus != null">political_status,</if>
|
||||
<if test="hksz2 != null">hksz2,</if>
|
||||
<if test="dispositionService != null">disposition_service,</if>
|
||||
<if test="letterService != null">letter_service,</if>
|
||||
@@ -208,6 +211,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="mz != null">#{mz},</if>
|
||||
<if test="birthday != null">#{birthday},</if>
|
||||
<if test="jg != null">#{jg},</if>
|
||||
<if test="politicalStatus != null">#{politicalStatus},</if>
|
||||
<if test="hksz2 != null">#{hksz2},</if>
|
||||
<if test="dispositionService != null">#{dispositionService},</if>
|
||||
<if test="letterService != null">#{letterService},</if>
|
||||
@@ -245,6 +249,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="mz != null">mz = #{mz},</if>
|
||||
<if test="birthday != null">birthday = #{birthday},</if>
|
||||
<if test="jg != null">jg = #{jg},</if>
|
||||
<if test="politicalStatus != null">political_status = #{politicalStatus},</if>
|
||||
<if test="hksz2 != null">hksz2 = #{hksz2},</if>
|
||||
<if test="dispositionService != null">disposition_service = #{dispositionService},</if>
|
||||
<if test="letterService != null">letter_service = #{letterService},</if>
|
||||
|
@@ -33,6 +33,7 @@
|
||||
<result property="mz" column="mz"/>
|
||||
<result property="birthday" column="birthday"/>
|
||||
<result property="jg" column="jg"/>
|
||||
<result property="politicalStatus" column="political_status" />
|
||||
<result property="hksz2" column="hksz2"/>
|
||||
<result property="relieveService" column="relieve_service"/>
|
||||
</resultMap>
|
||||
@@ -65,6 +66,7 @@
|
||||
mz,
|
||||
birthday,
|
||||
jg,
|
||||
political_status,
|
||||
hksz2,
|
||||
relieve_service,
|
||||
applicant_name
|
||||
@@ -107,6 +109,7 @@
|
||||
<if test="mz != null and mz != ''">and mz = #{mz}</if>
|
||||
<if test="birthday != null and birthday != ''">and birthday = #{birthday}</if>
|
||||
<if test="jg != null and jg != ''">and jg = #{jg}</if>
|
||||
<if test="politicalStatus != null and politicalStatus != ''">and political_status = #{politicalStatus}</if>
|
||||
<if test="hksz2 != null and hksz2 != ''">and hksz2 = #{hksz2}</if>
|
||||
<if test="relieveService != null and relieveService != ''">and relieve_service = #{relieveService}</if>
|
||||
</where>
|
||||
@@ -147,6 +150,7 @@
|
||||
a.mz,
|
||||
a.birthday,
|
||||
a.jg,
|
||||
a.political_status,
|
||||
a.hksz2,
|
||||
a.relieve_service,
|
||||
a.applicant_name
|
||||
@@ -187,6 +191,7 @@
|
||||
<if test="mz != null and mz != ''">and a.mz = #{mz}</if>
|
||||
<if test="birthday != null and birthday != ''">and a.birthday = #{birthday}</if>
|
||||
<if test="jg != null and jg != ''">and a.jg = #{jg}</if>
|
||||
<if test="politicalStatus != null and politicalStatus != ''">and a.political_status = #{politicalStatus}</if>
|
||||
<if test="hksz2 != null and hksz2 != ''">and a.hksz2 = #{hksz2}</if>
|
||||
<if test="relieveService != null and relieveService != ''">and a.relieve_service = #{relieveService}</if>
|
||||
<if test="params.employeeId != null and params.employeeId != ''">and d.employee_id = #{params.employeeId}</if>
|
||||
@@ -222,6 +227,7 @@
|
||||
a.mz,
|
||||
a.birthday,
|
||||
a.jg,
|
||||
a.political_status,
|
||||
a.hksz2,
|
||||
a.relieve_service,
|
||||
a.applicant_name
|
||||
@@ -262,6 +268,7 @@
|
||||
<if test="mz != null and mz != ''">and a.mz = #{mz}</if>
|
||||
<if test="birthday != null and birthday != ''">and a.birthday = #{birthday}</if>
|
||||
<if test="jg != null and jg != ''">and a.jg = #{jg}</if>
|
||||
<if test="politicalStatus != null and politicalStatus != ''">and a.political_status = #{politicalStatus}</if>
|
||||
<if test="hksz2 != null and hksz2 != ''">and a.hksz2 = #{hksz2}</if>
|
||||
<if test="relieveService != null and relieveService != ''">and a.relieve_service = #{relieveService}</if>
|
||||
<if test="params.employeeId != null and params.employeeId != ''">and d.employee_id = #{params.employeeId}</if>
|
||||
@@ -300,6 +307,7 @@
|
||||
<if test="mz != null">mz,</if>
|
||||
<if test="birthday != null">birthday,</if>
|
||||
<if test="jg != null">jg,</if>
|
||||
<if test="politicalStatus != null">political_status,</if>
|
||||
<if test="hksz2 != null">hksz2,</if>
|
||||
<if test="relieveService != null">relieve_service,</if>
|
||||
<if test="applicantName != null">applicant_name,</if>
|
||||
@@ -331,6 +339,7 @@
|
||||
<if test="mz != null">#{mz},</if>
|
||||
<if test="birthday != null">#{birthday},</if>
|
||||
<if test="jg != null">#{jg},</if>
|
||||
<if test="politicalStatus != null">#{politicalStatus},</if>
|
||||
<if test="hksz2 != null">#{hksz2},</if>
|
||||
<if test="relieveService != null">#{relieveService},</if>
|
||||
<if test="applicantName != null">#{applicantName},</if>
|
||||
@@ -366,6 +375,7 @@
|
||||
<if test="mz != null">mz = #{mz},</if>
|
||||
<if test="birthday != null">birthday = #{birthday},</if>
|
||||
<if test="jg != null">jg = #{jg},</if>
|
||||
<if test="politicalStatus != null">political_status = #{politicalStatus},</if>
|
||||
<if test="hksz2 != null">hksz2 = #{hksz2},</if>
|
||||
<if test="relieveService != null">relieve_service = #{relieveService},</if>
|
||||
<if test="applicantName != null">applicant_name = #{applicantName},</if>
|
||||
|
@@ -4,6 +4,7 @@ import com.srs.system.domain.StudentMentalRating;
|
||||
import com.srs.system.domain.vo.StudentMentalRatingVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface StudentMentalRatingMapper {
|
||||
@@ -47,5 +48,13 @@ public interface StudentMentalRatingMapper {
|
||||
/** 单学号全部记录 */
|
||||
List<StudentMentalRating> selectByStuNo(@Param("stuNo") String stuNo);
|
||||
|
||||
/**
|
||||
* 查询今天的心理评级记录 陈冠元
|
||||
* @param studentId 学生ID
|
||||
* @param today 今天的日期
|
||||
* @return 记录数
|
||||
*/
|
||||
int countTodayByStudentId(@Param("studentId") String studentId, @Param("today") LocalDate today);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -94,4 +94,12 @@
|
||||
WHERE student_id = #{stuNo}
|
||||
ORDER BY created_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 查询今天该学生是否已经填写过心理评测 陈冠元 -->
|
||||
<select id="countTodayByStudentId" resultType="int">
|
||||
SELECT COUNT(*)
|
||||
FROM student_mental_rating
|
||||
WHERE student_id = #{studentId}
|
||||
AND DATE(created_time) = #{today}
|
||||
</select>
|
||||
</mapper>
|
Reference in New Issue
Block a user