flowable笔记 - 简单的通用流程

简介
通用流程可以用于一些基本的申请,例如请假、加班。
大致过程是:

1. 创建申请
2. 分配给审批人(需要审批人列表,当前审批人) -> 有下一个审批人 -> 3 -> 无 -> 4
3. 审批人审批 -> 同意 -> 2 -> 拒绝 -> 5
4. 存储数据,发送通知
5. 结束

比较简单,唯一的难点就是动态设置审批人或者审批组,下面开始代码部分。

bpmn20文件

    ...<!-- standardRequest用来开始流程,在flowable里称为processDefinitionKey --><process id="standardRequest" name="标准申请流程" isExecutable="true"><!-- 第一步:开始流程,创建申请 --><startEvent id="startEvent" name="创建申请"/><sequenceFlow sourceRef="startEvent" targetRef="assignToAuditor"/><!-- 第二步:分配审批人 --><!-- 这个AssignToAuditorDelegate类就是解决动态设置审批人的Java类 --><!-- 审批人列表需要从外部传入或者根据当前流程id来从数据库获取 --><serviceTask id="assignToAuditor" name="分配审批人" flowable:class="me.xwbz.flowable.delegate.AssignToAuditorDelegate"/><sequenceFlow sourceRef="assignToAuditor" targetRef="auditorExist"/><!-- 唯一网关:类似于switch,只能通过一个序列流 --><!-- 这里就是要么存在,要么不存在 --><!-- 使用default属性定义默认序列流,在其他序列流条件都不满足的情况下使用  --><exclusiveGateway id="auditorExist" name="审批人是否存在" default="auditorNotExistFlow"/><sequenceFlow sourceRef="auditorExist" targetRef="approveTask"><!-- auditMethod是Spring里的一个bean,下面有提到 --><!-- execution是flowable内部变量,类型是org.flowable.engine.delegate.DelegateExecution,也就是serviceTask里的代理方法拿到的 --><conditionExpression xsi:type="tFormalExpression"><![CDATA[${auditMethod.existAuditor(execution)}]]></conditionExpression></sequenceFlow><sequenceFlow id="auditorNotExistFlow" sourceRef="auditorExist" targetRef="agreeDelegate" /><!-- 第三步:审批人审批 --><userTask id="approveTask" name="等待审批"flowable:candidateGroups="${auditMethod.getCandidateGroups(execution)}"flowable:candidateUsers="${auditMethod.getCandidateUsers(execution)}"/><sequenceFlow sourceRef="approveTask" targetRef="decision"/><!-- 唯一网关:一个审批一个审批人 --><exclusiveGateway id="decision" default="rejectFlow"/><sequenceFlow sourceRef="decision" targetRef="assignToAuditor"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${auditMethod.isApproved(execution)}]]></conditionExpression></sequenceFlow><sequenceFlow id="rejectFlow" sourceRef="decision" targetRef="rejectDelegate" /><!-- 第四步:同意后存储数据,发送通知 --><serviceTask id="agreeDelegate" name="数据存储"flowable:class="me.xwbz.flowable.delegate.StandardRequestAgreeDelegate"/><sequenceFlow sourceRef="agreeDelegate" targetRef="approveEnd"/><serviceTask id="rejectDelegate" name="回复拒绝消息"flowable:class="me.xwbz.flowable.delegate.BaseRejectDelegate"/><sequenceFlow sourceRef="rejectDelegate" targetRef="rejectEnd"/><!-- 第五步:结束 --><endEvent id="approveEnd" name="已同意"/><endEvent id="rejectEnd" name="已驳回"/></process>...

常量部分
这次没有另外存储数据,所以变量都是直接存储到flowable自带的变量表里 强烈建议大家另外存储,自带的查询起来非常麻烦!

