当前位置: 首页 > news >正文

Spring Boot集成Activity7实现简单的审批流

由于客户对于系统里的一些新增数据,例如照片墙、照片等,想实现上级逐级审批通过才可见的效果,于是引入了Acitivity7工作流技术来实现,本文是对实现过程的介绍讲解,由于我是中途交接前同事的这块需求,所以具体实现方式和代码编写我暂时先按前同事的思路简单介绍,不代表我个人看法。

参考文章:

springboot+Activiti7整合实践 (一)_vue2集成activit7-CSDN博客

org.springframework.security.core.userdetails.UsernameNotFoundException,三步解决Activiti7和Security冲突问题_cause: org.springframework.security.core.userdetai-CSDN博客

Activiti7笔记(二)Activiti7一共涉及到25张表,哪些操作会涉及哪些表,每张表的作用是什么_activiti7数据表详细解读-CSDN博客

Activiti7笔记(一)Activiti7是什么,入门流程操作的代码实现-腾讯云开发者社区-腾讯云 (tencent.com)

文章目录

  • 一、引入依赖
  • 二、修改配置文件
  • 三、解决Activity7和Security框架冲突
  • 四、启动项目,生成activity的数据库表
    • 审批流过程
  • 五、画流程图
  • 六、部署流程
  • 七、发起流程
    • 1.示例
    • 2.相关代码
  • 八、审批过程
    • 1.审核通过
      • 1.示例
      • 2.相关代码
    • 2.审批不通过
      • 1.相关代码
  • 九、业务表相关字段
    • 1.任务表
    • 2.业务数据表
  • 十、业务表数据权限变化

一、引入依赖

        <!--activity7工作流--><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>${activity.starter.version}</version><exclusions><exclusion><!-- 项目引入了mybatis-plus,则需要排除 --><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.activiti.dependencies</groupId><artifactId>activiti-dependencies</artifactId><version>${activity.starter.version}</version><type>pom</type></dependency><!-- 生成流程图 --><dependency><groupId>org.activiti</groupId><artifactId>activiti-image-generator</artifactId><version>${activity.starter.version}</version></dependency>

二、修改配置文件

  spring:#activity工作流配置activiti:# 自动部署验证设置:true-开启(默认)、false-关闭check-process-definitions: false# 保存历史数据history-level: full# 检测历史表是否存在db-history-used: true# 关闭自动部署deployment-mode: never-fail# 对数据库中所有表进行更新操作,如果表不存在,则自动创建# create_drop:启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)# drop-create:启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)database-schema-update: true# 解决频繁查询SQL问题async-executor-activate: false

db-history-userdhistory-level,建议按图中配置,方便查询工作流历史记录

三、解决Activity7和Security框架冲突

场景:由于Activity升级到7版本后,引入了Security来用于权限校验,但是本项目自身已经引入了Security框架,于是发生了冲突

【如果项目用的不是security框架,例如用的shiro,需要在启动项排除,参考:springboot+Activiti7整合实践 (一)_vue2集成activit7-CSDN博客】

  • 网上有很多的相关报错和解决办法,【参考:org.springframework.security.core.userdetails.UsernameNotFoundException,三步解决Activiti7和Security冲突问题_cause: org.springframework.security.core.userdetai-CSDN博客】

  • 但本项目主要报错的地方是 根据userid获取用户任务列表这个逻辑,使用activity7原生api方法会报错;

  • 解决办法(前同事处理的):不去排除冲突,直接另外新写一个方法去查activity7的表,实现上述的逻辑

本项目
在这里插入图片描述
在这里插入图片描述

网上其他做法(用的activity原生的api):

从Activiti工作流中检索所有用户任务_在activiti 7中获取流程实例的所有任务_如何在Activiti工作流中的单独实例中强制顺序执行任务 - 腾讯云开发者社区 - 腾讯云 (tencent.com)
在这里插入图片描述

四、启动项目,生成activity的数据库表

引入依赖、配置好后,第一次启动会在数据库生成相关的表
在这里插入图片描述

具体介绍:Activiti7笔记(二)Activiti7一共涉及到25张表,哪些操作会涉及哪些表,每张表的作用是什么_activiti7数据表详细解读-CSDN博客

本项目主要用到的表:

  • 流程部署表 :[ACT_RE_PROCDEF]

    每对一个流程图部署后,会记录在该表里(部署过程下面会讲到)

    在这里插入图片描述

  • 历史流程实例表:[ACT_HI_PROCINST]

    流程一次从头到尾执行,对应一个流程实例,流程结束时会保留下来

  • 运行时流程执行对象表 :[ACT_RU_EXECUTION]

    流程实例与执行对象的关系:‌一个流程实例在执行过程中,‌如果流程包含分支或聚合,‌那么执行对象的数量可以多个【至少有2条数据,其中第1条是对应历史流程实例表】。‌这是因为流程实例在运行过程中可能会产生多个并行的执行路径,‌每个路径上的任务或活动都可以视为一个执行对象。‌例如,‌在一个具有多个分支的审批流程中,‌不同的审批人可能会同时处理不同的分支任务,‌这些分支任务就代表了多个执行对象

    Activiti工作流学习(二)流程实例、执行对象、任务 - 百度文库 (baidu.com)

    这个表里面主要记录的是当前已经执行到哪个节点了,把对应的节点对象记录到这个里面

    流程结束后,这张表对应的数据会清除

    在这里插入图片描述

在这里插入图片描述

  • 运行时节点人员数据信息表 :[ACT_RU_IDENTITYLINK]

    运行时用户关系信息,存储任务节点与参与者的相关信息
    也就是只要当前任务有参与者,就会将参与者的信息保存到这个表,多个人参与,保存多个信息

    在这里插入图片描述

  • **运行时任务节点表:[ACT_RU_TASK] **

    一个流程有多个节点,到某一个节点的时候,都会更新这个表,将当前节点的数据更新到这个表

    在这里插入图片描述

审批流过程

  1. 部署流程,到ACT_RE_PROCDEF表看有没有新增一条对应的数据
  2. 启动流程实例,执行流程对象(可能多个),到ACT_HI_PROCINSTACT_RU_EXECUTION表看有没有新增对应数据
  3. 完成整个流程,过程主要看ACT_RU_TASKACT_RU_IDENTITYLINK表的更新情况

