章节索引 :

在WorkFlowServiceImpl类中添加下面代码

@Service
public class WorkflowServiceImpl implements WorkflowService {
    @Override
    public void approvalTask(HashMap param) {
        String taskId = MapUtil.getStr(param, "taskId");
        String approval = MapUtil.getStr(param, "approval");
        taskService.setVariableLocal(taskId, "result", approval);
        taskService.complete(taskId);
    }

    @Override
    public void archiveTask(HashMap param) {
        String taskId = MapUtil.getStr(param, "taskId");
        int userId = MapUtil.getInt(param, "userId");
        JSONArray files = (JSONArray) param.get("files");
        taskService.setVariable(taskId, "files", files); //把归档文件信息存储在工作流实例中
        taskService.setVariable(taskId,"filing",false);
        //认领该任务,否则查询不到我审批过该记录
        taskService.setOwner(taskId, userId + "");
        taskService.setAssignee(taskId, userId + "");
        taskService.complete(taskId);
    }


    @Override
    public boolean searchProcessStatus(String instanceId) {
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        if (instance != null) {
            //工作流未结束
            return false;
        } else {
            //工作流已经结束
            return true;
        }
    }

    @Override
    public void deleteProcessById(String uuid, String instanceId, String type, String reason) {
        long count = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).count();
        if (count > 0) {
            runtimeService.deleteProcessInstance(instanceId, reason);    //删除工作流
        }
        count = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).count();
        if (count > 0) {
            historyService.deleteHistoricProcessInstance(instanceId);   //删除工作流历史
        }
        //判断是否是会议工作流,然后删除定时器
        if (type.equals("会议申请")) {
            quartzUtil.deleteJob(uuid, "会议开始任务组");
            quartzUtil.deleteJob(uuid, "会议结束任务组");
            quartzUtil.deleteJob(uuid, "会议工作流组");
            quartzUtil.deleteJob(uuid, "创建会议室ID任务组");
        }
    }
        @Override
    public String startLeaveProcess(HashMap param) {
        String instanceId = runtimeService.startProcessInstanceByKey("leave", param).getProcessInstanceId(); //启动工作流
        return instanceId;
    }

    @Override
    public String startReimProcess(HashMap param) {
        String instanceId = runtimeService.startProcessInstanceByKey("reim", param).getProcessInstanceId(); //启动工作流
        return instanceId;
    }
}

创建MeetingService接口,代码如下:

public interface MeetingService {
    public HashMap searchMeetingByInstanceId(String instanceId);

    public HashMap searchMeetingByUUID(String uuid);

    public Long searchRoomIdByUUID(String uuid);

    public List<String> searchUserMeetingInMonth(HashMap param);

    public void updateMeetingStatus(HashMap param);

    public ArrayList<Integer> searchMeetingUnpresent(String uuid);

    public int updateMeetingUnpresent(HashMap param);
}

创建MeetingServiceImpl类,代码如下:

package com.example.emos.workflow.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.example.emos.workflow.db.dao.TbMeetingDao;
import com.example.emos.workflow.exception.EmosException;
import com.example.emos.workflow.service.MeetingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class MeetingServiceImpl implements MeetingService {
    @Autowired
    private TbMeetingDao meetingDao;

    @Autowired
    @Qualifier(value = "redisTemplate")
    private RedisTemplate redisTemplate;


    public HashMap searchMeetingByInstanceId(String instanceId) {
        HashMap map = meetingDao.searchMeetingByInstanceId(instanceId);
        String date = map.get("date").toString();
        String start = map.get("start").toString();
        DateTime startDate = DateUtil.parse(date + " " + start, "yyyy-MM-dd HH:mm");
        String end = map.get("end").toString();
        DateTime endDate = DateUtil.parse(date + " " + end, "yyyy-MM-dd HH:mm");
        long hours = DateUtil.between(endDate, startDate, DateUnit.HOUR, true);
        map.put("hours", hours);
        return map;
    }

    @Override
    public HashMap searchMeetingByUUID(String uuid) {
        HashMap meeting = meetingDao.searchMeetingByUUID(uuid);
        return meeting;
    }


    @Override
    public Long searchRoomIdByUUID(String uuid) {
        Object temp = redisTemplate.opsForValue().get(uuid);
        long roomId = Long.parseLong(temp.toString());
        return roomId;
    }

    @Override
    public List<String> searchUserMeetingInMonth(HashMap param) {
        List list = meetingDao.searchUserMeetingInMonth(param);
        return list;
    }

    @Override
    public void updateMeetingStatus(HashMap param) {
        int row = meetingDao.updateMeetingStatus(param);
        if (row != 1) {
            throw new EmosException("会议状态更新失败");
        }
    }

    @Override
    public ArrayList<Integer> searchMeetingUnpresent(String uuid) {
        ArrayList<Integer> list=meetingDao.searchMeetingUnpresent(uuid);
        return list;
    }

    @Override
    public int updateMeetingUnpresent(HashMap param) {
        int rows=meetingDao.updateMeetingUnpresent(param);
        return rows;
    }
}

创建NotifyMeetingService.java类

@Slf4j
@Component
public class NotifyMeetingService implements JavaDelegate {
    @Autowired
    private QuartzUtil quartzUtil;

    @Autowired
    private MeetingService meetingService;

    @Override
    public void execute(DelegateExecution delegateExecution) {
        Map map = delegateExecution.getVariables();
        String uuid = MapUtil.getStr(map, "uuid");
        String url = MapUtil.getStr(map, "url");
        String result = MapUtil.getStr(map, "result");
        HashMap data = meetingService.searchMeetingByUUID(uuid);
        String title = MapUtil.getStr(data, "title");
        String date = MapUtil.getStr(data, "date");
        String start = MapUtil.getStr(data, "start");
        String end = MapUtil.getStr(data, "end");
        if (result.equals("同意")) {
            meetingService.updateMeetingStatus(new HashMap() {{
                put("uuid", uuid);
                put("status", 3);
            }});

            String meetingType = delegateExecution.getVariable("meetingType", String.class);
            //线上会议要创建视频会议室
            if (meetingType.equals("线上会议")) {
                JobDetail jobDetail = JobBuilder.newJob(MeetingRoomJob.class).build();
                Map param = jobDetail.getJobDataMap();
                param.put("uuid", uuid);
                Date expire = DateUtil.parse(date + " " + end, "yyyy-MM-dd HH:mm");
                param.put("expire", expire);
                //会议开始前15分钟,计算roomID
                Date executeDate = DateUtil.parse(date + " " + start, "yyyy-MM-dd HH:mm").offset(DateField.MINUTE, -15);

                quartzUtil.addJob(jobDetail, uuid, "创建会议室ID任务组", executeDate);
            }
        } 
        quartzUtil.deleteJob(uuid, "会议工作流组");
        JSONObject json = new JSONObject();
        String processId = delegateExecution.getProcessInstanceId();
        json.set("processId", processId);
    }
}

创建NotifyLeaveService.java类

@Component
public class NotifyLeaveService implements JavaDelegate {
    @Autowired
    private HistoryService historyService;

    @Autowired
    private TbLeaveDao leaveDao;

    @Autowired
    private TbUserDao userDao;

    @Autowired
    private EmailTask emailTask;

    @Override
    public void execute(DelegateExecution delegateExecution) {
        //查找该任务流中最后一个人的审批任务
        HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables()
                .includeTaskLocalVariables().processInstanceId(delegateExecution.getProcessInstanceId())
                .orderByHistoricTaskInstanceEndTime().list().get(0);
        //获取最后的审批人的审批结果
        String result = taskInstance.getTaskLocalVariables().get("result").toString();
        delegateExecution.setVariable("result", result);
        String instanceId = delegateExecution.getProcessInstanceId();
        //修改请假状态
        HashMap param = new HashMap() {{
            put("status", "同意".equals(result) ? 3 : 2);
            put("instanceId", instanceId);
        }};

        int rows = leaveDao.updateLeaveStatus(param);
        if (rows != 1) {
            throw new EmosException("更新请假记录状态失败");
        }
        
    }
}

