引入工作流程activiti 6.0

master
pengqiang 2021-11-09 15:26:08 +08:00
parent 1485897c9c
commit a64949e943
34 changed files with 4836 additions and 4 deletions

View File

@ -60,6 +60,18 @@ dependencies {
compile group: 'commons-lang', name: 'commons-lang', version: '2.6' compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
compile group: 'org.projectlombok', name: 'lombok', version: '1.16.20'
/*activiti start*/
compile group: 'org.activiti', name: 'activiti-spring-boot-starter-basic', version: '6.0.0'
/*activiti在线编辑器相关*/
compile group: 'org.activiti', name: 'activiti-json-converter', version: '6.0.0'
compile group: 'org.apache.xmlgraphics', name: 'batik-codec', version: '1.7'
/*activiti end*/
compileOnly 'org.springframework.boot:spring-boot-configuration-processor' compileOnly 'org.springframework.boot:spring-boot-configuration-processor'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'

View File

@ -0,0 +1,22 @@
package cn.palmte.work.config.activiti;
public class ActConstant {
/**
*
*/
public static final String START_PROCESS_USERID="startUserId";
public static final String PROC_INS_ID="procInsId";
/**
*
*/
public static final int TASK_INDEX_FIRST_USER_TASK= 1;
public static final int TYPE_APPROVE= 1;
public static final int TYPE_ROLLBACK= 2;
}

View File

@ -0,0 +1,39 @@
package cn.palmte.work.config.activiti;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class ActivitiConfig {
@Autowired
PlatformTransactionManager transactionManager;
@Autowired
DataSource dataSource;
@Bean
public SpringProcessEngineConfiguration getProcessEngineConfiguration() {
SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration();
config.setDataSource(dataSource);
config.setTransactionManager(transactionManager);
config.setDbHistoryUsed(true);
config.setHistory("full");
config.setActivityFontName("宋体");
//数据库更新策略
//flase 默认值。activiti在启动时会对比数据库表中保存的版本如果没有表或者版本不匹配将抛出异常。生产环境常用
//true activiti会对数据库中所有表进行更新操作。如果表不存在则自动创建。开发时常用
//create_drop 在activiti启动时创建表在关闭时删除表必须手动关闭引擎才能删除表单元测试常用
//drop-create 在activiti启动时删除原来的旧表然后在创建新表不需要手动关闭引擎
config.setDatabaseSchemaUpdate("true");
return config;
}
}

View File

@ -0,0 +1,82 @@
package cn.palmte.work.controller.backend;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.service.ActModelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*/
@Controller
@RequestMapping("/actModel")
public class ActModelController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ActModelController.class);
@Autowired
private ActModelService activitiModelService;
@RequestMapping("/list")
public String list(@RequestParam(value = "keywords", required = false) String keywords,
@RequestParam(value = PAGE_NUMBER, defaultValue = DEFAULT_PAGE_NUMBER) int pageNumber,
@RequestParam(value = PAGE_SIZE, defaultValue = DEFAULT_PAGE_SIZE) int pageSize,
Map<String, Object> model) {
ConcurrentHashMap<String, String> searchInfo = getSearchInfo(keywords, model);
model.put("pager", activitiModelService.list(searchInfo, pageNumber, pageSize));
return "/admin/act_model_list";
}
@GetMapping(value = "/add")
public String add(Map<String, Object> model) {
return "/admin/act_model_input";
}
@RequestMapping("/save")
public String save(HttpServletRequest request) {
try {
activitiModelService.createModel(request.getParameter("procDefKey"), request.getParameter("modelName"));
} catch (Exception e) {
logger.error("", e);
}
return "redirect:/actModel/list";
}
@ResponseBody
@GetMapping(value = "/delete")
public ResponseMsg delete(@RequestParam("ids") String ids) {
if ("".equals(ids)) {
return ResponseMsg.buildFailedMsg("删除失败,无选中项!");
} else {
String[] deleteIds = ids.split("#%#");
for (String id : deleteIds) {
activitiModelService.deleteModel(id);
}
return ResponseMsg.buildSuccessMsg("删除成功");
}
}
@ResponseBody
@GetMapping(value = "/deploy")
public ResponseMsg deploy(@RequestParam("id") String id) {
try {
activitiModelService.deploy(id);
} catch (Exception e) {
logger.error("", e);
return ResponseMsg.buildFailedMsg(e.getMessage());
}
return ResponseMsg.buildSuccessMsg("部署成功");
}
}

View File

@ -0,0 +1,132 @@
package cn.palmte.work.controller.backend;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
*
*/
@RestController
public class ActModelEditorController implements ModelDataJsonConstants {
protected static final Logger LOGGER = LoggerFactory.getLogger(ActModelEditorController.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
/**
*
*
* @return
*/
@ResponseBody
@RequestMapping(value = "/editor/stencilset", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
public String getStencilset() {
InputStream stencilsetStream = this.getClass().getClassLoader().getResourceAsStream("stencilset.json");
try {
return IOUtils.toString(stencilsetStream, "utf-8");
} catch (Exception e) {
//logger.error("an exception happens in try catch statement", e);
throw new ActivitiException("Error while loading stencil set", e);
} finally {
if (stencilsetStream != null) {
try {
stencilsetStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* idjson
*
* @param modelId
* @return
*/
@RequestMapping(value = "/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
public ObjectNode getEditorJson(@PathVariable String modelId) {
ObjectNode modelNode = null;
Model model = repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = objectMapper.createObjectNode();
modelNode.put(MODEL_NAME, model.getName());
}
modelNode.put(MODEL_ID, model.getId());
String content = new String(repositoryService.getModelEditorSource(model.getId()), "utf-8");
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(content);
modelNode.put("model", editorJsonNode);
} catch (Exception e) {
LOGGER.error("an exception happens in try catch statement", e);
throw new ActivitiException("Error creating model JSON", e);
}
}
return modelNode;
}
/**
*
*
* @param modelId
* @param values
*/
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping(value = "/model/{modelId}/save", method = RequestMethod.PUT)
public void saveModel(@PathVariable String modelId, @RequestParam MultiValueMap<String, String> values) {
try {
Model model = repositoryService.getModel(modelId);
ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
modelJson.put(MODEL_NAME, values.getFirst("name"));
modelJson.put(MODEL_DESCRIPTION, values.getFirst("description"));
model.setMetaInfo(modelJson.toString());
model.setName(values.getFirst("name"));
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream(values.getFirst("svg_xml").getBytes("utf-8"));
TranscoderInput input = new TranscoderInput(svgStream);
PNGTranscoder transcoder = new PNGTranscoder();
// Setup output
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);
// Do the transformation
transcoder.transcode(input, output);
final byte[] result = outStream.toByteArray();
repositoryService.addModelEditorSourceExtra(model.getId(), result);
outStream.close();
} catch (Exception e) {
LOGGER.error("an exception happens in try catch statement", e);
throw new ActivitiException("Error saving model", e);
}
}
}

View File

@ -0,0 +1,100 @@
package cn.palmte.work.controller.backend;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.service.ActProcDefService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*/
@Controller
@RequestMapping("/actProcDef")
public class ActProcDefController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ActProcDefController.class);
@Autowired
private ActProcDefService actProcDefService;
@RequestMapping("/list")
public String list(@RequestParam(value = "keywords", required = false) String keywords,
@RequestParam(value = PAGE_NUMBER, defaultValue = DEFAULT_PAGE_NUMBER) int pageNumber,
@RequestParam(value = PAGE_SIZE, defaultValue = DEFAULT_PAGE_SIZE) int pageSize,
Map<String, Object> model) {
ConcurrentHashMap<String, String> searchInfo = getSearchInfo(keywords, model);
model.put("pager", actProcDefService.list(searchInfo, pageNumber, pageSize));
return "/admin/act_proc_def_list";
}
/**
*
*
* @param response
* @param deploymentId
* @throws Exception
*/
@RequestMapping("/procDefPng/{deploymentId}")
public void png(HttpServletResponse response, @PathVariable("deploymentId") String deploymentId) throws Exception {
actProcDefService.createProcDefPng(response, deploymentId);
}
/**
* xml
*
* @param deploymentId
* @return
*/
@RequestMapping("/xml/{deploymentId}")
public void xml(HttpServletResponse response, @PathVariable("deploymentId") String deploymentId) throws Exception {
actProcDefService.getXmlByDeploymentId(response, deploymentId);
}
/**
*
*
* @param ids
* @return
*/
@ResponseBody
@GetMapping(value = "/delete")
public ResponseMsg delete(@RequestParam("ids") String ids) {
if ("".equals(ids)) {
return ResponseMsg.buildFailedMsg("删除失败,无选中项!");
} else {
String[] deleteIds = ids.split("#%#");
for (String id : deleteIds) {
actProcDefService.deleteDeployment(id);
}
return ResponseMsg.buildSuccessMsg("删除成功");
}
}
/**
*
*
* @param id
* @param status 1- 2-
* @return
*/
@ResponseBody
@GetMapping(value = "/suspend")
public String suspend(@RequestParam String id, @RequestParam int status) {
actProcDefService.suspend(id, status);
return "redirect:/actProcDef/list";
}
}

View File

@ -0,0 +1,92 @@
package cn.palmte.work.controller.backend;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.service.ActProcInsService;
import cn.palmte.work.utils.InterfaceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*/
@Controller
@RequestMapping("/actProcIns")
public class ActProcInsController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ActProcInsController.class);
@Autowired
private ActProcInsService actProcInsService;
/**
*
* @param keywords
* @param pageNumber
* @param pageSize
* @param model
* @return
*/
@RequestMapping("/list")
public String list(@RequestParam(value = "keywords", required = false) String keywords,
@RequestParam(value = PAGE_NUMBER, defaultValue = DEFAULT_PAGE_NUMBER) int pageNumber,
@RequestParam(value = PAGE_SIZE, defaultValue = DEFAULT_PAGE_SIZE) int pageSize,
Map<String, Object> model) {
ConcurrentHashMap<String, String> searchInfo = getSearchInfo(keywords, model);
model.put("pager", actProcInsService.list(searchInfo, pageNumber, pageSize));
return "/admin/act_proc_ins_list";
}
/**
*
*
* @param procInsId
* @param reason
* @return
*/
@ResponseBody
@GetMapping(value = "/deleteProcessInstance")
public ResponseMsg deleteProcessInstance(@RequestParam String procInsId, @RequestParam String reason) {
actProcInsService.deleteProcessInstance(procInsId, reason);
return ResponseMsg.buildSuccessMsg("撤销成功");
}
/**
*
*
* @param response
* @param procInstId
* @throws Exception
*/
@RequestMapping("/procInsPng/{procInstId}")
public void png(HttpServletResponse response, @PathVariable("procInstId") String procInstId) throws Exception {
actProcInsService.createProcInsPng(response, procInstId);
}
/**
*
*
* @return
*/
@ResponseBody
@GetMapping(value = "/startProcIns")
public ResponseMsg startProcessInstance(@RequestParam String procDefKey) throws Exception{
Map<String, Object> variables = new HashMap<>();
variables.put(ActConstant.START_PROCESS_USERID, InterfaceUtil.getAdminId());
String procInsId = actProcInsService.startProcessInstance(procDefKey, variables);
return ResponseMsg.buildSuccessMsg("流程启动成功", procInsId);
}
}

View File

