从0到1搭建自己的OA系统(二)

三、通知公告发布流程搭建

3.1 功能策划

在第一篇学习笔记中已经将“通知公告”拆分为两个部分,分别为

  • 全部公告:主要是实现查看所有用户已经走完发布流程且是发布状态的公告;
  • 我的公告:实现当前用户新建公告、发起审批流程并发布公告的编辑位置;

在笔记一中已经实现上述两个部分的前后端的开发,但对于发起审批流程的过程尚未开发,本次予以实现,使用户在“我的公告”中能够完成以下操作:

  • 可以新建、修改、删除公告的文稿
  • 可以发起审批流程
  • 可以对在流程中的公告文稿强制取消审批流程、删除流程、重新发起流程
  • 可以在公告中显示当前流程状态和审批流程进度

3.2 流程引擎

为实现上述功能策划,需引入流程引擎。目前与ruoyi整合在一起的floawable或activiti的开源框架有很多,本笔记学习过程中重点参考了KonBAI大佬的RuoYi-Flowable-Plus项目,开源项目地址:

https://gitee.com/KonBAI-Q/ruoyi-flowable-plus

个人感觉这个项目整合的非常好,有兴趣可以深入学习并STAR

3.3 新增SQL表并生成代码

流程开发计划在上述流程引擎的基础上搭建。故需要建立一个SQL表,用以存储通知公告表与流程表之间的关联:

sys_workflow_notice表:

使用ruoyi框架中代码生成器生成代码,本笔记学习过程中,因不涉及到此新增表的前端,主要使用生成的main代码,并设置包的位置为 com.ruoyi.workflow:

controller为统一管理,放置在了admin模块下 com.ruoyi.web.controller,以便于统一管理

3.4 flowable表单构建

为了flowable流程能够与本笔记一中改造的“我的公告”的表单关联起来,计划在flowable模块中按照“我的公告”中的新建表单仿制构建一个flowable表单,当在“我的公告”中点击“发起流程”按钮时,后端直接将“我的公告”中的表单内容自动填写到这个flowable表单中:

重点是公告标题、公告类型、公告内容三个部分,尤其注意这三个部分的字段名应该与“我的公告”中对应的表单元素的字段名保持一致,本笔记中命名为noticeTitle、noticeType、noticeContent

过程问题及解决:

问题1:flowable表单构建的公告类型中字段noticeType的类型为int类型、而“我的公告”中表单的类型为string类型,导致两个表单关联后,在flowable表单中此处显示的是个数字,而不是数字对应的字典值,解决思路为更改flowable表单这个组件的数据类型为字符型:

  • 修改前端文件 utils/generator/config.js 中下拉选择组件的value值为:
  {
    __config__: {
      label: '下拉选择',
      showLabel: true,
      labelWidth: null,
      tag: 'el-select',
      tagIcon: 'select',
      layout: 'colFormItem',
      span: 24,
      required: true,
      regList: [],
      changeTag: true,
      document: 'https://element.eleme.cn/#/zh-CN/component/select'
    },
    __slot__: {
      options: [{
        label: '选项一',
        value: "1"
      }, {
        label: '选项二',
        value: "2"
      }]
    },
    placeholder: '请选择',
    style: {width: '100%'},
    clearable: true,
    disabled: false,
    filterable: false,
    multiple: false
  },
  • 修改前端文件 views/tool/build/RightPanel.vue中的添加选项时的value默认为String(item.value),使构建表单的下拉选择组件时右侧添加时默认为字符类型数值: 
<div v-for="(item, index) in activeData.__slot__.options" :key="index" class="select-item">
    <div class="select-line-icon option-drag">
        <i class="el-icon-s-operation" />
    </div>
    <el-input v-model="item.label" placeholder="选项名" size="small" />
    <el-input placeholder="选项值" size="small" :value="String(item.value)"  @input="setOptionValue(item, $event)" />
    <div class="close-btn select-line-icon" @click="activeData.__slot__.options.splice(index, 1)">
    <i class="el-icon-remove-outline" />
    </div>
