This commit is contained in:
2026-03-20 11:09:40 +08:00
parent 48b797eb64
commit 0510979604
5 changed files with 465 additions and 0 deletions
@@ -0,0 +1,66 @@
package com.budwk.app.todo.service;
import com.budwk.app.flow.entity.ProcessTask;
/**
* @ClassName TodoService
* @Author JyuHsin
* @Date 2026/3/12 14:12
* @Version 1.0
* @Description TODO
*/
public interface TodoService {
/**
* 调用本接口,发起一个待办任务
* 若是首个任务,则会同时创建一条事务
* 若非首个任务,则需关联上一条待办任务
*
* @param task
*/
void addTransaction(ProcessTask task);
/**
* 调用本接口,对当前节点的任务进行办理,当前任务进入已办
*
* @param task
*/
void taskHandle(ProcessTask task);
/**
* 调用本接口,发起任务转办,转办给转办人(可以多个),被转办人可以在我的代办列表查看任务
*
* @param task
*/
void taskComplaint(ProcessTask task);
/**
* 调用本接口,对当前节点的任务进行撤回,并创建出新的任务节点
* 建议不调用本接口,因为会创建新的任务节点
* 如果撤回,可调用taskTerminate,这个接口只操作本任务
* @param task
*/
void taskWithdraw(ProcessTask task);
/**
* 调用本接口,发起任务终止,结束该任务
*
* @param task
*/
void taskTerminate(ProcessTask task);
/**
* 调用本接口,对删除任务的事务进行恢复
*
* @param task
*/
void transRecover(ProcessTask task);
/**
* 调用本接口,对当前任务的事务进行删除
*
* @param task
*/
void deleteProcessInstance(ProcessTask task);
}
@@ -0,0 +1,248 @@
package com.budwk.app.todo.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.entity.ProcessDefine;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.entity.ProcessTaskActor;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.todo.service.TodoService;
import com.budwk.app.todo.util.TodoPlatformTokenUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.web.commons.base.Globals;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 南昌大学统一待办平台接口实现
* @author JyuHsin
*/
@Slf4j
@IocBean
public class TodoServiceImpl implements TodoService {
@Inject
private Dao dao;
@Inject
private RedisService redisService;
@Inject
private FlowEngine flowEngine;
@Inject
private TodoPlatformTokenUtil tokenUtil;
private static final String BASE_URL = "https://poa.ncu.edu.cn/apis/ttc/v1/transaction/poa/";
private static final String APP_ID = "72e34a101d3011f1a74cbae5193504f4";
private static final int SUCCESS_CODE = 0;
private interface ApiUrl {
String ADD_TRANSACTION = BASE_URL + "addTransaction";
String TASK_HANDLE = BASE_URL + "taskHandle";
String TASK_COMPLAINT = BASE_URL + "taskComplaint";
String TASK_WITHDRAW = BASE_URL + "taskWithdraw";
String TASK_TERMINATE = BASE_URL + "taskTerminate";
String TRANS_RECOVER = BASE_URL + "transRecover";
String TRANS_DELETED = BASE_URL + "transDeleted";
}
/**
* 统一发送POST请求
*/
private JSONObject sendPost(String url, Map<String, Object> params) {
try {
if(!Globals.sso) {
log.error("【待办平台】本地开发模式不允许调用待办");
return null;
}
if(!Globals.MyConfig.getBoolean("AppTodo", false)) {
log.error("【待办平台】待办配置未开启");
return null;
}
String token = tokenUtil.getToken();
log.info("【待办平台】调用接口:{},请求参数:{}", url, Json.toJson(params));
String body = HttpUtil.createPost(url)
.header("Authorization", token)
.header("Content-Type", "application/json")
.body(Json.toJson(List.of(params)))
.execute()
.body();
log.info("【待办平台】接口响应:{}", body);
return JSONUtil.parseObj(body);
} catch (Exception e) {
log.error("【待办平台】接口调用异常:{}", e.getMessage(), e);
return null;
}
}
/**
* 统一处理响应结果
*/
private void handleResult(JSONObject result, String apiName) {
if (result == null) {
log.error("【待办平台】{} 调用失败:接口响应为空", apiName);
return;
}
if (result.getInt("code") == SUCCESS_CODE) {
log.info("【待办平台】{} 调用成功", apiName);
} else {
log.error("【待办平台】{} 调用失败:{}", apiName, result.getStr("message"));
}
}
/**
* 获取处理人字符串
*/
private String getHandlers(Long taskId) {
List<ProcessTaskActor> list = dao.query(ProcessTaskActor.class,
Cnd.where(ProcessTaskActor::getProcessTaskId, "=", taskId));
return list.stream()
.map(ProcessTaskActor::getActorAccount)
.collect(Collectors.joining(","));
}
@Override
public void addTransaction(ProcessTask task) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
ProcessDefine define = dao.fetch(ProcessDefine.class, instance.getProcessDefineId());
Sys_user sysUser = dao.fetch(Sys_user.class, instance.getOperator());
ProcessTask parentTask = dao.fetch(ProcessTask.class, task.getTaskParentId());
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("nodeId", task.getId());
params.put("nodeName", task.getDisplayName());
params.put("taskId", task.getId());
params.put("taskName", task.getDisplayName());
params.put("transTypeCode", define.getName());
params.put("transTypeName", define.getDisplayName());
params.put("handlers", getHandlers(task.getId()));
if (!ObjectUtil.equals(0, parentTask.getTaskParentId().intValue())) {
params.put("previousTaskId", task.getTaskParentId());
}
params.put("canDelegate", 0);
params.put("autoCreateTransType", true);
params.put("initiator", sysUser.getLoginname());
params.put("initiatorName", sysUser.getUsername());
params.put("serialNumber", task.getId());
if(StrUtil.isNotBlank(task.getFormKey())) {
params.put("handleUrl", Globals.AppDomain + task.getFormKey());
params.put("viewUrl", Globals.AppDomain + task.getFormKey());
}
if(StrUtil.isNotBlank(task.getH5FormKey())) {
params.put("mHandleUrl", Globals.AppDomain + task.getH5FormKey());
params.put("mViewUrl", Globals.AppDomain + task.getH5FormKey());
}
JSONObject result = sendPost(ApiUrl.ADD_TRANSACTION, params);
handleResult(result, "创建待办任务");
}
@Override
public void taskHandle(ProcessTask task) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
String status = instance.getState().equals(ProcessInstanceStateEnum.FINISHED.getCode()) ? "end" : "none";
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("handler", SecurityUtil.getUserLoginname());
params.put("previousTaskId", task.getId());
params.put("status", status);
JSONObject result = sendPost(ApiUrl.TASK_HANDLE, params);
handleResult(result, "办理待办任务");
}
@Override
public void taskComplaint(ProcessTask task) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
Sys_user sysUser = dao.fetch(Sys_user.class, instance.getOperator());
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("createTime", "");
params.put("creater", sysUser.getLoginname());
params.put("creator", sysUser.getLoginname());
params.put("handlers", getHandlers(task.getId()));
params.put("handlingGroup", "");
params.put("operator", sysUser.getLoginname());
params.put("taskId", task.getId());
JSONObject result = sendPost(ApiUrl.TASK_COMPLAINT, params);
handleResult(result, "转办待办任务");
}
@Override
public void taskWithdraw(ProcessTask task) {
// 已注释逻辑保持不变
}
@Override
public void taskTerminate(ProcessTask task) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
Sys_user sysUser = dao.fetch(Sys_user.class, instance.getOperator());
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("creater", sysUser.getLoginname());
params.put("creator", sysUser.getLoginname());
params.put("handlers", getHandlers(task.getId()));
params.put("operator", SecurityUtil.getUserLoginname());
params.put("taskId", task.getId());
JSONObject result = sendPost(ApiUrl.TASK_TERMINATE, params);
handleResult(result, "终止待办任务");
}
@Override
public void transRecover(ProcessTask task) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
Sys_user sysUser = dao.fetch(Sys_user.class, instance.getOperator());
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("creater", sysUser.getLoginname());
params.put("taskId", task.getId());
JSONObject result = sendPost(ApiUrl.TRANS_RECOVER, params);
handleResult(result, "事务恢复");
}
@Override
public void deleteProcessInstance(ProcessTask task) {
ProcessInstance instance = dao.fetch(ProcessInstance.class, task.getProcessInstanceId());
Sys_user sysUser = dao.fetch(Sys_user.class, instance.getOperator());
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("creater", sysUser.getLoginname());
params.put("creator", sysUser.getLoginname());
params.put("taskId", task.getId());
JSONObject result = sendPost(ApiUrl.TRANS_DELETED, params);
handleResult(result, "事务删除");
}
}
@@ -0,0 +1,62 @@
package com.budwk.app.todo.util;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RedisConstant;
import lombok.extern.slf4j.Slf4j;
import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName TodoPlatformUtil
* @Author JyuHsin
* @Date 2026/3/12 14:05
* @Version 1.0
* @Description TODO
*/
@Slf4j
@IocBean
public class TodoPlatformTokenUtil {
@Inject
private RedisService redisService;
private static final String TOKEN_URL = "https://poa.ncu.edu.cn/oauth2/token";
private static final String CLINT_ID = "dP7SXC1MoYAJHPh6k8Cgsda8tGY=";
private static final String CLINT_SECRET = "PFAwVcXOsVFzP3Jwb4RyO5V97o8j8zNTQ8QSTre8oIc=";
private static final String SCOPE = "ttc:v1:writeTransaction";
public String getToken() {
String key = RedisConstant.REDIS_TODO_TOKEN_KEY;
String token = redisService.get(key);
if (StrUtil.isBlank(token)) {
// 构造表单参数
Map<String, String> param = new HashMap<>();
param.put("grant_type", "client_credentials");
param.put("client_id", CLINT_ID);
param.put("client_secret", CLINT_SECRET);
param.put("scope", SCOPE);
String body = HttpUtil.createPost(TOKEN_URL)
.header("Content-Type", "application/x-www-form-urlencoded")
.formStr(param).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】获取待办token接口回调数据:{}", Json.toJson(resultJsonBody));
if (StrUtil.isNotBlank(resultJsonBody.getStr("access_token"))) {
String accessToken = resultJsonBody.getStr("access_token");
Integer expiresIn = resultJsonBody.getInt("expires_in");
redisService.setex(key, expiresIn, accessToken);
return accessToken;
}
}
return token;
}
}
@@ -0,0 +1,87 @@
package com.budwk.app.zhgh.dayofficework.message.strategy.impl;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.message.enums.GlobalMessageChannel;
import com.budwk.app.zhgh.dayofficework.message.strategy.GlobalMessageSendStrategy;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import java.util.List;
/**
* @ClassName WechatEnterpriseMessageSendStrategy
* @Author JyuHsin
* @Date 2026/2/27 15:42
* @Version 1.0
* @Description TODO
*/
@IocBean
@Slf4j
public class WechatEnterpriseMessageSendStrategy implements GlobalMessageSendStrategy {
@Inject
private Dao dao;
@Inject
private SmsService smsService;
@Override
public GlobalMessageChannel getChannel() {
return GlobalMessageChannel.WECHAT_ENTERPRISE;
}
@Override
public boolean supportAsync() {
return GlobalMessageSendStrategy.super.supportAsync();
}
@Override
public boolean isEnabled() {
return false;
}
@Override
public void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
try {
log.info("开始发送企业微信消息,标题:{},接收人数量:{}", title, receiverIds.size());
// 查询用户手机号
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getId, "in", receiverIds));
sendMessage(title, content, type, users, config);
log.info("企业微信消息发送完成,标题:{},接收人数量:{}", title, users.size());
} catch (Exception e) {
log.error("企业微信消息发送失败,标题:{},错误:{}", title, e.getMessage(), e);
throw new RuntimeException("企业微信消息发送失败:" + e.getMessage(), e);
}
}
@Override
public void sendByLoginName(String title, String content, Integer type, List<String> receiverLoginNames, JSONObject config) {
try {
log.info("开始发送企业微信消息(按登录名),标题:{},接收人数量:{}", title, receiverLoginNames.size());
// 查询用户手机号
List<Sys_user> users = dao.query(Sys_user.class, Cnd.where(Sys_user::getLoginname, "in", receiverLoginNames));
sendMessage(title, content, type, users, config);
log.info("企业微信消息发送完成(按登录名),标题:{},接收人数量:{}", title, users.size());
} catch (Exception e) {
log.error("企业微信消息发送失败(按登录名),标题:{},错误:{}", title, e.getMessage(), e);
throw new RuntimeException("企业微信消息发送失败:" + e.getMessage(), e);
}
}
private void sendMessage(String title, String content, Integer type, List<Sys_user> users, JSONObject config) {
if(Lang.isEmpty(users)) {
return;
}
List<String> list = users.stream().map(Sys_user::getLoginname).toList();
smsService.massSendTextCard(list, "智慧工会", content, config.getStr("pcUrl"), config.getStr("mobileUrl"));
}
}
File diff suppressed because one or more lines are too long