@ -0,0 +1,119 @@
package cn.palmte.work.controller.backend;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.model.ActScript;
import cn.palmte.work.model.ActScriptRepository;
import cn.palmte.work.service.ActScriptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
*
*/
@Controller
@RequestMapping("/actScript")
public class ActScriptController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ActScriptController.class);
@Autowired
private ActScriptService actScriptService;
@Autowired
private ActScriptRepository actScriptRepository;
@RequestMapping("/list")
public String list(@RequestParam(value = "keywords", required = false) String keywords,
@RequestParam(value = PAGE_NUMBER, defaultValue = DEFAULT_PAGE_NUMBER) int pageNumber,
@RequestParam(value = PAGE_SIZE, defaultValue = DEFAULT_PAGE_SIZE) int pageSize,
Map<String, Object> model) {
ConcurrentHashMap<String, String> searchInfo = getSearchInfo(keywords, model);
model.put("pager", actScriptService.list(searchInfo, pageNumber, pageSize));
return "/admin/act_script_list";
}
@GetMapping(value = "/add")
public String add(Map<String, Object> model) {
List<String> list = getScriptList();
model.put("actScript", new ActScript());
model.put("classList", list);
List<String> methodList = new ArrayList<>();
for (String l : list) {
methodList.addAll(getMethodList(l));
}
model.put("methodList", methodList);
return "/admin/act_script_input";
}
private List<String> getScriptList() {
List<String> list = new ArrayList<>(1);
list.add("cn.palmte.work.service.ActCallbackScript");
return list;
}
private List<String> getMethodList(String className) {
List<String> list = new ArrayList<>();
try {
Method[] methods = Class.forName(className).getDeclaredMethods();
for (Method method : methods) {
list.add(method.getName());
}
} catch (ClassNotFoundException e) {
logger.error("", e);
}
return list;
}
@GetMapping(value = "/edit")
public String edit(@RequestParam int id, Map<String, Object> model) {
List<String> list = getScriptList();
model.put("actScript", actScriptRepository.findOne(id));
model.put("classList", list);
List<String> methodList = new ArrayList<>();
for (String l : list) {
methodList.addAll(getMethodList(l));
}
model.put("methodList", methodList);
return "/admin/act_script_input";
}
@RequestMapping("/save")
public String save(ActScript actScript) {
actScriptService.save(actScript);
return "redirect:/actScript/list";
}
@ResponseBody
@GetMapping(value = "/delete")
public ResponseMsg delete(@RequestParam("ids") String ids){
if ("".equals(ids)){
return ResponseMsg.buildFailedMsg("删除失败,无选中项!");
}else {
String[] deleteIds=ids.split("#%#");
for (String id : deleteIds) {
actScriptService.delete(Integer.parseInt(id));
}
return ResponseMsg.buildSuccessMsg("删除成功");
}
}
}

View File

@ -0,0 +1,91 @@
package cn.palmte.work.controller.backend;
import cn.palmte.work.bean.ResponseMsg;
import cn.palmte.work.model.ActScriptRepository;
import cn.palmte.work.model.ActTaskDef;
import cn.palmte.work.model.AdminRepository;
import cn.palmte.work.service.ActTaskDefService;
import cn.palmte.work.service.SysRoleService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
*
*/
@Controller
@RequestMapping("/actTaskDef")
public class ActTaskDefController extends BaseController {
private static final Logger logger = LoggerFactory.getLogger(ActTaskDefController.class);
@Autowired
private AdminRepository adminRepository;
@Autowired
private SysRoleService sysRoleService;
@Autowired
private ActScriptRepository actScriptRepository;
@Autowired
private ActTaskDefService actTaskDefService;
/**
*
*
* @param procDefId
* @param model
* @return
*/
@RequestMapping("/config/{procDefId}")
public String list(@PathVariable String procDefId, Map<String, Object> model) {
List<ActTaskDef> list = actTaskDefService.findByProcDefId(procDefId);
model.put("procDefId", procDefId);
model.put("taskList", list);
model.put("procDefName", list.get(0).getProcDefName());
model.put("roleList", sysRoleService.getAllEnableSysRole());
model.put("adminList", adminRepository.getAllEnable());
model.put("scriptList", actScriptRepository.findAll());
return "/admin/act_task_def";
}
/**
*
*
* @param taskDef
* @return
*/
@ResponseBody
@GetMapping(value = "/saveConfig")
public ResponseMsg saveConfig(ActTaskDef taskDef) {
actTaskDefService.saveConfig(taskDef);
return ResponseMsg.buildSuccessMsg("成功");
}
/**
*
*
*
* @return
*/
@ResponseBody
@PostMapping(value = "/completeTask")
public ResponseMsg completeTask(@RequestBody String json) {
JSONObject jsonParam = JSON.parseObject(json);
actTaskDefService.completeTask(jsonParam);
return ResponseMsg.buildSuccessMsg("处理成功");
}
}

View File

@ -0,0 +1,41 @@
package cn.palmte.work.model;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
*
*/
@Data
@Entity
@Table(name = "act_script")
public class ActScript {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(name = "script_name")
private String scriptName;
/**
*
*/
@Column(name = "class_name")
private String className;
/**
*
*/
@Column(name = "class_method")
private String classMethod;
@Column(name = "created_time")
private Date createdTime;
@Column(name = "last_updated_time")
private Date lastUpdatedTime;
}

View File

@ -0,0 +1,8 @@
package cn.palmte.work.model;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ActScriptRepository extends JpaRepository<ActScript, Integer> {
}

View File

@ -0,0 +1,97 @@
package cn.palmte.work.model;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
/**
*
*/
@Data
@Entity
@Table(name = "act_task_def")
public class ActTaskDef {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
/**
*
*/
@Column(name = "task_name")
private String taskName;
@Column(name = "task_key")
private String taskKey;
/**
* 0- 1-
*/
@Column(name = "task_type")
private int taskType;
@Column(name = "proc_def_id")
private String procDefId;
@Column(name = "proc_def_name")
private String procDefName;
@Column(name = "proc_def_key")
private String procDefKey;
/**
* 退key
*/
@Column(name = "rollback_task_key")
private String rollbackTaskKey;
/**
* -1 0- 1-
*/
@Column(name = "task_index")
private int taskIndex;
/**
*
*/
@Column(name = "candidate_users")
private String candidateUsers;
/**
*
*/
@Column(name = "candidate_roles")
private String candidateRoles;
/**
* act_scriptid
*/
@Column(name = "end_script")
private int endScript;
/**
* act_scriptid
*/
@Column(name = "rollback_script")
private int rollbackScript;
@Column(name = "created_time")
private Date createdTime;
@Column(name = "last_updated_time")
private Date lastUpdatedTime;
@Transient
private List<String> candidateUserList;
@Transient
private List<String> candidateRoleList;
}

View File

@ -0,0 +1,16 @@
package cn.palmte.work.model;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ActTaskDefRepository extends JpaRepository<ActTaskDef, Integer> {
List<ActTaskDef> findByProcDefId(String procDefId);
ActTaskDef findFirstByProcDefIdAndTaskKey(String procDefId, String taskKey);
void deleteByProcDefId(String procDefId);
}

View File

@ -34,4 +34,13 @@ public interface AdminRepository extends JpaRepository<Admin, Integer> {
@Query("from Admin where isDeleted=0 AND telephone=?1") @Query("from Admin where isDeleted=0 AND telephone=?1")
Admin findByTelephone(String phone); Admin findByTelephone(String phone);
/**
*
* @return
*/
@Query("from Admin where isDeleted=0 AND enabled=1")
List<Admin> getAllEnable();
} }

View File

@ -0,0 +1,21 @@
package cn.palmte.work.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class ActModel {
private int id;
private String modelName;
private int rev;
private String procDefKey;
private Date createdTime;
private Date lastUpdatedTime;
}

View File

@ -0,0 +1,19 @@
package cn.palmte.work.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class ActProcDef {
private String id;
private String procName;
private String procKey;
private String version;
private String deploymentId;
private String resourceName;
private String dgrmResourceName;
private Date deployTime;
private int suspensionState;
}

View File

@ -0,0 +1,23 @@
package cn.palmte.work.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class ActProcIns {
private String procInsId;
private String procName;
private String procKey;
private String version;
private String user;
private Date startTime;
private String currentTask;
private String currentTaskId;
private String candidateUsers;
private Date endTime;
}

View File

@ -17,15 +17,15 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import top.jfunc.common.db.QueryHelper; import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page; import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.bean.Record;
import top.jfunc.common.db.utils.Pagination; import top.jfunc.common.db.utils.Pagination;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.beans.Transient; import java.beans.Transient;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.*;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/** /**
* Created by wang.lin@esstx.cn on 2018/4/20. * Created by wang.lin@esstx.cn on 2018/4/20.
@ -260,4 +260,38 @@ public class AccountService {
return null; return null;
} }
/**
* id
* @param id
* @return
*/
public String getNameById(int id) {
Admin one = adminRepository.findOne(id);
return one == null ? "" : one.getRealName();
}
/**
* id
* @param roleIds
* @return
*/
public List<String> getUserIsByRole(List<String> roleIds) {
if (roleIds == null || roleIds.isEmpty()) {
return new ArrayList<>();
}
String sql = "select u.id as id from sys_user_role ur left join sys_user u on u.id=ur.user_id where ur.role_id in (?)";
String ids = roleIds.stream().collect(Collectors.joining());
List<Record> records = pagination.find(sql, ids);
if (records == null || records.isEmpty()) {
return new ArrayList<>();
}
List<String> userIds = new ArrayList<>(roleIds.size());
for (Record record : records) {
userIds.add(record.getInt("id") + "");
}
return userIds;
}
} }

View File

@ -0,0 +1,35 @@
package cn.palmte.work.service;
import cn.palmte.work.config.activiti.ActConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.util.*;
/**
* Map
*/
@Service
public class ActCallbackScript {
private static final Logger logger = LoggerFactory.getLogger(ActCallbackScript.class);
public void endScriptDemo(Map map) {
logger.info("--- endScriptDemo--- : {} ", map);
String startUserId = (String)map.get(ActConstant.START_PROCESS_USERID);
String procInsId = (String)map.get(ActConstant.START_PROCESS_USERID);
logger.info(" startUserId:{}, procInsId:{}", startUserId, procInsId);
}
public void rollbackScriptDemo(Map map) {
logger.info("--- rollbackScriptDemo--- : {} ", map);
String startUserId = (String)map.get(ActConstant.START_PROCESS_USERID);
String procInsId = (String)map.get(ActConstant.START_PROCESS_USERID);
logger.info(" startUserId:{}, procInsId:{}", startUserId, procInsId);
}
}

View File

@ -0,0 +1,39 @@
package cn.palmte.work.service;
import com.alibaba.fastjson.JSONObject;
import org.activiti.engine.delegate.DelegateTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
/**
*
*/
@Service
public class ActListenerService {
private static final Logger logger = LoggerFactory.getLogger(ActListenerService.class);
@Autowired
private ActTaskDefService actTaskDefService;
public void create(DelegateTask delegateTask) throws Exception {
logger.info("--- {}", JSONObject.toJSONString(delegateTask));
// 每一个任务节点监听运行时都要初始化流程定义规则数据到 流程引擎中
String procDefId = delegateTask.getProcessDefinitionId();
String procInsId = delegateTask.getProcessInstanceId();
String taskDefKey = delegateTask.getTaskDefinitionKey();
Set<String> candidateUsers = actTaskDefService.findCandidateUsers(procDefId, procInsId, taskDefKey);
logger.info("addCandidateUsers : {}", candidateUsers);
delegateTask.addCandidateUsers(candidateUsers);
}
}

View File

