This commit is contained in:
=
2026-03-19 15:20:03 +08:00
43 changed files with 1534 additions and 1162 deletions
@@ -32,6 +32,8 @@ public class RedisConstant {
// 短信token // 短信token
public final static String REDIS_SMS_TOKEN_KEY = PLATFORM_REDIS_PREFIX + "platfrom:sms:token:"; public final static String REDIS_SMS_TOKEN_KEY = PLATFORM_REDIS_PREFIX + "platfrom:sms:token:";
public final static String REDIS_TODO_TOKEN_KEY = PLATFORM_REDIS_PREFIX + "platfrom:todo:token:";
public final static String REDIS_KEY_API_SIGN_DEPLOY_NONCE = PLATFORM_REDIS_PREFIX + "api:sign:deploy:nonce:"; public final static String REDIS_KEY_API_SIGN_DEPLOY_NONCE = PLATFORM_REDIS_PREFIX + "api:sign:deploy:nonce:";
public final static String REDIS_KEY_API_SIGN_OPEN_NONCE = PLATFORM_REDIS_PREFIX + "api:sign:open:nonce:"; public final static String REDIS_KEY_API_SIGN_OPEN_NONCE = PLATFORM_REDIS_PREFIX + "api:sign:open:nonce:";
@@ -66,6 +66,7 @@ RoleConstant {
PROPOSAL_BRANCH_SCHOOL_LEADER("提案分管校领导"), PROPOSAL_BRANCH_SCHOOL_LEADER("提案分管校领导"),
PROPOSAL_UNIT_LEADER("提案承办单位领导"), PROPOSAL_UNIT_LEADER("提案承办单位领导"),
PROPOSAL_UNIT_PROXY("提案承办单位代理答复人"), PROPOSAL_UNIT_PROXY("提案承办单位代理答复人"),
PROPOSAL_PERSONNEL_OFFICE_ADMIN("提案人事处管理员"),
WORKER_CONGRESS_DELEGATE_FORMAL("工代会正式代表"), WORKER_CONGRESS_DELEGATE_FORMAL("工代会正式代表"),
WORKER_CONGRESS_DELEGATE_ATTENDANCE("工代会列席代表"), WORKER_CONGRESS_DELEGATE_ATTENDANCE("工代会列席代表"),
@@ -2,12 +2,131 @@ package com.budwk.app.base.sms;
import java.util.List; import java.util.List;
/**
* 短信/消息发送服务接口
* 保持参数直观可见,调用者无需查看实体类即可知晓参数含义
*/
public interface SmsService { public interface SmsService {
/** /**
* 单发 * 单发普通消息(无标题)
* @param loginName 工号 * @param loginName 接收人工号(必填)
* @param content 内容 * @param content 消息内容(必填)
*/ * @return
void send(String loginName, String content); */
void sendSingleMessage(String loginName, String content);
/**
* 单发普通消息(带标题)
* @param loginName 接收人工号(必填)
* @param title 消息标题(可选,可为null)
* @param content 消息内容(必填)
* @return
*/
void sendSingleMessage(String loginName, String title, String content);
/**
* 单发文本卡片消息
* @param loginName 接收人工号(必填)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void sendSingleTextCard(String loginName, String content, String pcUrl, String mobileUrl);
/**
* 单发文本卡片消息(带标题)
* @param loginName 接收人工号(必填)
* @param title 卡片标题(可选,可为null)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void sendSingleTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl);
/**
* 单发图文卡片消息
* @param loginName 接收人工号(必填)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param picUrl 图片链接(必填)
* @return
*/
void sendSinglePicTextCard(String loginName, String content, String pcUrl, String mobileUrl, String picUrl);
/**
* 单发图文卡片消息(带标题)
* @param loginName 接收人工号(必填)
* @param title 卡片标题(可选,可为null)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param picUrl 图片链接(必填)
* @return
*/
void sendSinglePicTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl, String picUrl);
/**
* 群发普通消息(多人接收相同内容)
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param content 消息内容(必填)
* @return void
*/
void massSendMessage(List<String> loginNames, String content);
/**
* 群发普通消息(多人接收相同内容)
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param title 消息标题(可选,可为null)
* @param content 消息内容(必填)
* @return void
*/
void massSendMessage(List<String> loginNames, String title, String content);
/**
* 群发短信文本卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void massSendTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl);
/**
* 群发短信文本卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param title 卡片标题(可选,可为null)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @return
*/
void massSendTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl);
/**
* 群发图文卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param picUrl 图片链接(必填)
* @return
*/
void massSendPicTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String picUrl);
/**
* 群发图文卡片消息
* @param loginNames 接收人工号列表(必填,不能为空列表)
* @param title 卡片标题(可选,可为null)
* @param content 卡片内容(必填)
* @param pcUrl PC端跳转链接(必填)
* @param mobileUrl 移动端跳转链接(必填)
* @param picUrl 图片链接(必填)
* @return
*/
void massSendPicTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl);
} }
@@ -1,27 +1,171 @@
package com.budwk.app.base.sms.impl.ncu; package com.budwk.app.base.sms.impl.ncu;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.sms.SmsService; import com.budwk.app.base.sms.SmsService;
import com.budwk.app.base.sms.impl.ncu.util.SmsTokenUtil; import com.budwk.app.base.sms.impl.ncu.util.SmsTokenUtil;
import com.budwk.app.web.commons.base.Globals;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/** /**
* 南昌大学短信实现 * 南昌大学短信实现
*/ */
@IocBean
@Slf4j @Slf4j
@IocBean
public class SmsNcuServiceImpl implements SmsService { public class SmsNcuServiceImpl implements SmsService {
private static final String SEND_MSG_URL = "https://api.ucpass.cn/sms/send"; @Inject
private static final String APP_ID = ""; private SmsTokenUtil smsTokenUtil;
private static final String SEND_MSG_URL = "https://poa.ncu.edu.cn/apis/messagecenter/v1/poaMessage/messageSend";
private static final String APP_ID = "72e34a101d3011f1a74cbae5193504f4";
private static final String TEMPLATE_ID = "MT1773223005000";
@Override @Override
public void send(String loginName, String content) { public void sendSingleMessage(String loginName, String content) {
String token = SmsTokenUtil.getToken(); this.sendSingleMessage(loginName, "智慧工会", content);
} }
@Override
public void sendSingleMessage(String loginName, String title, String content) {
this.massSendMessage(List.of(loginName), title, content);
}
@Override
public void sendSingleTextCard(String loginName, String content, String pcUrl, String mobileUrl) {
this.sendSingleTextCard(loginName, "智慧工会", content, pcUrl, mobileUrl);
}
@Override
public void sendSingleTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl) {
this.massSendTextCard(List.of(loginName), title, content, pcUrl, mobileUrl);
}
@Override
public void sendSinglePicTextCard(String loginName, String content, String pcUrl, String mobileUrl, String picUrl) {
this.sendSinglePicTextCard(loginName, "智慧工会", content, pcUrl, mobileUrl, picUrl);
}
@Override
public void sendSinglePicTextCard(String loginName, String title, String content, String pcUrl, String mobileUrl, String picUrl) {
this.massSendPicTextCard(List.of(loginName), title, content, pcUrl, mobileUrl, picUrl);
}
@Override
public void massSendMessage(List<String> loginNames, String content) {
this.massSendMessage(loginNames, "智慧工会", content);
}
@Override
public void massSendMessage(List<String> loginNames, String title, String content) {
String list = String.join(",", loginNames);
this.doSend("text", list, title, content, null, null, null);
}
@Override
public void massSendTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl) {
this.massSendTextCard(loginNames, "智慧工会", content, pcUrl, mobileUrl);
}
@Override
public void massSendTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl) {
String list = String.join(",", loginNames);
this.doSend("textCard", list, title, content, pcUrl, mobileUrl, null);
}
@Override
public void massSendPicTextCard(List<String> loginNames, String content, String pcUrl, String mobileUrl, String picUrl) {
this.massSendPicTextCard(loginNames, "智慧工会", content, pcUrl, mobileUrl, picUrl);
}
@Override
public void massSendPicTextCard(List<String> loginNames, String title, String content, String pcUrl, String mobileUrl, String picUrl) {
String list = String.join(",", loginNames);
this.doSend("news", list, title, content, pcUrl, mobileUrl, picUrl);
}
private void doSend(String sendType,
String receivers,
String title,
String content,
String pcUrl,
String mobileUrl,
String picUrl) {
if(!Globals.sso) {
log.error("【log】本地开发模式不允许调用消息接口");
return;
}
if(!Globals.MyConfig.getBoolean("AppNotify", false)) {
log.error("【log】消息配置未开启");
return;
}
if (Lang.isEmpty(receivers)) {
log.error("【log】send fail by receivers is null");
return;
}
try {
String token = smsTokenUtil.getToken();
if (Lang.isEmpty(token)) {
log.error("【log】send fail by token is null");
return;
}
Map<String, Object> params = new HashMap<>();
params.put("appId", APP_ID);
params.put("sendId", "");
params.put("sendType", Stream.of("WECHAT").toArray());
params.put("messageTypeCode", TEMPLATE_ID);
params.put("promise", false);
params.put("importantIdentity", false);
params.put("toPersons", receivers);
Map<String, Object> dataParams = new HashMap<>();
dataParams.put("title", title);
dataParams.put("paramValueJson", Map.of("content", content));
if ("textCard".equals(sendType)) {
dataParams.put("url", pcUrl);
dataParams.put("mobileUrl", mobileUrl);
}
if ("news".equals(sendType)) {
dataParams.put("url", pcUrl);
dataParams.put("mobileUrl", mobileUrl);
dataParams.put("coverImageUrl", picUrl);
}
params.put("data", dataParams);
log.info("【log】发送消息通知,请求参数:{}", Json.toJson(params));
String body = HttpUtil.createPost(SEND_MSG_URL)
.header("Authorization", token)
.header("Content-Type", "application/json")
.body(Json.toJson(params)).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body);
log.info("【log】发送消息通知,接口响应数据:{}", body);
if (resultJsonBody.getInt("code") == 0) {
log.info("【log】发送消息通知成功");
JSONObject data = resultJsonBody.getJSONObject("data");
String sendId = data.getStr("sendId");
System.out.println(sendId);
} else {
log.info("【log】发送消息通知失败:{}", resultJsonBody.getStr("message"));
}
} catch (Exception e) {
log.error("【log】发送消息通知失败", e);
}
}
} }
@@ -5,8 +5,13 @@ import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RedisConstant; import com.budwk.app.base.constant.RedisConstant;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.nutz.integration.jedis.RedisService; import org.nutz.integration.jedis.RedisService;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.mvc.Mvcs;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -18,21 +23,19 @@ import java.util.Map;
* @Date 2025/12/3 9:53 * @Date 2025/12/3 9:53
* @注释 :短信token工具类 * @注释 :短信token工具类
*/ */
@Slf4j
@IocBean
public class SmsTokenUtil { public class SmsTokenUtil {
@Inject @Inject
private static RedisService redisService; private RedisService redisService;
private static final String TOKEN_URL = "https://api.ucpass.cn/token"; 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 = "messagecenter:v1:sendMessage";
private static final String CLINT_ID = ""; public String getToken() {
private static final String CLINT_SECRET = "";
private static final String SCOPE = "";
public static String getToken() {
String key = RedisConstant.REDIS_SMS_TOKEN_KEY; String key = RedisConstant.REDIS_SMS_TOKEN_KEY;
String token = redisService.get(key); String token = redisService.get(key);
if (StrUtil.isBlank(token)) { if (StrUtil.isBlank(token)) {
@@ -48,7 +51,8 @@ public class SmsTokenUtil {
.formStr(param).execute().body(); .formStr(param).execute().body();
JSONObject resultJsonBody = JSONUtil.parseObj(body); JSONObject resultJsonBody = JSONUtil.parseObj(body);
if (resultJsonBody.getInt("code") == 200) { log.info("【log】获取消息token接口回调数据:{}", Json.toJson(resultJsonBody));
if (StrUtil.isNotBlank(resultJsonBody.getStr("access_token"))) {
String accessToken = resultJsonBody.getStr("access_token"); String accessToken = resultJsonBody.getStr("access_token");
Integer expiresIn = resultJsonBody.getInt("expires_in"); Integer expiresIn = resultJsonBody.getInt("expires_in");
redisService.setex(key, expiresIn, accessToken); redisService.setex(key, expiresIn, accessToken);
@@ -23,6 +23,7 @@ import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.vo.HighLightVO; import com.budwk.app.flow.vo.HighLightVO;
import com.budwk.app.flow.vo.ProcessInstanceVO; import com.budwk.app.flow.vo.ProcessInstanceVO;
import com.budwk.app.flow.vo.ProcessTaskVO; import com.budwk.app.flow.vo.ProcessTaskVO;
import com.budwk.app.todo.service.TodoService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@@ -52,6 +53,8 @@ public class FlowCommonController {
private Dao dao; private Dao dao;
@Inject @Inject
private FlowCommonService flowCommonService; private FlowCommonService flowCommonService;
@Inject
private TodoService todoService;
@At("/approval/form") @At("/approval/form")
@SaCheckLogin @SaCheckLogin
@@ -281,6 +284,7 @@ public class FlowCommonController {
// 发送任务撤回事件 确保上面执行成功 // 发送任务撤回事件 确保上面执行成功
for (ProcessTask task : taskList) { for (ProcessTask task : taskList) {
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build()); ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build());
todoService.taskTerminate(task);
} }
return Result.success(); return Result.success();
@@ -1,9 +1,11 @@
package com.budwk.app.flow.listenter; package com.budwk.app.flow.listenter;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.flow.engine.event.ProcessEvent; import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessEventListener; import com.budwk.app.flow.engine.event.ProcessEventListener;
import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessEventTypeEnum; import com.budwk.app.flow.enums.ProcessEventTypeEnum;
import com.budwk.app.todo.service.TodoService;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
@@ -16,12 +18,19 @@ public class ProcessTaskEndEventListener implements ProcessEventListener {
@Inject @Inject
private Dao dao; private Dao dao;
@Inject
private TodoService todoService;
@Override @Override
public void onEvent(ProcessEvent event) { public void onEvent(ProcessEvent event) {
if (event.getEventType() == ProcessEventTypeEnum.PROCESS_TASK_END) { if (event.getEventType() == ProcessEventTypeEnum.PROCESS_TASK_END) {
Long sourceId = event.getSourceId(); Long sourceId = event.getSourceId();
ProcessTask task = dao.fetch(ProcessTask.class, sourceId); ProcessTask task = dao.fetch(ProcessTask.class, sourceId);
// 好了,任务完成,就可以调接口完成待办了
if (!ObjectUtil.equals(0, task.getTaskParentId().intValue())) {
todoService.taskHandle(task);
}
System.out.println("流程任务完成事件:" + task.getDisplayName()); System.out.println("流程任务完成事件:" + task.getDisplayName());
} }
} }
@@ -1,6 +1,7 @@
package com.budwk.app.flow.listenter; package com.budwk.app.flow.listenter;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.event.ProcessEvent; import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessEventListener; import com.budwk.app.flow.engine.event.ProcessEventListener;
@@ -8,6 +9,8 @@ import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask; import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.entity.ProcessTaskActor; import com.budwk.app.flow.entity.ProcessTaskActor;
import com.budwk.app.flow.enums.ProcessEventTypeEnum; import com.budwk.app.flow.enums.ProcessEventTypeEnum;
import com.budwk.app.todo.service.TodoService;
import com.budwk.app.web.commons.base.Globals;
import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService; import com.budwk.app.zhgh.dayofficework.message.service.GlobalMessageSendService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
@@ -30,6 +33,8 @@ public class ProcessTaskStartEventListener implements ProcessEventListener {
private Dao dao; private Dao dao;
@Inject @Inject
private GlobalMessageSendService globalMessageSendService; private GlobalMessageSendService globalMessageSendService;
@Inject
private TodoService todoService;
@Override @Override
public void onEvent(ProcessEvent event) { public void onEvent(ProcessEvent event) {
@@ -58,13 +63,23 @@ public class ProcessTaskStartEventListener implements ProcessEventListener {
List<ProcessTaskActor> taskActors = dao.query(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "=", task.getId())); List<ProcessTaskActor> taskActors = dao.query(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "=", task.getId()));
for (ProcessTaskActor taskActor : taskActors) { for (ProcessTaskActor taskActor : taskActors) {
JSONObject config = new JSONObject();
config.set("pcUrl", Globals.AppDomain + task.getFormKey());
// 模拟发送消息 // 模拟发送消息
String message = StrUtil.format("您有一条待办任务,实例:{},处理环节:{}", taskActor.getActorName(), instanceName, taskDisplayName); String message = "";
log.info(message); if(StrUtil.isNotBlank(task.getH5FormKey())) {
config.set("mobileUrl", Globals.AppDomain + task.getH5FormKey());
message = StrUtil.format("您有一条待办任务,实例:{},处理环节:{}。", instanceName, taskDisplayName);
} else {
config.set("mobileUrl", Globals.AppDomain + "/platform/tooltip/h5");
message = StrUtil.format("您有一条待办任务,实例:{},处理环节:{},请到电脑端智慧工会系统审核。", instanceName, taskDisplayName);
}
log.info(message);
globalMessageSendService.sendMessage(taskDisplayName, message, 2, List.of(taskActor.getActorId()), null); globalMessageSendService.sendMessage(taskDisplayName, message, 2, List.of(taskActor.getActorId()), null);
} }
todoService.addTransaction(task);
} }
} }
@@ -2,18 +2,32 @@ package com.budwk.app.flow.service;
import cn.hutool.core.lang.Dict; import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowEngine; import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.flow.enums.ProcessSubmitTypeEnum; import com.budwk.app.flow.engine.event.ProcessEvent;
import com.budwk.app.flow.engine.event.ProcessPublisher;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.*;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import java.util.List;
@IocBean @IocBean
public class FlowCommonService { public class FlowCommonService {
@Inject @Inject
private FlowEngine flowEngine; private FlowEngine flowEngine;
@Inject
private Dao dao;
/** /**
* 执行任务 * 执行任务
@@ -65,4 +79,42 @@ public class FlowCommonService {
} }
} }
@Aop(TransAop.READ_COMMITTED)
public Result revokeTask(Long taskId) {
// 自己任务
ProcessTask selfTask = dao.fetch(ProcessTask.class, taskId);
selfTask.setTaskState(ProcessTaskStateEnum.DOING.getCode());
// 撤销任务
List<ProcessTask> taskList = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskParentId, "=", taskId));
for (ProcessTask task : taskList) {
task.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
dao.update(task);
}
// 会签并行任务 撤销后续任务
if (selfTask.getPerformType().equals(ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode())) {
List<ProcessTask> doingTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode())
.and(ProcessTask::getProcessInstanceId, "=", selfTask.getProcessInstanceId()));
for (ProcessTask doingTask : doingTasks) {
doingTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode());
dao.update(doingTask);
}
}
// 激活
dao.update(selfTask);
// 流程激活
dao.update(ProcessInstance.class, Chain.make("state", ProcessInstanceStateEnum.DOING.getCode()),Cnd.where(ProcessInstance::getId, "=", selfTask.getProcessInstanceId()));
// 发送任务撤回事件 确保上面执行成功
for (ProcessTask task : taskList) {
ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build());
}
return Result.success();
}
} }
@@ -211,4 +211,13 @@ public interface ProcessTaskService extends BaseService<ProcessTask> {
*/ */
List<ProcessTask> getDoingTaskByBizIdTaskName(List<String> bizIds, String taskName); List<ProcessTask> getDoingTaskByBizIdTaskName(List<String> bizIds, String taskName);
/**
* 获取已结束的任务
*
* @param bizIds 业务ID
* @param taskName 任务名称
* @return
*/
List<ProcessTask> getDoneTaskByBizIdTaskName(List<String> bizIds, String taskName);
} }
@@ -7,7 +7,6 @@ import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.expression.ExpressionUtil; import cn.hutool.extra.expression.ExpressionUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl; import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.vo.LabelValueVO; import com.budwk.app.base.vo.LabelValueVO;
@@ -32,6 +31,8 @@ import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.flow.vo.HighLightVO; import com.budwk.app.flow.vo.HighLightVO;
import com.budwk.app.flow.vo.ProcessInstanceVO; import com.budwk.app.flow.vo.ProcessInstanceVO;
import com.budwk.app.flow.vo.ProcessTaskVO; import com.budwk.app.flow.vo.ProcessTaskVO;
import com.budwk.app.todo.service.TodoService;
import com.budwk.app.todo.service.impl.TodoServiceImpl;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import org.nutz.aop.interceptor.ioc.TransAop; import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain; import org.nutz.dao.Chain;
@@ -42,7 +43,9 @@ import org.nutz.dao.sql.Sql;
import org.nutz.ioc.aop.Aop; import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap; import org.nutz.lang.util.NutMap;
import org.nutz.mvc.Mvcs;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -281,7 +284,7 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl<ProcessInstance>
List<ProcessTask> processTaskList = flowEngine.processTaskService().getDoingTaskList(processInstanceId, new String[]{}); List<ProcessTask> processTaskList = flowEngine.processTaskService().getDoingTaskList(processInstanceId, new String[]{});
// 拿到历史任务-按更新时间倒序 // 拿到历史任务-按更新时间倒序
List<ProcessTask> hisProcessTaskList = flowEngine.processTaskService().query(Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId).desc(ProcessTask::getUpdatedAt)); List<ProcessTask> hisProcessTaskList = flowEngine.processTaskService().query(Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId).desc(ProcessTask::getUpdatedAt));
processTaskList.forEach(task -> { processTaskList.forEach(task -> {
if (!vo.getActiveNodeNames().contains(task.getTaskName())) { if (!vo.getActiveNodeNames().contains(task.getTaskName())) {
vo.getActiveNodeNames().add(task.getTaskName()); vo.getActiveNodeNames().add(task.getTaskName());
recursionModel(processModel.getStart(), processInstance, processTaskList, hisProcessTaskList, task.getTaskName(), vo); recursionModel(processModel.getStart(), processInstance, processTaskList, hisProcessTaskList, task.getTaskName(), vo);
@@ -556,6 +559,14 @@ public class ProcessInstanceServiceImpl extends BaseServiceImpl<ProcessInstance>
public void deleteProcessInstanceById(Long processInstanceId) { public void deleteProcessInstanceById(Long processInstanceId) {
List<ProcessTask> taskList = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId)); List<ProcessTask> taskList = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "=", processInstanceId));
List<Long> taskIds = taskList.stream().map(ProcessTask::getId).toList(); List<Long> taskIds = taskList.stream().map(ProcessTask::getId).toList();
// 删除学校待办
if(Lang.isNotEmpty(taskList)) {
ProcessTask task = taskList.get(taskList.size() - 1);
TodoService todoService = Mvcs.getIoc().get(TodoServiceImpl.class);
todoService.deleteProcessInstance(task);
}
dao().clear(ProcessTask.class, Cnd.where(ProcessTask::getId, "in", taskIds)); dao().clear(ProcessTask.class, Cnd.where(ProcessTask::getId, "in", taskIds));
dao().clear(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", processInstanceId)); dao().clear(ProcessInstance.class, Cnd.where(ProcessInstance::getId, "=", processInstanceId));
} }
@@ -490,6 +490,16 @@ public class ProcessTaskServiceImpl extends BaseServiceImpl<ProcessTask> impleme
} }
@Override
public List<ProcessTask> getDoneTaskByBizIdTaskName(List<String> bizIds, String taskName) {
List<ProcessInstance> instances = dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", bizIds));
List<Long> instanceIds = instances.stream().map(ProcessInstance::getId).toList();
List<ProcessTask> tasks = dao().query(ProcessTask.class, Cnd.where(ProcessTask::getProcessInstanceId, "in", instanceIds)
.and(ProcessTask::getTaskName, "=", taskName)
.and(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.FINISHED.getCode()));
return tasks;
}
@Override @Override
public List<ProcessTask> getDoingTaskByBizIdTaskName(List<String> bizIds, String taskName) { public List<ProcessTask> getDoingTaskByBizIdTaskName(List<String> bizIds, String taskName) {
List<ProcessInstance> instances = dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", bizIds)); List<ProcessInstance> instances = dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "in", bizIds));
@@ -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;
}
}
@@ -32,10 +32,10 @@ public class SmsMessageSendStrategy implements GlobalMessageSendStrategy {
return true; // 短信发送支持异步 return true; // 短信发送支持异步
} }
// @Override @Override
// public boolean isEnabled() { public boolean isEnabled() {
// return true; return false;
// } }
@Override @Override
public void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config) { public void send(String title, String content, Integer type, List<String> receiverIds, JSONObject config) {
@@ -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"));
}
}
@@ -36,7 +36,7 @@ import java.util.stream.Collectors;
/** /**
* @author zhf * @author zhf
* @date 2025/6/23 15:25 * @date 2025/6/23 15:25
* @description 分工会二次推选 * @description 候选人录入
*/ */
@At("/platform/executiveCommittee/delegationTwoPush") @At("/platform/executiveCommittee/delegationTwoPush")
@Ok("json:full") @Ok("json:full")
@@ -62,7 +62,8 @@ public class ExecutiveCommitteeDelegationTwoPushController {
public Result pageData(PageForm pageForm, public Result pageData(PageForm pageForm,
String teacherMeetId, String teacherMeetId,
String delegationId, String delegationId,
String unionId) { String unionId,
String roleCode) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
t1.*, t1.*,
@@ -86,9 +87,9 @@ public class ExecutiveCommitteeDelegationTwoPushController {
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.andEX("t1.pushUserId", "=", SecurityUtil.getUserId());
cnd.andEX("t1.teacherMeetId", "=", teacherMeetId); cnd.andEX("t1.teacherMeetId", "=", teacherMeetId);
cnd.andEX("t2.delegationId", "=", delegationId); cnd.andEX("t2.delegationId", "=", delegationId);
cnd.andEX("t1.roleCode", "=", roleCode);
if (StpUtil.hasRole(RoleConstant.SYSADMIN.name()) || if (StpUtil.hasRole(RoleConstant.SYSADMIN.name()) ||
StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) || StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) ||
StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) { StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
@@ -109,9 +110,9 @@ public class ExecutiveCommitteeDelegationTwoPushController {
@At @At
@SaCheckPermission("executiveCommittee.delegationTwoPush") @SaCheckPermission("executiveCommittee.delegationTwoPush")
@SLog(type = "执委会推选-分工会二次推选", tag = "查询可以二次推选的名单", param = true, result = true) @SLog(type = "执委会推选-候选人录入", tag = "查询可以二次推选的名单", param = true, result = true)
public Result getDelegationUser(String teacherMeetId) { public Result getDelegationUser(String teacherMeetId) {
if (!(StpUtil.hasRole(RoleConstant.SYSADMIN.name()) || /* if (!(StpUtil.hasRole(RoleConstant.SYSADMIN.name()) ||
StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) || StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) ||
StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()))) { StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()))) {
return Result.error("没有权限,只有分工会主席才能推选"); return Result.error("没有权限,只有分工会主席才能推选");
@@ -126,10 +127,9 @@ public class ExecutiveCommitteeDelegationTwoPushController {
} }
if (config.getSecondEndTime().getTime() < System.currentTimeMillis()) { if (config.getSecondEndTime().getTime() < System.currentTimeMillis()) {
return Result.error("二次预选已结束"); return Result.error("二次预选已结束");
} }*/
List<ExecutiveCommitteeTwoPush> userValue = dao.query(ExecutiveCommitteeTwoPush.class, List<ExecutiveCommitteeTwoPush> userValue = dao.query(ExecutiveCommitteeTwoPush.class,
Cnd.where("teacherMeetId", "=", teacherMeetId) Cnd.where("teacherMeetId", "=", teacherMeetId));
.and("pushUserId", "=", SecurityUtil.getUserId()));
List<String> userIds = userValue.stream().map(v -> v.getUserId()).collect(Collectors.toList()); List<String> userIds = userValue.stream().map(v -> v.getUserId()).collect(Collectors.toList());
@@ -151,20 +151,20 @@ public class ExecutiveCommitteeDelegationTwoPushController {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.andEX("t1.teacherMeetId", "=", teacherMeetId); cnd.andEX("t1.teacherMeetId", "=", teacherMeetId);
cnd.andEX("t1.userId", "not in", userIds); cnd.andEX("t1.userId", "not in", userIds);
cnd.andEX("t1.userId", "!=", SecurityUtil.getUserId());
sql.setCondition(cnd); sql.setCondition(cnd);
List<NutMap> userData = baseService.listMap(sql); List<NutMap> userData = baseService.listMap(sql);
return Result.success(Map.of("userData", userData, "userValue", userValue, "committeeQuotaCount", config.getCommitteeQuotaCount())); return Result.success(Map.of("userData", userData, "userValue", userValue, "committeeQuotaCount",0));
} }
@At @At
@SaCheckPermission("executiveCommittee.delegationTwoPush") @SaCheckPermission("executiveCommittee.delegationTwoPush")
@ApiOperation("推选委员") @ApiOperation("推选委员")
@SLog(tag = "执委会推选-分工会二次推选", msg = "推选委员") @SLog(tag = "执委会推选-候选人录入", msg = "推选委员")
public Result addOnePush(@Param("userValue") String[] userValue, public Result addOnePush(@Param("userValue") String[] userValue,
String teacherMeetId) { String teacherMeetId,
String roleCode) {
try { try {
if (!(StpUtil.hasRole(RoleConstant.SYSADMIN.name()) || /* if (!(StpUtil.hasRole(RoleConstant.SYSADMIN.name()) ||
StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) || StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) ||
StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()))) { StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name()))) {
return Result.error("没有权限,只有分工会主席才能推选"); return Result.error("没有权限,只有分工会主席才能推选");
@@ -187,7 +187,7 @@ public class ExecutiveCommitteeDelegationTwoPushController {
if (dbCount + userValue.length > config.getCommitteeQuotaCount()) { if (dbCount + userValue.length > config.getCommitteeQuotaCount()) {
return Result.error("推选人数限制" + config.getCommitteeQuotaCount() + "人"); return Result.error("推选人数限制" + config.getCommitteeQuotaCount() + "人");
} }
*/
List<Teacher_congress_delegate> dbList = dao.query(Teacher_congress_delegate.class, List<Teacher_congress_delegate> dbList = dao.query(Teacher_congress_delegate.class,
Cnd.where(Teacher_congress_delegate::getSessionId, "=", teacherMeetId) Cnd.where(Teacher_congress_delegate::getSessionId, "=", teacherMeetId)
.andEX(Teacher_congress_delegate::getUserId, "in", userValue)); .andEX(Teacher_congress_delegate::getUserId, "in", userValue));
@@ -206,6 +206,7 @@ public class ExecutiveCommitteeDelegationTwoPushController {
twoPush.setPushUserName(SecurityUtil.getUserUsername()); twoPush.setPushUserName(SecurityUtil.getUserUsername());
twoPush.setPushLoginName(SecurityUtil.getUserLoginname()); twoPush.setPushLoginName(SecurityUtil.getUserLoginname());
twoPush.setTeacherMeetId(teacherMeetId); twoPush.setTeacherMeetId(teacherMeetId);
twoPush.setRoleCode(roleCode);
list.add(twoPush); list.add(twoPush);
} }
dao.insert(list); dao.insert(list);
@@ -219,16 +220,16 @@ public class ExecutiveCommitteeDelegationTwoPushController {
@At @At
@ApiOperation("删除推选人员") @ApiOperation("删除推选人员")
@SaCheckPermission("executiveCommittee.delegationTwoPush") @SaCheckPermission("executiveCommittee.delegationTwoPush")
@SLog(tag = "执委会推选-分工会二次推选", msg = "删除推选人员") @SLog(tag = "执委会推选-候选人录入", msg = "删除推选人员")
public Result doDelete(@Valid String id, @Valid String teacherMeetId) { public Result doDelete(@Valid String id, @Valid String teacherMeetId) {
ExecutiveCommitteeConfig config = dao.fetch(ExecutiveCommitteeConfig.class, ExecutiveCommitteeConfig config = dao.fetch(ExecutiveCommitteeConfig.class,
Cnd.where("teacherMeetId", "=", teacherMeetId)); Cnd.where("teacherMeetId", "=", teacherMeetId));
if (ObjectUtil.isEmpty(config)) { if (ObjectUtil.isEmpty(config)) {
return Result.error("请先配置基础信息"); return Result.error("请先配置基础信息");
} }
if (config.getSecondEndTime().getTime() < System.currentTimeMillis()) { /* if (config.getSecondEndTime().getTime() < System.currentTimeMillis()) {
return Result.error("二次预选已结束不能删除!"); return Result.error("二次预选已结束不能删除!");
} }*/
int num = dao.clear(ExecutiveCommitteeTwoPush.class, Cnd.where("id", "=", id)); int num = dao.clear(ExecutiveCommitteeTwoPush.class, Cnd.where("id", "=", id));
return num >= 0 ? Result.success() : Result.error(); return num >= 0 ? Result.success() : Result.error();
} }
@@ -113,7 +113,7 @@ public class ExecutiveCommitteeMemberController {
entityList.add(new ExcelExportEntity("联系方式", "mobile", 20)); entityList.add(new ExcelExportEntity("联系方式", "mobile", 20));
entityList.add(new ExcelExportEntity("所属单位", "unitName", 20)); entityList.add(new ExcelExportEntity("所属单位", "unitName", 20));
entityList.add(new ExcelExportEntity("所属代表团", "delegationName", 20)); entityList.add(new ExcelExportEntity("所属代表团", "delegationName", 20));
entityList.add(new ExcelExportEntity("票数", "pushCount", 20)); entityList.add(new ExcelExportEntity("委员类别", "addTypeName", 20));
response.setContentType("application/octet-stream"); response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("委员会预选名单.xlsx", "UTF-8")); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("委员会预选名单.xlsx", "UTF-8"));
@@ -1,21 +1,22 @@
package com.budwk.app.zhgh.democratic.executiveCommittee.controller; package com.budwk.app.zhgh.democratic.executiveCommittee.controller;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil; import cn.hutool.extra.pinyin.PinyinUtil;
import com.budwk.app.base.annotation.SLog; import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.param.PageForm; import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.executiveCommittee.models.ExecutiveCommitteeConfig; import com.budwk.app.zhgh.democratic.executiveCommittee.models.ExecutiveCommitteeTwoPush;
import com.budwk.app.zhgh.democratic.executiveCommittee.models.ExecutiveCommitteeOnePush;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
@@ -29,10 +30,8 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
/** /**
* @author zhf * @author zhf
@@ -43,7 +42,7 @@ import java.util.stream.Collectors;
@At("/platform/executiveCommittee/preparatoryGroupPush") @At("/platform/executiveCommittee/preparatoryGroupPush")
@Ok("json:full") @Ok("json:full")
@Slf4j @Slf4j
@Api(tags = "执委会推选-筹备组推选") @Api(tags = "执委会推选-正式委员录入")
public class ExecutiveCommitteePushController { public class ExecutiveCommitteePushController {
@Inject @Inject
@@ -63,38 +62,49 @@ public class ExecutiveCommitteePushController {
public Result pageData(PageForm pageForm, public Result pageData(PageForm pageForm,
String teacherMeetId, String teacherMeetId,
String delegationId, String delegationId,
String unionId) { String unionId,
String roleCode) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
t1.*, t1.*,
t2.name AS unitName, t2.loginName,
t3.name unionName, t2.userName,
t4.sex, t3.name AS unitName,
t4.name unionName,
t5.sex,
TIMESTAMPDIFF( TIMESTAMPDIFF(
YEAR, YEAR,
t4.birthday, t5.birthday,
CURDATE()) AS age, CURDATE()) AS age,
t5.name AS delegationName t6.name AS delegationName
FROM FROM
`executive_committee_one_push` t1 `executive_committee_two_push` t1
LEFT JOIN sys_unit t2 ON t1.unitId = t2.id LEFT JOIN executive_committee_one_push t2 on t2.userId=t1.userId and t2.teacherMeetId=t1.teacherMeetId
LEFT JOIN `sys_union` t3 ON t3.id = t2.unionid LEFT JOIN sys_unit t3 ON t2.unitId = t3.id
LEFT JOIN `vw_user` t4 ON t4.id = t1.userId LEFT JOIN `sys_union` t4 ON t4.id = t3.unionid
LEFT JOIN teacher_congress_delegation t5 ON t5.id = t1.delegationId LEFT JOIN `vw_user` t5 ON t5.id = t1.userId
LEFT JOIN teacher_congress_delegation t6 ON t6.id = t2.delegationId
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.andEX("t1.teacherMeetId", "=", teacherMeetId); cnd.andEX("t1.teacherMeetId", "=", teacherMeetId);
cnd.andEX("t1.delegationId", "=", delegationId); cnd.andEX("t2.delegationId", "=", delegationId);
cnd.andEX("t3.id", "=", unionId); cnd.andEX("t1.roleCode", "=", roleCode);
cnd.andEX("t1.addType", "=", 2); cnd.andEX("t1.isFormal", "=", true);
if (StpUtil.hasRole(RoleConstant.SYSADMIN.name()) ||
StpUtil.hasRole(RoleConstant.SCHOOL_UNION_ADMIN.name()) ||
StpUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
cnd.andEX("t4.id", "=", unionId);
} else {
cnd.andEX("t4.id", "=", SecurityUtil.getUnionId());
}
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) { if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup group = new SqlExpressionGroup(); SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("t1.userName", pageForm.getSearchKeyword()); group.orLike("t2.userName", pageForm.getSearchKeyword());
group.orLike("t1.loginName", pageForm.getSearchKeyword()); group.orLike("t2.loginName", pageForm.getSearchKeyword());
cnd.and(group); cnd.and(group);
} }
cnd.asc("t5.code").asc("t1.firstLetter"); cnd.asc("t6.code").asc("t2.firstLetter");
sql.setCondition(cnd); sql.setCondition(cnd);
return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql)); return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
} }
@@ -104,93 +114,53 @@ public class ExecutiveCommitteePushController {
@SaCheckPermission("executiveCommittee.preparatoryGroupPush") @SaCheckPermission("executiveCommittee.preparatoryGroupPush")
@ApiOperation("查询可以推选委员和已经推选的人员") @ApiOperation("查询可以推选委员和已经推选的人员")
public Result getDelegationUser(String teacherMeetId) { public Result getDelegationUser(String teacherMeetId) {
List<ExecutiveCommitteeOnePush> userValue = dao.query(ExecutiveCommitteeOnePush.class, List<ExecutiveCommitteeTwoPush> twoPushList = dao.query(ExecutiveCommitteeTwoPush.class,
Cnd.where("teacherMeetId", "=", teacherMeetId) Cnd.where("teacherMeetId", "=", teacherMeetId).and(ExecutiveCommitteeTwoPush::getRoleCode, "is not", null));
.and("addType", "=", 2));
List<ExecutiveCommitteeOnePush> onePushList = dao.query(ExecutiveCommitteeOnePush.class, Cnd.NEW()); List<ExecutiveCommitteeTwoPush> userValue = twoPushList.stream().filter(ExecutiveCommitteeTwoPush::getIsFormal).toList();
List<String> userIds = onePushList.stream().map(ExecutiveCommitteeOnePush::getUserId).collect(Collectors.toList()); List<String> userIds = userValue.stream().map(ExecutiveCommitteeTwoPush::getUserId).toList();
ExecutiveCommitteeConfig config = dao.fetch(ExecutiveCommitteeConfig.class,
Cnd.where("teacherMeetId", "=", teacherMeetId));
Cnd cnd = Cnd.NEW();
cnd.andEX("db.sessionId", "=", teacherMeetId);
cnd.andEX("db.userId", "not in", userIds);
cnd.andEX("db.userId", "!=", SecurityUtil.getUserId());
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
db.*, t2.id userId,
u.professionalTitle, t2.username userName,
u.professionalLevel, t2.loginname loginName,
TIMESTAMPDIFF(YEAR, u.birthday, CURDATE()) AS age t2.unitName,
t2.sex,
t2.professionalTitle,
t2.professionalLevel,
t1.roleCode,
TIMESTAMPDIFF(
YEAR,
t2.birthday,
CURDATE()) age
FROM FROM
teacher_congress_delegate db executive_committee_two_push t1
LEFT JOIN `vw_user` u ON db.userId = u.id LEFT JOIN `vw_user` t2 ON t1.userId = t2.id
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW();
cnd.andEX("t1.teacherMeetId", "=", teacherMeetId);
cnd.andEX("t1.userId", "not in", userIds);
sql.setCondition(cnd); sql.setCondition(cnd);
List<NutMap> userData = baseService.listMap(sql); List<NutMap> userData = baseService.listMap(sql);
return Result.success(Map.of("userData", userData, "userValue", userValue,"prepareGroupQuotaCount",config.getPrepareGroupQuotaCount())); return Result.success(Map.of("userData", userData, "userValue", userValue));
} }
@At @At
@SaCheckPermission("executiveCommittee.preparatoryGroupPush") @SaCheckPermission("executiveCommittee.preparatoryGroupPush")
@SLog(tag = "执委会推选-筹备组推选", msg = "推选委员") @SLog(tag = "执委会推选-正式委员录入", msg = "推选委员")
public Result addOnePush(@Param("userValue") String[] userValue, public Result addOnePush(@Param("userValue") String[] userValue,
String teacherMeetId) { String teacherMeetId) {
try { try {
if (ObjectUtil.isEmpty(userValue)) {
ExecutiveCommitteeConfig config = dao.fetch(ExecutiveCommitteeConfig.class, return Result.error("请选择要录入的委员!");
Cnd.where("teacherMeetId", "=", teacherMeetId));
/* if (ObjectUtil.isEmpty(config)) {
return Result.error("请先配置基础信息");
} }
if (config.getFirstStartTime().getTime() > System.currentTimeMillis()) { if (ObjectUtil.isEmpty(teacherMeetId)) {
return Result.error("请等待一次预选开始时间"); return Result.error("请选择教代会!");
} }
if (config.getFirstEndTime().getTime() < System.currentTimeMillis()) { List<ExecutiveCommitteeTwoPush> twoPushList = dao.query(ExecutiveCommitteeTwoPush.class, Cnd.where(ExecutiveCommitteeTwoPush::getTeacherMeetId, "=", teacherMeetId).and(ExecutiveCommitteeTwoPush::getUserId, "in", userValue));
return Result.error("一次预选已结束"); List<String> ids = twoPushList.stream().map(ExecutiveCommitteeTwoPush::getId).toList();
}*/ dao.update(ExecutiveCommitteeTwoPush.class, Chain.make("isFormal", true), Cnd.where("id", "in", ids));
int dbCount = dao.count(ExecutiveCommitteeOnePush.class,
Cnd.where("teacherMeetId", "=", teacherMeetId)
.and("addType", "=", 2));
if (dbCount + userValue.length > config.getPrepareGroupQuotaCount()) {
return Result.error("推选人数限制" + config.getPrepareGroupQuotaCount() + "");
}
Sql sql = Sqls.create("""
SELECT
t1.*
FROM
teacher_congress_delegate t1
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t1.sessionId", "=", teacherMeetId);
cnd.andEX("t1.userId", "in", userValue);
sql.setCondition(cnd);
List<NutMap> dbList = baseService.listMap(sql);
List<ExecutiveCommitteeOnePush> list = new ArrayList<>();
for (String id : userValue) {
NutMap jdhDb = dbList.stream().filter(v -> v.getString("userId").equals(id)).findFirst().orElse(null);
if (ObjectUtil.isEmpty(jdhDb)) {
return Result.error("请选择正确的代表!");
}
ExecutiveCommitteeOnePush onePush = new ExecutiveCommitteeOnePush();
onePush.setPushDate(DateUtil.date());
onePush.setUserId(jdhDb.getString("userId"));
onePush.setUserName(jdhDb.getString("userName"));
onePush.setLoginName(jdhDb.getString("loginName"));
onePush.setDelegationId(jdhDb.getString("delegationId"));
onePush.setUnionId(jdhDb.getString("unionId"));
onePush.setUnitId(jdhDb.getString("unitId"));
onePush.setTeacherMeetId(teacherMeetId);
String firstLetter = String.valueOf(getFirstLetter(jdhDb.getString("userName")));
onePush.setFirstLetter(firstLetter);
onePush.setAddType(2);
list.add(onePush);
}
dao.insert(list);
return Result.success("添加成功!"); return Result.success("添加成功!");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -202,16 +172,9 @@ public class ExecutiveCommitteePushController {
@SaCheckPermission("executiveCommittee.preparatoryGroupPush") @SaCheckPermission("executiveCommittee.preparatoryGroupPush")
@SLog(tag = "执委会推选-筹备组推选", msg = "删除推选的人") @SLog(tag = "执委会推选-筹备组推选", msg = "删除推选的人")
public Result doDelete(@Valid String id, @Valid String teacherMeetId) { public Result doDelete(@Valid String id, @Valid String teacherMeetId) {
ExecutiveCommitteeConfig config = dao.fetch(ExecutiveCommitteeConfig.class, dao.update(ExecutiveCommitteeTwoPush.class, Chain.make("isFormal", false), Cnd.where("id", "=", id));
Cnd.where("teacherMeetId", "=", teacherMeetId)); return Result.success("删除成功!");
if (ObjectUtil.isEmpty(config)) {
return Result.error("请先配置基础信息");
}
int num = dao.clear(ExecutiveCommitteeOnePush.class, Cnd.where("id", "=", id));
return num >= 0 ? Result.success() : Result.error();
} }
public static char getFirstLetter(String str) { public static char getFirstLetter(String str) {
if (str == null || str.isEmpty()) { if (str == null || str.isEmpty()) {
throw new IllegalArgumentException("字符串不能为空"); throw new IllegalArgumentException("字符串不能为空");
@@ -270,7 +270,6 @@ public class ExecutiveCommitteeUnionPushController {
// 自动完成第一个申请任务 // 自动完成第一个申请任务
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null); List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
for (ProcessTask task : doingTaskList) { for (ProcessTask task : doingTaskList) {
args.put(FlowConst.NEXT_NODE_OPERATOR, ExecutiveCommitteeUnionPushInfo.getUserId());
flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args); flowEngine.executeProcessTask(task.getId(), SecurityUtil.getUserId(), args);
} }
}); });
@@ -66,4 +66,15 @@ public class ExecutiveCommitteeTwoPush extends BaseModel implements Serializable
@Comment("推选时间") @Comment("推选时间")
@ColDefine(type = ColType.DATETIME) @ColDefine(type = ColType.DATETIME)
private DateTime pushDate; private DateTime pushDate;
@Column
@Comment("录入类型1.执委会委员/2.工会委员会委员/3.经审委员会委员")
@ColDefine(type = ColType.VARCHAR,width = 30)
private String roleCode;
@Column
@Comment("是否正式委员")
@Default("0")
@ColDefine(type = ColType.BOOLEAN)
private Boolean isFormal;
} }
@@ -37,7 +37,12 @@ public class ExecutiveCommitteeMemberServiceImpl extends BaseServiceImpl impleme
YEAR, YEAR,
u.birthday, u.birthday,
CURDATE()) AS age, CURDATE()) AS age,
( SELECT count( 1 ) FROM executive_committee_two_push tp WHERE tp.userId = op.userId ) as pushCount ( SELECT count( 1 ) FROM executive_committee_two_push tp WHERE tp.userId = op.userId ) as pushCount,
CASE op.addType
WHEN 1 THEN '执委会委员'
WHEN 3 THEN '工会委员会委员'
ELSE '未知'
END AS addTypeName
FROM FROM
executive_committee_one_push op executive_committee_one_push op
LEFT JOIN sys_unit it ON op.unitId = it.id LEFT JOIN sys_unit it ON op.unitId = it.id
@@ -2,11 +2,14 @@ package com.budwk.app.zhgh.democratic.proposal.controller.query;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.democratic.proposal.controller.vo.ProposalInfoPageVO; import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalQueryComprehensiveParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalQueryComprehensiveParam;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService; import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@@ -24,7 +27,6 @@ import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Arrays;
@IocBean @IocBean
@At("/platform/proposal/query/comprehensive") @At("/platform/proposal/query/comprehensive")
@@ -65,6 +67,7 @@ public class ProposalQueryComprehensiveController {
GROUP_CONCAT(DISTINCT CASE WHEN pru.isMaster = 0 THEN pru.unitName END) AS slaveUnitNames GROUP_CONCAT(DISTINCT CASE WHEN pru.isMaster = 0 THEN pru.unitName END) AS slaveUnitNames
FROM FROM
proposal_info info proposal_info info
LEFT JOIN vw_user us ON us.loginname=info.createUserLoginName
LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN proposal_merge mer ON mer.proposalId = info.id LEFT JOIN proposal_merge mer ON mer.proposalId = info.id
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
@@ -77,25 +80,25 @@ public class ProposalQueryComprehensiveController {
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
if(ArrayUtil.isNotEmpty(pageForm.getOrigins())){ if (ArrayUtil.isNotEmpty(pageForm.getOrigins()) && ObjectUtil.isNotEmpty(pageForm.getCommonKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup(); SqlExpressionGroup seg = new SqlExpressionGroup();
// 案名 // 案名
if(ArrayUtil.contains(pageForm.getOrigins(),"name")){ if (ArrayUtil.contains(pageForm.getOrigins(), "name")) {
for (String keyword : pageForm.getCommonKeyword().split(" ")) { for (String keyword : pageForm.getCommonKeyword().split(" ")) {
seg.orLike("info.name", keyword); seg.orLike("info.name", keyword);
} }
} }
// 案由 // 案由
if(ArrayUtil.contains(pageForm.getOrigins(),"brief")){ if (ArrayUtil.contains(pageForm.getOrigins(), "brief")) {
for (String keyword : pageForm.getCommonKeyword().split(" ")) { for (String keyword : pageForm.getCommonKeyword().split(" ")) {
seg.orLike("info.brief", keyword); seg.orLike("info.brief", keyword);
} }
} }
// 提案人 // 提案人
if(ArrayUtil.contains(pageForm.getOrigins(),"createUserName")){ if (ArrayUtil.contains(pageForm.getOrigins(), "createUserName")) {
for (String keyword : pageForm.getCommonKeyword().split(" ")) { for (String keyword : pageForm.getCommonKeyword().split(" ")) {
seg.orLike("info.createUserName", keyword); seg.orLike("info.createUserName", keyword);
seg.orLike("info.createUserLoginName", keyword); seg.orLike("info.createUserLoginName", keyword);
@@ -103,21 +106,24 @@ public class ProposalQueryComprehensiveController {
} }
// 答复内容 // 答复内容
if(ArrayUtil.contains(pageForm.getOrigins(),"answer")){ if (ArrayUtil.contains(pageForm.getOrigins(), "answer")) {
for (String keyword : pageForm.getCommonKeyword().split(" ")) { for (String keyword : pageForm.getCommonKeyword().split(" ")) {
seg.orLike("JSON_EXTRACT(replyTask.variable,'$.tf_opinion')", keyword); seg.orLike("JSON_EXTRACT(replyTask.variable,'$.tf_opinion')", keyword);
} }
} }
if(!seg.isEmpty()){ if (!seg.isEmpty()) {
cnd.and(seg); cnd.and(seg);
} }
} }
if(StrUtil.isNotBlank(pageForm.getUnderTakeUnitId())){ if (StrUtil.isNotBlank(pageForm.getUnderTakeUnitId())) {
cnd.andEX("pru.unitId", "=", pageForm.getUnderTakeUnitId()); cnd.andEX("pru.unitId", "=", pageForm.getUnderTakeUnitId());
} }
ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm); ProposalQueryComprehensiveParam.buildSearch(cnd, pageForm);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(),RoleConstant.SCHOOL_UNION_PROPOSAL_ADMIN.name())){
cnd.and("us.unionId", "=", SecurityUtil.getUnionId());
}
cnd.groupBy("info.id"); cnd.groupBy("info.id");
sql.setCondition(cnd); sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql); Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
@@ -1,23 +1,26 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact; package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.bpm.enums.BpmProcessTaskStatusEnum; import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.bpm.models.BpmProcessTask; import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.bpm.service.BpmService; import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.entity.ProcessTaskActor;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.controller.vo.ProposalInfoPageVO; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalUndertake;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalCommitteeFilingUnitApprovalParam;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingUnitService; import com.budwk.app.zhgh.democratic.proposal.service.ProposalCommitteeFilingUnitService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -25,10 +28,10 @@ import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.aop.Aop; import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject; import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean; import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.mvc.annotation.At; import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok; import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param; import org.nutz.mvc.annotation.Param;
@@ -46,10 +49,15 @@ public class ProposalCommitteeFilingUnitController {
@Inject @Inject
private BaseService baseService; private BaseService baseService;
@Inject @Inject
private BpmService bpmService;
@Inject
private ProposalCommitteeFilingUnitService proposalCommitteeFilingUnitService; private ProposalCommitteeFilingUnitService proposalCommitteeFilingUnitService;
@Inject
private ProposalCommonService proposalCommonService;
@Inject
private FlowCommonService flowCommonService;
@Inject
private ProcessTaskService processTaskService;
@At("") @At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html") @Ok("beetl:/platform/zhgh/democratic/proposal/transact/committeeFilingUnit/index.html")
@SaCheckPermission("proposal.committeeFilingUnit") @SaCheckPermission("proposal.committeeFilingUnit")
@@ -64,123 +72,143 @@ public class ProposalCommitteeFilingUnitController {
SELECT SELECT
info.*, info.*,
type.name AS typeName, type.name AS typeName,
tcs.fullName AS sessionName, IF(mer.proposalId IS NOT NULL, 1, 0) AS merge,
tcd.`name` AS delegationName, tcs.fullName AS sessionName,
inst.id processInstanceId, tcd.`name` AS delegationName,
inst.processInstanceNodeId, ins.id AS instanceId,
inst.processInstanceNodeName, ins.businessNo,
inst.processInstanceTaskIds, ins.state instanceState,
inst.processInstanceStatus, ins.variable instanceVariable,
task.id processInstanceTaskId, ins.processDefineId instanceProcessDefineId,
task.taskStatus processInstanceTaskStatus, t.id taskId,
COUNT(p.consolidationIds) > 0 AS isConsolidation, t.taskName AS taskKey,
EXISTS ( t.displayName taskName,
SELECT 1 t.taskType,
FROM bpm_process_task next_task t.performType taskPerformType,
WHERE next_task.prevTaskId = task.id t.taskState,
AND next_task.taskStatus = 'COMPLETE' t.finishTime,
) AS nextTaskIsComplete t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL OR (ins.state = 20 AND info.caseFilingResult = 'NOT'), 1, 0) AS canRevoke
FROM FROM
bpm_process_task task wf_process_task t
INNER JOIN bpm_process_node_define nd ON nd.id = task.processTaskNodeId LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
INNER JOIN bpm_process_instance inst ON inst.id = task.processInstanceId LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
INNER JOIN proposal_info info ON info.id = inst.processInstanceBusinessId LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN proposal_consolidation p ON JSON_CONTAINS(p.consolidationIds, JSON_QUOTE(info.id)) LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN teacher_congress_session tcs ON tcs.id = info.sessionId LEFT JOIN proposal_merge mer ON mer.proposalId = info.id
LEFT JOIN teacher_congress_delegation tcd ON tcd.id = info.delegationId LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN bpm_process_task nt ON nt.prevTaskId = task.id AND nt.taskStatus = 'COMPLETE' LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition $condition
"""); """);
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "committeeFilingUnit");
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and(new Static("JSON_CONTAINS( task.assignments, '\"%s\"' )".formatted(SecurityUtil.getUserLoginname()))); cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
} }
cnd.and("nd.nodeCode", "=", 70);
if (approval) { if (approval) {
cnd.and("task.taskStatus", "=", BpmProcessTaskStatusEnum.COMPLETE); cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else { } else {
cnd.and("task.taskStatus", "=", BpmProcessTaskStatusEnum.ACTIVE); cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
} }
ProposalSearchParam.buildSearch(cnd, pageForm); ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.and(new Static(""" cnd.groupBy("t.id");
NOT EXISTS( cnd.desc("t.createdAt");
SELECT 1
FROM bpm_process_task t2
WHERE t2.processInstanceId = task.processInstanceId
AND t2.processTaskNodeCode = task.processTaskNodeCode
AND t2.createdOn > task.createdOn
)
"""));
cnd.groupBy("info.id");
cnd.groupBy("task.id");
sql.setCondition(cnd); sql.setCondition(cnd);
Pagination pagination = baseService.listPageVO(pageForm, sql, ProposalInfoPageVO.class); Pagination pagination = proposalCommitteeFilingUnitService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination); return Result.success(pagination);
} }
@At @At
@SaCheckPermission("proposal.committeeFilingUnit") @SaCheckPermission("proposal.committeeFilingUnit")
@ApiOperation("执行任务")
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SLog(tag = "委员会确认承办单位", msg = "委员会确认承办单位") public Result executeTask(@Param("data") String data) {
@ApiOperation("委员会确认承办单位") Dict args = Json.fromJson(Dict.class, data);
public Result approval(@Valid @Param("approval") ProposalCommitteeFilingUnitApprovalParam approvalParam) { String proposalId = args.getStr("proposalId");
proposalCommitteeFilingUnitService.approval(approvalParam);
return Result.success();
}
@At // 查询是否提案
@SaCheckPermission("proposal.committeeFilingUnit")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "委员会确认承办单位", msg = "委员会确认承办单位撤回")
public Result revoke(@Valid String taskId) {
proposalCommitteeFilingUnitService.revoke(taskId);
return Result.success();
}
@At // 单条审核
@SaCheckPermission("proposal.committeeFilingUnit") List<String> mergeProposalIds = proposalCommonService.mergeProposal(proposalId);
@ApiOperation("查询立案的结果及承办单位") if (ObjectUtil.isEmpty(mergeProposalIds)) {
public Result committeeFiling(@Valid String processInstanceId, @Valid String processInstanceTaskId) { flowCommonService.executeTask(args);
JSONObject newJson = new JSONObject(); return Result.success();
BpmProcessTask caseUnitTask = proposalCommitteeFilingUnitService.dao().fetch(BpmProcessTask.class,
Cnd.where(BpmProcessTask::getId, "=", processInstanceTaskId)
.and(BpmProcessTask::getProcessInstanceId, "=", processInstanceId)
.and(BpmProcessTask::getProcessTaskNodeCode, "=", 70)
.and(BpmProcessTask::getDelFlag, "=", 0)
);
if (ObjectUtil.isNotNull(caseUnitTask) && ObjectUtil.isNotEmpty(caseUnitTask.getExtVariable())) {
JSONObject jsonObject = caseUnitTask.getExtVariable();
newJson.set("caseFilingResult", jsonObject.get("caseFilingResult"));
newJson.set("hostUnitId", jsonObject.get("hostUnitId"));
newJson.set("helpUnitIds", jsonObject.getBeanList("helpUnitIds", String.class));
newJson.set("approvalOpinion", jsonObject.get("approvalOpinion"));
newJson.set("consolidationIds", jsonObject.getBeanList("consolidationIds", String.class));
return Result.success(newJson);
} }
BpmProcessTask caseTask = proposalCommitteeFilingUnitService.dao().fetch(BpmProcessTask.class, // 并案审核
Cnd.where(BpmProcessTask::getProcessInstanceId, "=", processInstanceId) ProcessTask thisTask = baseService.dao().fetch(ProcessTask.class, args.getLong(FlowConst.PROCESS_TASK_ID_KEY));
.and(BpmProcessTask::getDelFlag, "=", 0) List<ProcessTask> mergeTasks = processTaskService.getDoingTaskByBizIdTaskName(mergeProposalIds, thisTask.getTaskName());
.and(BpmProcessTask::getProcessTaskNodeCode, "=", 60) for (ProcessTask mergeTask : mergeTasks) {
.desc(BpmProcessTask::getCreatedOn) Dict cloneArgs = args.clone();
); cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, mergeTask.getId());
JSONObject jsonObject = caseTask.getExtVariable(); flowCommonService.executeTask(cloneArgs);
newJson.set("caseFilingResult", jsonObject.get("caseFilingResult")); }
newJson.set("hostUnitId", jsonObject.get("hostUnitId"));
newJson.set("helpUnitIds", jsonObject.getBeanList("helpUnitIds", String.class)); ProposalInfo info = proposalCommonService.fetch(proposalId);
newJson.set("approvalOpinion", jsonObject.get("approvalOpinion")); info.setCaseFilingResult(args.getStr("caseFilingResult"));
newJson.set("consolidationIds", jsonObject.getBeanList("consolidationIds", String.class)); return Result.success();
return Result.success(newJson);
} }
@At
@SaCheckLogin
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("撤销任务")
public Result revokeTask(@Param("taskId") Long taskId,@Param("proposalId")String proposalId) {
// 单条审核
List<String> mergeProposalIds = proposalCommonService.mergeProposal(proposalId);
if (ObjectUtil.isEmpty(mergeProposalIds)) {
flowCommonService.revokeTask(taskId);
return Result.success();
}
// 并案审核
ProcessTask thisTask = baseService.dao().fetch(ProcessTask.class, taskId);
List<ProcessTask> mergeTasks = processTaskService.getDoneTaskByBizIdTaskName(mergeProposalIds, thisTask.getTaskName());
for (ProcessTask mergeTask : mergeTasks) {
flowCommonService.revokeTask(mergeTask.getId());
}
return Result.success();
}
@At @At
@SaCheckPermission("proposal.committeeFilingUnit") @SaCheckPermission("proposal.committeeFilingUnit")
@ApiOperation("查询承办单位") public Result doUpData(){
public Result listUnderTake() { List<ProcessTask> tasks = baseService.dao().query(ProcessTask.class, Cnd.where("taskName", "=", "personnelOffice"));
List<ProposalUndertake> list = baseService.dao().query(ProposalUndertake.class, Cnd.NEW().asc(ProposalUndertake::getCode)); List<Long> list = tasks.stream().map(ProcessTask::getId).toList();
return Result.success(list);
List<ProcessTaskActor> actorList = baseService.dao().query(ProcessTaskActor.class, Cnd.where(ProcessTaskActor::getProcessTaskId, "in", list));
List<ProcessInstance> instanceList = baseService.dao().query(ProcessInstance.class, Cnd.where(ProcessInstance::getProcessDefineId, "=", 396));
for (ProcessTask task : tasks) {
task.setTaskName("committee");
task.setDisplayName("提案委员会立案");
task.setFormKey("/platform/proposal/committee");
task.setH5FormKey("");
}
for (ProcessTaskActor actor : actorList) {
actor.setActorId("1fb89886bf4c43dcb48409c83d3363c3");
actor.setActorAccount("018054");
actor.setActorName("赵丽霞");
actor.setActorUnitName("工会");
actor.setActorUnitId("0011");
}
for (ProcessInstance instance : instanceList) {
instance.setProcessDefineId(399L);
}
baseService.update(tasks);
baseService.update(actorList);
baseService.update(instanceList);
return Result.success();
} }
} }
@@ -1,121 +0,0 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.bpm.service.BpmService;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalDelegationService;
import com.budwk.app.zhgh.democratic.proposal.service.common.ProposalCommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.validation.Valid;
import java.util.List;
/**
* @author zhf
* @date 2026/3/12 09:36
* @description 人事处审核
*/
@IocBean
@At("/platform/proposal/personnelOffice")
@Slf4j
@Ok("json:full")
@Api(tags = "提案-办理-人事处审核")
public class ProposalPersonnelOfficeController {
@Inject
private BaseService baseService;
@Inject
private ProposalCommonService proposalCommonService;
@Inject
private ProposalDelegationService proposalDelegationService;
@Inject
private BpmService bpmService;
@At("")
@Ok("beetl:/platform/zhgh/democratic/proposal/transact/personnelOffice/index.html")
@SaCheckPermission("proposal.personnelOffice")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/democratic/proposal/transact/personnelOffice/index.html")
@SaCheckPermission("h5.proposal.personnelOffice")
public void h5Index() {
}
@At
@SaCheckPermission("proposal.personnelOffice")
@ApiOperation("分页列表")
public Result pageData(@Valid ProposalSearchParam pageForm, boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
type.name AS typeName,
tcs.fullName AS sessionName,
tcd.`name` AS delegationName,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
LEFT JOIN proposal_info info ON info.id = ins.businessNo
LEFT JOIN proposal_type type on type.id = info.typeId
LEFT JOIN teacher_congress_session tcs on tcs.id = info.sessionId
LEFT JOIN teacher_congress_delegation tcd on tcd.id = info.delegationId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "personnelOffice");
if(!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())){
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
if (approval) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
ProposalSearchParam.buildSearch(cnd, pageForm);
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(),pageForm.getPageSize(), sql);
return Result.success(pagination);
}
}
@@ -1,13 +1,11 @@
package com.budwk.app.zhgh.democratic.proposal.controller.transact; package com.budwk.app.zhgh.democratic.proposal.controller.transact;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination; import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result; import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService; import com.budwk.app.base.service.BaseService;
import com.budwk.app.bpm.service.BpmService; import com.budwk.app.bpm.service.BpmService;
import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam; import com.budwk.app.zhgh.democratic.proposal.param.ProposalSearchParam;
import com.budwk.app.zhgh.democratic.proposal.service.ProposalSecondedService; import com.budwk.app.zhgh.democratic.proposal.service.ProposalSecondedService;
@@ -101,7 +99,7 @@ public class ProposalSecondedController {
Cnd cnd = Cnd.NEW(); Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "second"); cnd.and("t.taskName", "=", "second");
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) { if (!SecurityUtil.getUserLoginname().equals("sysadmin")) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId())); cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
} }
@@ -0,0 +1,46 @@
package com.budwk.app.zhgh.democratic.proposal.handler;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.flow.engine.AssignmentHandler;
import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.engine.model.TaskModel;
import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysRoleService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.lang.Lang;
import java.util.List;
/**
* @author zhf
* @date 2026/3/16 13:54
* @description 提案人事处管理员
*/
public class proposalPersonnelOfficeAdminHandler implements AssignmentHandler {
@Override
public List<String> assign(TaskModel model, Execution execution) {
SysRoleService sysRoleService = ServiceContext.find(SysRoleService.class);
Sys_role role = sysRoleService.getByCode(RoleConstant.PROPOSAL_PERSONNEL_OFFICE_ADMIN);
List<Sys_user_role> roles = ServiceContext.find(Dao.class).query(
Sys_user_role.class,
Cnd.where(Sys_user_role::getRoleId, "=", role.getId()));
if (Lang.isEmpty(roles)) {
throw new RuntimeException("当前登录用户所在单位未设置单位党委书记,请联系校工会进行设置。");
}
return roles.stream().map(Sys_user_role::getUserId).toList();
}
@Override
public String getMessage() {
return "获取当前提案人事处管理员";
}
@Override
public int getOrder() {
return AssignmentHandler.super.getOrder();
}
}
@@ -1,24 +1,23 @@
package com.budwk.app.zhgh.democratic.proposal.interceptor; package com.budwk.app.zhgh.democratic.proposal.interceptor;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant; import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.engine.FlowInterceptor; import com.budwk.app.flow.engine.FlowInterceptor;
import com.budwk.app.flow.engine.core.Execution; import com.budwk.app.flow.engine.core.Execution;
import com.budwk.app.flow.engine.core.ServiceContext; import com.budwk.app.flow.engine.core.ServiceContext;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.sys.models.Sys_role; import com.budwk.app.sys.models.Sys_role;
import com.budwk.app.sys.models.Sys_user; import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.sys.models.Sys_user_role;
import com.budwk.app.sys.services.SysRoleService; import com.budwk.app.sys.services.SysRoleService;
import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo; import com.budwk.app.zhgh.democratic.proposal.models.ProposalInfo;
import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation; import com.budwk.app.zhgh.democratic.teachercongress.delegation.models.Teacher_congress_delegation;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
import org.nutz.dao.Sqls; import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql; import org.nutz.dao.sql.Sql;
import java.util.List;
/** /**
* @ClassName ProposalDelegationPrefixInterceptor * @ClassName ProposalDelegationPrefixInterceptor
* @Author JyuHsin * @Author JyuHsin
@@ -77,6 +76,12 @@ public class ProposalDelegationPrefixInterceptor implements FlowInterceptor {
execution.getArgs().set(FlowConst.TASK_FORM_DATA_PREFIX + "delegationAudit", delegationAudit); execution.getArgs().set(FlowConst.TASK_FORM_DATA_PREFIX + "delegationAudit", delegationAudit);
ProcessTask processTask = execution.getProcessTask();
Dict dict = JSONUtil.toBean(processTask.getVariable(), Dict.class);
dict.set(FlowConst.TASK_FORM_DATA_PREFIX + "delegationAudit", delegationAudit);
processTask.setVariable(JSONUtil.toJsonStr(dict));
dao.update(processTask);
// 存储一些参数,方便查询副团长 // 存储一些参数,方便查询副团长
execution.getArgs().set("sessionId", proposalInfo.getSessionId()); execution.getArgs().set("sessionId", proposalInfo.getSessionId());
execution.getArgs().set("delegationId", proposalInfo.getDelegationId()); execution.getArgs().set("delegationId", proposalInfo.getDelegationId());
@@ -4,6 +4,7 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams; import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil; import cn.hutool.http.HtmlUtil;
import com.budwk.app.base.param.ExportTableColumns; import com.budwk.app.base.param.ExportTableColumns;
@@ -192,25 +193,30 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
// 添加序号,去除富文本,获取附议人 // 添加序号,去除富文本,获取附议人
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
list.get(i).put("index", i + 1); list.get(i).put("index", i + 1);
list.get(i).put("brief", HtmlUtil.cleanHtmlTag(list.get(i).getString("brief"))); list.get(i).put("brief", HtmlUtil.cleanHtmlTag(list.get(i).getString("brief")).replaceAll("&nbsp;"," "));
list.get(i).put("measures", HtmlUtil.cleanHtmlTag(list.get(i).getString("measures"))); list.get(i).put("measures", HtmlUtil.cleanHtmlTag(list.get(i).getString("measures")).replaceAll("&nbsp;"," "));
// 流程实例 // 流程实例
ProcessInstance instance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", list.get(i).getString("id"))); ProcessInstance instance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", list.get(i).getString("id")));
// 查询附议人信息 // 查询附议人信息
ProcessTask inviteTask = dao().fetch(ProcessTask.class, if (ObjectUtil.isNotEmpty(instance)){
Cnd.where(ProcessTask::getProcessInstanceId, "=", instance.getId()) ProcessTask inviteTask = dao().fetch(ProcessTask.class,
.and(ProcessTask::getTaskName, "=", "invite") Cnd.where(ProcessTask::getProcessInstanceId, "=", instance.getId())
.and(ProcessTask::getTaskState, "in", .and(ProcessTask::getTaskName, "=", "invite")
List.of(ProcessTaskStateEnum.DOING.getCode(), ProcessTaskStateEnum.FINISHED.getCode())) .and(ProcessTask::getTaskState, "in",
.desc(ProcessTask::getCreatedAt)); List.of(ProcessTaskStateEnum.DOING.getCode(), ProcessTaskStateEnum.FINISHED.getCode()))
.desc(ProcessTask::getCreatedAt));
if (inviteTask != null) { if (inviteTask != null) {
NutMap variable = Json.fromJson(NutMap.class, inviteTask.getVariable()); NutMap variable = Json.fromJson(NutMap.class, inviteTask.getVariable());
List<NutMap> seconders = variable.getAsList(FlowConst.TASK_FORM_DATA_PREFIX + "seconder", NutMap.class); List<NutMap> seconders = variable.getAsList(FlowConst.TASK_FORM_DATA_PREFIX + "seconder", NutMap.class);
list.get(i).put("secondedUserNames", seconders.stream().map(v -> v.getString("userName")).collect(Collectors.joining(","))); if (seconders != null){
list.get(i).put("secondedUserNames", seconders.stream().map(v -> v.getString("userName")).collect(Collectors.joining(",")));
}
}
} }
} }
List<ExcelExportEntity> exportEntities = new ArrayList<>(); List<ExcelExportEntity> exportEntities = new ArrayList<>();
@@ -570,8 +576,8 @@ public class ProposalExportServiceImpl extends BaseServiceImpl<ProposalInfo> imp
// 添加序号,去除富文本,获取附议人 // 添加序号,去除富文本,获取附议人
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
list.get(i).put("index", i + 1); list.get(i).put("index", i + 1);
list.get(i).put("brief", HtmlUtil.cleanHtmlTag(list.get(i).getString("brief"))); list.get(i).put("brief", HtmlUtil.cleanHtmlTag(list.get(i).getString("brief")).replaceAll("&nbsp;"," "));
list.get(i).put("measures", HtmlUtil.cleanHtmlTag(list.get(i).getString("measures"))); list.get(i).put("measures", HtmlUtil.cleanHtmlTag(list.get(i).getString("measures")).replaceAll("&nbsp;"," "));
// 流程实例 // 流程实例
// ProcessInstance instance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", list.get(i).getString("id"))); // ProcessInstance instance = dao().fetch(ProcessInstance.class, Cnd.where(ProcessInstance::getBusinessNo, "=", list.get(i).getString("id")));
File diff suppressed because one or more lines are too long
@@ -14,7 +14,8 @@
<!-- 引入 core 包和对应 css--> <!-- 引入 core 包和对应 css-->
<script src="/assets/platform/plugins/logicflow/logic-flow.js"></script> <script src="/assets/platform/plugins/logicflow/logic-flow.js"></script>
<link rel="stylesheet" href="/assets/platform/plugins/logicflow/index.css"/> <link rel="stylesheet" href="/assets/platform/plugins/logicflow/index.css"/>
<script src="https://cdn.jsdelivr.net/npm/@logicflow/extension@2.1.4/dist/index.min.js"></script> <script src="/assets/platform/plugins/logicflow/index.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension@2.1.4/dist/index.min.js"></script>-->
<script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script> <script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>
<style> <style>
#snaker-flow-preview { #snaker-flow-preview {
@@ -27,7 +27,7 @@ layout("/layouts/platform.html"){
></el-option> ></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="代表团" > <search-item label="代表团">
<el-select clearable filterable placeholder="请选择代表团" style="width: 100%" <el-select clearable filterable placeholder="请选择代表团" style="width: 100%"
v-model="pageForm.delegationId"> v-model="pageForm.delegationId">
<el-option :key="item.id" :label="item.name" :value="item.id" <el-option :key="item.id" :label="item.name" :value="item.id"
@@ -41,6 +41,19 @@ layout("/layouts/platform.html"){
v-for="item in unionOptions"></el-option> v-for="item in unionOptions"></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="委员类别">
<el-select
v-model="pageForm.roleCode"
filterable
placeholder="请选择委员类别"
clearable
style="width:100%;"
>
<el-option label="执委会委员" value="1"></el-option>
<el-option label="工会委员会委员" value="2"></el-option>
<el-option label="经审委员会委员" value="3"></el-option>
</el-select>
</search-item>
</search> </search>
</el-card> </el-card>
<el-card shadow="never" class="mt10"> <el-card shadow="never" class="mt10">
@@ -50,7 +63,7 @@ layout("/layouts/platform.html"){
size="small" size="small"
type="primary" type="primary"
@click="$refs.onePushFormalRef.onOpen(pageForm.teacherMeetId)" @click="$refs.onePushFormalRef.onOpen(pageForm.teacherMeetId)"
>委员推选 >候选委员录入
</el-button> </el-button>
</table-tool> </table-tool>
@@ -59,6 +72,7 @@ layout("/layouts/platform.html"){
:data="tableData" :data="tableData"
row-key="id" row-key="id"
@sort-change="pageOrder" @sort-change="pageOrder"
v-loading="tableLoading"
> >
<el-table-column :index="indexMethod" align="center" header-align="center" <el-table-column :index="indexMethod" align="center" header-align="center"
label="序号" label="序号"
@@ -72,6 +86,13 @@ layout("/layouts/platform.html"){
<el-table-column label="代表团" prop="delegationName"></el-table-column> <el-table-column label="代表团" prop="delegationName"></el-table-column>
<el-table-column label="所属工会" prop="unionName"></el-table-column> <el-table-column label="所属工会" prop="unionName"></el-table-column>
<el-table-column label="所属单位" prop="unitName"></el-table-column> <el-table-column label="所属单位" prop="unitName"></el-table-column>
<el-table-column label="委员类别" prop="roleCode">
<template scope="{row}">
<el-tag v-if="row.roleCode === '1'">执委会委员</el-tag>
<el-tag v-if="row.roleCode === '2'">工会委员会委员</el-tag>
<el-tag v-if="row.roleCode === '3'">经审委员会委员</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right"> <el-table-column label="操作" width="100" fixed="right">
<template scope="{row}"> <template scope="{row}">
<el-button <el-button
@@ -4,10 +4,19 @@ const DELEGATION_TWO_PUSH_FORMAL_DIALOG = {
<el-dialog <el-dialog
:close-on-click-modal="false" :close-on-click-modal="false"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
title="分工会最终投票" title="候选委员录入"
width="70%" width="70%"
> >
<div style="display:flex;align-items: center;margin-bottom: 20px">
<span style=" margin-right: 10px;white-space: nowrap;">录入类型:</span>
<el-radio-group v-model="roleCode">
<el-radio-button label="1">执委会委员</el-radio-button>
<el-radio-button label="2">工会委员会委员</el-radio-button>
<el-radio-button label="3">经审委员会委员</el-radio-button>
</el-radio-group>
</div>
<el-transfer <el-transfer
ref="transfer" ref="transfer"
v-model="userValue" v-model="userValue"
@@ -16,7 +25,7 @@ const DELEGATION_TWO_PUSH_FORMAL_DIALOG = {
:filter-method="filterMethod" :filter-method="filterMethod"
:props="{key: 'userId',label: 'name'}" :props="{key: 'userId',label: 'name'}"
:right-default-checked="rightChecked" :right-default-checked="rightChecked"
:titles="['可推选人员名单', '当前选择']" :titles="['可录入人员名单', '当前选择']"
filterable filterable
class="transfer-high" class="transfer-high"
> >
@@ -36,7 +45,6 @@ const DELEGATION_TWO_PUSH_FORMAL_DIALOG = {
</div> </div>
</el-transfer> </el-transfer>
<span slot="footer"> <span slot="footer">
<span style="color:red;font-size: 15px; display:flex;text-align: right;">投票名额:{{committeeQuotaCount}}个</span>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button> <el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="onConfirm">确 定</el-button> <el-button type="primary" @click="onConfirm">确 定</el-button>
@@ -53,7 +61,8 @@ const DELEGATION_TWO_PUSH_FORMAL_DIALOG = {
rightChecked: [], rightChecked: [],
attendanceRightChecked: [], attendanceRightChecked: [],
teacherMeetId: null, teacherMeetId: null,
committeeQuotaCount: 0 committeeQuotaCount: 0,
roleCode: null,
} }
}, },
methods: { methods: {
@@ -80,12 +89,17 @@ const DELEGATION_TWO_PUSH_FORMAL_DIALOG = {
return item.userName.indexOf(query) > -1 return item.userName.indexOf(query) > -1
}, },
async onConfirm() { async onConfirm() {
if (!this.roleCode) {
this.$message.error('请选择录入类型')
return
}
const { const {
code, code,
msg msg
} = await this.$axios.post('/platform/executiveCommittee/delegationTwoPush/addOnePush', { } = await this.$axios.post('/platform/executiveCommittee/delegationTwoPush/addOnePush', {
userValue: JSON.stringify(this.userValue), userValue: JSON.stringify(this.userValue),
teacherMeetId: this.teacherMeetId teacherMeetId: this.teacherMeetId,
roleCode: this.roleCode
}) })
if (code === 0) { if (code === 0) {
this.dialogVisible = false this.dialogVisible = false
@@ -80,8 +80,8 @@ layout("/layouts/platform.html"){
<el-table-column label="代表团" prop="delegationName"></el-table-column> <el-table-column label="代表团" prop="delegationName"></el-table-column>
<el-table-column label="所属工会" prop="unionName"></el-table-column> <el-table-column label="所属工会" prop="unionName"></el-table-column>
<el-table-column label="所属单位" prop="unitName"></el-table-column> <el-table-column label="所属单位" prop="unitName"></el-table-column>
<el-table-column label="票数" prop="pushCount"></el-table-column> <el-table-column label="委员类别" prop="addTypeName"></el-table-column>
<el-table-column label="操作" width="130" fixed="right"> <!--<el-table-column label="操作" width="130" fixed="right">
<template scope="{row}"> <template scope="{row}">
<el-button <el-button
size="mini" size="mini"
@@ -90,7 +90,7 @@ layout("/layouts/platform.html"){
>推选详情 >推选详情
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>-->
</el-table> </el-table>
<!--#include("/layouts/pagination.html"){}#--> <!--#include("/layouts/pagination.html"){}#-->
</el-card> </el-card>
@@ -41,6 +41,19 @@ layout("/layouts/platform.html"){
v-for="item in delegations"></el-option> v-for="item in delegations"></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="委员类别">
<el-select
v-model="pageForm.roleCode"
filterable
placeholder="请选择委员类别"
clearable
style="width:100%;"
>
<el-option label="执委会委员" value="1"></el-option>
<el-option label="工会委员会委员" value="2"></el-option>
<el-option label="经审委员会委员" value="3"></el-option>
</el-select>
</search-item>
</search> </search>
</el-card> </el-card>
<el-card shadow="never" class="mt10"> <el-card shadow="never" class="mt10">
@@ -50,7 +63,7 @@ layout("/layouts/platform.html"){
size="small" size="small"
type="primary" type="primary"
@click="$refs.onePushFormalRef.onOpen(pageForm.teacherMeetId)" @click="$refs.onePushFormalRef.onOpen(pageForm.teacherMeetId)"
>委员推选 >正式委员录入
</el-button> </el-button>
</table-tool> </table-tool>
@@ -59,6 +72,7 @@ layout("/layouts/platform.html"){
:data="tableData" :data="tableData"
row-key="id" row-key="id"
@sort-change="pageOrder" @sort-change="pageOrder"
v-loading="tableLoading"
> >
<el-table-column :index="indexMethod" align="center" header-align="center" <el-table-column :index="indexMethod" align="center" header-align="center"
label="序号" label="序号"
@@ -72,6 +86,13 @@ layout("/layouts/platform.html"){
<el-table-column align="center" label="代表团" prop="delegationName"></el-table-column> <el-table-column align="center" label="代表团" prop="delegationName"></el-table-column>
<el-table-column align="center" label="所属工会" prop="unionName"></el-table-column> <el-table-column align="center" label="所属工会" prop="unionName"></el-table-column>
<el-table-column align="center" label="所属单位" prop="unitName"></el-table-column> <el-table-column align="center" label="所属单位" prop="unitName"></el-table-column>
<el-table-column label="委员类别" prop="roleCode">
<template scope="{row}">
<el-tag v-if="row.roleCode === '1'">执委会委员</el-tag>
<el-tag v-if="row.roleCode === '2'">工会委员会委员</el-tag>
<el-tag v-if="row.roleCode === '3'">经审委员会委员</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="100" fixed="right"> <el-table-column align="center" label="操作" width="100" fixed="right">
<template scope="{row}"> <template scope="{row}">
<el-button <el-button
@@ -4,7 +4,7 @@ const PUSH_FORMAL_DIALOG = {
<el-dialog <el-dialog
:close-on-click-modal="false" :close-on-click-modal="false"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
title="筹备组推选" title="正式委员录入"
width="70%" width="70%"
> >
@@ -31,12 +31,12 @@ const PUSH_FORMAL_DIALOG = {
</span> </span>
<span class="detail-item">{{ option.age }}岁</span> <span class="detail-item">{{ option.age }}岁</span>
<span class="detail-item">{{ option.professionalTitle }}</span> <span class="detail-item">{{ option.professionalTitle }}</span>
<span class="detail-item" style="color:#ee0a24;">{{ option.roleCode==='1'?'执委会委员':option.roleCode==='2'?'工会委员会委员':'经审委员会委员' }}</span>
</div> </div>
</div> </div>
</div> </div>
</el-transfer> </el-transfer>
<span slot="footer"> <span slot="footer">
<span style="color:red;font-size: 15px; display:flex;text-align: right;">推选名额:{{prepareGroupQuotaCount}}个</span>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button> <el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="onConfirm">确 定</el-button> <el-button type="primary" @click="onConfirm">确 定</el-button>
@@ -144,7 +144,7 @@ const PROPOSAL_INFO = {
<!--提案委员会立案--> <!--提案委员会立案-->
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" <el-descriptions border class="flow-task-form" :column="3" :key="task.id"
v-else-if="task.taskName === 'committee'"> v-else-if="task.taskName === 'committee'||task.taskName === 'committeeFilingUnit'">
<el-descriptions-item label="办理用户">{{ task.taskFormData.userName <el-descriptions-item label="办理用户">{{ task.taskFormData.userName
}}({{task.taskFormData.loginName}}) }}({{task.taskFormData.loginName}})
</el-descriptions-item> </el-descriptions-item>
@@ -244,6 +244,7 @@ const PROPOSAL_INFO = {
this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => { this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.doneTasks = res.data this.doneTasks = res.data
this.$emit("done-tasks", this.doneTasks)
} }
}) })
}, },
@@ -147,9 +147,9 @@ layout("/layouts/platform.html"){
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="审核意见" prop="approvalOpinion" <el-form-item label="审核意见" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.approvalOpinion"></user-opinion-textarea> <user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
@@ -71,9 +71,9 @@ const merge = {
></el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="审核意见" prop="approvalOpinion" <el-form-item label="审核意见" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.approvalOpinion"></user-opinion-textarea> <user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
@@ -2,96 +2,91 @@
layout("/layouts/platform.html"){ layout("/layouts/platform.html"){
#--> #-->
<style></style> <div id="app">
<div id="app" v-cloak>
<guava ref="guava"> <guava ref="guava">
<el-card shadow="never"> <el-card shadow="never">
<search @search="doSearch"> <search @search="doSearch">
<search-item label="提案编号">
<el-input v-model="pageForm.code" placeholder="提案编号" @keyup.enter.native="doSearch" clearable style="width: 100%"></el-input>
</search-item>
<search-item label="提案名称">
<el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable style="width: 100%"></el-input>
</search-item>
<search-item label="提案人姓名">
<el-input
@keyup.enter.native="doSearch"
clearable
placeholder="请输入提案人姓名"
v-model="pageForm.createUserName"
style="width: 100%"
></el-input>
</search-item>
<search-item label="提案人工号">
<el-input
@keyup.enter.native="doSearch"
clearable
placeholder="请输入提案人工号"
v-model="pageForm.createUserLoginName"
style="width: 100%"
></el-input>
</search-item>
<search-item label="教代会"> <search-item label="教代会">
<el-select @change="meetingChange" clearable filterable placeholder="所属教代会" v-model="pageForm.sessionId"> <el-select @change="meetingChange" clearable filterable placeholder="所属教代会"
<el-option :key="item.id" :label="item.fullName" :value="item.id" v-for="item in sessionOptions"></el-option> v-model="pageForm.sessionId">
<el-option :key="item.id" :label="item.fullName" :value="item.id"
v-for="item in sessionOptions"></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="提案编号">
<el-input v-model="pageForm.code" placeholder="提案编号" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
</search-item>
<search-item label="提案名称">
<el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
</search-item>
<search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.createUserKeyword"></el-input>
</search-item>
<!-- <search-item label="提案人姓名">-->
<!-- <el-input-->
<!-- @keyup.enter.native="doSearch"-->
<!-- clearable-->
<!-- placeholder="请输入提案人姓名"-->
<!-- v-model="pageForm.createUserName"-->
<!-- style="width: 100%"-->
<!-- ></el-input>-->
<!-- </search-item>-->
<!-- <search-item label="提案人工号">-->
<!-- <el-input-->
<!-- @keyup.enter.native="doSearch"-->
<!-- clearable-->
<!-- placeholder="请输入提案人工号"-->
<!-- v-model="pageForm.createUserLoginName"-->
<!-- style="width: 100%"-->
<!-- ></el-input>-->
<!-- </search-item>-->
</search> </search>
</el-card> </el-card>
<el-card shadow="never"> <el-card shadow="never">
<table-tool> <table-tool :columns.sync="tableColumns">
<el-button @click="openConsolidationApproval" size="small" type="primary" class="mr5">重新并案审核</el-button> <!-- <el-button type="primary" icon="el-icon-plus" size="small" @click="openMerge">并案审核</el-button>-->
<el-radio-group v-model="pageForm.approval" @change="doSearch();$refs.tableRef.clearSelection()" size="small"> <el-button type="primary" icon="el-icon-plus" size="small" @click="doUpData">更新</el-button>
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button> <el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button> <el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group> </el-radio-group>
</table-tool> </table-tool>
<el-table <el-table :data="tableData" @sort-change="pageOrder" ref="tableRef" row-key="id" style="width: 100%">
:data="tableData" <el-table-column type="selection" width="50" fixed="left" v-if="!pageForm.approval"></el-table-column>
ref="tableRef" <el-table-column label="序号" width="50" type="index" :index="indexMethod"
@sort-change="pageOrder" fixed="left"></el-table-column>
header-align="center"
style="width: 100%"
:row-key="(val)=>{val.id + val.processInstanceTaskId}"
>
<el-table-column <el-table-column
v-if="!pageForm.approval" :label="column.label"
type="selection" :prop="column.prop"
reserve-selection :key="column.key"
:selectable="(row)=>row.processInstanceTaskStatus==='ACTIVE'" :min-width="column.width"
></el-table-column> :fixed="column.fixed"
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column> show-overflow-tooltip
<el-table-column label="提案编号" prop="code" width="120" sortable></el-table-column> v-for="column in tableColumns"
<el-table-column label="提案名称" prop="name" show-overflow-tooltip></el-table-column> v-if="column.visible !== false"
<el-table-column label="提案人" prop="createUserName"></el-table-column> >
<el-table-column label="代表团" prop="delegationName" show-overflow-tooltip></el-table-column> <template v-if="column.prop === 'caseFilingResult'" scope="{row}">
<el-table-column label="立案结果" prop="caseFilingResult"> <dict-tag v-if="row.taskState!==10"
<template scope="{row}"> :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT" :value="row.caseFilingResult"></dict-tag> :value="row.caseFilingResult"></dict-tag>
</template>
<template v-else-if="column.prop === 'merge'" scope="{row}">
<el-tag v-if="row.merge" size="small"></el-tag>
</template>
<template v-else-if="column.prop === 'instanceState'" scope="{row}">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="是否并案" prop="isConsolidation"> <el-table-column label="操作" fixed="right" width="300px">
<template scope="{row}"> <template slot-scope="{row}">
<el-tag size="mini" v-if="row.isConsolidation" type="success"></el-tag> <el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-tag size="mini" v-else type="danger"></el-tag> <el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核
</template>
</el-table-column>
<el-table-column label="当前节点" prop="processInstanceNodeName" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" fixed="right" width="200px">
<template scope="{row}">
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
<el-button v-if="row.processInstanceTaskStatus==='ACTIVE'" @click="openApproval(row)" size="mini" type="primary">
审核
</el-button> </el-button>
<el-button <el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
v-if="row.processInstanceTaskStatus==='COMPLETE' && !row.nextTaskIsComplete"
@click="openRevoke(row.processInstanceTaskId)"
size="mini"
type="danger"
>
撤回
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
@@ -99,225 +94,216 @@ layout("/layouts/platform.html"){
<!--#include("/layouts/pagination.html"){}#--> <!--#include("/layouts/pagination.html"){}#-->
</el-card> </el-card>
<template #public>
<proposal-info ref="infoRef"></proposal-info>
<div v-if="showApprovalForm">
<div class="process-title">{{formData.processInstanceNodeName}}</div>
<el-alert v-if="formData.isConsolidation" type="success" title="提醒:本提案已并案,您只需审核一次即可!"></el-alert>
<el-divider></el-divider>
<el-form :model="formData" ref="approvalFormRef" label-position="left" label-width="80px">
<el-form-item label="立案结果" prop="caseFilingResult" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<el-radio-group v-model="formData.caseFilingResult" size="small">
<el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code" border>{{item.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="主办单位"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.caseFilingResult)"
:rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.caseFilingResult),message:'必填',trigger:['change','blur']}]"
>
<el-select v-model="formData.hostUnitId" filterable clearable style="width: 100%">
<el-option
v-for="item in underTakeOptions"
:label="item.name"
:value="item.id"
:key="item.id"
:disabled="formData && formData.helpUnitIds.includes(item.id)"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="协办单位" v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.caseFilingResult)">
<el-select v-model="formData.helpUnitIds" filterable clearable multiple style="width: 100%">
<el-option
v-for="item in underTakeOptions"
:label="item.name"
:value="item.id"
:key="item.id"
:disabled="item.id===formData.hostUnitId"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="审核意见" prop="approvalOpinion" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.approvalOpinion"></user-opinion-textarea>
</el-form-item>
<!-- <el-form-item label="电子签名" prop="approvalSignature" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">-->
<!-- <pc-signature v-model="formData.approvalSignature"></pc-signature>-->
<!-- </el-form-item>-->
</el-form>
<el-row type="flex" justify="end">
<el-button plain @click="$refs.guava.index()">取消</el-button>
<el-button type="primary" @click="doApproval('DYNAMIC')">提交</el-button>
</el-row>
</div>
</template>
<template #edit> <template #edit>
<div class="process-title">并案提案列表</div> <proposal-info ref="proposalInfoRef" @done-tasks="doneTasks">
<el-table :data="consolidationProposalTableData" size="small"> <div v-if="showApprovalForm">
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column> <div class="process-title">
<el-table-column label="提案编号" prop="code" width="120px" sortable></el-table-column> {{formData.taskName}}
<el-table-column label="提案名称" prop="name" show-overflow-tooltip></el-table-column> </div>
<el-table-column label="提案人" prop="createUserName" width="200px"></el-table-column> <el-form :model="formData" ref="formRef" label-width="120px" :rules="formRules" label-suffix="">
<el-table-column label="代表团" prop="delegationName" width="300px"></el-table-column> <el-form-item label="立案结果" prop="tf_caseFilingResult"
<el-table-column label="立案结果" prop="caseFilingResult" width="100px"> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<template scope="{row}"> <el-radio-group v-model="formData.tf_caseFilingResult" size="small">
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT" :value="row.caseFilingResult"></dict-tag> <el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code"
</template> border>{{item.label}}
</el-table-column> </el-radio>
<el-table-column label="是否并案" prop="isConsolidation" width="100px"> </el-radio-group>
<template scope="{row}"> </el-form-item>
<el-tag size="mini" v-if="row.isConsolidation" type="success"></el-tag> <el-form-item v-if="['CONFIRM_FILING'].includes(formData.tf_caseFilingResult)" label="立案类型"
<el-tag size="mini" v-else type="danger"></el-tag> prop="tf_caseFilingType"
</template> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
</el-table-column> <el-radio-group v-model="formData.tf_caseFilingType" size="small">
<el-table-column label="操作" width="100px"> <el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_TYPE" :label="item.code" border>
<template scope="{row}"> {{item.label}}
<el-link size="mini" type="primary" @click="openViewDialog(row)">查看</el-link> </el-radio>
</template> </el-radio-group>
</el-table-column> </el-form-item>
</el-table> <el-form-item
label="主办单位"
<div class="process-title">{{consolidationFormData.processInstanceNodeName}}</div> v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)"
<el-form :model="consolidationFormData" ref="consolidationFormRef" label-position="left" label-width="80px"> :rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult),message:'必填',trigger:['change','blur']}]"
<el-form-item label="立案结果" prop="caseFilingResult" :rules="[{required:true,message:'必填',trigger:['change','blur']}]"> prop="tf_masterUnitId"
<el-radio-group v-model="consolidationFormData.caseFilingResult" size="small"> >
<el-radio v-for="item in dict.type.PROPOSAL_CASE_FILING_RESULT" :label="item.code" border>{{item.label}}</el-radio> <el-select v-model="formData.tf_masterUnitId" filterable clearable style="width: 100%">
</el-radio-group> <el-option
</el-form-item> v-for="item in underTakeOptions"
<el-form-item :label="item.name"
label="主办单位" :value="item.id"
v-if="['CONFIRM_FILING','SUGGESTION'].includes(consolidationFormData.caseFilingResult)" :key="item.id"
:rules="[{required:['CONFIRM_FILING','SUGGESTION'].includes(consolidationFormData.caseFilingResult),message:'必填',trigger:['change','blur']}]" :disabled="formData && formData.tf_slaveUnitIds.includes(item.id)"
> ></el-option>
<el-select v-model="consolidationFormData.hostUnitId" filterable clearable style="width: 100%"> </el-select>
<el-option </el-form-item>
v-for="item in underTakeOptions" <el-form-item label="协办单位"
:label="item.name" v-if="['CONFIRM_FILING','SUGGESTION'].includes(formData.tf_caseFilingResult)"
:value="item.id" prop="tf_slaveUnitIds"
:key="item.id" >
:disabled="consolidationFormData && consolidationFormData.helpUnitIds.includes(item.id)" <el-select v-model="formData.tf_slaveUnitIds" filterable clearable multiple
></el-option> style="width: 100%">
</el-select> <el-option
</el-form-item> v-for="item in underTakeOptions"
<el-form-item label="协办单位" v-if="['CONFIRM_FILING','SUGGESTION'].includes(consolidationFormData.caseFilingResult)"> :label="item.name"
<el-select v-model="consolidationFormData.helpUnitIds" filterable clearable multiple style="width: 100%"> :value="item.id"
<el-option :key="item.id"
v-for="item in underTakeOptions" :disabled="item.id===formData.tf_masterUnitId"
:label="item.name" ></el-option>
:value="item.id" </el-select>
:key="item.id" </el-form-item>
:disabled="item.id===consolidationFormData.hostUnitId" <el-form-item label="审核意见" prop="tf_opinion"
></el-option> :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
</el-select> <user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item> </el-form-item>
<el-form-item label="审核意见" prop="approvalOpinion" :rules="[{required:true,message:'必填',trigger:['change','blur']}]"> </el-form>
<user-opinion-textarea v-model="consolidationFormData.approvalOpinion"></user-opinion-textarea> <el-row type="flex" justify="end">
</el-form-item> <el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-form-item label="电子签名" prop="approvalSignature" :rules="[{required:true,message:'必填',trigger:['change','blur']}]"> <el-button @click="handleTaskAction(6)" size="small" type="info">退回到提案人</el-button>
<pc-signature v-model="consolidationFormData.approvalSignature"></pc-signature> <el-button @click="handleTaskAction(1)" size="small" type="primary">提交</el-button>
</el-form-item> </el-row>
</el-form> </div>
<el-row type="flex" justify="end"> </proposal-info>
<el-button plain @click="$refs.guava.index()">取消</el-button>
<el-button type="primary" @click="doConsolidationApproval('PASS')">提交</el-button>
</el-row>
</template> </template>
<el-dialog title="提案详情" :visible.sync="viewDialogVisible" width="1200px" top="50px" append-to-body> <template #public>
<div style="max-height: 80vh; overflow-y: auto"> <merge ref="mergeRef" @close="$refs.guava.index()"></merge>
<proposal-info ref="infoDialogRef"></proposal-info> </template>
</div>
</el-dialog>
</guava> </guava>
</div> </div>
<script> <script>
<!--#include("../../common/info.js"){}#--> <!--#include('../../common/info.js'){}#-->
const vue = new Vue({ <!--#include('../committeeFiling/merge.js'){}#-->
new Vue({
el: "#app", el: "#app",
dicts: ["PROPOSAL_CASE_FILING_RESULT"],
store, store,
dicts: ["PROPOSAL_CASE_FILING_RESULT", "PROPOSAL_CASE_FILING_TYPE"],
mixins: [initTableMixins], mixins: [initTableMixins],
components: { components: {
"proposal-info": PROPOSAL_INFO_COMPONENT "proposal-info": PROPOSAL_INFO,
merge
}, },
data() { data() {
return { return {
sessionOptions: [], tableColumns: [
delegationOptions: [], {label: "提案编号", prop: "code"},
underTakeOptions: [], {label: "提案名称", prop: "name", width: "200px"},
formData: { {label: "提案类别", prop: "typeName"},
hostUnitIds: null, {label: "届次", prop: "sessionName"},
helpUnitId: [] {label: "立案结果", prop: "caseFilingResult"},
}, {label: "代表团", prop: "delegationName"},
{label: "当前节点", prop: "curTaskName"},
{label: "流程状态", prop: "instanceState"}
],
pageForm: { pageForm: {
approval: false approval: false
}, },
formData: {
tf_masterUnitId: null,
tf_slaveUnitIds: []
},
showApprovalForm: false, showApprovalForm: false,
//并案的提案列表 sessionOptions: [],
consolidationProposalTableData: [], delegationOptions: [],
consolidationFormData: {}, underTakeOptions: [],
viewDialogVisible: false
} }
}, },
methods: { methods: {
doUpData(){
this.$axios.post(loc()+"/doUpData")
},
openView(row) { openView(row) {
this.$refs.guava.public(() => { this.$refs.guava.edit(() => {
this.$refs.infoRef.onOpen(row.id)
this.showApprovalForm = false this.showApprovalForm = false
this.$refs.proposalInfoRef.onOpen(row)
}) })
}, },
openApproval(row) { doneTasks(val) {
this.$refs.guava.public(() => { const data = val[val.length - 1]
this.$refs.infoRef.onOpen(row.id) this.$set(this.formData, "tf_caseFilingResult", data.ext.tf_caseFilingResult)
this.formData = row.approvalParam this.$set(this.formData, "tf_caseFilingType", data.ext.tf_caseFilingType)
this.formData.isConsolidation = row.isConsolidation this.$set(this.formData, "tf_masterUnitId", data.ext.tf_masterUnitId)
this.$set(this.formData, "tf_slaveUnitIds", data.ext.tf_slaveUnitIds)
this.$axios },
.post(loc() + "/committeeFiling", { openAudit(row) {
processInstanceId: row.processInstanceId, this.$refs.guava.edit(() => {
processInstanceTaskId: row.processInstanceTaskId
})
.then((res) => {
if (res.code === 0) {
this.$set(this.formData, "caseFilingResult", res.data.caseFilingResult)
this.$set(this.formData, "hostUnitId", res.data.hostUnitId)
this.$set(this.formData, "helpUnitIds", res.data.helpUnitIds || [])
this.$set(this.formData, "approvalOpinion", res.data.approvalOpinion)
this.$set(this.formData, "proposalIds", res.data.consolidationIds || [row.id])
}
})
this.showApprovalForm = true this.showApprovalForm = true
}) this.formData = {
}, proposalId: row.id,
doApproval(approvalType) { processTaskId: row.taskId,
this.formData.bpmTaskApprovalType = approvalType taskName: row.curTaskName,
this.$refs.approvalFormRef.validate((valid) => { tf_masterUnitId: null,
if (valid) { tf_slaveUnitIds: []
this.$axios
.post(loc() + "/approval", {
approval: JSON.stringify(this.formData)
})
.then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
})
} }
this.$refs.proposalInfoRef.onOpen(row)
}) })
}, },
openRevoke(taskId) {
handleTaskAction(val) {
this.$refs.formRef.validate(valid => {
if (!valid) return
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
// 是否有协办单位
let tf_helpunitreply;
if (this.formData.tf_slaveUnitIds && this.formData.tf_slaveUnitIds.length > 0) {
tf_helpunitreply = 'HAS_HELP_UNIT'
} else {
tf_helpunitreply = 'NO_HELP_UNIT'
}
const loading = createLoading('提交中')
this.$axios.post("/platform/proposal/committeeFilingUnit/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val,
tf_masterUnitName: this.formData.tf_masterUnitId ? this.underTakeOptions.find(v => v.id === this.formData.tf_masterUnitId)?.name : null,
tf_slaveUnitNames: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name),
tf_slaveUnitNameStr: this.formData.tf_slaveUnitIds.map(v => this.underTakeOptions.find(v2 => v2.id === v)?.name).join(','),
tf_helpunitreply: tf_helpunitreply
})
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
loading.close()
})
})
})
},
// 打开并案审核
openMerge() {
const selection = this.$refs.tableRef.selection
if (selection.length < 2) {
this.$message.warning('请先勾选需要并案审核的提案,至少需要两条提案')
return
}
this.$refs.guava.public(() => {
this.$refs.mergeRef.onOpen(selection, {
processTaskIds: selection.map(v => v.taskId),
taskName: selection[0].curTaskName,
tf_masterUnitId: null,
tf_slaveUnitIds: []
})
})
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", { this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "info" type: "info"
}).then(() => { }).then(() => {
this.$axios.post(loc() + "/revoke", { taskId }).then((res) => { this.$axios.post("/platform/proposal/committeeFilingUnit/revokeTask", {
taskId: row.taskId,
proposalId: row.id
}).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.$message.success(res.msg) this.$message.success(res.msg)
this.doSearch() this.doSearch()
@@ -326,78 +312,12 @@ layout("/layouts/platform.html"){
}) })
}, },
viewSingleProposal(row) { // 教代会
this.proposalInfoVisible = true
this.$nextTick(() => {
this.$refs.infoRef.onOpen(row.id)
})
},
//打开并案审核
openConsolidationApproval() {
const selection = this.$refs.tableRef.selection
if (selection.length < 2) {
this.$message.error("并案审核至少需要选择两条提案")
return
}
this.consolidationProposalTableData = selection
this.$refs.guava.edit()
this.consolidationFormData = selection[0].approvalParam
this.$set(this.consolidationFormData, "caseFilingResult", null)
this.$set(this.consolidationFormData, "hostUnitId", null)
this.$set(this.consolidationFormData, "helpUnitIds", [])
this.$set(this.consolidationFormData, "helpUnitIds", [])
this.$set(this.consolidationFormData, "approvalOpinion", null)
this.$set(
this.consolidationFormData,
"proposalIds",
selection.map((v) => v.id)
)
},
openViewDialog(row) {
this.viewDialogVisible = true
this.$nextTick(() => {
this.$refs.infoDialogRef.onOpen(row.id)
})
},
//并案审核
doConsolidationApproval() {
this.consolidationFormData.bpmTaskApprovalType = "DYNAMIC"
this.$refs.consolidationFormRef.validate((valid) => {
if (valid) {
this.$axios
.post(loc() + "/approval", {
approval: JSON.stringify(this.consolidationFormData)
})
.then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$refs.tableRef.clearSelection()
this.$message.success(res.msg)
this.doSearch()
}
})
}
})
},
//教代会change
async meetingChange(val) { async meetingChange(val) {
this.formData.delegationId = null this.doSearch()
this.formData.committeeId = null
this.delegationOptions = await proposal.getDelegation(val)
this.committeeOptions = await this.getInstitutions(val)
},
listDelegation() {
this.$axios.post("/platform/proposal/common/listDelegation", { sessionId: this.pageForm.sessionId }).then((res) => {
if (res.code === 0) {
this.delegationOptions = res.data
}
})
}, },
// 查询开启的教代会
listOpenSession() { listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => { this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
if (res.code === 0) { if (res.code === 0) {
@@ -405,14 +325,14 @@ layout("/layouts/platform.html"){
if (this.sessionOptions) { if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id) this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.pageData() this.pageData()
this.listDelegation()
} }
} }
}) })
}, },
// 查询承办单位
listUnderTake() { listUnderTake() {
this.$axios.post(loc() + "/listUnderTake").then((res) => { this.$axios.post("/platform/proposal/common/listUnderTake").then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.underTakeOptions = res.data this.underTakeOptions = res.data
} }
@@ -420,11 +340,13 @@ layout("/layouts/platform.html"){
} }
}, },
created() { created() {
this.pageData()
this.listOpenSession() this.listOpenSession()
this.listUnderTake() this.listUnderTake()
} }
}) })
</script> </script>
<!--# <!--#
} }
#--> #-->
@@ -1,210 +0,0 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="教代会">
<el-select @change="meetingChange" clearable filterable placeholder="所属教代会"
v-model="pageForm.sessionId">
<el-option :key="item.id" :label="item.fullName" :value="item.id"
v-for="item in sessionOptions"></el-option>
</el-select>
</search-item>
<search-item label="提案名称">
<el-input v-model="pageForm.name" placeholder="提案名称" @keyup.enter.native="doSearch" clearable
style="width: 100%"></el-input>
</search-item>
<search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.createUserKeyword"></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool :columns.sync="tableColumns">
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column label="序号" width="50" type="index" :index="indexMethod" fixed="left"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:key="column.key"
:min-width="column.width"
:fixed="column.fixed"
show-overflow-tooltip
v-for="column in tableColumns"
v-if="column.visible !== false"
>
<template v-if="column.prop === 'caseFilingResult'" scope="{row}">
<dict-tag :options="dict.type.PROPOSAL_CASE_FILING_RESULT"
:value="row.caseFilingResult"></dict-tag>
</template>
<template v-else-if="column.prop === 'merge'" scope="{row}">
<el-tag v-if="row.merge" size="small"></el-tag>
</template>
<template v-else-if="column.prop === 'instanceState'" scope="{row}">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="300px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核
</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">
{{formData.taskName}}
</div>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix=""
class="flow-task-form">
<el-form-item label="审批意见" prop="tf_opinion"
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到提案人</el-button>
<el-button @click="handleTaskAction(2)" size="small" type="danger">不同意</el-button>
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意</el-button>
</el-row>
</div>
</proposal-info>
</template>
</guava>
</div>
<script>
<!--#include('../../common/info.js'){}#-->
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
components: {
"proposal-info": PROPOSAL_INFO
},
data() {
return {
sessionOptions: [],
tableColumns: [
{label: "提案编号", prop: "code"},
{label: "提案名称", prop: "name", width: "200px"},
{label: "提案类别", prop: "typeName"},
{label: "届次", prop: "sessionName"},
{label: "代表团", prop: "delegationName"},
{label: "当前节点", prop: "curTaskName"},
{label: "流程状态", prop: "instanceState"}
],
pageForm: {
approval: false
},
showApprovalForm: false
}
},
methods: {
openView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
this.$refs.proposalInfoRef.onOpen(row)
})
},
openAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
taskName: row.curTaskName
}
this.$refs.proposalInfoRef.onOpen(row)
})
},
handleTaskAction(val) {
this.$refs.formRef.validate(valid => {
if (!valid) return
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
})
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
})
})
})
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", {taskId: row.taskId}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
})
})
},
// 教代会
async meetingChange(val) {
this.formData.delegationId = null
this.formData.committeeId = null
this.doSearch()
},
// 查询开启的教代会
listOpenSession() {
this.$axios.post("/platform/proposal/common/listOpenSession").then((res) => {
if (res.code === 0) {
this.sessionOptions = res.data
if (this.sessionOptions) {
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
this.pageData()
}
}
})
}
},
created() {
this.pageData()
this.listOpenSession()
}
})
</script>
<!--#
}
#-->
@@ -1,190 +0,0 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="团长审核" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-search
v-model="pageForm.name"
:show-action="false"
:reverse-color="false"
input-align="left"
placeholder="请输入提案名称搜索"
@search="doSearch"
></van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.sessionId" :options="sessionOptions" :multiple="false"
@change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText"
@change="(val)=>{this.pageForm.approval = val==='1';this.doSearch();}">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<table-list api="/platform/proposal/personnelOffice/pageData" :page_form.sync="pageForm" @ready="onReady"
ref="tableListRef"
title="name">
<template v-slot="{index,row}">
<table-column label="提案编号">{{row.code}}</table-column>
<table-column label="提案类别">{{row.typeName}}</table-column>
<table-column label="提案人">{{row.createUserName}}</table-column>
<table-column label="代表团">{{row.delegationName}}</table-column>
<table-column label="当前节点">{{row.curTaskName}}</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<i class="fa fa-edit"></i>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<i class="fa fa-reply"></i>
<span>撤回</span>
</div>
</template>
</table-list>
<proposal-info ref="proposalInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field
v-model="formData.tf_opinion"
name="tf_opinion"
label="审批意见"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
required
maxlength="100"
show-word-limit
></van-field>
</van-form>
<div style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<van-button type="danger" block @click="handleTaskAction(6)">退回</van-button>
<van-button type="danger" block @click="handleTaskAction(2)">不同意</van-button>
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
</div>
</div>
</proposal-info>
</div>
<script>
<!--#include("../../common/info.js"){}#-->
const vue = new Vue({
el: "#app",
store,
data() {
return {
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
name: null,
sessionId: null,
approvalText: "0",
approval: false
},
sessionOptions: [],
formData: {},
showApprovalForm: false
}
},
components: {
"proposal-info": PROPOSAL_INFO
},
methods: {
onReady() {
this.listSession()
},
listSession() {
this.$axios.post("/platform/proposal/common/listSession").then((res) => {
if (res.code === 0) {
this.sessionOptions = [
{
text: "全部届次",
value: null
}
].concat(res.data.map((v) => ({text: v.fullName, value: v.id})))
if (this.sessionOptions.length > 0) {
this.pageForm.sessionId = this.sessionOptions[0].value
this.doSearch()
}
}
})
},
onView(row) {
this.showApprovalForm = false
this.$refs.proposalInfoRef.onOpen(row)
},
onApproval(row) {
this.showApprovalForm = true
this.$refs.proposalInfoRef.onOpen(row)
this.formData = {
processTaskId: row.taskId,
taskName: row.curTaskName
}
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
})
}).then((res) => {
if (res.code === 0) {
this.$refs.proposalInfoRef.onClose()
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(() => {
})
} catch (error) {
}
},
onRevoke(row){
this.$dialog.confirm({
title: "提示",
message: "您确定要撤回吗?"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", {taskId: row.taskId}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg)
this.doSearch()
}
})
})
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
},
}
})
</script>
<!--#
}
#-->