</div>

问题2:flowable表单搭建完毕后,前端浏览器不定时的报错:

vue.runtime.esm.js:619  [Vue warn]: Unknown custom element: <tinymce> - did you register the component correctly? For recursive components, make sure to provide the "name" option.......

同时前端页面中的内容编辑器组件出现问题,无法正常显示:  

解决:经查主要是组件未能及时加载造成的,故将组件挂载到全局,针对根目录下 main.js修改

3.5 bpmn流程图

按“发起-审核-结束”的简易流程搭建bpmn流程图如下:

其中开始中添加构建的flowable表单,发起指定任务执行者为发起人,审核按角色指定发起人员所在部门上级,并将模式改为或签(有一人审批即可通过)

完成bpmn流程图搭建后予以部署:

部署完毕后,可在数据库中的act_re_procdef 表中查询到对应的ID号:

 需要记录下此ID号,导入“我的公告”前端vue文件中,作为“我的公告”中的数据填写至flowable表单时请求的api的参数,导入方式可参考下节。

3.6 前端代码改造

在 views/system/notice目录下新增一config.js文件,用以存放上述procdef的ID号:

export const DefinitionId = "Process_1709900320599:1:3019a4bd-dd46-11ee-89d6-7aaf089de9eb";

其中的字符串为本笔记搭建的流程对应得prodef表的ID号。

之后针对create.vue文件作如下改造:

  • 状态显示为“新建”或“流程状态”,当存在流程状态时显示流程状态,否则显示“新建” 
<el-table-column label="状态" align="center" prop="status" width="100">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.wf_process_status" :value="scope.row.processStatus" v-if="scope.row.processStatus"/>
          <dict-tag :options="dict.type.sys_notice_status" :value="scope.row.status" v-else/>
        </template>
      </el-table-column>
dicts: ['sys_notice_status', 'sys_notice_type','wf_process_status'],
  • 流程操作 中,若未发起流程则显示为“发起”按钮,否则显示“详情”和“取消”,并且当流程状态为“已取消”时,显示为“详情”和“删除流程”
<el-table-column label="流程操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">

          <el-button
            type="text"
            size="mini"
            icon="el-icon-video-play"
            @click="handleStart(scope.row)"
            v-if="!scope.row.processStatus"
            v-hasPermi="['workflow:process:start']"
          >发起</el-button>
          <template v-else>

            <el-button
            type="text"
            size="mini"
            icon="el-icon-tickets"
            @click="handleFlowRecord(scope.row)"
            v-hasPermi="['workflow:process:query']"
          >详情</el-button>

          <el-button
            type="text"
            size="mini"
            icon="el-icon-circle-close"
            v-if="!scope.row.finishTime"
            @click="handleStop(scope.row)"
            v-hasPermi="['workflow:process:cancel']"
          >取消</el-button>

          <el-button
            type="text"
            size="mini"
            icon="el-icon-delete"
            @click="handleFlowDelete(scope.row)"
            v-if="scope.row.finishTime"
            v-hasPermi="['workflow:process:remove']"
          >删除流程</el-button>

          </template>
        </template>
</el-table-column>

对应的各按钮方法改造 :

  • 文档的修改、删除按钮操作:增加限制,当存在流程时不允许修改或删除文档

/** 修改按钮操作 */
    handleUpdate(row) {
      const noticeId = row.noticeId || this.ids
      this.noticeList.forEach(element => {
        if(element.noticeId == noticeId){
            if(element.processStatus) {
              this.$modal.msgError("非新建状态(存在流程)不能修改或删除!");
            } else {
              this.reset();
              const noticeId = row.noticeId || this.ids
              getNotice(noticeId).then(response => {
                this.form = response.data;
                this.open = true;
                this.title = "修改公告";
              });        
            }
        }
      }); 
    },
