个人博客网:https://wushaopei.github.io/ (你想要这里多有)
## 9 登录
9.1 修改登录页面表单项
<a class="btn btn-lg btn-success btn-block" onclick="dologin()" > 登录</a>
<button class="btn btn-lg btn-success btn-block" onclick="dologin()">登录</button>
- 修改登录表单项
<input name ="loginAcc" type="text" class="form-control" id="inputSuccess4" placeholder="请输入登录账号" autofocus>
<input name="loginPwd" type="text" class="form-control" id="inputSuccess4" placeholder="请输入登录密码" style="margin-top:10px;">
- 登录页面实现代码
- 不全,有待补充、修改
//获取表单提交数据,保存导数据库
@RequestMapping("/member/domain")
public String domain(Member member,Model model) {
// //doRegisterRemote方法会进行远程调用注册中心的“ProtalProvider”的服务,来进行查询
// ResultEntity<String> resultEntity = memberRemoteService.doRegisterRemote(member);
//
// String result = resultEntity.getResult();
//
// if(ResultEntity.SUCCESS.equals(result)) {
// return "member/login";
// }
//
// if(ResultEntity.FAILED.equals(result)) {
// String message = resultEntity.getMessage();
//
// model.addAttribute(ACConst.MESSAGE_ATTR_NAME, message);
// return "member/reg";
// }
System.err.println(member);
return "member/main";
}
9.2 登录到后台管理页面
- 页面跳转到
- 由登录检入后台管理页面main,通过方法检入到流程管理页面
@RequestMapping("/member/toCert")
public String toCert() {
return "member/cert";
}
在完整的架构中,后台管理页面由managerConsummer来进行跳转,并且所有的后台数据由Manager统一进行管理。这里只是查看管理页面等否正常跳转
尚筹网流程定义管理[流程定义部署]
实现Activiti 与SpringCloud的功能整合
10 环境搭建
10.1 ac-sc-manager-consumer(管理消费者)
- 依赖
<dependency>
<groupId>com.webcode.ac.springcloud</groupId>
<artifactId>SCMS-sc-feign-Api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka客户端工作时需要的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 客户端负载均衡功能支持,它可以帮我们根据服务名称访问注册中心注册的微服务 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
- application.yml
server:
port: 10004
spring:
application:
name: ManagerConsumer
freemarker:
suffix: .ftl
template-loader-path: classpath:/templates/
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:10000/eureka
- 主启动器
@SpringBootApplication
@EnableEurekaClient // 当前服务为Eureka客户端--注册中心调用客户端
@EnableFeignClients //当前服务为Feign客户端---负载均衡
public class ManagerConsummerMainType {
public static void main(String[] args) {
SpringApplication.run(ManagerConsummerMainType.class, args);
}
}
10 2ac-sc-manager-provider(管理服务商)
* 依赖
<dependency>
<groupId>com.webcode.ac.springcloud</groupId>
<artifactId>SCMS-sc-feign-Api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 加入Eureka客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- application.yml
server:
port: 10002 # 当前服务提供者id
spring:
application:
name: ManagerProvider # 一般作为服务注册的名
datasource: # 配置数据库连接池
name: druid-source
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://127.0.0.1:3306/springcloud?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8
username: root
password: root
dbcp2:
min-idle: 5
initial-size: 5
max-total: 5
max-wait-millis: 200
mybatis: # 配置mybatis的mapper.xml扫描的包路径
mapper-locations:
- classpath:mappers/*Mapper.xml
eureka:
client:
service-url:
defaultZone: http://localhost:10000/eureka # 注册中心地址
- 主启动器
@SpringBootApplication
@EnableEurekaClient //注册中心的客户端
@MapperScan("com.webcode.ac.sc.mapper")
public class ManagerProviderMainType {
public static void main(String[] args) {
SpringApplication.run(ManagerProviderMainType.class, args);
}
}
10.3 ac-sc-activiti-provider(流程服务商)
- 依赖
<dependency>
<groupId>com.webcode.ac.springcloud</groupId>
<artifactId>SCMS-sc-feign-Api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 加入Eureka客户端依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- Activiti场景启动器 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>5.21.0</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>com.webcode.ac.springcloud</groupId>
<artifactId>SCMS-sc-Common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- application.yml
server:
port: 10003
spring:
application:
name: ActivitiProvider
datasource:
name: druid-source
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://127.0.0.1:3306/springcloud?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=utf8
username: root
password: root
dbcp2:
min-idle: 5
initial-size: 5
max-total: 5
max-wait-millis: 200
eureka:
client:
service-url:
dafaultZone: http://localhost:10000/eureka
- 启动器
@SpringBootApplication //启动SpringBoot的服务器
@EnableEurekaClient //Eureka 的客户端
public class ActivitiProviderMainType {
public static void main(String[] args) {
SpringApplication.run(ActivitiProviderMainType.class, args);
}
}
11 显示后台主页
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W6FofLUH-1584113365508)(images/p23.png)]
11.1 创建 MainController
@Controller
public class MainController {
@RequestMapping("/main/index")
public String toMainPage() {
return "main";
}
}
- 添加 main 后台管理主页 —— main.ftl
11.2 加入页面和静态资源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z8sinvW7-1584113365509)(images/p24.png)]
12 流程定义列表显示
12.1 左侧菜单里的位置
由管理页面打开树形菜单列表需要携带当前所有的工作流数据,并将
为什么拿到的流程是最新的,而不会拿到所有的版本的流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xe2Mr6ot-1584113365511)(images/p26.png)]
- definition.ftl
<a href="/consumer/process/definition/list"><i class="glyphicon glyphicon-random"></i> 流程管理</a>
12.2 Consumer 的Controller方法
这里暂不写完,先用于测试
@Controller
public class ProcessController {
@RequestMapping("/consumer/process/definition/list")
public String showProcessDefinitionList(Model model) {
//未完待续...
List<Map<String,Object>> processDefinitionList = null;
model.addAttribute("processDefinitionList", processDefinitionList);
return "process/definition_list";
}
}
12. 3 Feign-API 中的远程接口
凡是查的是和Activite相关 的,我们查的都是它的为服务。可以理解为我们要查的是流的数据,那么,在这里,与流有关的服务提供者就是activiti-Provider.
@FeignClient(value="ActivitiProvider") //要调用的注册中心的远程服务的服务名
public interface ProcessRemoteService {
@RequestMapping("/get/process/definition/list/remote")
ResultEntity<List<Map<String, Object>>>getProcessDefinitionListRemote();
}
12.4 ActivitiProvider 的Controller
※ 自引用问题
衔接Consummer的方法测试自引用
※Direct self-reference leading to cycle
@RequestMapping("/get/process/definition/list/remote")
public List<ProcessDefinition> getProcessDefinitionListRemote() {
return repositoryService.createProcessDefinitionQuery().list();
}
ProcessDefinition对象中存在对自身的引用,会导致生成JSON数据时陷入死循环,所以不能返回ProcessDefinition类型。
12.5 完成Consummer 的Controller 方法(服务调用)
@Autowired
private ProcessRemoteService processRemoteService;
//获取当前数据库中所有的流程,并放入到model中,在流程管理页面通过请求域取出,
@RequestMapping("/consumer/process/definition/list")
public String showProcessDefinitionList(Model model){
//调用远程方法获取流程定义数据
ResultEntity<List<Map<String,Object>>> resultEntity = processRemoteService.getProcessDefinitionListRemote();
//检测远程方法调用的结果
String result = resultEntity.getResult();
if(ResultEntity.SUCCESS.equals(result)) {
//如果远程方法调用成功,则将苏据存入
List<Map<String, Object>> processDefinitionList = resultEntity.getData();
//将流的集合保存到域中
model.addAttribute("processDefinitionList", processDefinitionList);
return "process/definition_list";
}else {
//
model.addAttribute(ACConst.MESSAGE_ATTR_NAME, resultEntity.getMessage());
return "error";
}
}
12.6 完成ActivitiProvider 的Controller(服务提供)
act_re_procdef 表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TphpjUi8-1584113365511)(images/p30.png)]
@RestController
public class ProcessController {
@Autowired
private RepositoryService repositoryService;
@RequestMapping("/get/process/definition/list/remote")
public ResultEntity<List<Map<String,Object>>> getProcessDefinitionListRemote(){
ResultEntity<List<Map<String,Object>>> resultEntity = new ResultEntity<>();
try {
//1.查询全部ProcessDefinition的list(创建并查询全部的流程定义对象)
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().list();
//2.为了避免生成JSON数据时的自引用问题,将每一个ProcessDefinition对象转换为一个Map
List<Map<String, Object>> processDefinitionMapList = new ArrayList<>();
//3.遍历查询所有的流程定义对象
for (ProcessDefinition processDefinition : list) {
//4.创建Map集合用户保存流程定义对象
Map<String, Object> pdMap = new HashMap<>();
//5.获取每个当前流程定义对象的属性
String id = processDefinition.getId();
String key = processDefinition.getKey();
int version = processDefinition.getVersion();
String name = processDefinition.getName();
//6.保存流程定义对象的属性到集合中
pdMap.put("id", id);
pdMap.put("key", key);
pdMap.put("version", version);
pdMap.put("name", name);
//7.将单个流程定义map集合添加到多个流程定义List集合中
processDefinitionMapList.add(pdMap);
}
resultEntity.setResult(ResultEntity.SUCCESS);//返回结果
resultEntity.setData(processDefinitionMapList);//返回流程定义的List集合
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
resultEntity.setResult(ResultEntity.FAILED);
resultEntity.setMessage(e.getMessage());
}
return resultEntity;
}
12. 7 页面代码
将固定的流程项目条修改为动态的(通过结果服务数据库中取)
<br>
<hr style="clear:both;">
<div class="table-responsive">
<table class="table table-bordered">
<#if !Request.processDefinitionList??>
<tbody>
<tr><td>未查询到相关数据!</td></tr>
</tbody>
</#if>
<#if Request.processDefinitionList??>
<thead>
<tr>
<th width="30">#</th>
<th>流程名称</th>
<th>流程版本</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody>
<#list Request.processDefinitionList as pd>
<tr>
<td>${pd.id}</td>
<td>${pd.name}</td>
<td>${pd.version}</td>
<td>
<button type="button" class="btn btn-success btn-xs"><i class=" glyphicon glyphicon-eye-open"></i></button>
<button type="button" class="btn btn-danger btn-xs"><i class=" glyphicon glyphicon-remove"></i></button>
</td>
</tr>
</#list>
</tbody>
</#if>
</table>
</div>
13 上传流程定义文件【页面】
13.1 点击上传按钮弹出文件选择框
注意:在这里script的样式类库等位置要调到上面,可能存在使用域等因素,导致样式在上传流程定义文件表单项外面的下方时,会报错!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iTEkkotD-1584113365512)(images/p25.png)]
以下方便起见,可直接写在单击按钮代码上方。
第一步:创建一个不显示的表单
隐藏上传文件的表单项,只在上传文件时才弹出文件上传的文本框,由id绑定单击相应函数执行功能
<form id="uploadForm"
action="/consumer/process/upload"
method="post"
enctype="multipart/form-data"
style="display:none">
<input id="uploadBlock" type="file" name="bpmnFile"/>//接收单击相应函数弹出
</form>
第二步:编写jQuery代码
单击上传按钮,会调用内部的绑定响应函数等同于也单击了一次上传文件表单项的响应,弹出上传文本框
<script type="text/javascript">
$(function(){
$("#uploadBtn").click(function(){
//调用对应jQuery对象的click()方法就相当于点击了这个元素
//此时,click()方法中不传入回调函数,因为此时不是绑定单击响应函数
$("#uploadBlock").click(); //点击了这个元素会弹出文件上传文本框
});
});
</script>
13.2 选定文件后提交表单执行上传
选定一个文件后,对于文件上传框来说就触发了“值改变”事件,所以可以在文件上传框的值改变事件响应函数中提交表单。(这是因为执行上传后,当前文件上传框会包含有文件的名字、后缀等信息,性值发生改变,所以可以从中获取需要的数据进行校验是否上传成功)
$("#uploadBlock").change(function(){
$("#uploadForm").submit();
});
13.3 确认文件是否收到了
@RequestMapping("/consumer/process/upload")
public String uploadProcessDefinitionFile(@RequestParam("bpmnFile") MultipartFile multipartFile) {
boolean exists = !multipartFile.isEmpty();
System.err.println(exists?"文件接收到了!!!":"没有收到!");
return "main";
}
14 1 流程定义文件上传
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WzhGvxdm-1584113365514)(images/p28.png)]
14.1 Manager-Consummer 上传操作
- 基于前端后台管理页面的映射转发
- 获取请求的列表数据,并对其进行处理封装,再发送请求到服务生产商那里进行提交数据,并获取回调函数,以重定向携带数据前往流程管理页面,重新刷新展示流程图。
@RequestMapping("/consumer/process/upload")
public String uploadProcessDefinitionFile(@RequestParam("bpmnFile") MultipartFile bpmnFile,Model model) throws IOException {
if(bpmnFile.isEmpty()) {
System.out.println("为空"+bpmnFile);
}
System.out.println(bpmnFile.getOriginalFilename());
//1.获取接收到的文件的原始文件名
String originalFilename = bpmnFile.getOriginalFilename();
//2.借助UUID工具生成临时文件名
String string = UUID.randomUUID().toString();
//3.通过原始文件名获取文件扩展名
String tempFileExt = originalFilename.substring(originalFilename.lastIndexOf("."));
if(!".bpmn".equals(tempFileExt)) {
model.addAttribute("MESSAGE","请上传流程定义文件");
return "error";
}
//4.创建临时文件
File tempFile = File.createTempFile(string, originalFilename);
//5.将接收到的上传文件转存到tempFile中
bpmnFile.transferTo(tempFile);
//6.创建FileSystemResource对象
FileSystemResource fileSystemResource = new FileSystemResource(tempFile);
//7.将文件封装到Map中,便于作为请求参数发送给Provider
LinkedMultiValueMap<String,Object> paramMap = new LinkedMultiValueMap<>();
//※注意: 这里的键,就是将来在Provider中接收上传文件数据时的请求参数名
paramMap.add("processDefenitionFile", fileSystemResource);
//8.调用RestTemplate对象的方法发送请求
String url = "http://ActivitiProvider/deploy/process/definition/file/remote";
String response = restTemplate.postForObject(url, paramMap, String.class);
System.err.println("response="+response);
response.isEmpty();
if("SUCCESS".equals(response)) {
return "redirect:/consumer/process/definition/list";
}else {
model.addAttribute("MESSAGE", response);
return "error";
}
}
14.2 Activiti-Provider操作
@RequestMapping("/deploy/process/definition/file/remote")
public String deployProcessDefinitionFile(@RequestParam("processDefenitionFile") MultipartFile processDefenitionFile) throws IOException {
try {
//1.获取用来部署流程定义文件的输入流对象
InputStream inputStream = processDefenitionFile.getInputStream();
//2.获取原始文件名作为resourceName
String originalFilename = processDefenitionFile.getOriginalFilename();
//3.基于文件输入流执行部署
repositoryService.createDeployment().addInputStream(originalFilename, inputStream).deploy();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return e.getMessage();
}
return "SUCCESS";
}
//未完…