创建NotifyReimService.java类

@Component("notifyReimService")
public class NotifyReimService implements JavaDelegate {

    @Autowired
    private HistoryService historyService;

    @Autowired
    private TbUserDao userDao;

    @Autowired
    private TbReimDao reimDao;

    @Autowired
    private EmailTask emailTask;


    @Override
    public void execute(DelegateExecution delegateExecution) {
        //查找该任务流中最后一个人的审批任务
        HistoricTaskInstance taskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables()
                .includeTaskLocalVariables().processInstanceId(delegateExecution.getProcessInstanceId())
                .orderByHistoricTaskInstanceEndTime().list().get(0);
        //获取最后的审批人的审批结果
        String result = taskInstance.getTaskLocalVariables().get("result").toString();
        delegateExecution.setVariable("result", result);
        String instanceId = delegateExecution.getProcessInstanceId();
        //修改请假状态
        HashMap param = new HashMap() {{
            put("status", "同意".equals(result) ? 3 : 2);
            put("instanceId", instanceId);
        }};

        int rows = reimDao.updateReimStatus(param);
        if (rows != 1) {
            throw new EmosException("更新报销记录状态失败");
        }

    }
}

创建TbAmectDao.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.emos.workflow.db.dao.TbAmectDao">
    <insert id="insert" parameterType="com.example.emos.workflow.db.pojo.TbAmect">
        INSERT INTO tb_amect
        SET uuid = #{uuid},
            user_id = #{userId},
            amount = #{amount},
            type_id = #{typeId},
            reason = #{reason},
            prepay_id = #{prepayId},
            `status` = 1
    </insert>
</mapper>

创建TbAmectTypeDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.emos.workflow.db.dao.TbAmectTypeDao">
    <select id="searchByType" parameterType="String" resultType="HashMap">
        SELECT id, money FROM tb_amect_type WHERE type=#{type}
    </select>
</mapper>

创建TbLeaveDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.emos.workflow.db.dao.TbLeaveDao">
    <select id="searchLeaveByInstanceId" parameterType="String" resultType="HashMap">
        SELECT
            u.`name`,
            l.reason,
            u.id AS userId,
            DATE_FORMAT( l.`start`, '%Y-%m-%d %H:%i' ) AS `start`,
            DATE_FORMAT( l.`end`, '%Y-%m-%d %H:%i' ) AS `end`,
            l.days,
            l.type,
            l.`status`
        FROM
            tb_leave l
        JOIN tb_user u ON l.user_id = u.id
        WHERE l.instance_id=#{instanceId}
    </select>
    <update id="updateLeaveStatus" parameterType="HashMap">
        UPDATE tb_leave SET status=#{status} WHERE instance_id=#{instanceId}
    </update>

</mapper>