/** 删除按钮操作 */
    handleDelete(row) {

      const noticeId = row.noticeId || this.ids
      this.noticeList.forEach(element => {
        if(element.noticeId == noticeId){
            if(element.processStatus) {
              this.$modal.msgError("非新建状态(存在流程)不能修改或删除!");
            } else {

              this.$modal.confirm('是否确认删除公告编号为"' + noticeId + '"的数据项?').then(function() {
                return delNotice(noticeId);
              }).then(() => {
                this.getList();
                this.$modal.msgSuccess("删除成功");
              }).catch(() => {});

            }
          }
        });
    },
  • 流程的发起、详情、取消、删除操作:流程取消可显示删除 
 handleStart(row){
        // 启动流程并将表单数据加入流程变量
        startProcess(DefinitionId, JSON.stringify(row)).then(res => {
        this.$modal.msgSuccess(res.msg);
        this.getList();
      })
      // console.log(row);
    },
    /** 流程流转记录 */
    handleFlowRecord(row) {
      this.$router.push({
        path: '/workflow/process/detail/' + row.procInsId,
        query: {
          processed: false
        }
      })
    },
    /**  取消流程申请 */
    handleStop(row){
      const params = {
        procInsId: row.procInsId
      }
      const noticeIds = row.noticeId || this.ids
      this.$modal.confirm('是否取消公告编号为"' + noticeIds + '"的流程?').then(function() {
        return stopProcess(params);
      })
      .then( res => {
        this.$modal.msgSuccess(res.msg);
        this.getList();
      }).catch(() => {});
    },
    /** 删除流程按钮操作 */
    handleFlowDelete(row) {
      const ids = row.procInsId || this.ids;
      this.$confirm('是否确认删除流程定义编号为"' + ids + '"的数据项?', "警告", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      }).then(function() {
        return delProcess(ids);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      })
    },

其中的流程发起操作中的DefinitionId变量,即为导入的procdef的ID号

import { DefinitionId } from './config'

 导入的api信息如下