@ -0,0 +1,176 @@
package cn.palmte.work.service;
import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.model.ActTaskDef;
import cn.palmte.work.model.ActTaskDefRepository;
import cn.palmte.work.pojo.ActModel;
import cn.palmte.work.utils.InterfaceUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.utils.Pagination;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class ActModelService {
private static final Logger logger = LoggerFactory.getLogger(ActModelService.class);
@Autowired
private RepositoryService repositoryService; //管理流程定义 与流程定义和部署对象相关的Service
@Autowired
private ActTaskDefRepository actTaskDefRepository;
@Autowired
Pagination pagination;
public Page<ActModel> list(ConcurrentHashMap<String, String> searchInfo, int pageNumber, int pageSize) {
String select = "a.ID_ as id,a.REV_ as rev,a.NAME_ as modelName,a.KEY_ as procDefKey,a.CREATE_TIME_ as createdTime,a.LAST_UPDATE_TIME_ as lastUpdatedTime";
QueryHelper queryHelper = new QueryHelper(select, " act_re_model a");
String name = searchInfo.get("name");
queryHelper.addCondition(StringUtils.isNotEmpty(name), "a.NAME_=? or a.KEY_=?", name, name);
queryHelper.addOrderProperty("a.LAST_UPDATE_TIME_", false);
return pagination.paginate(queryHelper.getSql(), ActModel.class, pageNumber, pageSize);
}
public void createModel(String processId, String modelName) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvs");
editorNode.put("resourceId", "canvs");
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#"); //命名空间(禁止修改)
stencilSetNode.put("author", ""); //流程节点作者
editorNode.set("stencilset", stencilSetNode);
ObjectNode propertiesNode = objectMapper.createObjectNode();
propertiesNode.put("process_id", processId); //流程唯一标识
propertiesNode.put("process_author", InterfaceUtil.getAdmin().getUserName()); //流程作者
propertiesNode.put("name", modelName); //流程名称
editorNode.set("properties", propertiesNode);
ObjectNode modelObjectNode = objectMapper.createObjectNode();
modelObjectNode.put("name", modelName); //模型名称
modelObjectNode.put("revision", 1); //模型版本
modelObjectNode.put("description", ""); //模型描述
Model modelData = repositoryService.newModel();
//modelData.setCategory(category); //模型分类
modelData.setDeploymentId(null);
modelData.setKey(processId);
modelData.setMetaInfo(modelObjectNode.toString());
modelData.setName(modelName); //模型名称
modelData.setTenantId("");
modelData.setVersion(1);
//保存模型,存储数据到表act_re_model 流程设计模型部署表
repositoryService.saveModel(modelData);
repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));//保存资源,存储数据到表act_ge_bytearray 二进制数据表
}
/**
*
* act_re_model act_ge_bytearray
*
* @param modelId
*/
public void deleteModel(String modelId) {
repositoryService.deleteModel(modelId);
}
/**
*
*
* @param modelId
* @throws Exception
*/
public void deploy(String modelId) throws Exception {
Model modelData = repositoryService.getModel(modelId);
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
BpmnModel model = jsonConverter.convertToBpmnModel(modelNode);
ActivitiListener activitiListener = new ActivitiListener();
activitiListener.setEvent(TaskListener.EVENTNAME_CREATE);
activitiListener.setImplementation("${actListenerService.create(task)}");
activitiListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_EXPRESSION);
List<ActivitiListener> activitiListenerList = new ArrayList<>(1);
activitiListenerList.add(activitiListener);
List<ActTaskDef> taskList = new ArrayList<>();
Process process = model.getMainProcess();
Collection<FlowElement> flowElements = process.getFlowElements();
int i = 0;
ActTaskDef task;
ActTaskDef first = null;
for (FlowElement element : flowElements) {
if (element instanceof UserTask) {
UserTask userTask = (UserTask) element;
userTask.setTaskListeners(activitiListenerList);
task = new ActTaskDef();
task.setTaskName(element.getName());
task.setTaskKey(element.getId());
MultiInstanceLoopCharacteristics loopCharacteristics = ((UserTask) element).getLoopCharacteristics();
if (loopCharacteristics != null) {
task.setTaskType(1);
}
if (i == 1) {
task.setTaskIndex(ActConstant.TASK_INDEX_FIRST_USER_TASK);
first = task;
}
taskList.add(task);
}
i ++;
}
bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName() + ".bpmn20.xml";
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment().name(modelData.getName()); //部署名称
deploymentBuilder.addString(processName, new String(bpmnBytes, "utf-8"));
Deployment deployment = deploymentBuilder.deploy(); //完成部署
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
for (ActTaskDef actTaskDef : taskList) {
if (first != null) {
actTaskDef.setRollbackTaskKey(first.getTaskKey());
}
actTaskDef.setProcDefId(processDefinition.getId());
actTaskDef.setProcDefName(processDefinition.getName());
actTaskDef.setProcDefKey(processDefinition.getKey());
actTaskDef.setCreatedTime(new Date());
actTaskDef.setLastUpdatedTime(new Date());
}
actTaskDefRepository.save(taskList);
logger.info("deploy success: deploymentId:{}, procDefName:{}, procDefKey:{}", deployment.getId(), processDefinition.getName(), processDefinition.getKey());
}
}

View File