审批人列表:AUDITOR_LIST_KEY = "AUDITOR_LIST";
当前审批人:AUDITOR_KEY = "AUDITOR";
当前审批人下标:AUDITOR_IDX_KEY = "AUDITOR_IDX";
是否已审批:APPROVED_KEY = "AUDIT_APPROVED";
申请类型:AUDIT_TYPE_KEY = "AUDIT_TYPE";
申请状态:AUDIT_STATUS_KEY = "AUDIT_STATUS";
其他参数:AUDIT_PARAMS_KEY = "AUDIT_PARAMS";
申请状态
public enum AuditStatus {/** 待审批 */WAIT_AUDIT,/** 已同意申请 */AGREE_AUDIT,/** 已拒绝申请 */REJECT_AUDIT,/** 已取消 */CANCEL
}

申请人类型

public enum CandidateType{/** 候选人 */USER,/** 候选组 */GROUP
}

审批使用的方法定义
一个普通的Java类

package me.xwbz.flowable.method;import com.alibaba.fastjson.JSONObject;
import org.flowable.engine.delegate.DelegateExecution;/*** 审批相关的方法** 用于flowable流程使用*/
public class AuditMethod {/*** 是否存在审批者* <sequenceFlow sourceRef="decision" targetRef="assignToAuditor">*        <conditionExpression xsi:type="tFormalExpression">*                 <![CDATA[*                     ${auditMethod.existAuditor(execution)}*                 ]]>*    </conditionExpression>*  </sequenceFlow>*/public boolean existAuditor(DelegateExecution execution){return execution.hasVariable(AUDITOR_KEY);}/*** 获取当前审批者*/public JSONObject getCurrentAuditor(DelegateExecution execution){return JSONObject.parseObject((String)execution.getVariable(AUDITOR_KEY));}/*** 获取当前候选组*/public String getCandidateGroups(DelegateExecution execution){JSONObject candidate = getCurrentAuditor(execution);if(candidate.getIntValue("type") == CandidateType.GROUP.ordinal()) {return candidate.getString("id");}return null;}public String getCandidateUsers(DelegateExecution execution){JSONObject candidate = getCurrentAuditor(execution);if(candidate.getIntValue("type") == CandidateType.USER.ordinal()) {return candidate.getString("id");}return null;}/*** 获取当前审批者id* <userTask id="approveTask" name="等待审批" flowable:assignee="${auditMethod.getCurrentAuditorId(execution)}" />*/public String getCurrentAuditorId(DelegateExecution execution){JSONObject auditor = getCurrentAuditor(execution);return JSONObject.toJavaObject(auditor, User.class).getId();}/*** 是否同意申请*/public boolean isApproved(DelegateExecution execution){Boolean approved = execution.getVariable(APPROVED_KEY, Boolean.class);return approved != null && approved;}
}

流程结束处理

下面是同意处理,主要是更改状态。“拒绝处理”同理

可以根据业务增加消息通知,保存数据等。

package me.xwbz.flowable.delegate;import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;@Slf4j
public class BaseAgreeDelegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {log.info("{}已被同意", execution.getVariables());execution.setVariable(AUDIT_STATUS_KEY, AuditStatus.AGREE_AUDIT.toString());}
}

flowable结合Spring可以直接使用Spring里的bean。

像下面这样定义,然后直接${auditMethod.isApproved(execution)}就可以调用auditMethod里的isApproved方法。

import me.xwbz.flowable.method.AuditMethod;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FlowableConfig {@Bean(name = "auditMethod")public AuditMethod auditMethod(){return new AuditMethod();}
}

动态设置审批人
这个是配置在serviceTask里的,所以需要实现JavaDelegate接口