import { listCurrUserNotice,getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice"
import { startProcess , stopProcess, delProcess } from '@/api/workflow/process'

3.7 后端代码改造

 改造思路主要是两大部分,第一是查询全部已发布公告的方法改造,需要考虑公告对应的流程状态,即需要关联查询3.3中新增的sys_workflow_notice关联表;第二就是查询当前用户的所有公告并关联流程状态。为此,新增一NoticeFlowService业务逻辑类和相关视图返回数据的实体类

 

其中的NoticeFlowVo视图实体类为流程相关信息和通知公告相关信息的联合体:

@Data
public class NoticeFlowVo extends BaseEntity implements Serializable {
    /**
     * 任务编号
     */
    private String taskId;
    /**
     * 任务名称
     */
    private String taskName;
    /**
     * 任务Key
     */
    private String taskDefKey;
    /**
     * 任务执行人Id
     */
    private Long assigneeId;
    /**
     * 部门名称
     */
    @Deprecated
    private String deptName;
    /**
     * 流程发起人部门名称
     */
    @Deprecated
    private String startDeptName;
    /**
     * 任务执行人名称
     */
    private String assigneeName;
    /**
     * 流程发起人Id
     */
    private Long startUserId;
    /**
     * 流程发起人名称
     */
    private String startUserName;
    /**
     * 流程类型
     */
    private String category;
    /**
     * 流程变量信息
     */
    private Object procVars;
    /**
     * 局部变量信息
     */
    private Object taskLocalVars;
    /**
     * 流程部署编号
     */
    private String deployId;
    /**
     * 流程ID
     */
    private String procDefId;
    /**
     * 流程key
     */
    private String procDefKey;
    /**
     * 流程定义名称
     */
    private String procDefName;
    /**
     * 流程定义内置使用版本
     */
    private int procDefVersion;
    /**
     * 流程实例ID
     */
    private String procInsId;
    /**
     * 历史流程实例ID
     */
    private String hisProcInsId;
    /**
     * 任务耗时
     */
    private String duration;
    /**
     * 任务意见
     */
    private WfCommentDto comment;
    /**
     * 任务意见
     */
    private List<Comment> commentList;
    /**
     * 候选执行人
     */
    private String candidate;
    /**
     * 任务创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 任务完成时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date finishTime;

    /**
     * 流程状态
     */
    private String processStatus;


    /**
     * 数据表的顺序号
     */

    private Long noId;

    /**
     * 公告ID
     */

    private Long noticeId;

    /**
     * 公告标题
     */

    private String noticeTitle;

    /**
     * 公告类型(1通知 2公告)
     */
    private String noticeType;

    /**
     * 公告内容
     */
    private String noticeContent;

    /**
     * 公告状态(0正常 1关闭)
     */
    private String status;

    /**
     * 备注
     */
    private String remark;


    private Long isRead;
}

NoticeFlowService的业务逻辑重点处理上述2种情况的:

  • 所有流程状态为“已完成”的通知公告数据的查询功能:
@Override
    public TableDataInfo<SysNotice> selectPageNoticeFinishedList(SysNotice notice, PageQuery pageQuery, Long currentUserId) {
        LambdaQueryWrapper<SysNotice> lqw = new LambdaQueryWrapper<SysNotice>()
            .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())
            .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())
            .like(StringUtils.isNotBlank(notice.getCreateBy()), SysNotice::getCreateBy, notice.getCreateBy());
        // 查询所有通知公告及其对应的流程实例
        List<SysNotice> noticeList = sysNoticeBaseMapper.selectList(lqw);
        List<SysNotice> records = new ArrayList<>();
        int i = PageQuery.DEFAULT_PAGE_NUM;
        for (SysNotice one : noticeList) {
            SysWorkflowNoticeVo sysWorkflowNoticeVo = sysWorkflowNoticeService.queryOneByNoticeId(one.getNoticeId());
            if (sysWorkflowNoticeVo != null) {
                HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(sysWorkflowNoticeVo.getProcinstId());
                List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.list();
                HistoricProcessInstance hisIns = historicProcessInstances.get(0);
                HistoricVariableInstance processStatusVariable = historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(sysWorkflowNoticeVo.getProcinstId())
                    .variableName(ProcessConstants.PROCESS_STATUS_KEY)
                    .singleResult();
                //获取流程状态
                String processStatus = null;
                if (ObjectUtil.isNotNull(processStatusVariable)) {
                    // 获取流程节点信息
                    List<Comment> comments = wfProcessService.historyProcNodeList(hisIns).get(1).getCommentList();
                    //流程取消时,系统记录的流程状态也为已完成,此时与实际正常完成的流程无法区分,暂时按增加一条备注信息来处理,实际查询过程中查询此备注信息进行状态区分
                    if(!(comments.isEmpty())) {
                        System.out.println(comments.get(0).getType());
                        System.out.println(comments.get(0).getFullMessage());
                        if(comments.get(0).getType().contains("7") && comments.get(0).getFullMessage().contains("取消申请")){
                            processStatus = ProcessStatus.CANCELED.getStatus();
                            System.out.println(processStatus);
                        }else{
                            processStatus = Convert.toStr(processStatusVariable.getValue());
                        }
                    }
                }
                // 兼容旧流程
                if (processStatus == null) {
                    processStatus = ObjectUtil.isNull(hisIns.getEndTime()) ? ProcessStatus.RUNNING.getStatus() : ProcessStatus.COMPLETED.getStatus();
                }
                //增加序号的显示
                if (ProcessStatus.COMPLETED.getStatus().equals(processStatus)) {
                    one.setNoId((long) i);
                    one.setCreateBy(String.valueOf(sysUserService.selectUserByUserName(one.getCreateBy()).getNickName()));
                    SysUserNoticeVo isReadResult = sysUserNoticeService.queryByIdAndNoticeId(currentUserId, one.getNoticeId());
                    if(isReadResult != null){
                        one.setIsRead(Long.valueOf(dictDataService.selectDictValue("sys_notice_isread", "已读")));
                    } else {
                        one.setIsRead(Long.valueOf(dictDataService.selectDictValue("sys_notice_isread", "未读")));
                    }
                    records.add(one);
                    i++;
                }
            }

        }
        //分页
        Page<SysNotice> page = new Page<>(pageQuery.getPageNum() + 1, pageQuery.getPageSize());
        page.setTotal(records.size());
        int start = (pageQuery.getPageNum() - 1) * pageQuery.getPageSize();
        int end = pageQuery.getPageNum() * pageQuery.getPageSize();
        page.setRecords(records.stream().skip(start).limit(end).collect(Collectors.toList()));
        return TableDataInfo.build(page);
    }
  • 当前用户所有通知及流程状态的查询:
    @Override
    public TableDataInfo<NoticeFlowVo> selectPageCurrUserNoticeFlowList(SysNotice notice, PageQuery pageQuery, Long userId) {
        LambdaQueryWrapper<SysNotice> lqw = new LambdaQueryWrapper<SysNotice>()
            .like(StringUtils.isNotBlank(notice.getNoticeTitle()), SysNotice::getNoticeTitle, notice.getNoticeTitle())
            .eq(StringUtils.isNotBlank(notice.getNoticeType()), SysNotice::getNoticeType, notice.getNoticeType())
            .eq(SysNotice::getCreateBy, sysUserService.selectUserById(userId).getUserName());
        Page<SysNotice> page = sysNoticeBaseMapper.selectPage(pageQuery.build(), lqw);
        Page<NoticeFlowVo> result = new Page<>();
        result.setTotal(page.getTotal());
        int i = PageQuery.DEFAULT_PAGE_NUM;
        //所有当前用户的通知公告
        List<NoticeFlowVo> records = new ArrayList<>();
        for( SysNotice one : page.getRecords()) {
            NoticeFlowVo oneVo = new NoticeFlowVo();
            oneVo.setNoticeId(one.getNoticeId());
            oneVo.setNoticeTitle(one.getNoticeTitle());
            oneVo.setNoticeType(one.getNoticeType());
            oneVo.setNoticeContent(one.getNoticeContent());
            oneVo.setStatus(one.getStatus());
            oneVo.setCreateTime(one.getCreateTime());
            oneVo.setNoId(Long.valueOf((pageQuery.getPageNum()-1)*pageQuery.getPageSize()+i));
            i++;

            //查询相关流程的内容
            SysWorkflowNoticeVo sysWorkflowNoticeVo = sysWorkflowNoticeService.queryOneByNoticeId(one.getNoticeId());
            if(sysWorkflowNoticeVo != null) {
                HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(sysWorkflowNoticeVo.getProcinstId());
                List<HistoricProcessInstance> historicProcessInstances = historicProcessInstanceQuery.list();
                HistoricProcessInstance hisIns = historicProcessInstances.get(0);
                HistoricVariableInstance processStatusVariable =  historyService.createHistoricVariableInstanceQuery()
                    .processInstanceId(sysWorkflowNoticeVo.getProcinstId())
                    .variableName(ProcessConstants.PROCESS_STATUS_KEY)
                    .singleResult();
                String processStatus = null;
                if (ObjectUtil.isNotNull(processStatusVariable)) {
                    // 获取流程节点信息
                    List<Comment> comments = wfProcessService.historyProcNodeList(hisIns).get(1).getCommentList();
                    //流程取消时,系统记录的流程状态也为已完成,此时与实际正常完成的流程无法区分,暂时按增加一条备注信息来处理,实际查询过程中查询此备注信息进行状态区分
                    if(!(comments.isEmpty())) {
                        System.out.println(comments.get(0).getType());
                        System.out.println(comments.get(0).getFullMessage());
                        if(comments.get(0).getType().contains("7") && comments.get(0).getFullMessage().contains("取消申请")){
                            processStatus = ProcessStatus.CANCELED.getStatus();
                            System.out.println(processStatus);
                        }else{
                            processStatus = Convert.toStr(processStatusVariable.getValue());
                        }
                    }
                }
                // 兼容旧流程
                if (processStatus == null) {
                    processStatus = ObjectUtil.isNull(hisIns.getEndTime()) ? ProcessStatus.RUNNING.getStatus() : ProcessStatus.COMPLETED.getStatus();
                }
                oneVo.setProcessStatus(processStatus);
                oneVo.setCreateTime(hisIns.getStartTime());
                oneVo.setFinishTime(hisIns.getEndTime());
                oneVo.setProcInsId(hisIns.getId());
                // 计算耗时
                if (Objects.nonNull(hisIns.getEndTime())) {
                    oneVo.setDuration(DateUtils.getDatePoor(hisIns.getEndTime(), hisIns.getStartTime()));
                } else {
                    oneVo.setDuration(DateUtils.getDatePoor(DateUtils.getNowDate(), hisIns.getStartTime()));
                }
                // 流程部署实例信息
                Deployment deployment = repositoryService.createDeploymentQuery()
                    .deploymentId(hisIns.getDeploymentId()).singleResult();
                oneVo.setDeployId(hisIns.getDeploymentId());
                oneVo.setProcDefId(hisIns.getProcessDefinitionId());
                oneVo.setProcDefName(hisIns.getProcessDefinitionName());
                oneVo.setProcDefVersion(hisIns.getProcessDefinitionVersion());
                oneVo.setCategory(deployment.getCategory());
                // 当前所处流程
                List<Task> taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).includeIdentityLinks().list();
                if (CollUtil.isNotEmpty(taskList)) {
                    oneVo.setTaskName(taskList.stream().map(Task::getName).filter(StringUtils::isNotEmpty).collect(Collectors.joining(",")));
                }
            }
            records.add(oneVo);
        }
        result.setRecords(records);
        return  TableDataInfo.build(result);
    }