创建TbMeetingDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.emos.workflow.db.dao.TbMeetingDao">
    <select id="searchMeetingByInstanceId" parameterType="String" resultType="HashMap">
        SELECT
            m.id,
            m.uuid,
            u.name,
            u.photo,
            m.`desc`,
            m.place,
            m.type,
            m.date,
            DATE_FORMAT( m.START, '%H:%i' ) AS start,
            DATE_FORMAT( m.END, '%H:%i' ) AS end,
            (
                SELECT GROUP_CONCAT( u.name SEPARATOR "、") FROM tb_user u
                WHERE u.status = 1 AND JSON_CONTAINS ( m.members, CONVERT ( u.id, CHAR ))
            ) AS members
        FROM tb_meeting m
        JOIN tb_user u ON m.creator_id=u.id
        WHERE instance_id=#{instanceId};
    </select>
    <select id="searchMeetingMembersInSameDept" parameterType="String" resultType="boolean">
        SELECT
            IF(COUNT(DISTINCT u.dept_id)=1,TRUE,FALSE ) AS bool
        FROM
            tb_meeting m
                JOIN tb_user u ON JSON_CONTAINS ( m.members, CAST( u.id AS CHAR ) )
        WHERE m.uuid=#{uuid} AND u.status = 1
    </select>
    <select id="searchMeetingByUUID" parameterType="String" resultType="HashMap">
        SELECT
            m.uuid,
            m.title,
            u.name,
            DATE_FORMAT( m.date, '%Y-%m-%d' ) AS date,
			m.place,
			DATE_FORMAT( m.START, '%H:%i' ) AS start,
            DATE_FORMAT( m.END, '%H:%i' ) AS end,
			m.type,
			m.status,
			m.desc
		FROM
			tb_meeting m
		JOIN tb_user u ON m.creator_id = u.id
		WHERE m.uuid =#{uuid} AND u.status = 1
    </select>
    <select id="searchUserMeetingInMonth" parameterType="Map" resultType="String">
        SELECT
            DISTINCT DATE_FORMAT( m.date, '%Y/%m/%d' ) AS date
        FROM
            tb_meeting m
            JOIN tb_user u ON JSON_CONTAINS ( m.members, CAST( u.id AS CHAR ) )
        WHERE u.id = #{userId}
          AND u.status = 1
          AND m.status IN(3,4)
          AND DATE_FORMAT(m.date,'%Y/%m')=#{express}
    </select>
    <select id="searchMeetingUnpresent" parameterType="String" resultType="Integer">
        SELECT u.id
        FROM tb_meeting m JOIN tb_user u ON JSON_CONTAINS ( m.members, CONVERT ( u.id, CHAR ) )
             AND NOT JSON_CONTAINS ( IFNULL(m.present,JSON_ARRAY()), CONVERT ( u.id, CHAR ) )
        WHERE u.`status`=1 AND m.uuid=#{uuid}
    </select>
    <update id="updateMeetingStatus" parameterType="HashMap">
        UPDATE tb_meeting
        SET `status`=#{status}
        WHERE uuid=#{uuid}
    </update>
    <update id="updateMeetingUnpresent" parameterType="HashMap">
        UPDATE tb_meeting
        SET unpresent=#{unpresent}
        WHERE uuid=#{uuid}
    </update>
</mapper>

创建TbReimDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.emos.workflow.db.dao.TbReimDao">
    <select id="searchReimByInstanceId" parameterType="String" resultType="HashMap">
        SELECT r.id,
               u.`name`,
               d.dept_name AS deptId,
               r.content,
               r.amount,
               r.anleihen,
               r.balance,
               r.type_id AS typeId,
               r.`status`
        FROM tb_reim r
        JOIN tb_user u ON r.user_id = u.id
        JOIN tb_dept d ON u.dept_id = d.id
        WHERE instance_id = #{instanceId}
    </select>
    <update id="updateReimStatus" parameterType="HashMap">
        UPDATE tb_reim
        SET status = #{status}
        WHERE instance_id = #{instanceId}
    </update>
</mapper>

创建TbUserDao.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.emos.workflow.db.dao.TbUserDao">
    <select id="searchEmailByIds" resultType="String">
        SELECT email FROM tb_user
        WHERE id IN
        <foreach collection="array" open="(" close=")" item="one" separator=",">
            #{one}
        </foreach>
    </select>
    <select id="searchEmailByRoles" resultType="String">
        SELECT u.email
        FROM tb_user u
        JOIN tb_role r ON JSON_CONTAINS ( u.role, CONVERT ( r.id, CHAR ) )
        WHERE u.`status`=1
        AND r.role_name IN
        <foreach collection="array" open="(" close=")" item="one" separator=",">
            #{one}
        </foreach>
    </select>
</mapper>

创建TbAmectDao