五、画流程图

  • IDEA安装插件:

    在这里插入图片描述

  • 在resource下创建一个目录process存放流程图文件

    在这里插入图片描述

  • 右键,新建流程图文件

    在这里插入图片描述

  • 具体画图过程省略,这里介绍画完之后的流程图的重点信息

    2个审批节点的流程图为例(增加、减少节点都需要另外画图部署)

    1. 定义流程的id和名字(后续代码可以取到)

      在这里插入图片描述

    2. 开始节点

      在这里插入图片描述

    3. 审批节点

      第1个审批节点:

      在这里插入图片描述

      第2个审批节点,同上

      在这里插入图片描述

    4. 结束节点

      在这里插入图片描述

    5. 二级节点之后的网关

      这里二级审批完之后,会出现2种不同的可能走向(审批通过,继续到一级审批节点;审批不通过,直接结束),所以需要加上一个网关

      【由于时间较赶等原因,本项目不做回退的实现,所以审核不通过都是直接走向结束节点(一级审批节点通过或不通过都是走向结束节点)】

      在这里插入图片描述

    6. 节点之间或节点与网关之间的连接线

      在这里插入图片描述

      在这里插入图片描述

      在这里插入图片描述

      重点注意:网关后的线

      定义一个条件变量,当SecondJudge=true时(代码赋值),即审核通过,会走向 一级审核 节点,否则走向结束节点,如下图所示

      【当二级组织发起审核,但是选择最终审批组织也是二级的时候,审核通过会赋值SecondJudge=false,直接走结束节点】

      在这里插入图片描述

      在这里插入图片描述

六、部署流程