修改controller中getLlist对应的公告查询方法为以上方法。至此主要后端的改造已完成。

其他的后端改造:

  • WfTaskServiceImpl中的stopProcess方法中,增加两行取消后的备注功能
taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.REVOKE.getType(),
StringUtils.isBlank(bo.getComment()) ? "取消申请" : bo.getComment());
  •  WfProcessServiceImpl中的deletProcessByIds方法,增加删除流程后同时删除sys_workfow_notice表中的相关流程数据的功能
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProcessByIds(String[] instanceIds) {
        List<String> ids = Arrays.asList(instanceIds);
        // 校验流程是否结束
        long activeInsCount = runtimeService.createProcessInstanceQuery()
            .processInstanceIds(new HashSet<>(ids)).active().count();
        if (activeInsCount > 0) {
            throw new ServiceException("不允许删除进行中的流程实例");
        }
        //删除流程实例的同时,需要删除sys_workflow_notice表中的数据
        List<String>  sysWorkflowNoticeId = new ArrayList<>();
        for (String instanceId : instanceIds) {
            SysWorkflowNoticeVo one = sysWorkflowNoticeService.queryOneByProcinsId(instanceId);
            if(one != null){
                sysWorkflowNoticeId.add(instanceId);
            }
        }
        sysWorkflowNoticeService.deleteWithValidByIds(sysWorkflowNoticeId, !sysWorkflowNoticeId.isEmpty());
        // 删除历史流程实例
        historyService.bulkDeleteHistoricProcessInstances(ids);
    }
  • WfProcessServiceImpl中的startProcess方法,增加发起流程后的在sys_workflow_notice中添加关联信息的功能