@ -0,0 +1,145 @@
package cn.palmte.work.service;
import cn.palmte.work.model.ActTaskDefRepository;
import cn.palmte.work.pojo.ActProcDef;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.utils.Pagination;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class ActProcDefService {
private static final Logger logger = LoggerFactory.getLogger(ActProcDefService.class);
@Autowired
private ProcessEngine processEngine; //流程引擎对象
@Autowired
private RepositoryService repositoryService; //管理流程定义 与流程定义和部署对象相关的Service
@Autowired
private ActTaskDefRepository actTaskDefRepository;
@Autowired
Pagination pagination;
public Page<ActProcDef> list(ConcurrentHashMap<String, String> searchInfo, int pageNumber, int pageSize) {
String select = "select p.ID_ as id,p.NAME_ as procName,p.KEY_ as procKey,p.VERSION_ as version,p.DEPLOYMENT_ID_ as deploymentId,p.RESOURCE_NAME_ as resourceName,\n" +
" p.DGRM_RESOURCE_NAME_ as dgrmResourceName,p.SUSPENSION_STATE_ as suspensionState, d.DEPLOY_TIME_ as deployTime ";
QueryHelper queryHelper = new QueryHelper(select, " act_re_procdef p LEFT JOIN act_re_deployment d on p.DEPLOYMENT_ID_ = d.ID_");
String name = searchInfo.get("name");
queryHelper.addCondition(StringUtils.isNotEmpty(name), "p.NAME_=? or p.KEY_=?", name, name);
queryHelper.addOrderProperty("p.KEY_,p.VERSION_", false);
return pagination.paginate(queryHelper.getSql(), ActProcDef.class, pageNumber, pageSize);
}
public void getXmlByDeploymentId(HttpServletResponse response, String deploymentId) throws IOException {
InputStream pic=null;
try {
pic= getXmlStreamByDeploymentId(deploymentId);
byte[] b = new byte[1024];
int len = -1;
while ((len = pic.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}catch (Exception e){
logger.error("an exception happens in try catch statement", e);
}finally {
if(pic!=null) {
pic.close();
}
}
}
public InputStream getXmlStreamByDeploymentId(String deploymentId) throws IOException{
List<String> names = repositoryService.getDeploymentResourceNames(deploymentId);
for (String name : names) {
if(name.contains("xml") ) {
return repositoryService.getResourceAsStream(deploymentId, name);
}
}
return null;
}
/**
* png
*
* @param response
* @param deploymentId
* @throws IOException
*/
public void createProcDefPng(HttpServletResponse response, String deploymentId) throws IOException {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); // 获取bpmnModel
InputStream inputStream = generateDiagramInputStream(bpmnModel, new ArrayList<>(), new ArrayList<>());
responsePng(response, inputStream);
}
public void responsePng(HttpServletResponse response, InputStream inputStream) throws IOException {
InputStream pic = null;
try {
pic = inputStream;
byte[] b = new byte[1024];
int len = -1;
while ((len = pic.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
} catch (Exception e) {
logger.error("an exception happens in try catch statement", e);
} finally {
if (pic != null) {
pic.close();
}
}
}
public InputStream generateDiagramInputStream(BpmnModel bpmnModel, List<String> executedActivityIdList, List<String> flowIds) {
try {
ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
return processDiagramGenerator.generateDiagram(bpmnModel, "png", executedActivityIdList,
flowIds, "宋体", "微软雅黑", "黑体", null, 2.0); //使用默认配置获得流程图表生成器,并生成追踪图片字符流
} catch (Exception e) {
logger.error("an exception happens in try catch statement", e);
return null;
}
}
@Transactional(rollbackFor = Exception.class)
public void deleteDeployment(String deploymentId) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
actTaskDefRepository.deleteByProcDefId(processDefinition.getId());
//repositoryService.deleteDeployment(deploymentId); //不带级联的删除,此删除只能删除没有启动的流程,否则抛出异常 .act_re_deploymentact_re_procdef 和 act_ge_bytearray 三张表中相关数据都删除
repositoryService.deleteDeployment(deploymentId, true); //级联删除,不管流程是否启动,都可以删除
}
public void suspend(String procDefId, int status) {
if (1 == status) {
repositoryService.activateProcessDefinitionById(procDefId, true, null);
}else{
repositoryService.suspendProcessDefinitionById(procDefId, true, null);
}
}
}

View File

@ -0,0 +1,299 @@
package cn.palmte.work.service;
import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.exception.ResponseException;
import cn.palmte.work.pojo.ActProcIns;
import cn.palmte.work.utils.InterfaceUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.bean.Record;
import top.jfunc.common.db.utils.Pagination;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class ActProcInsService {
private static final Logger logger = LoggerFactory.getLogger(ActProcInsService.class);
@Autowired
private RepositoryService repositoryService; //管理流程定义 与流程定义和部署对象相关的Service
@Autowired
private TaskService taskService; //任务管理 与正在执行的任务管理相关的Service
@Autowired
private AccountService accountService;
@Autowired
private RuntimeService runtimeService; //与正在执行的流程实例和执行对象相关的Service(执行管理,包括启动、推进、删除流程实例等操作)
@Autowired
Pagination pagination;
@Autowired
private ActProcDefService actProcDefService;
@Autowired
private HistoryService historyService; //历史管理(执行完的数据的管理)
public Page<ActProcIns> list(ConcurrentHashMap<String, String> searchInfo, int pageNumber, int pageSize) {
String select = "select h.proc_inst_id_ as procInsId,h.proc_def_id_ as procDefId," +
"h.start_time_ as startTime,h.end_time_ as endTime,p.key_ as procKey," +
"p.name_ as procName,p.NAME_ as dgrmResourceName,p.version_ as version, GROUP_CONCAT(t.NAME_) as currentTask, GROUP_CONCAT(t.ID_) as currentTaskId";
QueryHelper queryHelper = new QueryHelper(select, " act_hi_procinst h " +
"left join ACT_RE_PROCDEF p on h.PROC_DEF_ID_ =p.ID_ " +
"LEFT JOIN act_ru_task t on t.PROC_INST_ID_=h.proc_inst_id_ ");
queryHelper.addGroupProperty("h.PROC_INST_ID_");
queryHelper.addOrderProperty("h.start_time_", false);
Page<ActProcIns> paginate = pagination.paginate(queryHelper.getSql(), ActProcIns.class, pageNumber, pageSize);
List<ActProcIns> list = paginate.getList();
for (ActProcIns ins : list) {
//查询流程发起人
Record record = getVariable(ActConstant.START_PROCESS_USERID, ins.getProcInsId());
if (record != null) {
String userId = getStartUserId(ins.getProcInsId());
ins.setUser(accountService.getNameById(Integer.parseInt(userId)));
}
//查询当前任务审批人
String currentTaskId = ins.getCurrentTaskId();
if (StringUtils.isNotBlank(currentTaskId)) {
String[] split = currentTaskId.split(",");
String candidateUsers = "";
for (String taskId : split) {
List<IdentityLink> identityLinksForTask = taskService.getIdentityLinksForTask(taskId);
for (IdentityLink identityLink : identityLinksForTask) {
if ("assignee".equals(identityLink.getType()) || "candidate".equals(identityLink.getType())) {
String userId = identityLink.getUserId();
if (StringUtils.isNotBlank(candidateUsers)) {
candidateUsers = candidateUsers + ",";
}
candidateUsers += accountService.getNameById(Integer.parseInt(userId));
}
}
}
ins.setCandidateUsers(candidateUsers);
}
}
return paginate;
}
/**
*
*
* @param variableName
* @param procInsId
* @return
*/
public Record getVariable(String variableName, String procInsId) {
String sql = "select TEXT_ as text from ACT_HI_VARINST where NAME_=? and PROC_INST_ID_=?";
return pagination.findFirst(sql, variableName, procInsId);
}
public String getStartUserId(String procInsId) {
Record record = getVariable(ActConstant.START_PROCESS_USERID, procInsId);
if (record != null) {
return record.getStr("text");
}
return "0";
}
public List<Record> getVariables(String procInsId) {
String sql = "select NAME_ as name, TEXT_ as text from ACT_HI_VARINST where PROC_INST_ID_=?";
return pagination.find(sql, procInsId);
}
public void deleteProcessInstance(String procInsId, String reason) {
runtimeService.deleteProcessInstance(procInsId, reason); //作废流程
}
public void createProcInsPng(HttpServletResponse response, String procInsId) throws IOException {
try {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procInsId).singleResult(); //获取历史流程实例
//获取流程中已经执行的节点,按照执行先后顺序排序
List<HistoricActivityInstance> hai = historyService.createHistoricActivityInstanceQuery().processInstanceId(procInsId).orderByHistoricActivityInstanceStartTime().asc().list();
// 历史流程节点中
List<HistoricActivityInstance> newHisActInstanceList = new ArrayList<HistoricActivityInstance>();
List<HistoricActivityInstance> newHisTaskInstanceList = new ArrayList<HistoricActivityInstance>();
if (hai != null && hai.size() > 0) {
for (int i = 0; i < hai.size(); i++) {
HistoricActivityInstance historicActivityInstance = hai.get(i);
String activityType = historicActivityInstance.getActivityType();
if (activityType.equals("startEvent") || activityType.equals("endEvent")) {
newHisActInstanceList.add(historicActivityInstance);
} else if (activityType.equals("serviceTask") || activityType.equals("userTask") || activityType.equals("exclusiveGateway") || activityType.equals("parallelGateway")) {
if (newHisTaskInstanceList.size() > 0) {
for (int j = 0; j < newHisTaskInstanceList.size(); j++) {
HistoricActivityInstance historicTaskInstance = newHisTaskInstanceList.get(j);
if (historicTaskInstance.getActivityId().equals(historicActivityInstance.getActivityId())) { //如果列表中已包括
newHisTaskInstanceList.clear();
newHisTaskInstanceList.add(historicActivityInstance);
break;
} else {
newHisTaskInstanceList.add(historicActivityInstance);
break;
}
}
} else {
newHisTaskInstanceList.add(historicActivityInstance);
}
}
}
}
for (int i = 0; i < newHisActInstanceList.size(); i++) {
HistoricActivityInstance historicActivityInstance = newHisActInstanceList.get(i);
newHisTaskInstanceList.add(historicActivityInstance);
}
List<String> executedActivityIdList = new ArrayList<String>(); // 构造已执行的节点ID集合
for (HistoricActivityInstance activityInstance : newHisTaskInstanceList) {
executedActivityIdList.add(activityInstance.getActivityId());
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId()); // 获取bpmnModel
List<String> flowIds = this.getExecutedFlows(bpmnModel, newHisTaskInstanceList); // 获取流程已发生流转的线ID集合
InputStream inputStream = actProcDefService.generateDiagramInputStream(bpmnModel, executedActivityIdList, flowIds);
actProcDefService.responsePng(response, inputStream);
} catch (Exception e) {
logger.error("an exception happens in try catch statement", e);
}
}
public List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
List<String> flowIdList = new ArrayList<String>(); //流转线ID集合
List<FlowNode> historicFlowNodeList = new LinkedList<FlowNode>(); //全部活动实例
List<HistoricActivityInstance> finishedActivityInstanceList = new LinkedList<HistoricActivityInstance>(); //已完成的历史活动节点
List list = new ArrayList();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
historicFlowNodeList.add((FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true));
if (historicActivityInstance.getEndTime() != null) {
finishedActivityInstanceList.add(historicActivityInstance);
}
}
for (int x = 0; x < historicActivityInstances.size(); x++) {
HistoricActivityInstance historicActivityInstance = historicActivityInstances.get(x);
String activityType = historicActivityInstance.getActivityType();
String activityId = historicActivityInstance.getActivityId();
if (!list.contains(activityId) && ("userTask".equals(activityType)
|| "serviceTask".equals(activityType)
|| "endEvent".equals(activityType)
|| "exclusiveGateway".equals(activityType)
|| "parallelGateway".equals(activityType))) {
list.add(activityId);
}
}
/**遍历已完成的活动实例从每个实例的outgoingFlows中找到已执行的*/
FlowNode currentFlowNode = null;
for (HistoricActivityInstance currentActivityInstance : finishedActivityInstanceList) {
/**获得当前活动对应的节点信息及outgoingFlows信息*/
currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
List<SequenceFlow> sequenceFlowList = currentFlowNode.getOutgoingFlows();
/**
* outgoingFlows
*
* 1.outgoingFlows
* 2.outgoingFlows
*/
FlowNode targetFlowNode = null;
if ("parallelGateway".equals(currentActivityInstance.getActivityType())
|| "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
for (SequenceFlow sequenceFlow : sequenceFlowList) { //遍历历史活动节点找到匹配Flow目标节点的
targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
if (historicFlowNodeList.contains(targetFlowNode)) {
flowIdList.add(sequenceFlow.getId());
}
}
} else {
List<Map<String, String>> tempMapList = new LinkedList<Map<String, String>>();
// for(SequenceFlow sequenceFlow : sequenceFlowList) { //遍历历史活动节点找到匹配Flow目标节点的
for (int i = 0; i < sequenceFlowList.size(); i++) { //遍历历史活动节点找到匹配Flow目标节点的
SequenceFlow sequenceFlow = sequenceFlowList.get(i);
int taskSeq = list.indexOf(sequenceFlow.getSourceRef()); // 获取当前flow目标节点key在审批顺序
String nextTaskKey = ""; // 下一个任务节点
String beforeTaskKey = sequenceFlow.getSourceRef(); //上一个任务节点
if ((taskSeq + 1) < list.size()) { // 判断下一个任务节点是否存在
nextTaskKey = String.valueOf(list.get((taskSeq + 1)));
}
if (taskSeq == list.size() - 1) {
nextTaskKey = String.valueOf(list.get((taskSeq)));
}
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef()) && sequenceFlow.getSourceRef().equals(beforeTaskKey) && sequenceFlow.getTargetRef().equals(nextTaskKey)) {
Map<String, String> map = new HashMap<>();
map.put("flowId", sequenceFlow.getId());
map.put("activityStartTime", String.valueOf(historicActivityInstance.getStartTime().getTime()));
tempMapList.add(map);
}
}
}
String flowId = null;
for (Map<String, String> map : tempMapList) {
flowId = map.get("flowId");
flowIdList.add(flowId);
}
}
}
return flowIdList;
}
public String startProcessInstance(String procDefKey, Map<String, Object> variables) {
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(procDefKey).active().orderByProcessDefinitionVersion().desc().list();
if (list == null || list.isEmpty()) {
throw new ResponseException("procDefKey" + procDefKey + " 未定义");
}
//取最新版本的流程定义进行启动流程实列
ProcessDefinition processDefinition = list.get(0);
//启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId(), variables);
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult();
if (task == null) {
throw new ResponseException("procDefKey" + procDefKey + " 启动异常");
}
//设置发起人为办理人 然后完成任务 任务转入下一个审批节点
task.setAssignee(InterfaceUtil.getAdminId() + "");
taskService.complete(task.getId());
return processInstance.getId();
}
}

View File

@ -0,0 +1,55 @@
package cn.palmte.work.service;
import cn.palmte.work.model.ActScript;
import cn.palmte.work.model.ActScriptRepository;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.QueryHelper;
import top.jfunc.common.db.bean.Page;
import top.jfunc.common.db.utils.Pagination;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class ActScriptService {
private static final Logger logger = LoggerFactory.getLogger(ActScriptService.class);
@Autowired
private ActScriptRepository actScriptRepository;
@Autowired
Pagination pagination;
public Page<ActScript> list(ConcurrentHashMap<String, String> searchInfo, int pageNumber, int pageSize) {
QueryHelper queryHelper = new QueryHelper("a.*", " act_script a");
String name = searchInfo.get("name");
queryHelper.addCondition(StringUtils.isNotEmpty(name), "a.script_name=? ", name);
queryHelper.addOrderProperty("a.last_updated_time", false);
return pagination.paginate(queryHelper.getSql(), ActScript.class, pageNumber, pageSize);
}
public void save(ActScript actScript) {
Date now = new Date();
int id = actScript.getId();
if (id == 0) {
actScript.setCreatedTime(now);
}else {
ActScript one = actScriptRepository.findOne(id);
actScript.setCreatedTime(one.getCreatedTime());
}
actScript.setLastUpdatedTime(now);
actScriptRepository.save(actScript);
}
public void delete(int id) {
actScriptRepository.delete(id);
}
}

View File