@Mapper
public interface TbAmectDao {
    public int insert(TbAmect amect);
}

创建TbAmectTypeDao

@Mapper
public interface TbAmectTypeDao {
    public HashMap searchByType(String type);
}

创建TbLeaveDao

@Mapper
public interface TbLeaveDao {
    public HashMap searchLeaveByInstanceId(String instanceId);

    public int updateLeaveStatus(HashMap param);
}

创建TbMeetingDao

@Mapper
public interface TbMeetingDao {
    public HashMap searchMeetingByInstanceId(String instanceId);

    public boolean searchMeetingMembersInSameDept(String uuid);

    public HashMap searchMeetingByUUID(String uuid);

    public List<String> searchUserMeetingInMonth(HashMap param);

    public int updateMeetingStatus(HashMap param);

    public ArrayList<Integer> searchMeetingUnpresent(String uuid);

    public int updateMeetingUnpresent(HashMap param);
}

创建TbReimDao

@Mapper
public interface TbReimDao {
    public HashMap searchReimByInstanceId(String instanceId);

    public int updateReimStatus(HashMap param);
}

创建TbUserDao

@Mapper
public interface TbUserDao {
    public ArrayList<String> searchEmailByIds(int[] ids);

    public ArrayList<String> searchEmailByRoles(String[] roles);
}

创建Approval

@Data
public class Approval {
    private String processId;
    private String taskId;
    private String title;
    private String type;
    private String creatorName;
    private String createDate;
    private String status;
    private String result;
    private boolean filing;
}

创建TbAmect

@Data
public class TbAmect {
    private Integer id;
    private String uuid;
    private Integer userId;
    private BigDecimal amount;
    private Integer typeId;
    private String reason;
    private String prepayId;
    private Byte status;
    private Date createTime;
}

创建TbAmectType

@Data
public class TbAmectType {
    private Integer id;
    private String type;
    private BigDecimal money;
    private Boolean systemic;
}

创建TbLeave

@Data
public class TbLeave {
    private Integer id;
    private Integer userId;
    private String reason;
    private String start;
    private String end;
    private String days;
    private Byte type;
    private Byte status;
    private String instanceId;
    private Date createTime;
}

创建TbMeeting

@Data
public class TbMeeting implements Serializable {
    /**
     * 主键
     */
    private Long id;

    /**
     * UUID
     */
    private String uuid;

    /**
     * 会议题目
     */
    private String title;

    /**
     * 创建人ID
     */
    private Long creatorId;

    /**
     * 日期
     */
    private String date;

    /**
     * 开会地点
     */
    private String place;

    /**
     * 开始时间
     */
    private String start;

    /**
     * 结束时间
     */
    private String end;

    /**
     * 会议类型(1在线会议,2线下会议)
     */
    private Short type;

    /**
     * 参与者
     */
    private String members;

    /**
     * 会议内容
     */
    private String desc;

    /**
     * 工作流实例ID
     */
    private String instanceId;

    private String present;

    private String unpresent;

    /**
     * 状态(1未开始,2进行中,3已结束)
     */
    private Short status;

    /**
     * 创建时间
     */
    private Date createTime;

    private static final long serialVersionUID = 1L;
}

创建TbReim

@Data
public class TbReim {
    private Integer id;
    private Integer userId;
    private String content;
    private BigDecimal amount;
    private BigDecimal anleihen;
    private BigDecimal balance;
    private Byte typeId;
    private Byte status;
    private String instanceId;
    private Date createTime;
}

创建TbUser

@Data
public class TbUser implements Serializable {
    /**
     * 主键
     */
    private Integer id;

    /**
     * 用户名
     */
    private String username;


    /**
     * 密码
     */
    private String password;

    /**
     * 长期授权字符串
     */
    private String openId;

    /**
     * 昵称
     */
    private String nickname;

    /**
     * 头像网址
     */
    private String photo;

    /**
     * 姓名
     */
    private String name;

