文章目录

  • 前言
      • 项目开始前导入项目的准备工作
    • 1.后端项目的common类
      • 1.2.使用WebSocket
      • 1.2.1对于前端的公共页面
      • 1.2.2对于账户的登陆方面
    • 2.登陆、用户的增删改查![在这里插入图片描述](https://img-blog.csdnimg.cn/c78d00a756704379b1ac9bdc247a43b2.png)
      • 2.1退出登陆
      • 2.2新增用户功能
      • 2.4 gantt图的处理
    • 会议申请的工作流项目接口
      • 3.2查询周日历详情;
    • 第五章:任务审批
      • 5.1加载对应的BPMN时序图
      • TRTC
      • 入会签到功能的实现

前言

Emos是一个前后端分离的项目;采用了当下流行的前后端分离架构及技术( Java、SSM、Vue3.0 ),课可作为全面提升前后端技术水平的正确选择。

  • 技术栈图
  • 系统性构建大型分布式OA系统;多对多视频&云存储功能;基于SSM的后端分布式项目,多角色多任务工作流审批

项目开始前导入项目的准备工作

  • 解决为啥导入项目maven配置好的没有用
  • 导入pom文件Maven初始化项目

1.后端项目的common类

  • 1.PageUtils类里面有相关的变量用来保存对应的“总页数”,“总记录”,“当前页数‘,”分页记录“等数据,查询分页的时候,在service层我们返回PageUtils就行
@Data
public class PageUtils implements Serializable {private static final long serialVersionUID = 1L;/*** 总记录数*/private long totalCount;/*** 每页记录数*/private int pageSize;/*** 总页数*/private int totalPage;/*** 当前页数*/private int pageIndex;/*** 列表数据*/private List list;public PageUtils(List list, long totalCount, int pageIndex, int pageSize) {this.list = list;this.totalCount = totalCount;this.pageSize = pageSize;this.pageIndex = pageIndex;this.totalPage = (int) Math.ceil((double) totalCount / pageSize);}}
  • 2.在前后端分离的框架,后端返回前端的数据是一个json格式,不限制具体业务的数据内容,但是JSON内部的属性要统一,比如某个属性是状态码,某个属性是异常信息,某个属性是分页数据。在项目中创建一个R类作为返回前端的数据
public class R extends HashMap<String, Object> {public R() {put("code", HttpStatus.SC_OK);put("msg", "success");}public R put(String key, Object value) {super.put(key, value);return this;}public static R ok() {return new R();}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R error(String msg) {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);}public static R error() {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");}}

1.2.使用WebSocket

WebSocket是一种长连接的会话技术,用了它之后,前端和后端项目的通信省去了每次都要重新创建HTTP请求的麻烦。而且你也不用担心,长连接会耗光后端服务器的网络资源。因为WebSocket长连接是有过期时间的,比如
说我们可以设置超过5分钟,前后端没有通信就自动切断WebSocket连接。
支付罚款单的业务中,我们不是在前端采用定时轮询的方式查询付款是否成功,而是后端项目收到腾讯服务器
的付款通知以后,再把结果推送给前端页面,这样就省去后端处理很多前端的轮询请求。

1.2.1对于前端的公共页面

  • 因为是基于VUE的,因此我们采用父子路由,比方说顶部和左边菜单都是公用的,因此对于公共页面就可以进行抽取

1.2.2对于账户的登陆方面

  • 采用前后端验证,颁发Token令牌,认证机制前端验证提供的Token令牌再去Reids进行查看是否有缓存的Token,原因就是防止黑客或者用postman测试自己生成一个token,这样就算通过前端验证,但是还需要再redis缓存中进行查阅,如果没有便不成功。

2.登陆、用户的增删改查

  • 用户登陆流程
  • 前端传username和password,我们进行到数据库进行查询即可,由于密码是进行加密了,因此我们需要对前端传过来的进行加密
    <!--用户登陆方法--><select id="login" resultType="java.lang.Integer" parameterType="java.util.HashMap">selectidfromtb_userwhere username = #{username}andpassword = HEX(AES_ENCRYPT(#{password},#{username}))LIMIT 1;</select>
  • controller
    @PostMapping("/login")@Operation(summary = "登陆系统")public R login(@Valid @RequestBody LoginForm form){HashMap params = JSONUtil.parse(form).toBean(HashMap.class);Integer userId = userService.login(params);R r = R.ok().put("result",userId!=null ? true:false);if(userId!=null){StpUtil.setLoginId(userId);Set<String> permissions = userService.searchUserPermissions(userId);r.put("permissions",permissions);}return r;}
  • 我们这里前端的数据传过来都用的form类,使用FORM类的好处是可以为变量设置注解,自动完成后端验证
  • 我们知道登陆是需要前后端一起验证的,因此前端验证如下

2.1退出登陆

退出的时候,我们会将这个redis缓存中的数据进行清除,同时让浏览器的保存的cookie过期
这里我们采用的是SAtoken依赖

Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、Session会话、单点登录、OAuth2.0、微服务网关鉴权 等一系列权限相关问题。

  • 依赖引入
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.28.0</version>
</dependency>
    @GetMapping("/logout")@Operation(summary = "退出系统")public R logout(){StpUtil.logout();return R.ok();}

2.2新增用户功能

  • 这里包含了新增普通员工和超级管理员,其中超级管理员是需要激活码的。
    <insert id="insert" parameterType="com.example.emos.api.db.pojo.TbUser">INSERT INTO tb_userSET<if test="username!=null">username = #{username},</if><if test="password!=null">password = HEX(AES_ENCRYPT(#{password},#{username})),</if><if test="openId!=null">open_id = #{openId},</if><if test="nickname!=null">nickname = #{nickname},</if><if test="photo!=null">photo = #{photo},</if><if test="name!=null">name = #{name},</if><if test="sex!=null">sex = #{sex},</if><if test="tel!=null">tel = #{tel},</if><if test="email!=null">email=#{email},</if><if test="hiredate!=null">hiredate = #{hiredate},</if>role = #{role},<if test="root!=null">root = #{root},</if><if test="deptId!=null">dept_id = #{deptId},</if>status = #{status},create_time = #{createTime}</insert>

2.4 gantt图的处理


此处的分页比较复杂,首先tb_meeting保存的是线下和线上的各种会议。所以就有可能查询出来某个会议室某天有多条记录,然而再甘特图上,每个会议室只能出现一次,那么有两种解决方法:第一在前端编写代码时合并在相同会议室召开的会议,把他们的开会时间显示在甘特图上面。这里我们选择从后端解决问题,查询数据的时候就把相同会议的会议合成一条记录,不同的开会时间写到JSON数组当中。这里选择第二个解决方案。

    <select id="searchOfflineMeetingByPage" resultType="java.util.HashMap">select mr.name,temp.meetingfromtb_meeting_room mrleft join(select place,CONCAT("[",GROUP_CONCAT(JSON_OBJECT('start',DATE_FORMAT(start,'%H:i%'),'end',DATE_FORMAT(end,'%H:i%'),'status',`status`,'time',ROUND(timestampdiff(MINUTE,CONCAT(date," ",start),CONCAT(date," ",end))))),"]") AS meetingfrom tb_meetingwhere type = 2 and `status` != 2 and date = #{date}<if test="mold=='我的会议'">AND JSON_CONTAINS(members,CAST(#{userId} AS CHAR))</if>GROUP BY place) temp ON mr.name = temp.placewhere mr.`status`=1limit #{start},#{length};</select>

会议申请的工作流项目接口

  • 工作流项目提供了许多的Web方法让我们使用,创建会议申请

当浏览器发送请求时,java代码是一行一行同步执行的,但是工作流创建需要几秒的时间,所以需要异步
那么java中最好就是在开一个线程去调用我们的工作流
其中meetingWorkFolw就是一个任务线程

原因就是:

线程任务类发送HTTP请求给工作流项目,提交的数据中包括了会议申请人的userId\姓名、部门经理的userId、以及总经理的userId\那么这些数据都是要经过SQL查询的,包括线程任务类拿到instanceId之后,要更新tb_meeting表中的记录,所以还要定义UPDATE语句。

    @PostMapping("/insert")@Operation(summary = "添加会议")@SaCheckLoginpublic R insert(@Valid @RequestBody InsertMeetingForm form){DateTime start = DateUtil.parse(form.getDate() + " "+ form.getStart());DateTime end = DateUtil.parse(form.getDate()+" "+form.getEnd());if(start.isAfterOrEquals(end)){return R.error("结束时间必须大于开始时间");}else if(new DateTime().isAfterOrEquals(start)){return R.error("会议开始时间不能早于当前时间");//来的时候就已经开始了}TbMeeting meeting = JSONUtil.parse(form).toBean(TbMeeting.class);meeting.setUuid(UUID.randomUUID().toString(true));meeting.setCreatorId(StpUtil.getLoginIdAsInt());meeting.setStatus((short)1);int rows = meetingService.insert(meeting);return R.ok().put("rows",rows);}

3.2查询周日历详情;

  • DAO层
    <select id="searchOfflineMeetingInWeek" parameterType="HashMap" resultType="HashMap">SELECT id,title,DATE_FORMAT(`date`,"%m/%d") AS `date`,DATE_FORMAT(start,'%H:%i') AS `start`,ROUND(timestampdiff(MINUTE,CONCAT(`date`," ",`start`),CONCAT(`date`," ",`end`))/30) AS time,uuid,instance_id AS instanceId,IF(creator_id=#{userId},"true","false") AS isCreator,`status`FROM tb_meetingWHERE type = 2AND place = #{place}AND `status` != 2AND date BETWEEN #{startDate} AND #{endDate}<if test="mold=='我的会议'">AND JSON_CONTAINS(members, CAST(#{userId} AS CHAR))</if></select>
  • 查询当前会议searchCurrentMeetingInfo;用来显示会议概览信息
    <select id="searchMeetingInfo" parameterType="long" resultType="HashMap">SELECT m.title,m.date,m.place,DATE_FORMAT(m.`start`,'%H:%i') AS `start`,DATE_FORMAT(m.`end`,'%H:%i') AS `end`,m.status,CONCAT( "[", GROUP_CONCAT( JSON_OBJECT ( "name", u.name, "photo", u.photo ) SEPARATOR "," ), "]" ) AS membersFROM tb_meeting mLEFT JOIN tb_user u ON JSON_CONTAINS ( m.members, CONVERT ( u.id, CHAR ) )WHERE m.id = #{id} AND u.status=1</select><select id="searchCurrentMeetingInfo" parameterType="long" resultType="HashMap">SELECT m.title,m.date,m.place,DATE_FORMAT(m.`start`,'%H:%i') AS `start`,DATE_FORMAT(m.`end`,'%H:%i') AS `end`,m.status,(SELECT CONCAT( "[", GROUP_CONCAT( JSON_OBJECT ( "name", u.name, "photo", u.photo ) SEPARATOR "," ), "]" )FROM tb_user u WHERE JSON_CONTAINS ( m.present, CONVERT ( u.id, CHAR ) )) AS  present,(SELECT CONCAT( "[", GROUP_CONCAT( JSON_OBJECT ( "name", u.name, "photo", u.photo ) SEPARATOR "," ), "]" )FROM tb_user u WHERE JSON_CONTAINS ( m.unpresent, CONVERT ( u.id, CHAR ) )) AS  unpresentFROM  tb_meeting mWHERE m.id = #{id}</select>
  • Controller层的类
    @PostMapping("/searchOfflineMeetingInWeek")@Operation(summary = "查询某个会议室的一周会议")@SaCheckLoginpublic R searchOfflineMeetingInWeek(@Valid @RequestBody SearchOfflineMeetingInWeekForm form) {String date = form.getDate();DateTime startDate, endDate;if (date != null && date.length() > 0) {//从date开始,生成七天日期startDate = DateUtil.parseDate(date);endDate = startDate.offsetNew(DateField.DAY_OF_WEEK, 6);} else {//查询当前日期,生成本周的日期startDate = DateUtil.beginOfWeek(new Date());endDate = DateUtil.endOfWeek(new Date());}HashMap param = new HashMap() {{put("place", form.getName());put("startDate", startDate.toDateStr());put("endDate", endDate.toDateStr());put("mold", form.getMold());put("userId", StpUtil.getLoginIdAsLong());}};ArrayList list = meetingService.searchOfflineMeetingInWeek(param);//生成周日历水平表头的文字标题DateRange range = DateUtil.range(startDate, endDate, DateField.DAY_OF_WEEK);ArrayList days = new ArrayList();range.forEach(one -> {JSONObject json = new JSONObject();json.set("date", one.toString("MM/dd"));json.set("day", one.dayOfWeekEnum().toChinese("周"));days.add(json);});return R.ok().put("list", list).put("days", days);}

第五章:任务审批


最终显示图:

  • 这里需求的实现,一个是对其进行分页,其二就是审批详情以及工作流提供的详情图
  • 审批任务页面是为用户提供审批服务的,所以页面中的各种申请记录对应的都是工作流实例,只需要调用工作流项目就能获取到分页记录了。

  • 时序图
  • 分页需求
    @PostMapping("/searchTaskByPage")@Operation(summary = "查询分页任务列表")@SaCheckPermission(value = {"WORKFLOW:APPROVAL", "FILE:ARCHIVE"}, mode = SaMode.OR)public R searTaskByPage(@Valid @RequestBody SearchTaskByPageForm form){HashMap param = JSONUtil.parse(form).toBean(HashMap.class);int userId = StpUtil.getLoginIdAsInt();param.put("userId", userId);param.put("role", userService.searchUserRoles(userId));List<JSONObject> list = Arrays.asList(new JSONObject().set("createName", "花哥").set("title", "测试线上会议申请").set("type", "会议申请").set("filling", false).set("processId", IdUtil.randomUUID()).set("taskId", IdUtil.randomUUID()).set("createDate", "2022-10-10").set("status", "待审批").set("creatorName","裴承林"));Long totalCount = 1L;Integer pageIndex = 1;Integer pageSize = 1;PageUtils pageUtils = new PageUtils(list, totalCount, pageIndex, pageSize);
//         return R.ok().put("page", pageUtils);
//        HashMap param = JSONUtil.parse(form).toBean(HashMap.class);
//        int userId = StpUtil.getLoginIdAsInt();
//        param.put("userId",userId);
//        param.put("role",userService.searchUserRoles(userId));
//        PageUtils pageUtils = approvalService.searchTaskByPage(param);return R.ok().put("page",pageUtils);
  • service
    @Overridepublic PageUtils searchTaskByPage(HashMap param) {param.put("code",code);param.put("tcode",tcode);String url = workflow+"/workflow/searchTaskByPage";//发送http请求HttpResponse resp  = HttpRequest.post(url).header("Content-Type","application/json").body(JSONUtil.toJsonStr(param)).execute();if(resp.getStatus()==200){JSONObject json = JSONUtil.parseObj(resp.body());//请求体拿到JSONObject page = json.getJSONObject("page");ArrayList list = page.get("list", ArrayList.class);Long totalCount = page.getLong("totalCount");Integer pageIndex = page.getInt("pageIndex");Integer pageSize = page.getInt("pageSize");PageUtils pageUtils = new PageUtils(list,totalCount,pageIndex,pageSize);return pageUtils;}else{log.error(resp.body());throw new EmosException("工作流执行异常");}}

5.1加载对应的BPMN时序图

  • 如果我们在Servcie上发出Http请求那么势必会在Service层提取响应的BPMN图,然后把对象在返回给Web层,如果直接在Web层发出HTTP请求,我们可以不用从工作流返回的图片中进行提取,直接通过IO流拷贝,把工作流响应的图片拷贝到Web方法的响应对象中。
   @GetMapping("/searchApprovalBpmn")@Operation(summary = "获取BPMN图形")@SaCheckPermission(value = {"WORKFLOW:APPROVAL", "FILE:ARCHIVE"}, mode = SaMode.OR)public void searchApprovalBpmn(String instanceId, HttpServletResponse response){if(StrUtil.isBlankIfStr(instanceId)){throw new EmosException("instanceId不能为空");}HashMap param = new HashMap(){{put("code",code);put("tcode",tcode);put("instanceId",instanceId);}};String url = workflow+"/workflow/searchApprovalContent";HttpResponse resp = HttpRequest.post(url).header("Content-Type","application/json").body(JSONUtil.toJsonStr(param)).execute();if(resp.getStatus()==200){try (InputStream in = resp.bodyStream();BufferedInputStream bin = new BufferedInputStream(in);OutputStream out = response.getOutputStream();BufferedOutputStream bout = new BufferedOutputStream(out);) {IoUtil.copy(bin, bout);} catch (Exception e) {log.error("执行异常", e);}}else{log.error("获取工作流BPMN失败");throw new EmosException("获取工作流BPMN失败");}}

TRTC

TRTC的服务,在线视频音频流,TRTC需要先创建对应的用户签名和视频会议roomID


这里主要可以参考腾讯云中的TRTC服务进行配置,我们的userSig肯定不能再前端生成,因此我们需要在后端进行加密后处理

文档:https://cloud.tencent.com/search/TRTC/1_1

入会签到功能的实现

    @PostMapping("/updateMeetingPresent")@Operation(summary = "用户签到功能")public R updateMeetingPresent(@Valid @RequestBody UpdateMeetingPresentForm form){int meetingId = form.getMeetingId();int userId = StpUtil.getLoginIdAsInt();HashMap param = new HashMap(){{put("meetingId",meetingId);put("userId",userId);}};boolean bool = meetingService.searchCanCheckinMeeting(param);if(bool){int rows = meetingService.updateMeetingPresent(param);return R.ok().put("rows",rows);}return R.ok().put("rows",0);}
  • 用户签到的xml
    <select id="searchCanCheckinMeeting" resultType="java.lang.Long" parameterType="HashMap">SELECT COUNT (*)FROM tb_meetingwhere id = #{meetingId}AND status in (3,4)AND NOW() BETWEEN DATE_SUB(CONCAT(date,"",`start`),INTERVAL 15 MINUTE)AND DATE_ADD(CONCAT(date,"",`end`),INTERVAL 15 MINUTE)AND JSON_CONTAINS(IFNULL(members,CAST(#{userId} AS CHAR))AND NOT JSON_CONTAINS(IFNULL(present,JSON_ARRAY()),CAST(#{userId} AS CHAR))</select>
    <update id="updateMeetingPresent" parameterType="HashMap">update tb_meetingSET present = JSON_ARRAY_APPEND(IFNULL(present,JSON_ARRAY()),'$',#{userId})where id = #{meetingId}AND NOW() >= DATE_SUB(CONCAT(date,"",`start`),INTERVAL 15 MINUTE)AND NOW() &lt;DATE_ADD(CONCAT(date,"",end),INTERVAL 15 MINUTE)AND JSON_CONTAINS(IFNULL(members,JSON_ARRAY())CAST(#{userId} AS CHAR))AND NOT JSON_CONTAINS(IFNULL(present,JSON_ARRAY()),CAST(#{userId} AS CHAR))</update>
  • 麦克风的音量的实现

慕课网EMOS在线办公系统源码笔记1-6章相关推荐

  1. Java计算机毕业设计四六级在线考试系统源码+系统+数据库+lw文档

    Java计算机毕业设计四六级在线考试系统源码+系统+数据库+lw文档 Java计算机毕业设计四六级在线考试系统源码+系统+数据库+lw文档 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 ...

  2. 计算机毕业设计Java医保局综合办公系统(源码+系统+mysql数据库+lw文档)

    计算机毕业设计Java医保局综合办公系统(源码+系统+mysql数据库+lw文档) 计算机毕业设计Java医保局综合办公系统(源码+系统+mysql数据库+lw文档) 本源码技术栈: 项目架构:B/S ...

  3. java计算机毕业设计星光在线光影系统源码+mysql数据库+lw文档+系统+调试部署

    java计算机毕业设计星光在线光影系统源码+mysql数据库+lw文档+系统+调试部署 java计算机毕业设计星光在线光影系统源码+mysql数据库+lw文档+系统+调试部署 本源码技术栈: 项目架构 ...

  4. 计算机毕业设计Java自动化办公系统(源码+系统+mysql数据库+lw文档)

    计算机毕业设计Java自动化办公系统(源码+系统+mysql数据库+lw文档) 计算机毕业设计Java自动化办公系统(源码+系统+mysql数据库+lw文档) 本源码技术栈: 项目架构:B/S架构 开 ...

  5. java计算机毕业设计高铁在线购票系统源码+mysql数据库+系统+lw文档+部署

    java计算机毕业设计高铁在线购票系统源码+mysql数据库+系统+lw文档+部署 java计算机毕业设计高铁在线购票系统源码+mysql数据库+系统+lw文档+部署 本源码技术栈: 项目架构:B/S ...

  6. 基于Java毕业设计在线阅读系统源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计在线阅读系统源码+系统+mysql+lw文档+部署软件 基于Java毕业设计在线阅读系统源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B/S架构 开发语言: ...

  7. java计算机毕业设计简易在线教学系统源码+数据库+系统+lw文档+mybatis+运行部署

    java计算机毕业设计简易在线教学系统源码+数据库+系统+lw文档+mybatis+运行部署 java计算机毕业设计简易在线教学系统源码+数据库+系统+lw文档+mybatis+运行部署 本源码技术栈 ...

  8. 基于Java毕业设计在线考试系统源码+系统+mysql+lw文档+部署软件

    基于Java毕业设计在线考试系统源码+系统+mysql+lw文档+部署软件 基于Java毕业设计在线考试系统源码+系统+mysql+lw文档+部署软件 本源码技术栈: 项目架构:B/S架构 开发语言: ...

  9. 计算机毕业设计Javahtml5在线医疗系统(源码+系统+mysql数据库+lw文档)

    计算机毕业设计Javahtml5在线医疗系统(源码+系统+mysql数据库+lw文档) 计算机毕业设计Javahtml5在线医疗系统(源码+系统+mysql数据库+lw文档) 本源码技术栈: 项目架构 ...

  10. java计算机毕业设计分时共享办公系统源码+mysql数据库+系统+lw文档+部署

    java计算机毕业设计分时共享办公系统源码+mysql数据库+系统+lw文档+部署 java计算机毕业设计分时共享办公系统源码+mysql数据库+系统+lw文档+部署 本源码技术栈: 项目架构:B/S ...

最新文章

  1. Linux性能分析命令工具汇总
  2. Twitter 广告平台实时计费系统的架构增强之道
  3. ios 权限提示语_撩妹神技?iOS 微信偷偷上线“拍一拍”
  4. Git_git的诞生
  5. python wxpython_python GUI wxPython
  6. Mysql 休眠连接过多,有可能导致“Too many connections”的错误
  7. multism中ui和uo应该怎么表示_Excel中VBA程序基本语法之强大的数组,了解数组的功能...
  8. Visual Studio Code 调试 React Native (RN) 步骤 Mac版
  9. MongoDB安装、配置与示例
  10. java案例代码17--正则表达式小案例
  11. 电脑文件同步备份软件哪个好用?
  12. littleVGL总结
  13. 指数型组织:未来组织发展的必然趋势
  14. 太极计算机logo,太极图:最古最美最富有涵义的标志
  15. 2022年AI领域有哪些重要突破?(附报告全文)
  16. Sci-Hub十周年迎来解封!科研er的福音!附可用网址!
  17. 汽车众筹系统:加速建设汽车金融新生态!
  18. JRebel 热部署
  19. opencv人脸识别之让电脑分清吴彦祖和彭于晏 (LBPH)
  20. 实时更新:TIOBE世界编程语言排行榜(2021年11月份)

热门文章

  1. WPF界面设计辅助工具--Blend
  2. 计算二阶矩阵特征值的技巧
  3. python----------基于TCP的交互式游戏设计
  4. 电脑网线,电脑网线主要分类
  5. 直播系统源码开发 JavaCV实现本机摄像头画面远程直播
  6. TabHost详细解析
  7. ad采样的符号位_AD采样后数据如何在FPGA中转化为有符号数
  8. SSM框架原理,作用及使用方法,详细解释
  9. HTML计算机代码元素
  10. 精细化管理设备资产,降低企业运营成本