本项目采用的是通过接口,手动部署的方式【每新建或修改流程图都要调用一次接口来部署(后续打算优化成自动部署或者定时任务调用接口部署)】

    @ApiOperation("手动部署照片审核")@GetMapping("/photo/process/{typeLevel}")public String auditingPhotoProcessByTypeLevel(@PathVariable("typeLevel") Integer typeLevel, @RequestParam("force") Boolean force) {return auditingPhotoService.auditingPhotoProcessByTypeLevel(typeLevel,force);}
    /*** 手动部署* @param typeLevel 组织等级* @param force 是否强制部署(当修改了流程图的时候需要传true)* @return 部署结果*/public String auditingPhotoProcessByTypeLevel(Integer typeLevel, Boolean force);
    @Overridepublic String auditingPhotoProcessByTypeLevel(Integer typeLevel, Boolean force) {switch (typeLevel) {case 6:auditingPhotoSixService.process(force);break;case 5:auditingPhotoFiveService.process(force);break;case 4:auditingPhotoFourService.process(force);break;case 3:auditingPhotoThreeService.process(force);break;case 2:auditingPhotoTwoService.process(force);break;case 1:auditingPhotoOneService.process(force);break;default:throw new ServicesException(ResultStatus.ARGS_VALID_ERROR);}return typeLevel + "级照片流程部署成功";}
    /*** 部署流程* @param force* @return*/public void process(Boolean force);
    public void process(Boolean force) {//判断流程是否已经部署,当force=0时,if (force == null || force) {//部署流程DeploymentBuilder builder = repositoryService.createDeployment();builder.addClasspathResource(AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML).disableSchemaValidation();String id = builder.deploy().getId();repositoryService.setDeploymentKey(id, AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID);} else {//检测流程是否已经部署过List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery().processDefinitionKey(AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID).list();if (!definitions.isEmpty()) {// 已经部署过流程定义throw new ServicesException(ResultStatus.AUDITING_PROCESS_DUPLICATE);} else {//部署流程DeploymentBuilder builder = repositoryService.createDeployment();builder.addClasspathResource(AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML).disableSchemaValidation();String id = builder.deploy().getId();repositoryService.setDeploymentKey(id, AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_SIX_ID);}}}

这里AuditingConstant.BpmnXmlPhotoPathConstant.AUDITING_PHOTO_SIX_XML是流程图的路径,如下图所示

在这里插入图片描述

部署好后,查看数据表 ACT_RE_PROCDEF 即可判断是否成功部署

在这里插入图片描述

七、发起流程

前提: 业务表(需要审批的)数据创建 ——> 点击提交审核

如下图的照片墙审核,创建一条新的数据,此时一开始没审核,状态是“草稿”,点击发布后,才会触发审批流程

在这里插入图片描述

1.示例

二级组织用户 新增一个照片墙,点击发布(选择审核等级为 上级(一级)组织,即需要经过两个审批节点才截止

【只要是二级发起的,都走的是2个审批节点的流程图,只不过根据结束节点会有不同的走向逻辑,如果这里选择本级(二级)结束,则不论二级通过不通过,都会直接走向结束节点】

在这里插入图片描述

点击发布后,会启动一个流程实例,并执行流程对象,见表ACT_HI_PROCINSTACT_RU_EXECUTION

ACT_HI_PROCINST(历史流程实例表)

在这里插入图片描述

ACT_RU_EXECUTION(运行时流程执行对象表):

在这里插入图片描述

如下图,当二级用户点击发步后,会发起审核,先走到 “二级审核2” 节点,对应上图的第1条数据

在这里插入图片描述

2.相关代码

    @ApiOperation("发起照片审核")@ApiImplicitParams({@ApiImplicitParam(type = "query", name = "taskName", value = "任务名称", required = true),@ApiImplicitParam(type = "query", name = "priority", value = "优先级", required = true),@ApiImplicitParam(type = "query", name = "desc", value = "描述", required = true),@ApiImplicitParam(type = "query", name = "photoWallId", value = "照片墙id", required = true),@ApiImplicitParam(type = "query", name = "auditingOrganizeId", value = "审核的组织id", required = true)})@ApiResponses({@ApiResponse(response = ResponseResult.class, message = "1", code = 200),@ApiResponse(response = ServiceException.class, message = "系统错误,请稍等!", code = 4000)})@PostMapping("/startPhotoAuditProcess")public ResponseResult<String> photoStartAuditing(@RequestBody Map<String, Object> map) {return ResponseResult.success(auditingPhotoService.photoStartAuditing(map));}
    /*** 启动流程* @param map* @return success*/public String photoStartAuditing(Map<String,Object> map);

下面代码解释:根据当前用户的组织级别,走不同的实现方法【代码比较臃肿,后续再优化,后面的代码同理】

    @Override@Transactional(rollbackFor = Exception.class)public String photoStartAuditing(Map<String, Object> map) {//根据照片墙id查找照片墙,给照片墙设置审核的组织IDPhotoWall photoWall = photoWallService.selectPhotoWallDetail(map.get("photoWallId") + "");photoWall.setOrganizeAuditingId(Long.parseLong(map.get("auditingOrganizeId") + ""));photoWall.setOpinion("");OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();photoWallService.updatePhotoWallForAuditing(photoWall);//当前组织的级别Integer typeLevel = userOrganize.getTypeLevel();Integer typeLevelSix = 6;Integer typeLevelFive = 5;Integer typeLevelFour = 4;Integer typeLevelThree = 3;Integer typeLevelTwo = 2;Integer typeLevelOne = 1;if (typeLevel.equals(typeLevelSix)) {auditingPhotoSixService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelFive)) {auditingPhotoFiveService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelFour)) {auditingPhotoFourService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelThree)) {auditingPhotoThreeService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelTwo)) {auditingPhotoTwoService.startAuditProcess(map);} else if (typeLevel.equals(typeLevelOne)) {auditingPhotoOneService.startAuditProcess(map);}return "success";}
/*** 启动流程* @param map*/
public void startAuditProcess(Map<String,Object> map);
public void startAuditProcess(Map<String, Object> map) {String id = map.get("photoWallId") + "";//修改照片墙的状态PhotoWall photoWall = photoWallService.selectPhotoWallDetail(id);photoWall.setAuditState(1);photoWall.setOrganizeAuditingId(Long.parseLong(map.get("auditingOrganizeId") + ""));Boolean code = photoWallService.updatePhotoWallForAuditing(photoWall);if (code) {// 获取当前登录用户的组织信息OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();//当前组织的级别Integer typeLevel = userOrganize.getTypeLevel();//用来封装存储每一级组织的审核人,key是integer,表示不同级的组织,value是个list集合,每一级组织的审核人HashMap<String, List<SysUser>> auditors = new HashMap<>(10);//找出每一级审批流人员activitiService.getAuditors(userOrganize,typeLevel,auditors);// 设置流程变量Map<String, Object> variables = new HashMap<>(10);//获得每级审核的审核人List<String> firstAuditor = getAuditorsId(auditors.get("1"));List<String> secondAuditor = getAuditorsId(auditors.get("2"));//审核人variables.put("firstAuditor", firstAuditor);variables.put("secondAuditor", secondAuditor);String formKey = AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_TWO_ID + ":";String bussinessKey = formKey + id;variables.put("bussinessKey", bussinessKey);// 启动流程实例runtimeService.startProcessInstanceByKey(AuditingConstant.BpmnIdConstant.AUDITING_PHOTO_TWO_ID, bussinessKey, variables);//给接下来的每一个审核人都创建一个,在刚发起的时候,是同一级的审核人List<SysUser> auditorsTypeLevel = auditors.get(typeLevel + "");//获得和登录用户同一级的审核人的idList<String> auditorsTypeLevelIds = getAuditorsId(auditorsTypeLevel);for (int i = 0; i < auditorsTypeLevelIds.size(); i++) {SysTask sysTask = new SysTask();sysTask.setTaskName((String) map.get("taskName"));sysTask.setPriority(Integer.valueOf(map.get("priority") + ""));sysTask.setDescribe((String) map.get("desc"));sysTask.setPhotoWallId((String) map.get("photoWallId"));sysTask.setCreatorName(SecurityUtils.getLoginUser().getUsername());sysTask.setCreatorId(SecurityUtils.getLoginUser().getUserId());//审核人的名字  这里要怎么设置啊,审核人的名字和id,用的是候选用户啊sysTask.setAuditingPeople(auditorsTypeLevel.get(i).getUserName());//审核人的idsysTask.setAuditingPeopleid(Long.parseLong(auditorsTypeLevelIds.get(i)));sysTask.setState(1);sysTask.setAuditingType(2);//任务表添加iSysTaskService.addTask(sysTask);//审核人的消息Message message = new Message();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");String date = sdf.format(new Date());message.setContent("你在" + date + "有一个照片墙的审核");message.setCreateTime(new Date());message.setState(1);message.setUserId(auditorsTypeLevelIds.get(i));message.setType(1);messageService.addMessage(message);}//登录用户的消息Message message = new Message();SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");String date = sdf.format(new Date());message.setContent("你在" + date + "发起了一个照片墙审核");message.setCreateTime(new Date());message.setState(1);message.setUserId(SecurityUtils.getLoginUser().getUserId() + "");message.setType(2);messageService.addMessage(message);}
}

上方代码重点

  • 设置变量,启动流程实例

    这里把每一级审核人提前都设置好(后续不变)【这里根据业务需求应该也可以设计成后续动态添加】

    业务id bussinessKey 规则在此设定,调用最后一行的activity api时候需要传递,同时将其设置到流程变量 variables 中,后续方便读取

    在这里插入图片描述

  • 业务部分

    在这里插入图片描述

    在这里插入图片描述

    1. 修改 照片墙业务表 流程相关字段
    2. 新增 审核任务业务表
    3. 发送消息

八、审批过程

现在流程走到第一个审批节点,即 二级审核2

在这里插入图片描述

此时,运行时节点人员数据信息表 ACT_RU_IDENTITYLINK,存入二级审核2 节点的 是审核人角色 的 用户,尾号20用户和尾号52用户都是该节点的审批人【该项目里,只要其中一个审批通过,就算通过,反之,一个拒绝就算拒绝】,

且被分配到了任务(同个任务),见表ACT_RU_TASK,如下图

在这里插入图片描述

在这里插入图片描述

对应业务表 任务表里,也加了2条数据

在这里插入图片描述

他们之间通过 BUSINESS_KEY_ 来关联

1.审核通过

审批通过,在activity7框架中,只需要设置SecondJudge变量,然后将task任务完成即可

在这里插入图片描述

1.示例

二级组织人员 尾号52用户 创建了一个照片墙,点击发布后,生成本级人员(尾号52和尾号20)的任务,此时52用户进入“我的待办”里(业务表 任务表),找到对应任务,点击审批通过,如下图

在这里插入图片描述

2.相关代码

@ApiOperation("照片墙审批通过")
@ApiImplicitParams({@ApiImplicitParam(type = "query", name = "id", value = "任务id", required = true),@ApiImplicitParam(type = "query", name = "opinion", value = "建议", required = false)
})
@PostMapping("/auditing/photo/approve/{id}")
public ResponseResult<String> auditingPhotoApprove(@PathVariable Integer id, @RequestParam(required = false) String opinion) {return ResponseResult.success(auditingPhotoService.auditingPhotoApprove(id, opinion));
}
/*** 审核通过* @param id* @param opinion* @return success*/
public String auditingPhotoApprove(Integer id,String opinion);

下面代码解释:

通过照片墙的创建者的组织等级,走不同的审核通过实现类

@Override
@Transactional(rollbackFor = Exception.class)
public String auditingPhotoApprove(Integer id, String opinion) {//id是任务的id,可以获得照片墙的id//再去新建表中查询这个照片墙是哪级创建的,//这个级数直接判断吗,然后去调用哪个类的approve方法?SysTask sysTask = iSysTaskService.selectTaskById(id);//通过任务的informatinid来获得照片墙idString photoId = sysTask.getPhotoWallId();//根据照片墙的id去查询审核创建者的级别LambdaQueryWrapper<PhotoWall> lambdaQueryWrapper = new LambdaQueryWrapper<PhotoWall>();lambdaQueryWrapper.eq(PhotoWall::getPhotoWallId, photoId);PhotoWall photoWall = photoWallService.getOne(lambdaQueryWrapper);//获得当前等级Long organizeId = photoWall.getOrganizeId();Integer userOrganizeLevel = iOrganizeService.searchOrganizeById(organizeId).getOrganizeLevel();Integer userOrganizeLevelOne = 1;Integer userOrganizeLevelTwo = 2;Integer userOrganizeLevelThree = 3;Integer userOrganizeLevelFour = 4;Integer userOrganizeLevelFive = 5;Integer userOrganizeLevelSix = 6;//获得审核组织的级别Long auditingOrganizeId = photoWall.getOrganizeAuditingId();Integer auditingOrganizeLevel = iOrganizeService.searchOrganizeById(auditingOrganizeId).getOrganizeLevel();if (userOrganizeLevel.equals(userOrganizeLevelOne)) {auditingPhotoOneService.approve(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelTwo)) {auditingPhotoTwoService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelThree)) {auditingPhotoThreeService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelFour)) {auditingPhotoFourService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelFive)) {auditingPhotoFiveService.approve(id, opinion, auditingOrganizeLevel);} else if (userOrganizeLevel.equals(userOrganizeLevelSix)) {auditingPhotoSixService.approve(id, opinion, auditingOrganizeLevel);}return "success";
}
/*** 审核通过* @param id* @param opinion* @param auditingOrganizeLevel*/
public void approve(Integer id, String opinion,Integer auditingOrganizeLevel);
@Override
public void approve(Integer id, String opinion, Integer auditingOrganizeLevel) {//获取任务的候选人是登录用户的任务的方法List<Task> taskList = activitiService.getTaskIdByCandidate(SecurityUtils.getLoginUser().getUserId().toString());//当前登录用户的组织级别Integer typeLevel = iOrganizeService.getUserOrganize().getTypeLevel();//搜索出工作台选的任务idSysTask sysTask = iSysTaskService.selectTaskById(id);//通过任务的photoWallId来获得照片墙idPhotoWall photoWall = photoWallService.selectPhotoWallDetail(sysTask.getPhotoWallId() + "");String photoWallId = photoWall.getPhotoWallId();auditingInformTwoApproveService(taskList, photoWallId, typeLevel, photoWall, auditingOrganizeLevel, opinion);// 获取当前登录用户的组织信息OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();//用来封装存储每一级组织的审核人,key是integer,表示不同级的组织,value是个list集合,每一级组织的审核人HashMap<String, List<SysUser>> auditors = new HashMap<>(10);//找出每一级审批流人员activitiService.getAuditors(userOrganize,typeLevel,auditors);List<String> auditorsList = new ArrayList<>();if(typeLevel.equals(AuditingConstant.Number.TWO)){auditorsList = getAuditorsId(auditors.get("2"));}if (typeLevel == 1) {auditorsList = getAuditorsId(auditors.get("1"));}sysTask.setState(2);sysTask.setOpinion(opinion);sysTask.setCompletionTime(new Date());iSysTaskService.updateTask(sysTask);Long userId1 = SecurityUtils.getLoginUser().getUserId();String photoWallId1 = sysTask.getPhotoWallId();//删除其他用户中关联这个照片墙id的任务//循环审核人列表for (String s : auditorsList) {//当审核人不等于登录用户时,即其他用户if (!Objects.equals(s, userId1 + "")) {//搜索出他的全部任务List<SysTask> sysTaskList = iSysTaskService.selectTaskByAuditorId(Long.parseLong(s));//循环任务for (SysTask task : sysTaskList) {//找出任务中关联的照片墙id等于当前登录用户审核的任务的关联的照片墙idif (photoWallId1.equals(task.getPhotoWallId())) {//删除iSysTaskService.deleteTask(task.getTaskId().intValue());}}}}if (typeLevel == 1 || typeLevel.equals(auditingOrganizeLevel)) {photoWall.setAuditState(2);photoWallService.updatePhotoWallForAuditing(photoWall);Message message = new Message();Date date = new Date();message.setContent("你的照片墙在" + typeLevel + "级审核通过");message.setCreateTime(date);message.setState(1);String userId = sysTask.getCreatorId() + "";message.setUserId(userId);message.setType(2);message.setTaskId(sysTask.getTaskId().intValue());messageService.addMessage(message);return;}if (typeLevel.equals(AuditingConstant.Number.TWO)) {auditorsList = getAuditorsId(auditors.get("1"));}for (String s : auditorsList) {SysTask sysTask1 = new SysTask();sysTask1.setTaskName(sysTask.getTaskName());sysTask1.setPriority(sysTask.getPriority());sysTask1.setDescribe(sysTask.getDescribe());sysTask1.setPhotoWallId(sysTask.getPhotoWallId());sysTask1.setCreatorName(SecurityUtils.getLoginUser().getUsername());sysTask1.setCreatorId(SecurityUtils.getLoginUser().getUserId());sysTask1.setAuditingPeopleid(Long.parseLong(s));SysUser sysUser = iSysUserService.selectUserById(Long.parseLong(s));sysTask1.setAuditingPeople(sysUser.getUserName());sysTask1.setState(1);sysTask1.setAuditingType(2);iSysTaskService.addTask(sysTask1);sendMessage(s, sysTask);}sendMessage(typeLevel, sysTask);
}
private void auditingInformTwoApproveService(List<Task> taskList, String photoWallId, Integer typeLevel, PhotoWall photoWall, Integer auditingOrganizeLevel, String opinion) {if (!ObjectUtils.isEmpty(taskList)) {for (Task item : taskList) {ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(item.getProcessInstanceId()).singleResult();//根据流程实例得到bussinesskey,格式是formkey:idString a = processInstance.getBusinessKey();if (StringUtils.isNotBlank(a)) {String[] b = a.split(":");String photoId = b[1];//判断bussinesskey,bussinesskey是任务表中的记录的id,在发起流程的时候会插入任务表,任务表的id也会变成流程变量if (photoId.equals(photoWallId)) {if (typeLevel == 2) {if (typeLevel.equals(auditingOrganizeLevel)) {taskService.setVariable(item.getId(), "SecondJudge", false);} else {taskService.setVariable(item.getId(), "SecondJudge", true);}} else if (typeLevel == 1) {//如果当前审核人是一级组织的,那审核就结束了photoWall.setAuditState(2);photoWall.setOpinion(opinion);photoWallService.updatePhotoWallForAuditing(photoWall);}taskService.addComment(item.getId(), item.getProcessInstanceId(), "审核通过");taskService.complete(item.getId());break;}}}}
}

上面代码重点

  • 找到activity的任务表中,当前该用户的所有任务

    在这里插入图片描述

    @Override
    public List<Task> getTaskIdByCandidate(String userId) {List<ActivitiTaskId> activitiTaskId = activitiMapper.getTaskIdByCandidate(userId);List<String> activitiTaskIds = new ArrayList<>();for (com.znak.platform.entity.ActivitiTaskId taskId : activitiTaskId) {activitiTaskIds.add(taskId.getTaskId());}List<Task> taskList = new ArrayList<>();for (String taskId : activitiTaskIds) {Task task = taskService.createTaskQuery().taskId(taskId).singleResult();taskList.add(task);}return taskList;
    }
    
    <select id="getTaskIdByCandidate" resultType="com.znak.platform.entity.ActivitiTaskId">select distinct TASK_ID_ from ACT_RU_IDENTITYLINKwhere USER_ID_ = #{userId}  and TASK_ID_ != ""
    </select>
    
  • 根据bussinesskey找到对应的任务,修改变量SecondJudge,并完成任务

    在这里插入图片描述

    这里注意,如果当前是走到一级审批节点了,则走下面的else if逻辑,直接将业务表修改审核通过(不用设置变量,因为没有网关了),如下图

    在这里插入图片描述

    在这里插入图片描述

  • 业务相关

    • 修改业务表的审批流相关字段,同时发送消息,同上

    • 然后删除同级别组织人员的 业务表 任务表,新建下一级的人员的业务表 任务表,见代码

2.审批不通过

过程和审批通过大致一样

在这里插入图片描述

1.相关代码

@ApiOperation("照片审核不通过")
@ApiResponses({@ApiResponse(response = ResponseResult.class, message = "1", code = 200),@ApiResponse(response = ServiceException.class, message = "系统错误,请稍等!", code = 4000)})
@ApiImplicitParams({//发起资讯申请相关字段@ApiImplicitParam(type = "query", name = "id", value = "任务id", required = true),@ApiImplicitParam(type = "query", name = "opinion", value = "建议", required = false)
})
@PostMapping("/auditing/photo/reject/{id}")
public ResponseResult<String> auditingPhotoReject(@PathVariable Integer id, @RequestParam(required = false) String opinion) {return ResponseResult.success(auditingPhotoService.auditingPhotoReject(id, opinion));
}
/*** 审核不通过* @param id* @param opinion* @return success*/
public String auditingPhotoReject(Integer id,String opinion);
@Override
@Transactional(rollbackFor = Exception.class)
public String auditingPhotoReject(Integer id, String opinion) {//id是任务的id,可以获得资讯的id//再去新建表中查询这个资讯是哪级创建的,//这个级数直接判断吗,然后去调用哪个类的approve方法?SysTask sysTask = iSysTaskService.selectTaskById(id);//通过任务的informatinid来获得资讯idString photoWallId = sysTask.getPhotoWallId();//根据资讯的id去查询审核创建者的级别LambdaQueryWrapper<PhotoWall> lambdaQueryWrapper = new LambdaQueryWrapper<PhotoWall>();lambdaQueryWrapper.eq(PhotoWall::getPhotoWallId, photoWallId);PhotoWall photoWall = photoWallService.getOne(lambdaQueryWrapper);//获得当前等级Long organizeId = photoWall.getOrganizeId();Integer userOrganizeLevel = iOrganizeService.searchOrganizeById(organizeId).getOrganizeLevel();Integer userOrganizeLevelOne = 1;Integer userOrganizeLevelTwo = 2;Integer userOrganizeLevelThree = 3;Integer userOrganizeLevelFour = 4;Integer userOrganizeLevelFive = 5;Integer userOrganizeLevelSix = 6;if (userOrganizeLevel.equals(userOrganizeLevelOne)) {auditingPhotoOneService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelTwo)) {auditingPhotoTwoService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelThree)) {auditingPhotoThreeService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelFour)) {auditingPhotoFourService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelFive)) {auditingPhotoFiveService.rej(id, opinion);} else if (userOrganizeLevel.equals(userOrganizeLevelSix)) {auditingPhotoSixService.rej(id, opinion);}return "success";
}
/*** 审核不通过* @param id* @param opinion*/
public void rej(Integer id, String opinion);
public void rej(Integer id, String opinion) {//获取任务的候选人是登录用户的任务的方法List<Task> taskList = activitiService.getTaskIdByCandidate(SecurityUtils.getLoginUser().getUserId().toString());//当前登录用户的组织级别Integer typeLevel = iOrganizeService.getUserOrganize().getTypeLevel();//搜索出工作台选的任务idSysTask sysTask = iSysTaskService.selectTaskById(id);//通过任务的informatinid来获得资讯idPhotoWall photoWall = photoWallService.selectPhotoWallDetail(sysTask.getPhotoWallId());String photoWallId = photoWall.getPhotoWallId();auditingInformTwoRejService(taskList, photoWallId, typeLevel, photoWall,opinion);// 获取当前登录用户的组织信息OrganizeTreeVO userOrganize = iOrganizeService.getUserOrganize();//用来封装存储每一级组织的审核人,key是integer,表示不同级的组织,value是个list集合,每一级组织的审核人HashMap<String, List<SysUser>> auditors = new HashMap<>(4);//找出每一级审批流人员activitiService.getAuditors(userOrganize,typeLevel,auditors);//防止四级发起的审核,然后任务到达三级的时候调用此方法,会报错4级审核人找不到List<String> auditorsList = new ArrayList<>();Integer typeLevelTwo = 2;if (typeLevel.equals(typeLevelTwo)) {auditorsList = getAuditorsId(auditors.get("2"));}if (typeLevel == 1) {auditorsList = getAuditorsId(auditors.get("1"));}//这一个等级的组织的审核任务处理,处理上一阶段的任务//获得当前级别的审核人sysTask.setState(3);sysTask.setOpinion(opinion);sysTask.setCompletionTime(new Date());iSysTaskService.updateTask(sysTask);Long userId1 = SecurityUtils.getLoginUser().getUserId();String photoWallId1 = photoWall.getPhotoWallId();//删除其他用户中关联这个咨询id的任务//循环审核人列表for (String s : auditorsList) {//当审核人不等于登录用户时,即其他用户if (!Objects.equals(s, userId1 + "")){//搜索出他的全部任务List<SysTask> sysTaskList = iSysTaskService.selectTaskByAuditorId(Long.parseLong(s));//循环任务for(SysTask task : sysTaskList){//找出任务中关联的照片墙id等于当前登录用户审核的任务的关联的照片墙idif(photoWallId1.equals(task.getPhotoWallId())){//删除iSysTaskService.deleteTask(task.getTaskId().intValue());}}}}Message message = new Message();Date date = new Date();message.setContent("你的照片墙在" + typeLevel + "级审核没有通过");message.setCreateTime(date);message.setState(1);//这个审核的是谁的流程String userId = sysTask.getCreatorId() + "";message.setUserId(userId);message.setType(2);message.setTaskId(sysTask.getTaskId().intValue());messageService.addMessage(message);}
private void auditingInformTwoRejService(List<Task> taskList, String photoWallId, Integer typeLevel, PhotoWall photoWall,String opinion) {if (!ObjectUtils.isEmpty(taskList)) {for (Task item : taskList) {ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(item.getProcessInstanceId()).singleResult();//根据流程实例得到bussinesskey,格式是formkey:idString a = processInstance.getBusinessKey();if (StringUtils.isNotBlank(a)) {String[] b = a.split(":");String photoId = b[1];//判断bussinesskey,bussinesskey是任务表中的记录的id,在发起流程的时候会插入任务表,任务表的id也会变成流程变量if (photoId.equals(photoWallId)) {//根据当前登录用户的组织级别判断是第几级判断,设置相关的流程变量if (typeLevel == 2) {photoWall.setAuditState(4);photoWall.setOpinion(opinion);photoWallService.updatePhotoWallForAuditing(photoWall);taskService.setVariable(item.getId(), "SecondJudge", false);} else if (typeLevel == 1) {//如果当前审核人是一级组织的,那审核就结束了photoWall.setAuditState(4);photoWall.setOpinion(opinion);photoWallService.updatePhotoWallForAuditing(photoWall);}taskService.addComment(item.getId(), item.getProcessInstanceId(), "审核不通过");taskService.complete(item.getId());break;}}}}
}

上面代码的重点:

  • 找到activity的任务表中,当前该用户的所有任务

    同上

  • 根据bussinesskey找到对应的任务,修改变量SecondJudge,并完成任务

    同上

  • 业务相关

    • 修改业务表的审批流相关字段,同时发送消息,同上

    • 然后删除同级别组织人员的 业务表 任务表,见代码

九、业务表相关字段

1.任务表

在这里插入图片描述

2.业务数据表

以 照片墙 为例

在这里插入图片描述

十、业务表数据权限变化

  • 审核前,即草稿状态的数据,只能看自己创建的
  • 审核通过,本级 到 最终审核组织 之间的用户都可见

相关文章:

Spring Boot集成Activity7实现简单的审批流

由于客户对于系统里的一些新增数据&#xff0c;例如照片墙、照片等&#xff0c;想实现上级逐级审批通过才可见的效果&#xff0c;于是引入了Acitivity7工作流技术来实现&#xff0c;本文是对实现过程的介绍讲解&#xff0c;由于我是中途交接前同事的这块需求&#xff0c;所以具…...

自动驾驶,革了谁的命

概述 从AI 的出现开始&#xff0c;到现在已经慢慢地开始改变着周遭的世界。 从对话聊天&#xff0c;到当前的看图识文&#xff0c;图片转动效等等&#xff0c;慢慢地在与实体结合后&#xff0c;其发挥的威力是巨大的。 科技将会是改变世界的核心驱动力之一&#xff0c;已经深…...

在线实习项目|泰迪智能科技企业级项目学习,暑期大数据人工智能学习

在线实习介绍 实习时间&#xff1a;每个项目周期七周左右 面向对象&#xff1a;大数据、计算机相关专业学生&#xff1b;大三、大四毕业年度学生 在线实习收获 1、获得项目实战技能&#xff0c;积累项目经验 2、获得在线实习证明 项目特点…...

【BUG】已解决:To update, run: python.exe -m pip install --upgrade pip

To update, run: python.exe -m pip install --upgrade pip 目录 To update, run: python.exe -m pip install --upgrade pip 【常见模块错误】 解决办法&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&…...

Lua 运算符

Lua 运算符 Lua 是一种轻量级的编程语言&#xff0c;广泛用于游戏开发、脚本编写和其他应用程序。它具有一套丰富的运算符&#xff0c;用于执行各种数学和逻辑操作。本文将详细介绍 Lua 中的运算符&#xff0c;包括算术运算符、关系运算符、逻辑运算符和其他特殊运算符。 算术…...

园区道路车辆智能管控视频解决方案,打造安全畅通的园区交通环境

一、背景需求分析 随着企业园区的快速发展和扩张&#xff0c;道路车辆管理成为了保障园区秩序、提升运营效率及确保员工安全的重要任务。针对这一需求&#xff0c;旭帆科技TSINGSEE青犀提出了一种企业园区道路车辆管控的解决方案&#xff0c;通过整合视频监控、智能识别等技术…...

MATLAB R2023b下载安装教程汉化中文版设置

MATLAB R2023b下载安装教程汉化中文版设置 Matlab 是一款功能强大的商业数学软件 Matlab&#xff08;Matrix Labortory&#xff09;即矩阵实验室&#xff0c;它在数值计算、数据分析、算法开发、建模与仿真等众多领域都发挥着重要作用。 Matlab 具有以下显著特点和优势&…...

Java二十三种设计模式-工厂方法模式(2/23)

工厂方法模式&#xff1a;设计模式中的瑞士军刀 引言 在软件开发中&#xff0c;工厂方法模式是一种常用的创建型设计模式&#xff0c;它用于处理对象的创建&#xff0c;将对象的实例化推迟到子类中进行。这种模式不仅简化了对象的创建过程&#xff0c;还提高了代码的可维护性…...

【iOS】OC类与对象的本质分析

目录 前言clang常用命令对象本质探索属性的本质对象的内存大小isa 指针探究 前言 OC 代码的底层实现都是 C/C代码&#xff0c;OC 的对象都是基于 C/C 的数据结构实现的&#xff0c;实际 OC 对象的本质就是结构体&#xff0c;那到底是一个怎样的结构体呢&#xff1f; clang常用…...

【机器学习】使用Python的dlib库实现人脸识别技术

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、传统人脸识别技术1. 基于几何特征的方法2. 基于模板匹配的方法3. 基于统计学习的方法 三、深度学习在脸识别中的应用1. 卷积神经网络&#xff08;CNN&#xff09;2. FaceNet和ArcFace 四、使用Python和dlib库实…...

GitHub 令牌泄漏, Python 核心资源库面临潜在攻击

TheHackerNews网站消息&#xff0c;软件供应链安全公司 JFrog 的网络安全研究人员称&#xff0c;他们发现了一个意外泄露的 GitHub 令牌&#xff0c;可授予 Python 语言 GitHub 存储库、Python 软件包索引&#xff08;PyPI&#xff09;和 Python 软件基金会&#xff08;PSF&…...

【面试题】Golang 锁的相关问题(第七篇)

目录 1.Mutex 几种状态 1. 锁定状态&#xff08;Locked&#xff09; 2. 未锁定状态&#xff08;Unlocked&#xff09; 3. 唤醒状态&#xff08;Woken&#xff09; 4. 饥饿状态&#xff08;Starving&#xff09; 5. 等待者计数&#xff08;Waiters Count&#xff09; 总结…...

深入剖析CommonJS modules和ECMAScript modules

目录 前言CommonJS&#xff1a;服务器端模块化的先驱背景与起源语法与机制 ECMAScript Modules&#xff1a;现代前端的基石背景与起源语法与机制 比较与权衡语法差异加载机制编译时与运行时运行时行为构建第三方库现代开发环境 结论 前言 在 JavaScript 生态系统中&#xff0c…...

角点检测及MATLAB实现

一、角点简介 角点通常指的是两条直线构成角时的交点。‌在更广泛的应用中&#xff0c;‌角点这一概念也被扩展到数字图像处理领域&#xff0c;‌其中角点被定义为图像中物体轮廓线的连接点&#xff0c;‌这些点在某方面属性特别突出&#xff0c;‌即在某些属性上强度最大或者最…...

TypeScript导学:从零开始

引言 TypeScript的背景 TypeScript是一种由微软开发的开源编程语言&#xff0c;它是JavaScript的一个超集&#xff0c;添加了可选的静态类型和基于类的面向对象编程。自2012年首次发布以来&#xff0c;TypeScript因其能够提高代码的可读性、可维护性和可扩展性而迅速获得了广…...

【BUG】已解决:IndexError: list index out of range

已解决&#xff1a;IndexError: list index out of range 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社区主…...

AWS-S3实现Minio分片上传、断点续传、秒传、分片下载、暂停下载

文章目录 前言一、功能展示上传功能点下载功能点效果展示 二、思路流程上传流程下载流程 三、代码示例四、疑问 前言 Amazon Simple Storage Service&#xff08;S3&#xff09;&#xff0c;简单存储服务&#xff0c;是一个公开的云存储服务。Web应用程序开发人员可以使用它存…...

Selenium - 设置元素等待及加载策略

7月18日资源分享&#xff1a; 耿直哥三部曲全——机器学习&#xff0c;强化学习&#xff0c;深度学习 链接: https://pan.baidu.com/s/1c_eVVeqCZmB6zszHt6ZXiw?pwdtf2a 在使用Selenium进行网页自动化测试时&#xff0c;一个常见的问题是页面加载速度和元素的可见性问题。…...

【数据结构】线性结构——数组、链表、栈和队列

目录 前言 一、数组&#xff08;Array&#xff09; 1.1优点 1.2缺点 1.3适用场景 二、链表&#xff08;Linked List&#xff09; 2.1优点 2.2缺点 2.3适用场景 三、栈&#xff08;Stack&#xff09; 3.1优点 3.2缺点 3.3适用场景 四、队列&#xff08;Queue&#xff09; 4.1优点…...

json将列表字典等转字符串,然后解析又转回来

在 Python 中使用 json 模块来方便地在数据和 JSON 格式字符串之间进行转换&#xff0c;以便进行数据的存储、传输或与其他支持 JSON 格式的系统进行交互。 JSON 字符串通过 json.loads() 函数转换为 Python 对象。 pthon对象通过json.dumps()转为字符串 import jsonstr_list…...

记录|.NET上位机开发和PLC通信的实现

本文记录源自&#xff1a;B站视频 实验结果&#xff1a;跟视频做下来是没有问题的。能运行。 自己补充做了视频中未实现的读取和写入数据部分【欢迎小伙伴指正不对的地方】 目录 前言一、项目Step1. 创建项目Step2. 创建动态图片展示Step3. 创建图片型按钮Step4. 创建下拉框Ste…...

微服务实战系列之玩转Docker(二)

前言 上一篇&#xff0c;博主对Docker的背景、理念和实现路径进行了简单的阐述。作为云原生技术的核心之一&#xff0c;轻量级的容器Docker&#xff0c;受到业界追捧。因为它抛弃了笨重的OS&#xff0c;也不带Data&#xff0c;可以说&#xff0c;能够留下来的都是打仗的“精锐…...

Linux:信号的概念与产生

信号概念 信号是进程之间事件异步通知的一种方式 在Linux命令行中&#xff0c;我们可以通过ctrl c来终止一个前台运行的进程&#xff0c;其实这就是一个发送信号的行为。我们按下ctrl c是在shell进程中&#xff0c;而被终止的进程&#xff0c;是在前台运行的另外一个进程。因…...

云监控(华为) | 实训学习day2(10)

spring boot基于框架的实现 简单应用 - 用户数据显示 开发步骤 第一步&#xff1a;文件-----》新建---项目 第二步:弹出的对话框中,左侧选择maven,右侧不选任何内容. 第三步&#xff0c;选择maven后&#xff0c;下一步 第4步 &#xff1a;出现对话框中填写项目名称 第5步&…...

数据结构第35节 性能优化 算法的选择

算法的选择对于优化程序性能至关重要。不同的算法在时间复杂度、空间复杂度以及适用场景上有着明显的差异。下面我将结合具体的代码示例&#xff0c;来讲解几种常见的算法选择及其优化方法。 示例 1: 排序算法 场景描述: 假设我们需要对一个整数数组进行排序。 算法选择: …...

每天一个数据分析题(四百三十六)- 正态分布

X为服从正态分布的随机变量N(2, 9), 如果P(X>c)P(X<c), 则c的值为&#xff08;&#xff09; A. 3 B. 2 C. 9 D. 2/3 数据分析认证考试介绍&#xff1a;点击进入 题目来源于CDA模拟题库 点击此处获取答案 数据分析专项练习题库 内容涵盖Python&#xff0c;SQL&…...

跟我学C++中级篇——虚函数的性能

一、虚函数性能 一般来说&#xff0c;面向对象的设计中&#xff0c;继承和多态是其中两个非常重要的特征。从使用的过程来看&#xff0c;一般应用到继承的&#xff0c;使用多态的可能性就非常大。而多态的实现有很多种&#xff0c; 但开发者通常认为的多态&#xff08;动多态&…...

trl - 微调、对齐大模型的全栈工具

文章目录 一、关于 TRL亮点 二、安装1、Python包2、从源码安装3、存储库 三、命令行界面&#xff08;CLI&#xff09;四、如何使用1、SFTTrainer2、RewardTrainer3、PPOTrainer4、DPOTrainer 五、其它开发 & 贡献参考文献最近策略优化 PPO直接偏好优化 DPO 一、关于 TRL T…...

GuLi商城-商品服务-API-品牌管理-品牌分类关联与级联更新

先配置mybatis分页&#xff1a; 品牌管理增加模糊查询&#xff1a; 品牌管理关联分类&#xff1a; 一个品牌可以有多个分类 一个分类也可以有多个品牌 多对多的关系&#xff0c;用中间表 涉及的类&#xff1a; 方法都比较简单&#xff0c;就不贴代码了...

【linux】服务器ubuntu安装cuda11.0、cuDNN教程,简单易懂,包教包会

【linux】服务器ubuntu安装cuda11.0、cuDNN教程&#xff0c;简单易懂&#xff0c;包教包会 【创作不易&#xff0c;求点赞关注收藏】 文章目录 【linux】服务器ubuntu安装cuda11.0、cuDNN教程&#xff0c;简单易懂&#xff0c;包教包会一、版本情况介绍二、安装cuda1、到官网…...

做网购的有哪几个网站/东莞seo管理

用JQuery Validate框架&#xff0c;在IE8下验证报错问题解决参考文章&#xff1a; &#xff08;1&#xff09;用JQuery Validate框架&#xff0c;在IE8下验证报错问题解决 &#xff08;2&#xff09;https://www.cnblogs.com/destimarve/p/5511257.html 备忘一下。...

三级分销网站建设报价方案/seo代做

这篇文章主要介绍了python批量提取图片信息并保存的实现&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧程序运行环境code# -*- coding:utf-8 -*-# ----------------…...

南京企业做网站/营销100个引流方案

第二题生日蜡烛(结果填空)某君从某年开始每年都举办一次生日party&#xff0c;并且每次都要吹熄与年龄相同根数的蜡烛。现在算起来&#xff0c;他一共吹熄了236根蜡烛。请问&#xff0c;他从多少岁开始过生日party的&#xff1f;请填写他开始过生日party的年龄数。注意&#xf…...

带做骑传奇私服网站/一键建站免费

前言 上篇文章给大家分享了前10个spark的企业面试题2020年最新Spark企业级面试题【上】&#xff0c;今天后续来了&#xff0c;来分享剩下的那个几个面试题。也祝大家找到自己喜欢的工作&#xff0c;一起加油&#xff0c;编写不易 请给老哥一个一键三连吧。 一、手写Spark-Wor…...

网站建设前期工作/西点培训班一般要多少学费

在场景设置的时候会有两种场景设置方式&#xff1a; 1&#xff0c;手动模式&#xff08;Manual Scenario) 2.面向目标的场景设置模式&#xff08;Goal Oriented scenario) 其中手动模式使用较多&#xff0c;而且灵活应用&#xff0c;能够更灵活的模拟实际的性能测试&#xff1b…...

网站建设公司的职责/苏州seo安严博客

NginxKeepalived搭建高可用负载平衡WEB 集群1.1环境规划&#xff1a;Nginx_master:192.168.5.129Nginx_backup:192.168.5.131Tomcat:192.168.5.132 端口:8080,9080操作系统&#xff1a;CentOS6.5 x86_64内核版本&#xff1a;2.6.32-696.el6.x86_64Nginx版本&#xff1a;nginx/1…...