    /**
     * 性别
     */
    private Object sex;

    /**
     * 手机号码
     */
    private String tel;

    /**
     * 邮箱
     */
    private String email;

    /**
     * 入职日期
     */
    private Date hiredate;

    /**
     * 角色
     */
    private Object role;

    /**
     * 是否是超级管理员
     */
    private Boolean root;

    /**
     * 部门编号
     */
    private Integer deptId;

    /**
     * 状态
     */
    private Byte status;

    /**
     * 创建时间
     */
    private Date createTime;

    private static final long serialVersionUID = 1L;
}

创建AmectServiceImpl

@Service
public class AmectServiceImpl implements AmectService {
    @Autowired
    private TbAmectDao amectDao;

    @Override
    public int insert(TbAmect amect) {
        int rows = amectDao.insert(amect);
        return rows;
    }
}

创建AmectTypeServiceImpl

@Service
public class AmectTypeServiceImpl implements AmectTypeService {
    @Autowired
    private TbAmectTypeDao amectTypeDao;

    @Override
    public HashMap searchByType(String type) {
        HashMap map= amectTypeDao.searchByType(type);
        return map;
    }
}

创建LeaveServiceImpl

@Service
public class LeaveServiceImpl implements LeaveService {
    @Autowired
    private TbLeaveDao leaveDao;

    @Override
    public HashMap searchLeaveByInstanceId(String instanceId) {
        HashMap map = leaveDao.searchLeaveByInstanceId(instanceId);
        return map;
    }
}

创建MeetingServiceImpl

@Service
public class MeetingServiceImpl implements MeetingService {
    @Autowired
    private TbMeetingDao meetingDao;

    @Autowired
    @Qualifier(value = "redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    public HashMap searchMeetingByUUID(String uuid) {
        HashMap meeting = meetingDao.searchMeetingByUUID(uuid);
        return meeting;
    }


    @Override
    public Long searchRoomIdByUUID(String uuid) {
        Object temp = redisTemplate.opsForValue().get(uuid);
        long roomId = Long.parseLong(temp.toString());
        return roomId;
    }

    @Override
    public List<String> searchUserMeetingInMonth(HashMap param) {
        List list = meetingDao.searchUserMeetingInMonth(param);
        return list;
    }

    @Override
    public void updateMeetingStatus(HashMap param) {
        int row = meetingDao.updateMeetingStatus(param);
        if (row != 1) {
            throw new EmosException("会议状态更新失败");
        }
    }

    @Override
    public ArrayList<Integer> searchMeetingUnpresent(String uuid) {
        ArrayList<Integer> list=meetingDao.searchMeetingUnpresent(uuid);
        return list;
    }

    @Override
    public int updateMeetingUnpresent(HashMap param) {
        int rows=meetingDao.updateMeetingUnpresent(param);
        return rows;
    }
}

创建ReimServiceImpl


@Service
public class ReimServiceImpl implements ReimService {

    @Autowired
    private TbReimDao reimDao;

    @Override
    public HashMap searchReimByInstanceId(String instanceId) {
        HashMap map = reimDao.searchReimByInstanceId(instanceId);
        return map;
    }
}

创建SearchApprovalBpmnForm

@Data
public class SearchApprovalBpmnForm {
    @NotBlank(message = "instanceId不能为空")
    private String instanceId;

    @NotBlank(message = "code不能为空")
    private String code;

    @NotBlank(message = "tcode不能为空")
    @Pattern(regexp = "^[0-9]{6}$",message = "tcode必须是6位数字")
    private String tcode;
}

创建ApprovalTaskForm

public class ApprovalTaskForm {

    @NotBlank(message = "taskId不能为空")
    private String taskId;

    @NotBlank(message = "approval不能为空")
    @Pattern(regexp = "^同意$|^不同意$", message = "approval内容不正确")
    private String approval;

    @NotBlank(message = "code不能为空")
    private String code;

