diff --git a/slife-activiti/src/main/java/com/slife/constants/ActivitiConstants.java b/slife-activiti/src/main/java/com/slife/constants/ActivitiConstants.java new file mode 100644 index 0000000..5524620 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/constants/ActivitiConstants.java @@ -0,0 +1,14 @@ +package com.slife.constants; + +/** + * @Author felixu + * @Date 2018.08.15 + */ +public interface ActivitiConstants { + + interface TaskStatus { + String TASK_STATUS_TODO = "todo"; + String TASK_STATUS_CLAIM = "claim"; + String TASK_STATUS_FINISH = "finish"; + } +} diff --git a/slife-activiti/src/main/java/com/slife/controller/TaskController.java b/slife-activiti/src/main/java/com/slife/controller/TaskController.java new file mode 100644 index 0000000..aa844ee --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/controller/TaskController.java @@ -0,0 +1,52 @@ +package com.slife.controller; + +import com.slife.base.entity.ReturnDTO; +import com.slife.base.vo.DataTable; +import com.slife.form.StartTaskForm; +import com.slife.service.TaskService; +import com.slife.util.ReturnDTOUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; + +/** + * @Author felixu + * @Date 2018.08.14 + */ +@Controller +@RequestMapping("/task") +@Api(value = "任务管理的前端控制器", tags = "任务管理接口", description = "任务相关") +public class TaskController { + + @Autowired + TaskService taskService; + + @ApiOperation(value = "进入流程管理页面", notes = "进入流程管理页面") + @GetMapping(value = "/todo") + public String list(Model model, HttpServletRequest request) { + model.addAttribute("url", request.getContextPath()+"/task/todo/"); + return "task/todoList"; + } + + @ApiOperation(value = "待办列表", notes = "待办列表") + @PostMapping("/todo/list") + @ResponseBody + public DataTable todoList(@RequestBody DataTable dt) { + return taskService.getTodoTasks(dt); + } + + @ApiOperation(value = "开始流程", notes = "开始任务") + @PostMapping("/todo/start") + public ReturnDTO start(@RequestBody StartTaskForm form, BindingResult result) { + if (result.hasErrors()) { + + } + return ReturnDTOUtil.success(); + } +} diff --git a/slife-activiti/src/main/java/com/slife/dto/TaskDTO.java b/slife-activiti/src/main/java/com/slife/dto/TaskDTO.java new file mode 100644 index 0000000..e0af984 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/dto/TaskDTO.java @@ -0,0 +1,29 @@ +package com.slife.dto; + +import lombok.Data; + +import java.util.Map; + +/** + * @Author felixu + * @Date 2018.08.15 + */ +@Data +public class TaskDTO { + private String applicant; + private String taskId; + private String taskName; + private String title; + private String pdName; + private String version; + private Long time; + private String processInstanceId; + private String status; + private Variable variable; + private String nodeKey; + private String processDefKey; + private String category; + public void setVariable(Map variable) { + this.variable = new Variable(variable); + } +} diff --git a/slife-activiti/src/main/java/com/slife/dto/Variable.java b/slife-activiti/src/main/java/com/slife/dto/Variable.java new file mode 100644 index 0000000..a4d3094 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/dto/Variable.java @@ -0,0 +1,80 @@ +package com.slife.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.slife.util.StringUtils; +import com.slife.utils.DateConverter; +import com.slife.utils.PropertyType; +import org.apache.commons.beanutils.ConvertUtils; + +import java.util.HashMap; +import java.util.Map; + +public class Variable { + + private Map map = new HashMap<>(); + + private String keys; + private String values; + private String types; + + public Variable(){ + + } + + public Variable(Map map){ + this.map = map; + } + + public String getKeys() { + return keys; + } + + public void setKeys(String keys) { + this.keys = keys; + } + + public String getValues() { + return values; + } + + public void setValues(String values) { + this.values = values; + } + + public String getTypes() { + return types; + } + + public void setTypes(String types) { + this.types = types; + } + + @JsonIgnore + public Map getVariableMap() { + + ConvertUtils.register(new DateConverter(), java.util.Date.class); + + if (StringUtils.isBlank(keys)) { + return map; + } + + String[] arrayKey = keys.split(","); + String[] arrayValue = values.split(","); + String[] arrayType = types.split(","); + for (int i = 0; i < arrayKey.length; i++) { + String key = arrayKey[i]; + String value = arrayValue[i]; + String type = arrayType[i]; + + Class targetType = Enum.valueOf(PropertyType.class, type).getValue(); + Object objectValue = ConvertUtils.convert(value, targetType); + map.put(key, objectValue); + } + return map; + } + + public Map getMap() { + return map; + } + +} diff --git a/slife-activiti/src/main/java/com/slife/form/StartTaskForm.java b/slife-activiti/src/main/java/com/slife/form/StartTaskForm.java new file mode 100644 index 0000000..ba1a380 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/form/StartTaskForm.java @@ -0,0 +1,20 @@ +package com.slife.form; + +import lombok.Data; +import org.hibernate.validator.constraints.NotEmpty; + +/** + * @Author felixu + * @Date 2018.08.14 + */ +@Data +public class StartTaskForm { + @NotEmpty + public String procDefKey; +// @NotEmpty + public String entityId; + @NotEmpty + public String title; +// public String userId; + public String comment; +} diff --git a/slife-activiti/src/main/java/com/slife/service/TaskService.java b/slife-activiti/src/main/java/com/slife/service/TaskService.java new file mode 100644 index 0000000..7315b34 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/service/TaskService.java @@ -0,0 +1,81 @@ +package com.slife.service; + +import com.slife.base.vo.DataTable; +import com.slife.constants.ActivitiConstants; +import com.slife.dto.TaskDTO; +import com.slife.shiro.SlifeSysUser; +import com.slife.util.StringUtils; +import com.slife.utils.ExtUtils; +import org.activiti.engine.RepositoryService; +import org.activiti.engine.repository.Deployment; +import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.engine.task.TaskInfo; +import org.activiti.engine.task.TaskQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Author felixu + * @Date 2018.08.14 + */ +@Service +@Transactional(rollbackFor = Exception.class) +public class TaskService { + + @Autowired + org.activiti.engine.TaskService actTaskService; + + @Autowired + RepositoryService repositoryService; + + /** + * 待办任务 + * @param title + * @param category + * @param page + * @param pageSize + * @return + */ + public DataTable getTodoTasks(DataTable dt) { + String userId = SlifeSysUser.id() + ""; + TaskQuery taskQuery = actTaskService.createTaskQuery().taskCandidateOrAssigned(userId) + .active().includeProcessVariables().orderByTaskCreateTime().desc(); + String category = (String) dt.getSearchParams().get("category"); + if (StringUtils.isNotBlank(category)) { + taskQuery.taskCategory(category); + } + String title = (String) dt.getSearchParams().get("title"); + if (StringUtils.isNotBlank(title)){ + taskQuery.processVariableValueLikeIgnoreCase("title", "%" + title + "%"); + } + Map> collect = taskQuery.listPage((dt.getPageNumber() - 1) * dt.getPageSize(), dt.getPageSize()) + .stream() + .map(task -> { + TaskDTO dto = getTaskDTO(task, task.getAssignee() == null ? + ActivitiConstants.TaskStatus.TASK_STATUS_CLAIM : ActivitiConstants.TaskStatus.TASK_STATUS_TODO); + dto.setTime(task.getCreateTime().getTime()); + return dto; + }).collect(Collectors.groupingBy(TaskDTO::getCategory)); + dt.setRows(Collections.singletonList(collect)); + return dt; + } + + private TaskDTO getTaskDTO(TaskInfo task, String status) { + ProcessDefinition processDefinition = getProcessDefinition(task.getProcessDefinitionId()); + String deploymentId = processDefinition.getDeploymentId(); + Deployment deployment = getDeployment(deploymentId); + return ExtUtils.toTaskDTO(task, status, processDefinition, deployment); + } + + private ProcessDefinition getProcessDefinition(String processDefinitionId) { + return repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult(); + } + + private Deployment getDeployment(String deploymentId) { + return repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult(); + } +} diff --git a/slife-activiti/src/main/java/com/slife/utils/DateConverter.java b/slife-activiti/src/main/java/com/slife/utils/DateConverter.java new file mode 100644 index 0000000..65aa0e1 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/utils/DateConverter.java @@ -0,0 +1,96 @@ +package com.slife.utils; + +import org.apache.commons.beanutils.Converter; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DateConverter implements Converter { + + private static final Logger logger = LoggerFactory.getLogger(DateConverter.class); + + private static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; + + private static final String DATETIME_PATTERN_NO_SECOND = "yyyy-MM-dd HH:mm"; + + private static final String DATE_PATTERN = "yyyy-MM-dd"; + + private static final String MONTH_PATTERN = "yyyy-MM"; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Object convert(Class type, Object value) { + Object result = null; + if (type == Date.class) { + try { + result = doConvertToDate(value); + } catch (ParseException e) { + e.printStackTrace(); + } + } else if (type == String.class) { + result = doConvertToString(value); + } + return result; + } + + /** + * Convert String to Date + * + * @param value + * @return + * @throws ParseException + */ + private Date doConvertToDate(Object value) throws ParseException { + Date result = null; + + if (value instanceof String) { + result = DateUtils.parseDate((String) value, new String[] { DATE_PATTERN, DATETIME_PATTERN, + DATETIME_PATTERN_NO_SECOND, MONTH_PATTERN }); + + // all patterns failed, try a milliseconds constructor + if (result == null && StringUtils.isNotEmpty((String) value)) { + + try { + result = new Date(new Long((String) value).longValue()); + } catch (Exception e) { + logger.error("Converting from milliseconds to Date fails!"); + e.printStackTrace(); + } + + } + + } else if (value instanceof Object[]) { + // let's try to convert the first element only + Object[] array = (Object[]) value; + + if (array.length >= 1) { + value = array[0]; + result = doConvertToDate(value); + } + + } else if (Date.class.isAssignableFrom(value.getClass())) { + result = (Date) value; + } + return result; + } + + /** + * Convert Date to String + * + * @param value + * @return + */ + private String doConvertToString(Object value) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATETIME_PATTERN); + String result = null; + if (value instanceof Date) { + result = simpleDateFormat.format(value); + } + return result; + } + +} \ No newline at end of file diff --git a/slife-activiti/src/main/java/com/slife/utils/ExtUtils.java b/slife-activiti/src/main/java/com/slife/utils/ExtUtils.java index 407c733..e9029de 100644 --- a/slife-activiti/src/main/java/com/slife/utils/ExtUtils.java +++ b/slife-activiti/src/main/java/com/slife/utils/ExtUtils.java @@ -1,5 +1,6 @@ package com.slife.utils; +import com.slife.dto.TaskDTO; import com.slife.entity.SysRole; import com.slife.entity.SysUser; import com.slife.enums.HttpCodeEnum; @@ -12,6 +13,8 @@ import org.activiti.engine.impl.persistence.entity.GroupEntity; import org.activiti.engine.impl.persistence.entity.UserEntity; import org.activiti.engine.repository.Deployment; import org.activiti.engine.repository.ProcessDefinition; +import org.activiti.engine.task.TaskInfo; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -113,21 +116,22 @@ public class ExtUtils { return dto; } -// public static TaskDTO toTaskDTO(TaskInfo task, String status, ProcessDefinition processDefinition, Deployment deployment) { -// TaskDTO dto = new TaskDTO(); -// dto.setTaskId(task.getId()); -// dto.setTaskName(task.getName()); -//// dto.setTime(historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult().getStartTime().getTime()); -// dto.setVariable(task.getTaskLocalVariables()); -// dto.setPdName(deployment.getName()); -// dto.setVersion("V:" + processDefinition.getVersion()); -// dto.setProcessInstanceId(task.getProcessInstanceId()); -// dto.setStatus(status); -// dto.setTitle((String) task.getProcessVariables().get("title")); -// dto.setNodeKey(task.getTaskDefinitionKey()); -// dto.setProcessDefKey(processDefinition.getKey()); -// return dto; -// } + public static TaskDTO toTaskDTO(TaskInfo task, String status, ProcessDefinition processDefinition, Deployment deployment) { + TaskDTO dto = new TaskDTO(); + dto.setTaskId(task.getId()); + dto.setTaskName(task.getName()); +// dto.setTime(historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult().getStartTime().getTime()); + dto.setVariable(task.getTaskLocalVariables()); + dto.setPdName(deployment.getName()); + dto.setVersion("V:" + processDefinition.getVersion()); + dto.setProcessInstanceId(task.getProcessInstanceId()); + dto.setStatus(status); + dto.setTitle((String) task.getProcessVariables().get("title")); + dto.setNodeKey(task.getTaskDefinitionKey()); + dto.setProcessDefKey(processDefinition.getKey()); + dto.setCategory(task.getCategory()); + return dto; + } // public static TaskListDTO toTaskListDTO(PageResultsDTO dto, String category) { // TaskListDTO taskList = new TaskListDTO(); diff --git a/slife-activiti/src/main/java/com/slife/utils/PropertyType.java b/slife-activiti/src/main/java/com/slife/utils/PropertyType.java new file mode 100644 index 0000000..93ac705 --- /dev/null +++ b/slife-activiti/src/main/java/com/slife/utils/PropertyType.java @@ -0,0 +1,25 @@ +package com.slife.utils; + +import java.util.Date; + +public enum PropertyType { + + S(String.class), + I(Integer.class), + L(Long.class), + F(Float.class), + N(Double.class), + D(Date.class), + SD(java.sql.Date.class), + B(Boolean.class); + + private Class clazz; + + private PropertyType(Class clazz) { + this.clazz = clazz; + } + + public Class getValue() { + return clazz; + } +} \ No newline at end of file diff --git a/slife-activiti/src/main/resources/static/model/model.js b/slife-activiti/src/main/resources/static/model/model.js deleted file mode 100644 index e92c9a0..0000000 --- a/slife-activiti/src/main/resources/static/model/model.js +++ /dev/null @@ -1,50 +0,0 @@ -function get_data(columns, sorts, uri) { - $('#exampleTable').bootstrapTable( - { - method: 'post', // 服务器数据的请求方式 get or post - url: url + uri, // 服务器数据的加载地址 - //showRefresh : true, - //showToggle : true, - //showColumns : true, - iconSize: 'outline', - toolbar: '#exampleToolbar', - striped: true, // 设置为true会有隔行变色效果 - dataType: "json", // 服务器返回的数据类型 - pagination: true, // 设置为true会在底部显示分页条 - // queryParamsType : "limit", - // //设置为limit则会发送符合RESTFull格式的参数 - singleSelect: false, // 设置为true将禁止多选 - // contentType : "application/x-www-form-urlencoded", - // //发送到服务器的数据编码类型 - pageSize: 10, // 如果设置了分页,每页数据条数 - pageNumber: 1, // 如果设置了分布,首页页码 - //search : true, // 是否显示搜索框 - //showColumns : true, // 是否显示内容下拉框(选择显示的列) - sidePagination: "server", // 设置在哪里进行分页,可选值为"client" 或者 - // "server" - queryParams: function (params) { - - var searchParams = {}; - $("#exampleToolbar ._search").each(function () { - searchParams[$(this).attr('name')] = $(this).val(); - }); - - var p = { - "pageNumber": this.pageNumber, - "pageSize": this.pageSize, - "searchParams": searchParams, - "sorts": sorts - - }; - return p; - - }, - // //请求服务器数据时,你可以通过重写参数的方式添加一些额外的参数,例如 toolbar 中的参数 如果 - // queryParamsType = 'limit' ,返回参数必须包含 - // limit, offset, search, sort, order 否则, 需要包含: - // pageSize, pageNumber, searchText, sortName, - // sortOrder. - // 返回false将会终止请求 - columns: columns - }); -} \ No newline at end of file diff --git a/slife-activiti/src/main/resources/static/model/process/processDefinition.js b/slife-activiti/src/main/resources/static/model/process/processDefinition.js index 68ce2b4..428d555 100644 --- a/slife-activiti/src/main/resources/static/model/process/processDefinition.js +++ b/slife-activiti/src/main/resources/static/model/process/processDefinition.js @@ -52,8 +52,7 @@ function getcolumns() { return c; } -// get_data(getcolumns(), {"revision": "desc"}, "list"); -load_data(getcolumns(), {"revision": "desc"}) +load_data_list(getcolumns(), {"revision": "desc"}, 'list') function dt_delete_button(row) { var deleteO = '' + value + ''; + } + }, + { + field: 'picName', + title: '流程图片', + align: 'center', + formatter: function (value, row, index) { + var procDefId = row.processonDefinitionId; + return '' + value + ''; + } + }, + { + field: 'deploymentTime', + title: '部署时间' + }, + { + title: '操作', + field: 'id', + align: 'center', + formatter: function (value, row, index) { + return dt_delete_button(row)+dt_active_button(row); + } + }]; + return c; +} + +load_data_list(getcolumns(), {"revision": "desc"}, 'list') + +function dt_delete_button(row) { + var deleteO = '删除 '; + + return deleteO; + +} + +function dt_active_button(row) { + var value = ''; + var status = ''; + if (row.suspend === false) { + value = '挂起'; + status = 'suspend'; + } else { + value = '激活' + status = 'active'; + } + var procDefId = row.processonDefinitionId; + var deleteO = ' ' + value + ' '; + return deleteO; +} + +function active(procDefId, state) { + $.ajax({ + url: url + "status", + type: "POST", + data: { + 'procDefId': procDefId, + 'status': state + }, + success: function (r) { + dataTable_rep_message(r) + } + }); +} \ No newline at end of file diff --git a/slife-activiti/src/main/resources/templates/process/processDefinitionList.ftl b/slife-activiti/src/main/resources/templates/process/processDefinitionList.ftl index 3096456..899cba3 100644 --- a/slife-activiti/src/main/resources/templates/process/processDefinitionList.ftl +++ b/slife-activiti/src/main/resources/templates/process/processDefinitionList.ftl @@ -37,7 +37,6 @@ -<#-- + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+ +
+
+ +
+ + +
+ +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/slife-activiti/src/main/resources/templates/task/todoList.ftl b/slife-activiti/src/main/resources/templates/task/todoList.ftl new file mode 100644 index 0000000..f8b29fe --- /dev/null +++ b/slife-activiti/src/main/resources/templates/task/todoList.ftl @@ -0,0 +1,141 @@ + + + + 模型管理 + + + + + + + + +
+
+
+
+ +
+ + + + + + + + + +
+ + +
+ +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/slife-web/src/main/java/com/slife/config/sitemesh/WebSiteMeshFilter.java b/slife-web/src/main/java/com/slife/config/sitemesh/WebSiteMeshFilter.java index efe8edc..4bbe8c0 100644 --- a/slife-web/src/main/java/com/slife/config/sitemesh/WebSiteMeshFilter.java +++ b/slife-web/src/main/java/com/slife/config/sitemesh/WebSiteMeshFilter.java @@ -15,7 +15,7 @@ public class WebSiteMeshFilter extends ConfigurableSiteMeshFilter { /** 需要装饰的访问路径 */ private String contentPath="/sys/user,/sys/menu,/sys/dict,/sys/role,/sys/office,/sys/,/index,/monitor/*," + - "/blog/content**,/404,/model,/process"; + "/blog/content**,/404,/model,/process,/task/**"; /** 装饰器页面路径 */ diff --git a/slife-web/src/main/resources/static/js/slife/datatable.js b/slife-web/src/main/resources/static/js/slife/datatable.js index f6982cf..9239e2d 100644 --- a/slife-web/src/main/resources/static/js/slife/datatable.js +++ b/slife-web/src/main/resources/static/js/slife/datatable.js @@ -126,10 +126,19 @@ function export_data(columns, sorts) { * @param columns */ function load_data(columns, sorts) { + load_data_list(columns, sorts, 'list') +} + +/** + * 加载表数据 + * @param url + * @param columns + */ +function load_data_list(columns, sorts, uri) { $('#exampleTable').bootstrapTable( { method: 'post', // 服务器数据的请求方式 get or post - url: url + "list", // 服务器数据的加载地址 + url: url + uri, // 服务器数据的加载地址 //showRefresh : true, //showToggle : true, //showColumns : true,