news 2026/2/10 2:37:18

Spring Boot整合Activiti的项目中实现抄送功能

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot整合Activiti的项目中实现抄送功能

目录

1、实现思路

2、在Spring Boot中集成Activiti

2.1、设计抄送表

2.2、抄送实体类

2.3、实现抄送服务

3、前端集成

3.1、抄送组件

3.2、抄送列表页面

4、高级功能扩展

4.1、邮件通知集成

4.2、消息推送集成(WebSocket)

4.3、 抄送规则配置

4.4、 应用配置文件

4.5、 流程定义中的抄送配置

5、总结


1、实现思路

在Activiti工作流中,抄送功能通常不是直接提供的,但可以通过扩展来实现。抄送功能可以理解为将某个任务的信息发送给相关人员,而不需要他们直接处理任务。在Spring Boot整合Activiti的项目中,我们可以通过以下方式实现抄送功能:

  1. 自定义抄送表:记录抄送的任务、抄送给谁、抄送时间等信息。
  2. 在任务创建或完成时触发抄送逻辑,将任务信息插入抄送表。
  3. 提供接口供用户查看自己被抄送的任务。

在Spring Boot项目中整合Activiti工作流的抄送功能,可以通过以下方案实现:

2、在Spring Boot中集成Activiti

pom.xml中添加Activiti依赖:

<dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.1.0.M6</version> </dependency>

2.1、设计抄送表

创建抄送表,表结构如下:

CREATE TABLE `act_cc_task` ( `id` varchar(64) NOT NULL COMMENT '主键', `task_id` varchar(64) DEFAULT NULL COMMENT '任务ID', `proc_inst_id` varchar(64) DEFAULT NULL COMMENT '流程实例ID', `proc_def_id` varchar(64) DEFAULT NULL COMMENT '流程定义ID', `title` varchar(255) DEFAULT NULL COMMENT '抄送标题', `content` text COMMENT '抄送内容', `from_user_id` varchar(64) DEFAULT NULL COMMENT '发起人', `to_user_ids` text COMMENT '接收人(多个用逗号分隔)', `status` int(1) DEFAULT '0' COMMENT '状态(0:未读,1:已读)', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `read_time` datetime DEFAULT NULL COMMENT '读取时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.2、抄送实体类

@Data @TableName("act_cc_task") public class CcTask { @TableId(type = IdType.ASSIGN_ID) private String id; private String taskId; private String procInstId; private String procDefId; private String title; private String content; private String fromUserId; private String toUserIds; // 多个用户用逗号分隔 private Integer status; private Date createTime; private Date readTime; @TableField(exist = false) private List<String> toUserList; // 接收人列表 }

2.3、实现抄送服务

创建一个抄送服务,用于处理抄送逻辑。例如,在任务完成时,将任务抄送给指定人员。

服务接口:

public interface CcTaskService { /** * 创建抄送任务 */ void createCcTask(String taskId, List<String> toUserIds, String title, String content); /** * 获取用户的抄送列表 */ PageInfo<CcTask> getUserCcTasks(String userId, Integer pageNum, Integer pageSize, Integer status); /** * 标记为已读 */ void markAsRead(String ccTaskId, String userId); /** * 批量抄送 */ void batchCcTask(List<String> taskIds, List<String> toUserIds, String reason); }

服务实现:

在任务完成时触发抄送,可以在任务完成事件监听器中触发抄送。例如,创建一个任务监听器

@Service @Slf4j public class CcTaskServiceImpl implements CcTaskService { @Autowired private CcTaskMapper ccTaskMapper; @Autowired private TaskService taskService; @Autowired private RuntimeService runtimeService; @Override @Transactional public void createCcTask(String taskId, List<String> toUserIds, String title, String content) { Task task = taskService.createTaskQuery() .taskId(taskId) .singleResult(); if (task == null) { throw new BusinessException("任务不存在"); } // 获取当前登录用户 String currentUserId = SecurityUtils.getCurrentUserId(); CcTask ccTask = new CcTask(); ccTask.setId(IdUtil.fastSimpleUUID()); ccTask.setTaskId(taskId); ccTask.setProcInstId(task.getProcessInstanceId()); ccTask.setProcDefId(task.getProcessDefinitionId()); ccTask.setTitle(title); ccTask.setContent(content); ccTask.setFromUserId(currentUserId); ccTask.setToUserIds(StringUtils.join(toUserIds, ",")); ccTask.setStatus(0); ccTask.setCreateTime(new Date()); ccTaskMapper.insert(ccTask); // 发送通知(可集成消息推送) sendNotification(ccTask); } /** * 使用监听器自动抄送 */ @Component public static class TaskCcListener implements TaskListener { @Autowired private CcTaskService ccTaskService; @Override public void notify(DelegateTask delegateTask) { String eventName = delegateTask.getEventName(); // 在任务创建时自动抄送 if ("create".equals(eventName)) { Object ccUsers = delegateTask.getVariable("ccUsers"); if (ccUsers != null) { List<String> userIds = (List<String>) ccUsers; if (!CollectionUtils.isEmpty(userIds)) { String taskName = delegateTask.getName(); String content = String.format("任务【%s】需要您知晓", taskName); ccTaskService.createCcTask( delegateTask.getId(), userIds, taskName, content ); } } } } } }

然后在流程定义中,在任务完成事件上绑定这个监听器。可以在BPMN文件中配置,也可以通过代码配置。

控制器层:

@RestController @RequestMapping("/api/cc") @Api(tags = "抄送管理") public class CcTaskController { @Autowired private CcTaskService ccTaskService; @PostMapping("/create") @ApiOperation("创建抄送") public Result createCcTask(@RequestBody CcTaskCreateDTO dto) { ccTaskService.createCcTask( dto.getTaskId(), dto.getToUserIds(), dto.getTitle(), dto.getContent() ); return Result.success(); } @GetMapping("/list") @ApiOperation("获取抄送列表") public Result<PageInfo<CcTask>> getCcList( @RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, @RequestParam(required = false) Integer status) { String userId = SecurityUtils.getCurrentUserId(); PageInfo<CcTask> result = ccTaskService.getUserCcTasks( userId, pageNum, pageSize, status ); return Result.success(result); } @PostMapping("/read/{id}") @ApiOperation("标记已读") public Result markAsRead(@PathVariable String id) { String userId = SecurityUtils.getCurrentUserId(); ccTaskService.markAsRead(id, userId); return Result.success(); } }

3、前端集成

3.1、抄送组件

<template> <div class="cc-task-container"> <!-- 抄送按钮 --> <el-button type="text" @click="showCcDialog"> <i class="el-icon-s-promotion"></i> 抄送 </el-button> <!-- 抄送对话框 --> <el-dialog title="任务抄送" :visible.sync="ccDialogVisible"> <el-form :model="ccForm"> <el-form-item label="接收人"> <el-select v-model="ccForm.userIds" multiple filterable placeholder="请选择接收人"> <el-option v-for="user in userList" :key="user.id" :label="user.name" :value="user.id"> </el-option> </el-select> </el-form-item> <el-form-item label="抄送说明"> <el-input type="textarea" v-model="ccForm.content" placeholder="请输入抄送说明" rows="4"> </el-input> </el-form-item> </el-form> <div slot="footer"> <el-button @click="ccDialogVisible = false">取消</el-button> <el-button type="primary" @click="submitCc">确定</el-button> </div> </el-dialog> </div> </template> <script> export default { props: { taskId: String, taskName: String }, data() { return { ccDialogVisible: false, userList: [], ccForm: { userIds: [], content: '' } } }, methods: { showCcDialog() { this.ccDialogVisible = true this.loadUserList() }, async loadUserList() { const res = await this.$api.user.getUserList() this.userList = res.data }, async submitCc() { const params = { taskId: this.taskId, toUserIds: this.ccForm.userIds, title: `任务【${this.taskName}】抄送`, content: this.ccForm.content } await this.$api.cc.create(params) this.$message.success('抄送成功') this.ccDialogVisible = false this.$emit('cc-success') } } } </script>

3.2、抄送列表页面

<template> <div class="cc-task-list"> <el-tabs v-model="activeStatus" @tab-click="handleTabClick"> <el-tab-pane label="未读" name="0"></el-tab-pane> <el-tab-pane label="已读" name="1"></el-tab-pane> <el-tab-pane label="全部" name=""></el-tab-pane> </el-tabs> <el-table :data="ccList" v-loading="loading"> <el-table-column prop="title" label="标题" width="200"> <template slot-scope="{row}"> <span :class="{'unread': row.status === 0}"> {{ row.title }} </span> </template> </el-table-column> <el-table-column prop="content" label="内容"></el-table-column> <el-table-column prop="fromUserName" label="发起人" width="100"> </el-table-column> <el-table-column prop="createTime" label="时间" width="180"> <template slot-scope="{row}"> {{ formatDate(row.createTime) }} </template> </el-table-column> <el-table-column label="操作" width="120"> <template slot-scope="{row}"> <el-button v-if="row.status === 0" type="text" @click="markAsRead(row.id)"> 标记已读 </el-button> <el-button type="text" @click="viewProcess(row.procInstId)"> 查看流程 </el-button> </template> </el-table-column> </el-table> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="pagination.pageNum" :page-sizes="[10, 20, 50, 100]" :page-size="pagination.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="pagination.total"> </el-pagination> </div> </template>

前端可以调用上述接口展示抄送列表,并允许用户标记已读。

注意事项

  • 抄送功能可以根据实际需求进行扩展,例如增加抄送类型(任务创建时抄送、任务完成时抄送等)。

  • 抄送人员可以从任务变量、流程变量、固定配置或者从用户选择中获取。

  • 抄送任务可能不需要处理,但需要记录,因此抄送表的设计可以根据业务需求调整。

4、高级功能扩展

4.1、邮件通知集成

@Component public class EmailCcNotifier { @Autowired private JavaMailSender mailSender; @Value("${spring.mail.username}") private String from; public void sendCcNotification(CcTask ccTask, User user) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom(from); message.setTo(user.getEmail()); message.setSubject("工作流抄送通知:" + ccTask.getTitle()); message.setText(buildEmailContent(ccTask)); mailSender.send(message); } private String buildEmailContent(CcTask ccTask) { return String.format( "您收到一条工作流抄送:\n" + "标题:%s\n" + "内容:%s\n" + "发起人:%s\n" + "时间:%s\n" + "请登录系统查看详情。", ccTask.getTitle(), ccTask.getContent(), ccTask.getFromUserId(), DateUtil.formatDateTime(ccTask.getCreateTime()) ); } }

4.2、消息推送集成(WebSocket)

@ServerEndpoint("/websocket/cc") @Component public class CcWebSocket { private static Map<String, Session> sessions = new ConcurrentHashMap<>(); @OnOpen public void onOpen(Session session) { String userId = getUserIdFromSession(session); sessions.put(userId, session); } /** * 向指定用户推送抄送消息 */ public static void sendCcMessage(String userId, CcTask ccTask) { Session session = sessions.get(userId); if (session != null && session.isOpen()) { try { String message = JSON.toJSONString( Map.of("type", "cc", "data", ccTask) ); session.getBasicRemote().sendText(message); } catch (IOException e) { log.error("发送WebSocket消息失败", e); } } } }

4.3、 抄送规则配置

@Component public class CcRuleEngine { /** * 根据规则自动抄送 * 规则示例: * 1. 特定节点自动抄送 * 2. 金额大于阈值抄送 * 3. 特定部门任务抄送 */ public List<String> getCcUsersByRule( String processDefinitionId, String taskDefinitionKey, Map<String, Object> variables) { List<String> userIds = new ArrayList<>(); // 示例:根据任务节点配置抄送 if ("approve".equals(taskDefinitionKey)) { // 审批节点抄送给部门经理 String deptId = (String) variables.get("deptId"); userIds.addAll(getDeptManagers(deptId)); } // 示例:根据金额阈值抄送 Double amount = (Double) variables.get("amount"); if (amount != null && amount > 10000) { userIds.addAll(getFinanceUsers()); } return userIds.stream().distinct().collect(Collectors.toList()); } }

4.4、 应用配置文件

# application.yml activiti: cc: enabled: true # 是否启用邮件通知 email-notify: true # 是否启用站内信 message-notify: true # 默认抄送人(角色) default-cc-roles: ROLE_DEPT_MANAGER,ROLE_ADMIN

4.5、 流程定义中的抄送配置

<!-- 在BPMN文件中添加抄送配置 --> <userTask id="approveTask" name="审批任务"> <extensionElements> <activiti:taskListener event="create" class="com.xxx.listener.AutoCcTaskListener"/> <activiti:formProperty id="ccUsers" name="抄送人" type="users"/> </extensionElements> </userTask>

5、总结

抄送功能的实现要点:

  1. 数据持久化:设计抄送表记录抄送信息

  2. 业务逻辑:提供抄送CRUD服务

  3. 流程集成:通过监听器自动触发抄送

  4. 用户通知:集成多种通知方式(站内信、邮件、推送)

  5. 权限控制:确保用户只能操作自己的抄送记录

  6. 配置灵活:支持规则引擎和动态配置

这种实现方式既保持了工作流引擎的纯净性,又通过扩展实现了业务需要的抄送功能。


“人的一生会经历很多痛苦,但回头想想,都是传奇”。



版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/8 18:12:26

PDFMathTranslate深度体验:用国产大模型实现学术论文精准翻译

PDFMathTranslate深度体验&#xff1a;用国产大模型实现学术论文精准翻译 【免费下载链接】PDFMathTranslate PDF scientific paper translation with preserved formats - 基于 AI 完整保留排版的 PDF 文档全文双语翻译&#xff0c;支持 Google/DeepL/Ollama/OpenAI 等服务&am…

作者头像 李华
网站建设 2026/2/5 17:09:23

AI工程宝典:产品运营的智能升级指南

AI工程宝典&#xff1a;产品运营的智能升级指南 【免费下载链接】aie-book [WIP] Resources for AI engineers. Also contains supporting materials for the book AI Engineering (Chip Huyen, 2025) 项目地址: https://gitcode.com/GitHub_Trending/ai/aie-book 还在为…

作者头像 李华
网站建设 2026/2/7 13:14:42

GeoJSON.io 终极指南:快速创建和编辑地理数据的免费在线工具

GeoJSON.io 终极指南&#xff1a;快速创建和编辑地理数据的免费在线工具 【免费下载链接】geojson.io A quick, simple tool for creating, viewing, and sharing spatial data 项目地址: https://gitcode.com/gh_mirrors/ge/geojson.io GeoJSON.io 是一款功能强大的免费…

作者头像 李华
网站建设 2026/2/6 17:07:07

0基础,想学习网络安全,看这一篇就够了

前言 肯定有很多人都听说过网络安全工程师这个职业&#xff0c;虽然比不上现在的前后端那么大红大紫&#xff0c;但也是一个不可小觑的技术职业&#xff0c;但你是否知道&#xff0c;网络安全工程师只是一个大的类&#xff0c;根据所作的工作内容不同可分为多个分支&#xff0…

作者头像 李华
网站建设 2026/1/29 10:57:23

Go项目配置管理终极指南:环境变量与配置文件的完美融合

Go项目配置管理终极指南&#xff1a;环境变量与配置文件的完美融合 【免费下载链接】go-clean-template Clean Architecture template for Golang services 项目地址: https://gitcode.com/gh_mirrors/go/go-clean-template 在Go项目开发中&#xff0c;你是否曾为不同环…

作者头像 李华
网站建设 2026/2/8 9:17:10

属于程序员的副业,从 0 到月入 2000,我靠挖“洞”致富!

【网络安全就业方向】网络安全专业的学生毕业后可以从事什么样的工作&#xff1f; 前言 2025年的今天&#xff0c;慎重进入网安行业吧&#xff0c;目前来说信息安全方向的就业对于学历的容忍度比软件开发要大得多&#xff0c;还有很多高中被挖过来的大佬。 理由很简单&#…

作者头像 李华