    @NotBlank(message = "tcode不能为空")
    @Pattern(regexp = "^[0-9]{6}$",message = "tcode必须是6位数字")
    private String tcode;

}
第一章 搭建开发环境
1-1 导学 1-2 搭建开发环境 1-3 MacOS环境的程序安装 1-4 本课程学习方法介绍 1-5 本章总结
第二章 运行项目工程
2-1 本章介绍 2-2 运行工作流项目 2-3 运行后端SpringBoot项目 2-4 运行移动端和前端项目 2-5 前后端项目分析 2-6 前端页面布局设计 2-7 本章总结
第三章 用户管理模块
3-1 本章介绍 3-2 用户登陆系统的流程说明 3-3 编写用户登陆程序(后端) 3-4 编写用户登陆程序(前端) 3-5 修改密码和退出登陆(后端) 3-6 修改密码和退出登陆(前端) 3-7 查询用户分页数据(后端) 3-8 查询用户分页数据(前端) 3-9 添加新用户(后端) 3-10 添加新用户(前端) 3-11 修改用户信息(后端) 3-12 修改用户信息(前端) 3-13 删除非管理员帐户(后端) 3-14 删除非管理员帐户(前端) 3-15 本章总结
第四章 角色管理
4-1 本章介绍 4-2 查询角色分页数据(后端) 4-3 查询角色分页数据(前端) 4-4 添加新角色(后端) 4-5 添加新角色(前端) 4-6 修改角色信息(后端) 4-7 修改角色信息(前端) 4-8 删除非内置角色(后端) 4-9 删除非内置角色(前端) 4-10 本章总结
第五章 部门管理
5-1 本章介绍 5-2 查询部门分页数据(后端) 5-3 查询部门分页数据(前端) 5-4 添加新部门(后端) 5-5 添加新部门(前端) 5-6 修改部门信息(后端) 5-7 修改部门信息(前端) 5-8 删除无用户的部门(后端) 5-9 删除无用户的部门(前端) 5-10 本章总结
第六章 会议室管理
6-1 本章介绍 6-2 查询会议室分页数据(后端) 6-3 查询会议室分页数据(前端) 6-4 添加新会议室(后端) 6-5 添加新会议室(前端) 6-6 修改会议室信息(后端) 6-7 修改会议室信息(前端) 6-8 删除空闲的会议室(后端) 6-9 删除空闲会议室(前端) 6-10 本章总结
第七章 线下会议管理
7-1 本章介绍 7-2 线下会议日程表(持久层) 7-3 线下会议日程表(业务层&Web层) 7-4 分析线下会议日程表前端设计 7-5 线下会议日程表(前端) 7-6 分析会议申请的执行流程 7-7 用异步线程开启线下会议审批流程 7-8 创建线下会议申请(后端) 7-9 创建线下会议申请(前端) 7-10 线下会议周日历(后端) 7-11 线下会议周日历(前端) 7-12 周日历弹窗浏览会议详情(前端) 7-13 删除线下会议申请(后端) 7-14 删除线下会议申请(前端) 7-15 本章总结 附-1 查询线上会议分页数据(后端) 附-2 查询线上会议分页数据(前端) 附-3 申请线上会议(前端) 附-4 删除线上会议申请(前端)
第八章 会议审批
8-1 章节介绍 8-2 查询会议申请分页数据(后端) 8-3 查询会议申请分页数据(前端) 8-4 查询审批任务详情信息(后端) 8-5 查询审批任务详情信息(前端) 8-6 加载BPMN实时进度图 8-7 审批会议申请(后端) 8-8 审批会议申请(前端) 8-9 本章总结
第九章 TRTC在线视频会议
9-1 本章介绍 9-2 获取用户签名和视频会议室RoomID 9-3 查询参会人,生成视频墙(后端) 9-4 生成视频会议室视频墙(前端) 9-5 如何创建TRTC视频推流 9-6 推送本地视频流,订阅远端视频流 9-7 实现入会签到功能 9-8 实时更新上线参会人列表 9-9 动态显示参会人语音强弱 9-10 挂断TRTC,退出视频会议 9-11 大屏显示某个远端视频 9-12 本地屏幕共享,广播推流 9-13 本章总结
第十章 罚款管理
10-1 本章介绍 10-2 查询罚款分页数据(后端) 10-3 查询罚款分页数据(前端) 10-4 添加新罚款记录(后端) 10-5 添加新罚款记录(前端) 10-6 修改罚款单(后端) 10-7 修改罚款单(前端) 10-8 删除罚款单(后端) 10-9 删除罚款单(前端) 10-10 了解微信Native支付罚款流程 10-11 设置内网穿透,用于接收付款结果 10-12 创建支付订单(持久层&业务层) 10-13 创建支付订单(Web层) 10-14 创建支付订单(前端) 10-15 接收付款结果(后端) 10-16 配置SpringBoot支持WebSo 10-17 推送付款结果 10-18 接收付款结果(前端) 10-19 主动查询付款结果(后端) 10-20 主动查询付款结果(前端) 10-21 本章总结 附-1 查询图表数据(后端) 附-2 显示图表数据(前端)
第十一章 罚款类型管理
11-1 本章介绍 11-2 查询罚款类别分页数据(后端) 11-3 查询罚款类别分页数据(前端) 11-4 添加新罚款类型(后端) 11-5 添加新罚款类型(前端) 11-6 修改罚款类型信息(后端) 11-7 修改罚款类型信息(前端) 11-8 删除罚款类型记录(后端) 11-9 删除罚款类型记录(前端) 11-10 本章总结
第十二章 请假管理
12-1 本章介绍 12-2 查询请假分页数据(后端) 12-3 查询请假分页数据(前端) 12-4 用异步线程开启请假审批 12-5 我要请假(后端) 12-6 我要请假(前端) 12-7 用异步线程关闭请假审批工作流实例 12-8 删除请假申请(后端) 12-9 删除请假申请(前端) 12-10 审批员工请假 12-11 生成请假单(后端) 12-12 生成请假单(前端) 12-13 封装腾讯云存储服务 12-14 执行请假归档(后端) 12-15 上传归档文件(前端) 12-16 执行请假归档(前端) 12-17 本章总结
第十三章 报销管理
13-1 本章介绍 13-2 查询报销分页数据(后端) 13-3 查询报销分页数据(前端) 13-4 用异步线程开启报销审批 13-5 创建报销申请(后端) 13-6 创建报销申请(前端) 13-7 生成PDF报销单(后端) 13-8 生成PDF报销单(前端) 13-9 审批报销申请 13-10 删除报销申请(后端) 13-11 删除报销申请(前端) 13-12 本章总结
第十四章 部署Emos项目
14-1 本章介绍 14-2 选购云主机 14-3 安装Docker环境 14-4 Docker中安装程序 14-5 在Docker中部署Java项目 14-6 在Docker中部署前端项目 14-7 本章总结 附录1 为云主机配置域名
第十五章 扩展功能
15-1 微信扫码登陆(后端生成二维码图片) 15-2 微信扫码登陆(前端加载二维码) 15-3 微信扫码登陆(微信小程序) 15-4 NFC扫码功能简介 15-5 NFC扫码识别
第十六章 员工离职
16-1 员工离职(一) 16-2 员工离职(二) 16-3 调试员工离职功能
第十七章 工作流
17-1 Activiti简介 17-2 创建工作流项目 17-3 BPMN入门 17-4 任务审批 16-5 会议审批工作流(一) 16-6 会议审批工作流(二) 16-7 审批工作流
附录
附录1 创建SpringBoot项目 附录2 集成常用工具库 附录3 整合权限验证与授权 附录4 允许跨域请求 附录5 封装全局异常 附录6 全局处理异常 附录7 开启Java异步执行 附录8 抵御XSS攻击 附录9 创建分页数据封装类