//同时记录到sys_workflow_notice表中
if(variables.containsKey("noticeId")){
            Long noticeId =Long.valueOf(String.valueOf(variables.get("noticeId")));
            String procInstId = processInstance.getId();
            SysWorkflowNoticeBo workflowNoticeBo = new  SysWorkflowNoticeBo();
            workflowNoticeBo.setProcinstId(procInstId);
            workflowNoticeBo.setNoticeId(noticeId);
            workflowNoticeBo.setDelFlag("0");
            sysWorkflowNoticeService.updateByBo(workflowNoticeBo);
        }
  • 同时改造代码生成器生成的SysWorkflowNoticeServiceImpl中的updateByBo方法,以支持上述的添加功能
    /**
     * 修改流程和通知关联
     */
    @Override
    public Boolean updateByBo(SysWorkflowNoticeBo bo) {
        SysWorkflowNotice update = BeanUtil.toBean(bo, SysWorkflowNotice.class);
        validEntityBeforeSave(update);
        SysWorkflowNoticeVo sysWorkflowNoticeVo = baseMapper.selectVoOne(new LambdaQueryWrapper<SysWorkflowNotice>().eq(SysWorkflowNotice::getProcinstId, bo.getProcinstId())
            .eq(SysWorkflowNotice::getNoticeId, bo.getNoticeId()));
        if (sysWorkflowNoticeVo != null) {
            return baseMapper.update(update, new LambdaUpdateWrapper<SysWorkflowNotice>().eq(SysWorkflowNotice::getProcinstId, bo.getProcinstId())) > 0 ;
        } else {
            return baseMapper.insert(update) > 0;
        }
    }