package me.xwbz.flowable.delegate;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;/*** delegate - 分配审批人*/
public class AssignToAuditorDelegate implements JavaDelegate {@Overridepublic void execute(DelegateExecution execution) {// 初始化变量,清空临时变量execution.removeVariable(APPROVED_KEY);execution.removeVariable(AUDITOR_KEY);execution.setVariable(AUDIT_STATUS_KEY, AuditStatus.WAIT_AUDIT.toString());// 拿到审批人列表JSONArray auditorList = JSON.parseArray(execution.getVariable(AUDITOR_LIST_KEY).toString());// 当前审批人在审批人列表的下标Integer auditorIdx = execution.getVariable(AUDITOR_IDX_KEY, Integer.class);if (auditorIdx == null) {// 第一次分配,初始化为第一个auditorIdx = 0;} else if (auditorIdx + 1 >= auditorList.size()) {//  所有审批人审批完成,结束分配return;} else {// 下一个auditorIdx++;}JSONObject auditor = auditorList.getJSONObject(auditorIdx);execution.setVariable(AUDITOR_KEY, auditor.toJSONString());execution.setVariable(AUDITOR_IDX_KEY, auditorIdx);}
}

开始流程
使用runtimeService#startProcessInstanceByKey开始这个流程,记得开始之前要使用identityService#setAuthenticatedUserId设置当前用户编号,这个是绑定到线程的,单线程环境下设置一次就行了。

Map<String, Object> vars = new HashMap<>();
// 放入申请类型
vars.put(AUDIT_TYPE_KEY, param.getType());
// 放入审批人人列表
vars.put(AUDITOR_LIST_KEY, JSONObject.toJSONString(param.getAuditors()));
// 放入其他参数
vars.put(AUDIT_PARAMS_KEY, param.getParams());
// 放入审批状态
vars.put(AUDIT_STATUS_KEY, AuditStatus.WAIT_AUDIT.toString());logger.debug("当前用户id: {} ", Authentication.getAuthenticatedUserId());
// 设置发起人
// identityService.setAuthenticatedUserId(user.getId());
ProcessInstance pro = runtimeService.startProcessInstanceByKey("standardRequest", 生成的编号, vars);
// 文件材料
if (param.getFiles() != null && !param.getFiles().isEmpty()) {// 上传附件,可以直接上传字节流(不建议)param.getFiles().forEach(file ->taskService.createAttachment("application/octet-stream", null,pro.getId(), file.getName(), null, file.getId()));
}

查看待我审批的任务
taskService.createTaskQuery()只能查询到正在进行的任务

要是想既能查询到正在进行的,也要结束的可以使用下面的语句:

TaskInfoQueryWrapper taskInfoQueryWrapper = runtime ? new TaskInfoQueryWrapper(taskService.createTaskQuery()) : new TaskInfoQueryWrapper(historyService.createHistoricTaskInstanceQuery());

也就是说你要先确定是要那种。

TaskQuery query = taskService.createTaskQuery()// or() 和 endOr()就像是左括号和右括号,中间用or连接条件// 指定是我审批的任务或者所在的组别审批的任务// 实在太复杂的情况,建议不使用flowable的查询.or().taskAssignee(user.getId()).taskCandidateUser(user.getId()).taskCandidateGroup(user.getGroup()).endOr();// 查询自定义字段if (StringUtils.isNotEmpty(auditType)) {query.processVariableValueEquals(AUDIT_TYPE_KEY, auditType);}if(auditStatus != null){query.processVariableValueEquals(AUDIT_STATUS_KEY, auditStatus.toString());}// 根据创建时间倒序
query.orderByTaskCreateTime().desc()// 分页.listPage(0, 10).stream().map(t -> {// 拿到这个任务的流程实例,用于显示流程开始时间、结束时间、业务编号HistoricProcessInstance p = historyService.createHistoricProcessInstanceQuery().processInstanceId(t.getProcessInstanceId()).singleResult();return new Process(p).withTask(t) // 拿到任务编号和任务名称// 拿到创建时和中途加入的自定义参数.withVariables(taskService.getVariables(t.getId())).withFiles(taskService.getProcessInstanceAttachments(p.getId()));
}).collect(Collectors.toList()

查看我已审批的任务
任务审批后就走下一个序列流,这里只能从历史纪录里获取已审批的。

当前设置历史纪录(HistoryLevel)粒度为audit,这是默认的。

注意这里不能筛选自定义参数,所以要么自定义sql,要么另外存储。

// 如果不需要筛选自定义参数
if(auditStatus == null && StringUtils.isEmpty(auditType)){return historyService.createHistoricActivityInstanceQuery()// 我审批的.taskAssignee(assignee)// 按照结束时间倒序.orderByHistoricActivityInstanceEndTime().desc()// 已结束的(其实就是判断有没有结束时间).finished()// 分页.listPage(firstIdx, pageSize);
}
// 否则需要自定义sql
// managementService.getTableName是用来获取表名的(加上上一篇提到的liquibase,估计flowable作者对数据表命名很纠结)
// 这里从HistoricVariableInstance对应的表里找到自定义参数
// 筛选对象类型不支持二进制,存储的时候尽量使用字符串、数字、布尔值、时间,用来比较的值也有很多限制,例如null不能用like比较。
String sql = "SELECT DISTINCT RES.* " +"FROM " + managementService.getTableName(HistoricActivityInstance.class) + " RES " +"INNER JOIN " + managementService.getTableName(HistoricVariableInstance.class) + " var " +"ON var.PROC_INST_ID_ = res.PROC_INST_ID_  " +"WHERE RES.ASSIGNEE_ = #{assignee} " +"AND RES.END_TIME_ IS NOT NULL ";
if(auditStatus != null && StringUtils.isNotEmpty(auditType)){sql += "AND ((var.name_ = #{typeKey} AND var.TEXT_ = #{typeValue}) OR  (var.name_ = #{statusKey} AND var.TEXT_ = #{statusValue}))";
} else if(auditStatus != null){sql += "AND var.name_ = #{statusKey} AND var.TEXT_ = #{statusValue}";
} else {sql += "AND var.name_ = #{typeKey} AND var.TEXT_ = #{typeValue}";
}
sql +=  " ORDER BY RES.END_TIME_ DESC";return historyService.createNativeHistoricActivityInstanceQuery().sql(sql)// 参数用#{assignee}占位后,再调用parameter("assignee", assignee)填入值// 参数值可以多出来没用到的,比hibernate好多了.parameter("assignee", assignee).parameter("typeKey", AUDIT_TYPE_KEY).parameter("typeValue", auditType).parameter("statusKey", AUDIT_STATUS_KEY).parameter("statusValue", auditStatus == null ? null : auditStatus.toString()).listPage(firstIdx, pageSize);

后续获取详细和自定义参数

list.stream().map(a -> {// 同上面的拿到这个任务的流程实例HistoricProcessInstance p = historyService.createHistoricProcessInstanceQuery().processInstanceId(a.getProcessInstanceId()).singleResult();// 因为任务已结束(我看到有提到删除任务TaskHelper#completeTask),所以只能从历史里获取Map<String, Object> params = historyService.createHistoricVariableInstanceQuery().processInstanceId(a.getProcessInstanceId()).list()// 拿到的是HistoricVariableInstance对象,需要转成原来存储的方式.stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));return new Process(p).withActivity(a).withVariables(params);
}).collect(Collectors.toList())

查看我创建的任务
这个比较方便拿到,但是当前最新的任务比较难拿到,有时还不准确

// startedBy:创建任务时设置的发起人
HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery().startedBy(user.getId());
// 自定义参数筛选
if (StringUtils.isNotEmpty(auditType)) {instanceQuery.variableValueEquals(AUDIT_TYPE_KEY, auditType);
}
if(auditStatus != null){instanceQuery.variableValueEquals(AUDIT_STATUS_KEY, auditStatus.toString());
}instanceQuery.orderByProcessInstanceStartTime().desc().listPage(firstIdx, pageSize).stream()//  获取其中的详细和自定义参数.map(this::convertHostoryProcess).collect(Collectors.toList())

获取其中的详细和自定义参数

    private Process convertHostoryProcess(HistoricProcessInstance p) {// 不管流程是否结束,到历史里查,最方便Map<String, Object> params = historyService.createHistoricVariableInstanceQuery().processInstanceId(p.getId()).list().stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));// 获取最新的一个userTask,也就是任务活动纪录List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery().processInstanceId(p.getId()).orderByHistoricActivityInstanceStartTime().desc().orderByHistoricActivityInstanceEndTime().asc().listPage(0, 1);Process data = new Process(p);if (!activities.isEmpty()) {data.withActivity(activities.get(0));}return data.withVariables(params);}

撤销流程实例(标记删除)
撤销后,流程直接中断,除了用户不能操作和多了结束时间、删除理由外,其他停留在撤销前的状态。

ProcessInstance process = runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
if (process == null) {throw new RuntimeException("该流程不在运行状态");
}
Task task = taskService.createTaskQuery().processInstanceId(id).singleResult();
runtimeService.setVariable(task.getExecutionId(), AUDIT_STATUS_KEY, AuditStatus.CANCEL.toString());
runtimeService.deleteProcessInstance(id, "用户撤销");

用户操作(同意、拒绝)

注意:如果beforeAgreeOrReject与taskService.complete在同一个事物里,且beforeAgreeOrReject里跟AssignToAuditorDelegate有更新数据库数据,会导致事物异常。

是否有审批权限需要自己判断。

操作后进入下一序列流,再次拿这个taskId会获取不到这个Task,所以上传审批意见和附件什么的要在操作前。

if(!isAssigneeOrCandidate(user, taskId)){throw new RuntimeException("无法操作");
}
// 同意前设置,上传审批意见和附件
beforeAgreeOrReject(user, taskId, AuditStatus.AGREE_AUDIT, "同意", reason);
// 拒绝前设置,上传审批意见和附件
// beforeAgreeOrReject(user, taskId, AuditStatus.REJECT_AUDIT, "拒绝", reason);
// 同意
taskService.complete(taskId, Collections.singletonMap(APPROVED_KEY, true));
// 拒绝
// taskService.complete(taskId, Collections.singletonMap(APPROVED_KEY, false));

判断是否有权限

public boolean isAssigneeOrCandidate(User user, String taskId){long count = taskService.createTaskQuery().taskId(taskId).or().taskAssignee(user.getId()).taskCandidateUser(user.getId()).taskCandidateGroup(user.getGroup()).endOr().count();return count > 0;
}

操作前设置,上传审批意见和附件

附件一般都另外存储,这里存一个id就行了 这里是存在ftp文件服务器里的

public void beforeAgreeOrReject(User user, String taskId, AuditStatus auditStatus, String operate, ReasonParam reason){// 组成员操作后方便查询taskService.setAssignee(taskId, user.getId());if(StringUtils.isNotEmpty(reason.getText())){// 审批意见taskService.addComment(taskId, null, null, reason.getText());}if (reason.getFiles() != null && !reason.getFiles().isEmpty()) {files.forEach(file ->// 上传附件,可以直接上传字节流(不建议)taskService.createAttachment("application/octet-stream", taskId,null, file.getName(), null, file.getId()));}
}

更新日志
2018-12-17
支持审批组,遇到的坑提醒

2019-01-05
代码上传到github,github地址,https://github.com/xwbz2017/flowable-demo
修改之前错误的地方,当然建议是看看上方的github代码

原文地址:https://www.cnblogs.com/xwbz/p/9886696.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/522742.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解SLB、EIP、NAT网关之间区别, 合理选择云上公网入口

摘要&#xff1a; 概述 阿里云的公网入口产品共有三个&#xff0c;SLB、EIP、NAT网关&#xff0c;这几个产品都可以作为云上资源的公网入口&#xff0c;他们之间有何区别&#xff0c;又分别应该在什么场景下使用呢&#xff1f; 点此查看原文&#xff1a;http://click.aliyun.co…

matlab 求n 的和,MATLAB求1的阶乘加到n的阶乘和 不要现有的函数,要自己编写出来的...

点击查看MATLAB求1的阶乘加到n的阶乘和 不要现有的函数&#xff0c;要自己编写出来的具体信息答&#xff1a;myfactorial (n) factorial(n) myfactorial (n) prod(1:n) 两个都可以吧。&#xff1a;)答&#xff1a;编写一个matlab文件&#xff0c;求1到n的阶乘之和。其代码编写…

Hadoop常见问题 | Hadoop能干什么?

戳蓝字“CSDN云计算”关注我们哦&#xff01;我们很荣幸能够见证Hadoop十几年间经历了从无到有&#xff0c;再到称王。感动于技术的日新月异时&#xff0c;希望通过本篇有问有答&#xff0c;带大家解决Hadoop的常见问题。1Q&#xff1a; Hadoop 的发展历史A&#xff1a;2Q&…

matlab 导入元胞,MATLAB导入xls文件以及cell的使用方法

使用matlab读入.xls的文件时候可以用[T,TXT,RAW]xlsread("filename.xls")导入也可以使用import data 的办法手动导入文件。但是我比较倾向于自动化程度比较高的前者&#xff0c;毕竟有时候导入的文件数量是几百个手动导入不科学。xlsread返回的参数有三个&#xff0c…

maven打包时跳过测试

在运行mvn install或mvn package时跳过Test <project> [...] <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.18.1<…

uDevice Center - IoT弹性在线多设备开发平台

摘要&#xff1a; IoT开发的一大痛点在于&#xff1a;硬件获取与setup路径太长&#xff1b;开发工具难以支持多设备联合开发。我们用uDevice Center把开发板/设备都搬到了线上&#xff0c;让用户可以不用买硬件接线&#xff0c;在线就能做IoT开发调试了。同时&#xff0c;uDevi…

快报:Python 被爆重大“黑料”!程序员:劲爆!

坐拥各大编程排行榜的Python&#xff0c;真的无敌了吗&#xff1f;在我一个朋友看来&#xff0c;他坚信 Python 可以做任何事情。但其实我是不服的&#xff0c;我相信很多人都有这种感觉。但是我最近当看 GitHub 年度项目数量时&#xff0c;我哭了&#xff0c;因为 Python 应用…

MATLAB灰度级数为1,图像处理 – 在matlab中更改灰度图像中的灰度级数

虽然result (img / 8)* 8确实将范围[0,255]中的灰度图像转换为该范围的子集但现在仅使用32个值,但它可能会产生不希望的伪像.可能产生视觉上更好的图像的方法称为改进的灰度量化(缩写为IGS).执行它的伪代码可以如下&#xff1a;mult 256 / (2^bits)mask 2^(8 - bits) - 1prev…

SpringBoot集成flowable-modeler(6.4.1) 实现免登

因公司需求需要将flowable的流程设计器集成到项目中&#xff0c;下面将最近的研究成果记录一下。 文章目录一、下载flowable-modeler源码二、添加相关maven包三、调用idm服务重新接口四、配置类五、启动类跳过登陆拦截六、配置文件一、下载flowable-modeler源码 把flowable-ui…

时代在召唤5G

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者| 白告天原创 |边缘计算社区错过房价飞涨的年代&#xff0c;你还要错过5G这班车吗&#xff1f;一 5G是什么&#xff1f;5G&#xff0c;就是第五代移动通信技术。和大哥大变成智能手机&#xff0c;绿皮火车变成和谐号都是一种大幅…

php中的空格键,使用PHP使用数组键中的空格解析Json响应

我收到一个邮政编码API的回复.但是,由于两个词之间存在空格,我无法弄清楚“地名”中的值.不太确定从这里去哪里.object(stdClass)#1 (4) {["post code"]>string(5) "42223"["country"]>string(13) "United States"["countr…

jboss连接池,断开后自动重连功能

最近客户现场的测试环境连的数据库极不稳定&#xff0c;经常会出现需要重新启动数据库的情况&#xff0c; 但是一旦重启数据库 则会出现 提示 ,执行sql错误&#xff0c;原因就是datasource 没有获取新的连接&#xff01; 那么解决办法就是怎样让jboss每次提供连接的时候都给我们…

API信息全掌控,方便你的日志管理——阿里云推出API网关打通日志服务

摘要&#xff1a; 近日&#xff0c;阿里云API网关对接了日志服务&#xff0c;可以输出用户在API网关产生的API调用日志&#xff0c;目前支持将 API 接入 API 网关的用户查看日志明细、概况、报表分析、在线查询等。 访问日志&#xff08;Acccess Log&#xff09;是由应用服务生…

GitHub 被爆开始实名制,以便于执行美国贸易制裁;特斯拉推出超大储能产品Megapack;高通宣布与腾讯游戏达成战略合作……...

关注并标星星CSDN云计算极客头条&#xff1a;速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周三次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go go 尼展示圆柱型透明式显示屏&a…

几何级数 函数 matlab,matlab 实验05数据的统计分析

数据的统计分析在日常生活中我们会在很多事件中收集到一些数据(比如&#xff1a;考试分数、窗口排队人数、月用电量、灯泡寿命、测量误差、产品质量、月降雨量等数据)&#xff0c;这些数据的产生一般都是随机的&#xff0e;这些随机数据乍看起来并没有什么规律&#xff0c;但通…

IDEA解决sun.misc.BASE64Encoder找不到jar包的解决方法

sun.misc.BASE64Encoder 不建议使用java.sun自带包中的内容 import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder;在项目中&#xff0c;设计到64位编码的。有时开发会用到JDK中自带的BASE64工具。但sun公司是建议不这样做的。尤其是更新了JDK版本&#xff0c;项目甚…

MaxCompute印尼开服,成为阿里云第十二个大数据服务节点

摘要&#xff1a; 人口超2.5亿的印度尼西亚&#xff0c;政府、通讯公司和银行等机构拥有繁杂的数据沉淀&#xff0c;他们正在加快应用大数据开发框架。MaxCompute势必加速这一进程&#xff0c;唤醒沉淀数据&#xff0c;最大化挖掘数据价值。 点此查看原文&#xff1a;http://cl…

大数据年代,我们的思想已被算法剥夺

戳蓝字“CSDN云计算”关注我们哦&#xff01;在这个信息爆炸的年代&#xff0c;我们能够获取信息的途径正越来越多。各类信息通过各种文字APP&#xff0c;图片APP&#xff0c;视频APP乃至微信朋友圈传达到你面前。任何一个新闻都可以在发酵后的1天内传遍整个世界。看起来人们正…

SpringBoot整合Editor.md实现Markdown编辑器

Editor.md 是一款开源的、可嵌入的 Markdown 在线编辑器&#xff08;组件&#xff09;&#xff0c;基于 CodeMirror、jQuery 和 Marked 构建。 文章目录一、技术选型及分支部署二、集成手册2.1. 下载项目2.2. 创建数据库2.3. 初始化数据库脚本2.4. 修改数据库用户名/密码 默认r…

oracle用户密码已过期,oracle用户密码过期的处理方法

oracle用户密码过期&#xff1f;不要紧&#xff0c;下面就为您介绍一个oracle用户密码过期的处理方法&#xff0c;供您参考&#xff0c;如果您对此方面感兴趣的话&#xff0c;不妨一看。在测试环境学习oracle,出现oracle用户密码过期的问题&#xff0c;更改密码提示验证未通过&…