SpringBoot整合Activiti的項(xiàng)目中實(shí)現(xiàn)抄送功能
1、實(shí)現(xiàn)思路
在Activiti工作流中,抄送功能通常不是直接提供的,但可以通過(guò)擴(kuò)展來(lái)實(shí)現(xiàn)。抄送功能可以理解為將某個(gè)任務(wù)的信息發(fā)送給相關(guān)人員,而不需要他們直接處理任務(wù)。在Spring Boot整合Activiti的項(xiàng)目中,我們可以通過(guò)以下方式實(shí)現(xiàn)抄送功能:
- 自定義抄送表:記錄抄送的任務(wù)、抄送給誰(shuí)、抄送時(shí)間等信息。
- 在任務(wù)創(chuàng)建或完成時(shí)觸發(fā)抄送邏輯,將任務(wù)信息插入抄送表。
- 提供接口供用戶(hù)查看自己被抄送的任務(wù)。
在Spring Boot項(xiàng)目中整合Activiti工作流的抄送功能,可以通過(guò)以下方案實(shí)現(xiàn):
2、在Spring Boot中集成Activiti
在pom.xml中添加Activiti依賴(lài):
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>2.1、設(shè)計(jì)抄送表
創(chuàng)建抄送表,表結(jié)構(gòu)如下:
CREATE TABLE `act_cc_task` ( `id` varchar(64) NOT NULL COMMENT '主鍵', `task_id` varchar(64) DEFAULT NULL COMMENT '任務(wù)ID', `proc_inst_id` varchar(64) DEFAULT NULL COMMENT '流程實(shí)例ID', `proc_def_id` varchar(64) DEFAULT NULL COMMENT '流程定義ID', `title` varchar(255) DEFAULT NULL COMMENT '抄送標(biāo)題', `content` text COMMENT '抄送內(nèi)容', `from_user_id` varchar(64) DEFAULT NULL COMMENT '發(fā)起人', `to_user_ids` text COMMENT '接收人(多個(gè)用逗號(hào)分隔)', `status` int(1) DEFAULT '0' COMMENT '狀態(tài)(0:未讀,1:已讀)', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間', `read_time` datetime DEFAULT NULL COMMENT '讀取時(shí)間', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2、抄送實(shí)體類(lèi)
@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; // 多個(gè)用戶(hù)用逗號(hào)分隔
private Integer status;
private Date createTime;
private Date readTime;
@TableField(exist = false)
private List<String> toUserList; // 接收人列表
}2.3、實(shí)現(xiàn)抄送服務(wù)
創(chuàng)建一個(gè)抄送服務(wù),用于處理抄送邏輯。例如,在任務(wù)完成時(shí),將任務(wù)抄送給指定人員。
服務(wù)接口:
public interface CcTaskService {
/**
* 創(chuàng)建抄送任務(wù)
*/
void createCcTask(String taskId, List<String> toUserIds,
String title, String content);
/**
* 獲取用戶(hù)的抄送列表
*/
PageInfo<CcTask> getUserCcTasks(String userId, Integer pageNum,
Integer pageSize, Integer status);
/**
* 標(biāo)記為已讀
*/
void markAsRead(String ccTaskId, String userId);
/**
* 批量抄送
*/
void batchCcTask(List<String> taskIds, List<String> toUserIds,
String reason);
}服務(wù)實(shí)現(xiàn):
在任務(wù)完成時(shí)觸發(fā)抄送,可以在任務(wù)完成事件監(jiān)聽(tīng)器中觸發(fā)抄送。例如,創(chuàng)建一個(gè)任務(wù)監(jiān)聽(tīng)器
@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("任務(wù)不存在");
}
// 獲取當(dāng)前登錄用戶(hù)
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);
// 發(fā)送通知(可集成消息推送)
sendNotification(ccTask);
}
/**
* 使用監(jiān)聽(tīng)器自動(dòng)抄送
*/
@Component
public static class TaskCcListener implements TaskListener {
@Autowired
private CcTaskService ccTaskService;
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
// 在任務(wù)創(chuàng)建時(shí)自動(dòng)抄送
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("任務(wù)【%s】需要您知曉", taskName);
ccTaskService.createCcTask(
delegateTask.getId(),
userIds,
taskName,
content
);
}
}
}
}
}
}然后在流程定義中,在任務(wù)完成事件上綁定這個(gè)監(jiān)聽(tīng)器??梢栽贐PMN文件中配置,也可以通過(guò)代碼配置。
控制器層:
@RestController
@RequestMapping("/api/cc")
@Api(tags = "抄送管理")
public class CcTaskController {
@Autowired
private CcTaskService ccTaskService;
@PostMapping("/create")
@ApiOperation("創(chuàng)建抄送")
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("標(biāo)記已讀")
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>
<!-- 抄送對(duì)話框 -->
<el-dialog title="任務(wù)抄送" :visible.sync="ccDialogVisible">
<el-form :model="ccForm">
<el-form-item label="接收人">
<el-select
v-model="ccForm.userIds"
multiple
filterable
placeholder="請(qǐng)選擇接收人">
<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="抄送說(shuō)明">
<el-input
type="textarea"
v-model="ccForm.content"
placeholder="請(qǐng)輸入抄送說(shuō)明"
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: `任務(wù)【${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、抄送列表頁(yè)面
<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="標(biāo)題" 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="內(nèi)容"></el-table-column>
<el-table-column prop="fromUserName" label="發(fā)起人" width="100">
</el-table-column>
<el-table-column prop="createTime" label="時(shí)間" 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)">
標(biāo)記已讀
</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>前端可以調(diào)用上述接口展示抄送列表,并允許用戶(hù)標(biāo)記已讀。
注意事項(xiàng)
- 抄送功能可以根據(jù)實(shí)際需求進(jìn)行擴(kuò)展,例如增加抄送類(lèi)型(任務(wù)創(chuàng)建時(shí)抄送、任務(wù)完成時(shí)抄送等)。
- 抄送人員可以從任務(wù)變量、流程變量、固定配置或者從用戶(hù)選擇中獲取。
- 抄送任務(wù)可能不需要處理,但需要記錄,因此抄送表的設(shè)計(jì)可以根據(jù)業(yè)務(wù)需求調(diào)整。
4、高級(jí)功能擴(kuò)展
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" +
"標(biāo)題:%s\n" +
"內(nèi)容:%s\n" +
"發(fā)起人:%s\n" +
"時(shí)間:%s\n" +
"請(qǐng)登錄系統(tǒng)查看詳情。",
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);
}
/**
* 向指定用戶(hù)推送抄送消息
*/
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("發(fā)送WebSocket消息失敗", e);
}
}
}
}4.3、 抄送規(guī)則配置
@Component
public class CcRuleEngine {
/**
* 根據(jù)規(guī)則自動(dòng)抄送
* 規(guī)則示例:
* 1. 特定節(jié)點(diǎn)自動(dòng)抄送
* 2. 金額大于閾值抄送
* 3. 特定部門(mén)任務(wù)抄送
*/
public List<String> getCcUsersByRule(
String processDefinitionId,
String taskDefinitionKey,
Map<String, Object> variables) {
List<String> userIds = new ArrayList<>();
// 示例:根據(jù)任務(wù)節(jié)點(diǎn)配置抄送
if ("approve".equals(taskDefinitionKey)) {
// 審批節(jié)點(diǎn)抄送給部門(mén)經(jīng)理
String deptId = (String) variables.get("deptId");
userIds.addAll(getDeptManagers(deptId));
}
// 示例:根據(jù)金額閾值抄送
Double amount = (Double) variables.get("amount");
if (amount != null && amount > 10000) {
userIds.addAll(getFinanceUsers());
}
return userIds.stream().distinct().collect(Collectors.toList());
}
}4.4、 應(yīng)用配置文件
# application.yml
activiti:
cc:
enabled: true
# 是否啟用郵件通知
email-notify: true
# 是否啟用站內(nèi)信
message-notify: true
# 默認(rèn)抄送人(角色)
default-cc-roles: ROLE_DEPT_MANAGER,ROLE_ADMIN4.5、 流程定義中的抄送配置
<!-- 在BPMN文件中添加抄送配置 -->
<userTask id="approveTask" name="審批任務(wù)">
<extensionElements>
<activiti:taskListener event="create"
class="com.xxx.listener.AutoCcTaskListener"/>
<activiti:formProperty id="ccUsers"
name="抄送人" type="users"/>
</extensionElements>
</userTask>5、總結(jié)
抄送功能的實(shí)現(xiàn)要點(diǎn):
- 數(shù)據(jù)持久化:設(shè)計(jì)抄送表記錄抄送信息
- 業(yè)務(wù)邏輯:提供抄送CRUD服務(wù)
- 流程集成:通過(guò)監(jiān)聽(tīng)器自動(dòng)觸發(fā)抄送
- 用戶(hù)通知:集成多種通知方式(站內(nèi)信、郵件、推送)
- 權(quán)限控制:確保用戶(hù)只能操作自己的抄送記錄
- 配置靈活:支持規(guī)則引擎和動(dòng)態(tài)配置
這種實(shí)現(xiàn)方式既保持了工作流引擎的純凈性,又通過(guò)擴(kuò)展實(shí)現(xiàn)了業(yè)務(wù)需要的抄送功能。
到此這篇關(guān)于SpringBoot整合Activiti的項(xiàng)目中實(shí)現(xiàn)抄送功能的文章就介紹到這了,更多相關(guān)SpringBoot Activiti抄送內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Spring Batch在大型企業(yè)中的最佳實(shí)踐
本篇文章主要介紹了淺談Spring Batch在大型企業(yè)中的最佳實(shí)踐,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
解決mybatis plus 分頁(yè)查詢(xún)有條數(shù),total和pages都是零的問(wèn)題
這篇文章主要介紹了解決mybatis plus 分頁(yè)查詢(xún)有條數(shù),total和pages都是零的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11
java高并發(fā)情況下高效的隨機(jī)數(shù)生成器
這篇文章主要介紹了java高并發(fā)情況下高效的隨機(jī)數(shù)生成器,對(duì)于性能有要求的同學(xué),可以參考下2021-04-04
Java 根據(jù)url下載網(wǎng)絡(luò)資源
這篇文章主要介紹了Java 根據(jù)url下載網(wǎng)絡(luò)資源的示例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-11-11
Java對(duì)世界不同時(shí)區(qū)timezone之間時(shí)間轉(zhuǎn)換的處理方法
這篇文章主要介紹了Java對(duì)世界不同時(shí)區(qū)timezone之間時(shí)間轉(zhuǎn)換的處理方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
一文掌握J(rèn)ava開(kāi)發(fā)工具M(jìn)aven(簡(jiǎn)單上手)
掌握maven的相關(guān)知識(shí)是Java開(kāi)發(fā)必備的技能,今天通過(guò)本文從入門(mén)安裝開(kāi)始,逐步深入講解maven的相關(guān)知識(shí),包括maven的安裝到簡(jiǎn)單上手maven項(xiàng)目開(kāi)發(fā),感興趣的朋友跟隨小編一起看看吧2021-06-06
Java數(shù)組操作經(jīng)典例題大總結(jié)
數(shù)組是在內(nèi)存中存儲(chǔ)相同數(shù)據(jù)類(lèi)型的連續(xù)的空間,聲明一個(gè)數(shù)組就是在內(nèi)存空間中劃出一串連續(xù)的空間,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)組操作經(jīng)典例題的相關(guān)資料,需要的朋友可以參考下2022-03-03