3.8 最终效果

最终实现的通知公告发布流程操作效果如下:

  • 新增公告:

 

  • 发起发布审批流程

 

  • 流程详情查看

 

  • 流程审批

  • 公告发布成功!

 

3.9 遗留问题 

改造完毕过程中,仍有些问题可以进一步改进,后期可以持续改善:

  •  流程强制取消后的状态标记使用的是注释,而没有直接写入流程状态,增加了一次查询过程,数据量大时影响效率,后期需考虑改进
  • 流程的procdefId号是建立完毕后在数据库手工查找的,再记录到config.js文件,这样等于是写死了,不利于流程模型的更新,后期考虑通过前端的对话框等方式予以配置
  • 流程到达审批者的时候,可以考虑增加一条即时的提醒弹出窗口,提醒审批者及时审批,后期考虑增加即时提醒弹窗功能
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
搭建 Linux+OA 系统需要以下步骤: 1. 安装 Linux 操作系统,可以选择 CentOS、Ubuntu 等版本; 2. 安装 Web 服务器,如 Apache、Nginx 等; 3. 安装 PHP 解释器和相关扩展,如 MySQL、Redis 等; 4. 下载并安装 OA 系统,如用 PHP 开发的 Discuz、用 Java 开发的 Huihoo 等。 具体步骤如下: 1. 安装 Linux 操作系统 可以选择 CentOS、Ubuntu 等版本,根据自己的需求选择合适的版本。安装过程中需要设置 root 用户密码和网络配置等。 2. 安装 Web 服务器 以 Apache 为例,可以使用以下命令进行安装: ``` sudo apt-get update sudo apt-get install apache2 ``` 安装完成后,可以通过浏览器访问 http://localhost/ 来测试是否安装成功。 3. 安装 PHP 解释器和相关扩展 以 PHP 7.4 为例,可以使用以下命令进行安装: ``` sudo apt-get install php7.4 php7.4-mysql php7.4-redis ``` 安装完成后,可以通过创建一个 phpinfo.php 文件来测试是否安装成功: ``` <?php phpinfo(); ?> ``` 将该文件放置在 Apache 的默认网站目录 /var/www/html/ 下,然后通过浏览器访问 http://localhost/phpinfo.php 来查看 PHP 的相关信息。 4. 下载并安装 OA 系统 可以选择用 PHP 开发的 Discuz、用 Java 开发的 Huihoo 等。以 Discuz 为例,可以使用以下命令进行安装: ``` sudo apt-get install unzip cd /var/www/html/ sudo wget https://download.comsenz.com/DiscuzX/3.5/Discuz_X3.5_SC_UTF8.zip sudo unzip Discuz_X3.5_SC_UTF8.zip sudo chown -R www-data:www-data Discuz_X3.5_SC_UTF8/ ``` 安装完成后,可以通过浏览器访问 http://localhost/Discuz_X3.5_SC_UTF8/ 来进入 Discuz 的安装向导。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值