@ -0,0 +1,240 @@
package cn.palmte.work.service;
import cn.palmte.work.config.activiti.ActConstant;
import cn.palmte.work.config.activiti.DeleteTaskCommand;
import cn.palmte.work.config.activiti.JumpCommand;
import cn.palmte.work.model.ActScript;
import cn.palmte.work.model.ActScriptRepository;
import cn.palmte.work.model.ActTaskDef;
import cn.palmte.work.model.ActTaskDefRepository;
import cn.palmte.work.utils.InterfaceUtil;
import com.alibaba.fastjson.JSONObject;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.*;
import org.activiti.engine.task.Task;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import top.jfunc.common.db.bean.Record;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.*;
@Service
public class ActTaskDefService {
private static final Logger logger = LoggerFactory.getLogger(ActTaskDefService.class);
@Autowired
private RepositoryService repositoryService; //管理流程定义 与流程定义和部署对象相关的Service
@Autowired
private ProcessEngine processEngine; //流程引擎对象
@Autowired
private RuntimeService runtimeService; //与正在执行的流程实例和执行对象相关的Service(执行管理,包括启动、推进、删除流程实例等操作)
@Autowired
private TaskService taskService; //任务管理 与正在执行的任务管理相关的Service
@Autowired
private ActTaskDefRepository actTaskDefRepository;
@Autowired
private AccountService accountService;
@Autowired
private ActProcInsService actProcInsService;
@Resource
private ApplicationContext applicationContext;
@Autowired
private ActScriptRepository actScriptRepository;
public List<ActTaskDef> findByProcDefId(String procDefId) {
List<ActTaskDef> list = actTaskDefRepository.findByProcDefId(procDefId);
for (ActTaskDef actTaskDef : list) {
ids2List(actTaskDef);
}
return list;
}
public ActTaskDef findFirstByProcDefIdAndTaskKey(String procDefId, String taskKey) {
ActTaskDef def = actTaskDefRepository.findFirstByProcDefIdAndTaskKey(procDefId, taskKey);
ids2List(def);
return def;
}
public void saveConfig(ActTaskDef taskDef) {
ActTaskDef one = actTaskDefRepository.findOne(taskDef.getId());
one.setCandidateUsers(taskDef.getCandidateUsers());
one.setCandidateRoles(taskDef.getCandidateRoles());
one.setRollbackTaskKey(taskDef.getRollbackTaskKey());
one.setEndScript(taskDef.getEndScript());
one.setRollbackScript(taskDef.getRollbackScript());
one.setLastUpdatedTime(new Date());
actTaskDefRepository.save(one);
logger.info("saveTaskConfig uerId:{}, config:{}", InterfaceUtil.getAdminId(), JSONObject.toJSONString(one));
}
private void ids2List(ActTaskDef actTaskDef) {
String candidateUsers = actTaskDef.getCandidateUsers();
List<String> userIdList = new ArrayList<>();
if (StringUtils.isNotBlank(candidateUsers)) {
userIdList = Arrays.asList(candidateUsers.split("#"));
}
actTaskDef.setCandidateUserList(userIdList);
String candidateRoles = actTaskDef.getCandidateRoles();
List<String> roleIdList = new ArrayList<>();
if (StringUtils.isNotBlank(candidateRoles)) {
roleIdList = Arrays.asList(candidateRoles.split("#"));
}
actTaskDef.setCandidateRoleList(roleIdList);
}
public Set<String> findCandidateUsers(String procDefId, String procInsId, String taskDefKey) {
ActTaskDef taskDef = findFirstByProcDefIdAndTaskKey(procDefId, taskDefKey);
if (taskDef.getTaskIndex() == ActConstant.TASK_INDEX_FIRST_USER_TASK) {
String startUserId = actProcInsService.getStartUserId(procInsId);
Set<String> res = new HashSet<>(1);
logger.info("findCandidateUsers-0-task:{}, startUserId:{}", taskDef.getTaskName(), startUserId);
res.add(startUserId);
return res;
}
List<String> resList = new ArrayList<>();
List<String> candidateUserList = taskDef.getCandidateUserList();
logger.info("findCandidateUsers-1-task:{}, userList:{}", taskDef.getTaskName(), candidateUserList);
if (!candidateUserList.isEmpty()) {
resList.addAll(candidateUserList);
}
List<String> candidateRoleList = taskDef.getCandidateRoleList();
logger.info("findCandidateUsers-2-task:{}, roleList:{}", taskDef.getTaskName(), candidateRoleList);
List<String> list = accountService.getUserIsByRole(candidateRoleList);
logger.info("findCandidateUsers-3-task:{}, userIdListByRole:{}", taskDef.getTaskName(), list);
if (!list.isEmpty()) {
resList.addAll(list);
}
Set<String> res = new HashSet<>(resList);
logger.info("findCandidateUsers-4-task:{}, resIds:{}", taskDef.getTaskName(), res);
return res;
}
/**
*
* @param json
*/
public void completeTask(JSONObject json) {
String taskId = json.getString("taskId");
String procInstId = json.getString("procInsId");
String message = json.getString("message");
int type = json.getInteger("type");
taskService.addComment(taskId, procInstId, message);
Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult();
ActTaskDef actTaskDef = findFirstByProcDefIdAndTaskKey(currentTask.getProcessDefinitionId(), currentTask.getTaskDefinitionKey());
if (ActConstant.TYPE_APPROVE == type) {
//审批通过
taskService.complete(taskId);
//执行配置的审批通过脚本
int endScript = actTaskDef.getEndScript();
if (endScript != 0) {
invokeEventScript(endScript, procInstId);
} else {
logger.info("未配置审批通过脚本 task:{}", actTaskDef.getTaskName());
}
} else if (ActConstant.TYPE_ROLLBACK == type) {
//驳回
String rollbackTaskKey = actTaskDef.getRollbackTaskKey();
jumpToTargetTask(taskId, rollbackTaskKey);
//执行配置的驳回脚本
int rollbackScript = actTaskDef.getRollbackScript();
if (rollbackScript != 0) {
invokeEventScript(rollbackScript, procInstId);
} else {
logger.info("未配置驳回脚本 task:{}", actTaskDef.getTaskName());
}
}
}
/**
*
*
* @param scriptId
* @param procInsId
*/
private void invokeEventScript(int scriptId, String procInsId) {
ActScript actScript = actScriptRepository.findOne(scriptId);
if (actScript == null) {
logger.info("脚本配置错误");
return;
}
Map<String, Object> map = new HashMap<>();
map.put(ActConstant.PROC_INS_ID, procInsId);
List<Record> variables = actProcInsService.getVariables(procInsId);
for (Record variable : variables) {
map.put(variable.getStr("name"), variable.get("text"));
}
//调用方法传递的参数
Object[] args = new Object[1];
args[0] = map;
logger.info("invokeEventScript class:{}, methond:{}, param:{}", actScript.getClassName(), actScript.getClassMethod(), map);
try {
Class<?> ownerClass = Class.forName(actScript.getClassName());
Object bean = applicationContext.getBean(ownerClass);
Class<?>[] paramsType = new Class[1];
paramsType[0] = Class.forName("java.util.Map");
//找到脚本方法对应的方法 注意有且只有一个以Map为参数的方法
Method method = ownerClass.getDeclaredMethod(actScript.getClassMethod(), paramsType);
method.invoke(bean, args);
} catch (Exception e) {
logger.error("", e);
}
}
/**
*
*
* @param currentTaskId id
* @param targetTaskDefKey key
*/
public void jumpToTargetTask(String currentTaskId, String targetTaskDefKey) {
Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
// 获取流程定义
org.activiti.bpmn.model.Process process = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess();
//获取目标节点定义
FlowNode targetNode = (FlowNode) process.getFlowElement(targetTaskDefKey);
ManagementService managementService = processEngine.getManagementService();
//删除当前运行任务
String executionEntityId = managementService.executeCommand(new DeleteTaskCommand(currentTask.getId()));
//流程执行到来源节点
managementService.executeCommand(new JumpCommand(targetNode, executionEntityId));
Task singleResult = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
singleResult.setParentTaskId(currentTask.getTaskDefinitionKey());
taskService.saveTask(singleResult);
}
}

View File

@ -43,7 +43,7 @@ fourcal.token.pc.expires=60*60*24
fourcal.excluded.client.urls= fourcal.excluded.client.urls=
fourcal.excluded.pc.urls= fourcal.excluded.pc.urls=
#\u6392\u9664\u67D0\u4E9B\u65E5\u5FD7\u7684\u6253\u5370 #\u6392\u9664\u67D0\u4E9B\u65E5\u5FD7\u7684\u6253\u5370
fourcal.log.excluded.urls= fourcal.log.excluded.urls=/editor/stencilset
fourcal.slideInterval=1800000 fourcal.slideInterval=1800000

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,233 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<div class="admin-content">
<div class="admin-content-body">
<div class="am-cf am-padding">
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">系统管理</strong> / <small>工作流程管理</small></div>
</div>
<form method="post" class="am-form" id="tmpForm" action="${base}/actModel/save">
<!--选项卡tabsbegin-->
<div class="am-tabs am-margin" data-am-tabs>
<ul class="am-tabs-nav am-nav am-nav-tabs">
<li class="am-active">
<a href="#tab1">模型信息</a>
</li>
</ul>
<div class="am-tabs-bd">
<div class="am-tab-panel am-fade am-in am-active" id="tab1">
<input name="modelId" id="modelId" type="hidden" value="${modelId!}"/>
<!--验证表单元素validate) begin-->
<!--input begin-->
<div class="am-g am-form-group am-margin-top">
<div class="am-u-sm-4 am-u-md-2 am-text-right">
<span style="color: red;">*</span>
模型(流程)名称
</div>
<div class="am-u-sm-6 am-u-md-6">
<input name="modelName" class="js-ajax-validate"
data-validate-async data-validation-message="请输入模型名称100字符以内"
type="text" id="modelName" value="${modelName!}" minlength="1" maxlength="100"
placeholder="请输入模型名称100字符以内" required <#--onblur="checkmodelName($(this));"--> onKeyUp="clearValidInfo()" />
</div>
<div class="am-u-sm-2 am-u-md-4 input-msg" id="role_name_valid"></div>
</div>
<div class="am-g am-form-group am-margin-top">
<div class="am-u-sm-4 am-u-md-2 am-text-right">
<span style="color: red;">*</span>
流程标识
</div>
<div class="am-u-sm-6 am-u-md-6">
<input name="procDefKey" class="js-ajax-validate"
data-validate-async data-validation-message="请输入流程标识20字符以内"
type="text" id="procDefKey" value="${procDefKey!}" minlength="1" maxlength="20"
placeholder="请输入流程标识20字符以内" required <#--onblur="checkprocDefKey($(this));"--> onKeyUp="clearValidInfo()" />
</div>
<div class="am-u-sm-2 am-u-md-4 input-msg" id="role_procDefKey_valid"></div>
</div>
<#--<div class="am-g am-form-group am-margin-top">
<div class="am-u-sm-4 am-u-md-2 am-text-right">
描述
</div>
<div class="am-u-sm-6 am-u-md-6">
<input name="discription" class="js-ajax-validate"
data-validate-async data-validation-message="请输入描述1000字符以内"
type="text" id="discription" value="${description!}" minlength="1" maxlength="100"
placeholder="请输入描述1000字符以内" &lt;#&ndash;onblur="checkprocDefKey($(this));"&ndash;&gt; onKeyUp="clearValidInfo()" />
</div>
<div class="am-u-sm-2 am-u-md-4 input-msg" id="role_description_valid"></div>
</div>-->
</div>
</div>
</div>
<!--选项卡tabsend-->
<div class="am-margin">
<button type="submit" class="am-btn am-btn-primary am-btn-xs">提交保存</button>
<button type="button" class="am-btn am-btn-warning am-btn-xs" onclick="javascript:history.go(-1);">返回上一级</button>
</div>
</form>
</div>
</div>
</@defaultLayout.layout>
<script type="text/javascript">
/*名称检验 start*/
var urlBase = "${base}";
var url;
function checkRoleName(obj){
var roleName = obj.val();
var roleID = $("#roleID").val();
if(roleName.length == 0){
showRoleNameAlert($("#roleName").date("data-validation-message"));
return;
}
$.ajax({
url : urlBase + "/role/check",
type : 'POST',
dataType : 'json',
data : {
role_id : roleID, role_name : roleName
},
cache : false
}).done(function (result) {
if (result.status==0) {
showRoleNameAlert(result.msg);
obj.focus();
}else{
showRoleNameAlert(result.msg);
}
}).fail(function (){
});
}
function clearValidInfo(){
showRoleNameAlert("");
}
function showRoleNameAlert(message){
var $alert = $("#role_name_valid").find('.am-alert');
if (!$alert.length) {
$alert = $('<div class="am-alert am-alert-danger"></div>').hide()
$alert.appendTo($("#role_name_valid"));
}
if(message.length==0){
$alert.hide();
}else{
$alert.text(message).show();
}
}
/*角色名称检验 end*/
/*复选框 全选start*/
$(".role-authority-checkall").click(function(){
var $parentNode = $(this).parent().parent();
var level = Number($parentNode.attr("level"));
var $allParentNextNode = $parentNode.nextUntil(".level-"+level);
var isChecked = $(this).is(":checked");
$allParentNextNode.each(function(){
var myLevel = Number($(this).attr("level"));
var $checks = $(this).children().find("input[type='checkbox']");
if(myLevel > level){
return changeCheckedStatus($checks,isChecked);
}else{
return;
}
});
var $checks = $(this).parent().prev().find("input[type='checkbox']");
changeCheckedStatus($checks,isChecked);
});
function changeCheckedStatus(checksObj,isChecked){
checksObj.each(function(){
if(isChecked){
$(this).prop("checked",true);
}else{
$(this).prop("checked",false);
}
});
}
/*复选框 全选end*/
$(function() {
/*表单验证begin*/
//自定义规则用法验证元素上加class="js-pattern-sort"
if ($.AMUI && $.AMUI.validator) {
$.AMUI.validator.patterns.sort = /^([0-9]+)$/;
}
$("#tmpForm").validator({
// 域通过验证时回调
onValid: function(validity) {
$(validity.field).closest('.am-form-group').find('.am-alert').hide();
},
// 域验证通过时添加的操作,通过该接口可定义各种验证提示
markValid: function(validity) {
// this is Validator instance
var $field = $(validity.field);
//add by zxl只对有required属性的字段进行验证
if(typeof($field.attr("required"))!="undefined"){
var options = this.options;
var $parent = $field.closest('.am-form-group');
$field.addClass(options.validClass).
removeClass(options.inValidClass);
$parent.addClass('am-form-success').removeClass('am-form-error');
options.onValid.call(this, validity);
}
},
// 验证出错时的回调, validity 对象包含相关信息,格式通 H5 表单元素的 validity 属性
onInValid: function(validity) {
var $field = $(validity.field);
var $group = $field.closest('.am-form-group');
var $alert = $group.find('.am-alert');
// 使用自定义的提示信息 或 插件内置的提示信息
var msg = $field.data('validationMessage') || this.getValidationMessage(validity);
if (!$alert.length) {
$alert = $("<div class='am-alert am-alert-danger'></div>").hide().
appendTo($group.find(".input-msg"));
}
console.log("onInValid : "+$field.val());
$alert.html(msg).show();
}
});
/*表单验证end*/
});
</script>
<style type="text/css">
/*验证:提示信息样式 begin*/
.am-alert-danger {
background-color: transparent;
border-color: transparent;
color: red;
}
.am-alert {
margin-bottom: 1em;
padding: .625em;
background: transparent;
border: none;
border-radius: 0;
}
/*验证:提示信息样式 end*/
</style>

View File

@ -0,0 +1,214 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="${base}/assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">工作流程</strong> /
<small>模型管理</small>
</div>
</div>
<form class="am-form" id="list-form" action="${base}/actModel/list" method="post">
<input type="hidden" id="keywords" name="keywords" value='${keywords!""}'/>
<div class="am-g">
<div class="am-u-sm-12 am-u-md-6" style="padding:0px 1.6rem 1.6rem 1rem;margin:0px;">
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<#--<@shiro.hasPermission name="ROLE_ADD">-->
<button type="button" class="am-btn am-btn-default"
onclick="location.href='${base}/actModel/add'">
<span class="am-icon-plus"></span>
新增
</button>
<#-- </@shiro.hasPermission>
<@shiro.hasPermission name="ROLE_DELRTE">-->
<button type="button" id="deleteButton" disabled="disabled"
class="am-btn am-btn-default"
onclick="deleteAll('${base}/actModel/delete')"><span
class="am-icon-trash-o"></span> 删除
</button>
<#--</@shiro.hasPermission>-->
</div>
</div>
</div>
<div class="am-u-sm-12 am-u-md-3">
<div class="am-input-group am-input-group-sm">
<input type="text" class="am-form-field" id="name" value="${name!}" placeholder="按名称和标识搜索"/>
<span class="am-input-group-btn">
<button id="searchButton" class="am-btn am-btn-default" type="button">搜索</button>
</span>
</div>
</div>
</div>
</form>
<div class="am-g">
<div class="am-u-sm-12 am-scrollable-horizontal">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-check">
<input type="checkbox" id="allCheck"></th>
<th class="table-title">id</th>
<th class="table-title">模型(流程)名称</th>
<th class="table-title">流程标识</th>
<th class="table-date">最新版本</th>
<th class="table-date">创建日期</th>
<th class="table-date">最后更新日期</th>
<th class="table-set am-text-center">操作</th>
</tr>
</thead>
<tbody>
<#if (pager.list)?exists>
<#list pager.list as list>
<tr>
<td>
<input type="checkbox" name="ids" value="${list.id}"/>
</td>
<td>${list.id!}</td>
<td>${list.modelName!}</td>
<td>${list.procDefKey!}</td>
<td>${list.rev!}</td>
<td><#if list.createdTime??>${list.createdTime?datetime}</#if></td>
<td><#if list.lastUpdatedTime??>${list.lastUpdatedTime?datetime}</#if></td>
<td>
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="window.open('${base}/activiti-editor/modeler.html?modelId==${list.id?c}')">
<span class="am-icon-pencil-square-o"></span>
流程设计
</button>
<#--<@shiro.hasPermission name="ROLE_EDIT">-->
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="deploy(${list.id?c})">
<span class="am-icon-pencil-square-o"></span>
部署
</button>
<#--</@shiro.hasPermission>-->
</div>
</div>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<div class="am-cf">
<!-- 分页 -->
<#if (pager.list)?exists && (pager.list?size>0) >
<div class="am-fr">
<#include "../common/common_pager.ftl">
</div>
<#else>
<div class="am-kai" align="center">
<h3>没有找到任何记录!</h3>
</div>
</#if>
</div>
</div>
</div>
</div>
</@defaultLayout.layout>
<script src="${base}/assets/js/amazeui.switch.js"></script>
<script type="text/javascript">
var urlBase = "${base}";
var url;
$(function () {
/*表单提交 start*/
var keywordsObj = {};
$("#searchButton").on("click", function () {
if ($("#name").val()) {
keywordsObj.name = $("#name").val();
}
var keywords = "";
if (!$.isEmptyObject(keywordsObj)) {
keywords = JSON.stringify(keywordsObj);
}
console.log("keywords = " + keywords);
$("#keywords").val(keywords);
$("#list-form").submit();
});
/*表单提交 end*/
/*复选框全选效果 start*/
$("body").on('click', '.list-item', function () {
$(".list-item").removeClass("tr-selected");
$(this).addClass('tr-selected');
});
$("#allCheck").click(function () {
$('input[name="ids"]').prop("checked", this.checked);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
var $citySubBox = $("input[name='ids']");
$citySubBox.click(function () {
$("#allCheckCity").prop("checked", $citySubBox.length == $("input[name='ids']:checked").length ? true : false);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
/*复选框全选效果 end*/
});
/*批量删除 start*/
var deleteAll = function (url) {
var $deleteButton = $("#deleteButton");// 删除按钮
var ids = "";
$("input[name='ids']:checked").each(function () {
ids += $(this).val() + "#%#";
});
var params = {ids: ids};
if (window.confirm('确定要删除吗?')) {
$.ajax({
url: url,
data: params,
dataType: "json",
async: false,
beforeSend: function (data) {
$deleteButton.prop("disabled", true)
},
success: function (data) {
$deleteButton.prop("disabled", false)
if (data.status == 0) {
alert(data.msg);
window.location.href = window.location.href;
} else if (data.status == 1) {
alert(data.msg);
}
}
});
}
}
/*批量删除 end*/
var deploy = function (id) {
var params = {id: id};
$.ajax({
url: '${base}/actModel/deploy',
data: params,
dataType: "json",
async: false,
success: function (data) {
layer.msg(data.msg);
}
});
}
$(function () {
});
</script>

View File

@ -0,0 +1,267 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="${base}/assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">工作流程</strong> /
<small>流程定义管理</small>
</div>
</div>
<form class="am-form" id="list-form" action="${base}/actProcDef/list" method="post">
<input type="hidden" id="keywords" name="keywords" value='${keywords!""}'/>
<div class="am-g">
<div class="am-u-sm-12 am-u-md-6" style="padding:0px 1.6rem 1.6rem 1rem;margin:0px;">
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<button type="button" id="deleteButton" disabled="disabled"
class="am-btn am-btn-default"
onclick="deleteAll('${base}/actProcDef/delete')"><span
class="am-icon-trash-o"></span> 删除
</button>
</div>
</div>
</div>
<div class="am-u-sm-12 am-u-md-3">
<div class="am-input-group am-input-group-sm">
<input type="text" class="am-form-field" id="name" value="${name!}" placeholder="按名称和标识搜索"/>
<span class="am-input-group-btn">
<button id="searchButton" class="am-btn am-btn-default" type="button">搜索</button>
</span>
</div>
</div>
</div>
</form>
<#-- <div class="am-modal am-modal-no-btn" tabindex="-1" id="doc-modal-1">
<div class="am-modal-dialog">
<div class="am-modal-hd">Modal 标题
<a href="javascript: void(0)" class="am-close am-close-spin" data-am-modal-close>&times;</a>
</div>
<div class="am-modal-bd">
<img id="img" name="img" src="${base}/actProcDef/png/2501">
</div>
</div>
</div>-->
<div class="am-g">
<div class="am-u-sm-12 am-scrollable-horizontal">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-check">
<input type="checkbox" id="allCheck"></th>
<th class="table-title">流程id</th>
<th class="table-title">流程名称</th>
<th class="table-title">流程标识</th>
<th class="table-date">版本</th>
<th class="table-date">预览</th>
<th class="table-date">激活/挂起</th>
<th class="table-date">部署时间</th>
<th class="table-set am-text-center">操作</th>
</tr>
</thead>
<tbody>
<#if (pager.list)?exists>
<#list pager.list as list>
<tr>
<td>
<input type="checkbox" name="ids" value="${list.deploymentId}"/>
</td>
<td>${list.id!}</td>
<td>${list.procName!}</td>
<td>${list.procKey!}</td>
<td>${list.version!}</td>
<td>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="window.open('${base}/actProcDef/procDefPng/${list.deploymentId!}')">
<span class="am-icon-pencil-square-o"></span>
流程图片
</button>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="window.open('${base}/actProcDef/xml/${list.deploymentId!}')">
<span class="am-icon-pencil-square-o"></span>
流程xml
</button>
</td>
<td>
<div class="switch-button">
<#--1激活 2挂起-->
<input id="${list.id}" type="checkbox" data-size='xs'
data-am-switch data-on-text="已激活" data-off-text="已挂起"
<#if list.suspensionState==1 >checked</#if>/>
</div>
</td>
<td><#if list.deployTime??>${list.deployTime?datetime}</#if></td>
<td>
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="location.href='${base}/actTaskDef/config/${list.id}'">
<span class="am-icon-pencil-square-o"></span>
任务配置
</button>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="startProcIns('${list.procKey}')">
<span class="am-icon-pencil-square-o"></span>
发起流程
</button>
</div>
</div>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<div class="am-cf">
<!-- 分页 -->
<#if (pager.list)?exists && (pager.list?size>0) >
<div class="am-fr">
<#include "../common/common_pager.ftl">
</div>
<#else>
<div class="am-kai" align="center">
<h3>没有找到任何记录!</h3>
</div>
</#if>
</div>
</div>
</div>
</div>
</@defaultLayout.layout>
<script src="${base}/assets/js/amazeui.switch.js"></script>
<script type="text/javascript">
var urlBase = "${base}";
var url;
$(function () {
/*表单提交 start*/
var keywordsObj = {};
$("#searchButton").on("click", function () {
if ($("#name").val()) {
keywordsObj.name = $("#name").val();
}
var keywords = "";
if (!$.isEmptyObject(keywordsObj)) {
keywords = JSON.stringify(keywordsObj);
}
console.log("keywords = " + keywords);
$("#keywords").val(keywords);
$("#list-form").submit();
});
/*表单提交 end*/
/*复选框全选效果 start*/
$("body").on('click', '.list-item', function () {
$(".list-item").removeClass("tr-selected");
$(this).addClass('tr-selected');
});
$("#allCheck").click(function () {
$('input[name="ids"]').prop("checked", this.checked);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
var $citySubBox = $("input[name='ids']");
$citySubBox.click(function () {
$("#allCheckCity").prop("checked", $citySubBox.length == $("input[name='ids']:checked").length ? true : false);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
/*复选框全选效果 end*/
});
/*批量删除 start*/
var deleteAll = function (url) {
var $deleteButton = $("#deleteButton");// 删除按钮
var ids = "";
$("input[name='ids']:checked").each(function () {
ids += $(this).val() + "#%#";
});
var params = {ids: ids};
if (window.confirm('确定要删除吗?')) {
$.ajax({
url: url,
data: params,
dataType: "json",
async: false,
beforeSend: function (data) {
$deleteButton.prop("disabled", true)
},
success: function (data) {
$deleteButton.prop("disabled", false)
if (data.status == 0) {
alert(data.msg);
window.location.href = window.location.href;
} else if (data.status == 1) {
alert(data.msg);
}
}
});
}
}
/*批量删除 end*/
/**
* 为每个启用或禁用按钮增加事件
*/
$(function () {
var $mycheckbox = $('.switch-button').find("input[type='checkbox']");
$mycheckbox.each(function () {
var myid = $(this).attr("id");
var prop = $(this).attr("prop");
$(this).on({
'switchChange.bootstrapSwitch': function (event, state) {
toggle(myid, state ? 1 : 2);
}
});
});
});
//启用或者禁用
var toggle = function (id, status) {
$.ajax({
url: "${base}/actProcDef/suspend",
data: {id: id, status: status},
type: "get",
dataType: "json",
async: false,
success: function (data) {
parent.layer.msg(data.msg);
}
});
};
var startProcIns = function (procDefKey) {
var params = {procDefKey: procDefKey};
$.ajax({
url: '${base}/actProcIns/startProcIns',
data: params,
dataType: "json",
async: false,
success: function (data) {
layer.msg(data.msg);
}
});
}
</script>

View File

@ -0,0 +1,240 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="${base}/assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">工作流程</strong> /
<small>流程实例</small>
</div>
</div>
<div class="am-g">
<div class="am-u-sm-12 am-scrollable-horizontal">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-title">流程名称</th>
<th class="table-title">流程标识</th>
<th class="table-title">流程版本</th>
<th class="table-date">申请人</th>
<th class="table-date">申请时间</th>
<th class="table-date">当前任务</th>
<th class="table-date">当前审批人</th>
<th class="table-date">结束时间</th>
<th class="table-set am-text-center">操作</th>
</tr>
</thead>
<tbody>
<#if (pager.list)?exists>
<#list pager.list as list>
<tr>
<td>${list.procName!}</td>
<td>${list.procKey!}</td>
<td>${list.version!}</td>
<td>${list.user!}</td>
<td>${list.startTime?datetime}</td>
<td>${list.currentTask!}</td>
<td>${list.candidateUsers!}</td>
<td><#if list.endTime??>${list.endTime?datetime}</#if></td>
<td>
<#if !list.endTime??>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="deleteProcIns('${list.procInsId}', '管理员撤销')">
<span class="am-icon-pencil-square-o"></span>
撤销流程
</button>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="completeTask('${list.procInsId}', '${list.currentTaskId}', 1)">
<span class="am-icon-pencil-square-o"></span>
审批通过
</button>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="completeTask('${list.procInsId}', '${list.currentTaskId}', 2)">
<span class="am-icon-pencil-square-o"></span>
驳回
</button>
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="window.open('${base}/actProcIns/procInsPng/${list.procInsId!}')">
<span class="am-icon-pencil-square-o"></span>
流程图片
</button>
</#if>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<div class="am-cf">
<!-- 分页 -->
<#if (pager.list)?exists && (pager.list?size>0) >
<div class="am-fr">
<#include "../common/common_pager.ftl">
</div>
<#else>
<div class="am-kai" align="center">
<h3>没有找到任何记录!</h3>
</div>
</#if>
</div>
</div>
</div>
</div>
</@defaultLayout.layout>
<script src="${base}/assets/js/amazeui.switch.js"></script>
<script type="text/javascript">
var urlBase = "${base}";
var url;
$(function () {
/*表单提交 start*/
var keywordsObj = {};
$("#searchButton").on("click", function () {
if ($("#name").val()) {
keywordsObj.name = $("#name").val();
}
var keywords = "";
if (!$.isEmptyObject(keywordsObj)) {
keywords = JSON.stringify(keywordsObj);
}
console.log("keywords = " + keywords);
$("#keywords").val(keywords);
$("#list-form").submit();
});
/*表单提交 end*/
/*复选框全选效果 start*/
$("body").on('click', '.list-item', function () {
$(".list-item").removeClass("tr-selected");
$(this).addClass('tr-selected');
});
$("#allCheck").click(function () {
$('input[name="ids"]').prop("checked", this.checked);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
var $citySubBox = $("input[name='ids']");
$citySubBox.click(function () {
$("#allCheckCity").prop("checked", $citySubBox.length == $("input[name='ids']:checked").length ? true : false);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
/*复选框全选效果 end*/
});
/*批量删除 start*/
var deleteAll = function (url) {
var $deleteButton = $("#deleteButton");// 删除按钮
var ids = "";
$("input[name='ids']:checked").each(function () {
ids += $(this).val() + "#%#";
});
var params = {ids: ids};
if (window.confirm('确定要删除吗?')) {
$.ajax({
url: url,
data: params,
dataType: "json",
async: false,
beforeSend: function (data) {
$deleteButton.prop("disabled", true)
},
success: function (data) {
$deleteButton.prop("disabled", false)
if (data.status == 0) {
alert(data.msg);
window.location.href = window.location.href;
} else if (data.status == 1) {
alert(data.msg);
}
}
});
}
}
/*批量删除 end*/
/**
* 为每个启用或禁用按钮增加事件
*/
$(function () {
var $mycheckbox = $('.switch-button').find("input[type='checkbox']");
$mycheckbox.each(function () {
var myid = $(this).attr("id");
var prop = $(this).attr("prop");
$(this).on({
'switchChange.bootstrapSwitch': function (event, state) {
toggle(myid, state ? 1 : 2);
}
});
});
});
//启用或者禁用
var toggle = function (id, status) {
$.ajax({
url: "${base}/actProcDef/suspend",
data: {id: id, status: status},
type: "get",
dataType: "json",
async: false,
success: function (data) {
parent.layer.msg(data.msg);
}
});
};
var deleteProcIns = function (procInsId, reason) {
var params = {procInsId: procInsId, reason: reason};
$.ajax({
url: '${base}/actProcIns/deleteProcessInstance',
data: params,
dataType: "json",
async: false,
success: function (data) {
alert(data.msg);
location.reload();
}
});
}
var completeTask = function (procInsId, taskId, type) {
var params = {
procInsId: procInsId,
taskId: taskId,
type: type,
message: '管理员审批'
};
$.ajax({
url: '${base}/actTaskDef/completeTask',
data: JSON.stringify(params),
dataType: "json",
contentType: "application/json",
type: 'post',
async: false,
success: function (data) {
alert(data.msg);
location.reload();
}
});
}
</script>

View File

@ -0,0 +1,203 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<div class="admin-content">
<div class="admin-content-body">
<div class="am-cf am-padding">
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">工作流程</strong> / <small>流程脚本</small></div>
</div>
<form method="post" class="am-form" id="tmpForm" action="${base}/actScript/save">
<!--选项卡tabsbegin-->
<div class="am-tabs am-margin" data-am-tabs>
<ul class="am-tabs-nav am-nav am-nav-tabs">
<li class="am-active">
<a href="#tab1">脚本编辑</a>
</li>
</ul>
<div class="am-tabs-bd">
<div class="am-tab-panel am-fade am-in am-active" id="tab1">
<input name="id" id="id" type="hidden" value="${actScript.id!}"/>
<!--验证表单元素validate) begin-->
<!--input begin-->
<div class="am-g am-form-group am-margin-top">
<div class="am-u-sm-4 am-u-md-2 am-text-right">
<span style="color: red;">*</span>
脚本名称
</div>
<div class="am-u-sm-6 am-u-md-6">
<input name="scriptName" class="js-ajax-validate"
data-validate-async data-validation-message="请输入脚本名称20字符以内"
type="text" id="scriptName" value="${actScript.scriptName!}" minlength="1" maxlength="100"
placeholder="请输入模型名称100字符以内" required onKeyUp="clearValidInfo()" />
</div>
<div class="am-u-sm-2 am-u-md-4 input-msg" id="role_name_valid"></div>
</div>
<div class="am-g am-form-group am-margin-top">
<div class="am-u-sm-4 am-u-md-2 am-text-right"><span style="color: red;">*</span>脚本所在类</div>
<div class="am-u-sm-6 am-u-md-6">
<select data-am-selected id="className" name="className" readonly="">
<#list classList as l>
<option value="${l}" <#if actScript.className! == l>selected</#if> >${l!}</option>
</#list>
</select>
</div>
<div class="am-u-sm-2 am-u-md-4 input-msg"></div>
</div>
<div class="am-g am-form-group am-margin-top">
<div class="am-u-sm-4 am-u-md-2 am-text-right"><span style="color: red;">*</span>方法名称</div>
<div class="am-u-sm-6 am-u-md-6">
<select data-am-selected id="classMethod" name="classMethod" readonly="">
<#list methodList as l>
<option value="${l}" <#if actScript.classMethod! == l>selected</#if> >${l!}</option>
</#list>
</select>
</div>
<div class="am-u-sm-2 am-u-md-4 input-msg"></div>
</div>
</div>
</div>
</div>
<!--选项卡tabsend-->
<div class="am-margin">
<button type="submit" class="am-btn am-btn-primary am-btn-xs">提交保存</button>
<button type="button" class="am-btn am-btn-warning am-btn-xs" onclick="javascript:history.go(-1);">返回上一级</button>
</div>
</form>
</div>
</div>
</@defaultLayout.layout>
<script type="text/javascript">
/*名称检验 start*/
var urlBase = "${base}";
var url;
function clearValidInfo(){
showRoleNameAlert("");
}
function showRoleNameAlert(message){
var $alert = $("#role_name_valid").find('.am-alert');
if (!$alert.length) {
$alert = $('<div class="am-alert am-alert-danger"></div>').hide()
$alert.appendTo($("#role_name_valid"));
}
if(message.length==0){
$alert.hide();
}else{
$alert.text(message).show();
}
}
/*角色名称检验 end*/
/*复选框 全选start*/
$(".role-authority-checkall").click(function(){
var $parentNode = $(this).parent().parent();
var level = Number($parentNode.attr("level"));
var $allParentNextNode = $parentNode.nextUntil(".level-"+level);
var isChecked = $(this).is(":checked");
$allParentNextNode.each(function(){
var myLevel = Number($(this).attr("level"));
var $checks = $(this).children().find("input[type='checkbox']");
if(myLevel > level){
return changeCheckedStatus($checks,isChecked);
}else{
return;
}
});
var $checks = $(this).parent().prev().find("input[type='checkbox']");
changeCheckedStatus($checks,isChecked);
});
function changeCheckedStatus(checksObj,isChecked){
checksObj.each(function(){
if(isChecked){
$(this).prop("checked",true);
}else{
$(this).prop("checked",false);
}
});
}
/*复选框 全选end*/
$(function() {
/*表单验证begin*/
//自定义规则用法验证元素上加class="js-pattern-sort"
if ($.AMUI && $.AMUI.validator) {
$.AMUI.validator.patterns.sort = /^([0-9]+)$/;
}
$("#tmpForm").validator({
// 域通过验证时回调
onValid: function(validity) {
$(validity.field).closest('.am-form-group').find('.am-alert').hide();
},
// 域验证通过时添加的操作,通过该接口可定义各种验证提示
markValid: function(validity) {
// this is Validator instance
var $field = $(validity.field);
//add by zxl只对有required属性的字段进行验证
if(typeof($field.attr("required"))!="undefined"){
var options = this.options;
var $parent = $field.closest('.am-form-group');
$field.addClass(options.validClass).
removeClass(options.inValidClass);
$parent.addClass('am-form-success').removeClass('am-form-error');
options.onValid.call(this, validity);
}
},
// 验证出错时的回调, validity 对象包含相关信息,格式通 H5 表单元素的 validity 属性
onInValid: function(validity) {
var $field = $(validity.field);
var $group = $field.closest('.am-form-group');
var $alert = $group.find('.am-alert');
// 使用自定义的提示信息 或 插件内置的提示信息
var msg = $field.data('validationMessage') || this.getValidationMessage(validity);
if (!$alert.length) {
$alert = $("<div class='am-alert am-alert-danger'></div>").hide().
appendTo($group.find(".input-msg"));
}
console.log("onInValid : "+$field.val());
$alert.html(msg).show();
}
});
/*表单验证end*/
});
</script>
<style type="text/css">
/*验证:提示信息样式 begin*/
.am-alert-danger {
background-color: transparent;
border-color: transparent;
color: red;
}
.am-alert {
margin-bottom: 1em;
padding: .625em;
background: transparent;
border: none;
border-radius: 0;
}
/*验证:提示信息样式 end*/
</style>

View File

@ -0,0 +1,204 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="${base}/assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">工作流程</strong> /
<small>模型管理</small>
</div>
</div>
<form class="am-form" id="list-form" action="${base}/actScript/list" method="post">
<input type="hidden" id="keywords" name="keywords" value='${keywords!""}'/>
<div class="am-g">
<div class="am-u-sm-12 am-u-md-6" style="padding:0px 1.6rem 1.6rem 1rem;margin:0px;">
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<#--<@shiro.hasPermission name="ROLE_ADD">-->
<button type="button" class="am-btn am-btn-default"
onclick="location.href='${base}/actScript/add'">
<span class="am-icon-plus"></span>
新增
</button>
<#-- </@shiro.hasPermission>
<@shiro.hasPermission name="ROLE_DELRTE">-->
<button type="button" id="deleteButton" disabled="disabled"
class="am-btn am-btn-default"
onclick="deleteAll('${base}/actScript/delete')"><span
class="am-icon-trash-o"></span> 删除
</button>
<#--</@shiro.hasPermission>-->
</div>
</div>
</div>
<div class="am-u-sm-12 am-u-md-3">
<div class="am-input-group am-input-group-sm">
<input type="text" class="am-form-field" id="name" value="${name!}" placeholder="按名称搜索"/>
<span class="am-input-group-btn">
<button id="searchButton" class="am-btn am-btn-default" type="button">搜索</button>
</span>
</div>
</div>
</div>
</form>
<div class="am-g">
<div class="am-u-sm-12 am-scrollable-horizontal">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-check">
<input type="checkbox" id="allCheck"></th>
<th class="table-title">id</th>
<th class="table-title">脚本名称</th>
<th class="table-title">脚本所在类</th>
<th class="table-date">脚本方法</th>
<th class="table-date">创建日期</th>
<th class="table-date">最后更新日期</th>
<th class="table-set am-text-center">操作</th>
</tr>
</thead>
<tbody>
<#if (pager.list)?exists>
<#list pager.list as list>
<tr>
<td>
<input type="checkbox" name="ids" value="${list.id}"/>
</td>
<td>${list.id!}</td>
<td>${list.scriptName!}</td>
<td>${list.className!}</td>
<td>${list.classMethod!}</td>
<td><#if list.createdTime??>${list.createdTime?datetime}</#if></td>
<td><#if list.lastUpdatedTime??>${list.lastUpdatedTime?datetime}</#if></td>
<td>
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="location.href='${base}/actScript/edit?id=${list.id}'"><span
class="am-icon-pencil-square-o"></span>编辑
</button>
</div>
</div>
</td>
</tr>
</#list>
</#if>
</tbody>
</table>
<div class="am-cf">
<!-- 分页 -->
<#if (pager.list)?exists && (pager.list?size>0) >
<div class="am-fr">
<#include "../common/common_pager.ftl">
</div>
<#else>
<div class="am-kai" align="center">
<h3>没有找到任何记录!</h3>
</div>
</#if>
</div>
</div>
</div>
</div>
</@defaultLayout.layout>
<script src="${base}/assets/js/amazeui.switch.js"></script>
<script type="text/javascript">
var urlBase = "${base}";
var url;
$(function () {
/*表单提交 start*/
var keywordsObj = {};
$("#searchButton").on("click", function () {
if ($("#name").val()) {
keywordsObj.name = $("#name").val();
}
var keywords = "";
if (!$.isEmptyObject(keywordsObj)) {
keywords = JSON.stringify(keywordsObj);
}
console.log("keywords = " + keywords);
$("#keywords").val(keywords);
$("#list-form").submit();
});
/*表单提交 end*/
/*复选框全选效果 start*/
$("body").on('click', '.list-item', function () {
$(".list-item").removeClass("tr-selected");
$(this).addClass('tr-selected');
});
$("#allCheck").click(function () {
$('input[name="ids"]').prop("checked", this.checked);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
var $citySubBox = $("input[name='ids']");
$citySubBox.click(function () {
$("#allCheckCity").prop("checked", $citySubBox.length == $("input[name='ids']:checked").length ? true : false);
$("#deleteButton").prop("disabled", $("input[name='ids']:checked").length == 0 ? true : false);
});
/*复选框全选效果 end*/
});
/*批量删除 start*/
var deleteAll = function (url) {
var $deleteButton = $("#deleteButton");// 删除按钮
var ids = "";
$("input[name='ids']:checked").each(function () {
ids += $(this).val() + "#%#";
});
var params = {ids: ids};
if (window.confirm('确定要删除吗?')) {
$.ajax({
url: url,
data: params,
dataType: "json",
async: false,
beforeSend: function (data) {
$deleteButton.prop("disabled", true)
},
success: function (data) {
$deleteButton.prop("disabled", false)
if (data.status == 0) {
alert(data.msg);
window.location.href = window.location.href;
} else if (data.status == 1) {
alert(data.msg);
}
}
});
}
}
/*批量删除 end*/
var deploy = function (id) {
var params = {id: id};
$.ajax({
url: '${base}/actScript/deploy',
data: params,
dataType: "json",
async: false,
success: function (data) {
layer.msg(data.msg);
}
});
}
$(function () {
});
</script>

View File

@ -0,0 +1,196 @@
<#assign base=request.contextPath />
<#import "../common/defaultLayout.ftl" as defaultLayout>
<@defaultLayout.layout>
<link rel="stylesheet" href="${base}/assets/css/amazeui.switch.css"/>
<div class="admin-content">
<div class="am-cf am-padding" style="padding:1rem 1.6rem 1.6rem 1rem;margin:0px;">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<div class="am-fl am-cf"><strong class="am-text-primary am-text-lg">${procDefName!}</strong> /
<small>任务设置</small>
</div>
</div>
<input name="procDefId" id="procDefId" type="hidden" value="${procDefId!}"/>
<div class="am-g">
<div class="am-u-sm-12 <#--am-scrollable-horizontal-->">
<!-- padding:1px 2px 3px 4px;上、右、下,和左 -->
<table class="am-table am-table-striped am-table-hover table-main">
<thead>
<tr class="am-text-nowrap">
<th class="table-title">序号</th>
<th class="table-title">任务名称</th>
<th class="table-title">任务类型</th>
<th class="table-date">回退任务</th>
<th class="table-set am-text-center">审批人</th>
<th class="table-set am-text-center">审批角色</th>
<th class="table-set am-text-center">审批通过脚本</th>
<th class="table-set am-text-center">审批驳回脚本</th>
<th class="table-set am-text-center">操作</th>
</tr>
</thead>
<tbody>
<#list taskList as list>
<tr>
<td>${list_index+1}</td>
<td>${list.taskName!}</td>
<td>
<#if list.taskIndex != 1>
<#if list.taskType == 0>
或签
<#else>
会签
</#if>
</#if>
</td>
<td>
<#if list.taskIndex != 1>
<select data-am-selected="{btnSize: 'sm',maxHeight: 500}"
id="rollbackTask_${list.id}">
<#list taskList as l>
<option value="${l.taskKey}"
<#if list.rollbackTaskKey == l.taskKey>selected</#if> >${l.taskName!}</option>
</#list>
</select>
</#if>
</td>
<td>
<#if list.taskIndex != 1>
<select multiple data-am-selected="{btnSize: 'sm',maxHeight: 500,searchBox: 1}"
id="userSelect_${list.id}">
<#list adminList as l>
<option value="${l.id}"
<#if list.candidateUserList?seq_contains(l.id?c)>selected</#if> >${l.realName!}</option>
</#list>
</select>
</#if>
</td>
<td>
<#if list.taskIndex != 1>
<select multiple data-am-selected="{btnSize: 'sm',maxHeight: 500,searchBox: 1}"
id="roleSelect_${list.id}">
<#list roleList as l>
<option value="${l.id}"
<#if list.candidateRoleList?seq_contains(l.id?c)>selected</#if> >${l.name!}</option>
</#list>
</select>
</#if>
</td>
<td>
<#if list.taskIndex != 1>
<select data-am-selected="{btnSize: 'sm',maxHeight: 500}"
id="scriptSelect_${list.id}">
<option value="0"></option>
<#list scriptList as l>
<option value="${l.id}"
<#if list.endScript! == l.id>selected</#if> >${l.scriptName!}</option>
</#list>
</select>
</#if>
</td>
<td>
<#if list.taskIndex != 1>
<select data-am-selected="{btnSize: 'sm',maxHeight: 500}"
id="rollbackScriptSelect_${list.id}">
<option value="0"></option>
<#list scriptList as l>
<option value="${l.id}"
<#if list.rollbackScript! == l.id>selected</#if> >${l.scriptName!}</option>
</#list>
</select>
</#if>
</td>
<td>
<#if list.taskIndex != 1>
<div class="am-btn-toolbar">
<div class="am-btn-group am-btn-group-xs">
<button type="button"
class="am-btn am-btn-default am-btn-xs am-text-secondary"
onclick="saveConfig('${list.id}')">
<span class="am-icon-pencil-square-o"></span>
保存
</button>
</div>
</div>
</#if>
</td>
</tr>
</#list>
</tbody>
</table>
</div>
</div>
<div class="am-margin">
<#--<button type="submit" class="am-btn am-btn-primary am-btn-xs">提交保存</button>-->
<button type="button" class="am-btn am-btn-warning am-btn-xs"
onclick="location.href='${base}/actProcDef/list'">返回
</button>
</div>
</div>
</@defaultLayout.layout>
<script src="${base}/assets/js/amazeui.switch.js"></script>
<script type="text/javascript">
var urlBase = "${base}";
var url;
var saveConfig = function (taskId) {
var rollbackTaskKey = $("#rollbackTask_" + taskId + " option:selected").val();
var endScript = $("#scriptSelect_" + taskId + " option:selected").val();
var rollbackScript = $("#rollbackScriptSelect_" + taskId + " option:selected").val();
var $userSelected = $("#userSelect_" + taskId + " option:selected");
var userIds = '';
$userSelected.each(function () {
if (userIds == '') {
userIds = $(this).val();
} else {
userIds = userIds + "#" + $(this).val();
}
});
var $roleSelected = $("#roleSelect_" + taskId + " option:selected");
var roleIds = '';
$roleSelected.each(function () {
if (roleIds == '') {
roleIds = $(this).val();
} else {
roleIds = roleIds + "#" + $(this).val();
}
});
var params = {
id: taskId,
procDefId: $("#procDefId").val(),
rollbackTaskKey: rollbackTaskKey,
endScript: endScript,
rollbackScript: rollbackScript,
candidateUsers: userIds,
candidateRoles: roleIds
};
$.ajax({
url: '${base}/actTaskDef/saveConfig',
data: params,
dataType: "json",
async: false,
success: function (data) {
layer.msg(data.msg);
}
});
}
</script>