package com.sobey.cmop.mvc.service.audit;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.sobey.cmop.mvc.comm.BaseSevcie;
import com.sobey.cmop.mvc.constant.AccountConstant;
import com.sobey.cmop.mvc.constant.ApplyConstant;
import com.sobey.cmop.mvc.constant.AuditConstant;
import com.sobey.cmop.mvc.constant.RedmineConstant;
import com.sobey.cmop.mvc.constant.ResourcesConstant;
import com.sobey.cmop.mvc.dao.AuditDao;
import com.sobey.cmop.mvc.dao.AuditFlowDao;
import com.sobey.cmop.mvc.entity.Apply;
import com.sobey.cmop.mvc.entity.Audit;
import com.sobey.cmop.mvc.entity.AuditFlow;
import com.sobey.cmop.mvc.entity.RedmineIssue;
import com.sobey.cmop.mvc.entity.Resources;
import com.sobey.cmop.mvc.entity.ServiceTag;
import com.sobey.cmop.mvc.entity.User;
import com.sobey.cmop.mvc.service.redmine.RedmineService;
import com.sobey.framework.utils.DynamicSpecifications;
import com.sobey.framework.utils.SearchFilter;
import com.sobey.framework.utils.SearchFilter.Operator;
import com.taskadapter.redmineapi.RedmineManager;
import com.taskadapter.redmineapi.bean.Issue;
import com.taskadapter.redmineapi.bean.Tracker;
/**
* 审批表 Audit & 审批流程 AuditFlow 相关的管理类.
*
* @author liukai
*/
@Service
@Transactional(readOnly = true)
public class AuditService extends BaseSevcie {
private static Logger logger = LoggerFactory.getLogger(AuditService.class);
@Resource
private AuditDao auditDao;
@Resource
private AuditFlowDao auditFlowDao;
// ============ 审批 Audit ============ //
public Audit getAudit(Integer id) {
return auditDao.findOne(id);
}
/**
* 根据 AuditFlow, Apply ,status 获得 审批记录Audit
*
* @param applyId
* 服务申请单
* @param status
* 审批记录状态
* @param auditFlow
* 服务审批流程
* @return
*/
public Audit findAuditByApplyIdAndStatusAndAuditFlow(Integer applyId, Integer status, AuditFlow auditFlow) {
return auditDao.findByApplyIdAndStatusAndAuditFlow(applyId, status, auditFlow);
}
/**
* 根据 AuditFlow, ServiceTag ,status 获得 审批记录Audit
*
* @param serviceTagId
* 服务标签
* @param status
* 审批记录状态
* @param auditFlow
* 服务审批流程
* @return
*/
public Audit findAuditByServiceTagIdAndStatusAndAuditFlow(Integer serviceTagId, Integer status, AuditFlow auditFlow) {
return auditDao.findByServiceTagIdAndStatusAndAuditFlow(serviceTagId, status, auditFlow);
}
/**
* 新增或更新Audit
*
* @param audit
* @return
*/
@Transactional(readOnly = false)
public Audit saveOrUpdateAudit(Audit audit) {
return auditDao.save(audit);
}
/**
* 获得Apply的 审批 Audit列表.
*
* @param applyId
* @return
*/
public List<Audit> getAuditListByApplyId(Integer applyId) {
return auditDao.findByApplyId(applyId);
}
/**
* 获得ServiceTag的 审批 Audit列表.
*
* @param applyId
* @return
*/
public List<Audit> getAuditListByServiceTagId(Integer serviceTagId) {
return auditDao.findByServiceTagId(serviceTagId);
}
/**
* 根据审批状态获得指定服务标签的审批记录.
*
* @param serviceTagId
* @param status
* @return
*/
public List<Audit> getAuditListByServiceTagIdAndStatus(Integer serviceTagId, Integer status) {
return auditDao.findByServiceTagIdAndStatus(serviceTagId, status);
}
/**
* 根据审批状态获得指定服务申请的审批记录.
*
* @param serviceTagId
* @param status
* @return
*/
public List<Audit> getAuditListByApplyIdAndStatus(Integer applyId, Integer status) {
return auditDao.findByapplyIdAndStatus(applyId, status);
}
// ============ 审批流程 AuditFlow============ //
/**
* 审批audit页面(Apply)的分页查询.
*
* @param searchParams
* 页面传递过来的参数
* @param pageNumber
* @param pageSize
* @return
*/
public Page<Audit> getAuditApplyPageable(Map<String, Object> searchParams, int pageNumber, int pageSize) {
PageRequest pageRequest = buildPageRequest(pageNumber, pageSize, new Sort(Direction.DESC, "apply.id"));
Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
filters.put("audit.auditFlow.user.id", new SearchFilter("auditFlow.user.id", Operator.EQ, getCurrentUserId()));
filters.put("audit.apply.id", new SearchFilter("apply.id", Operator.NotNull, null));
Specification<Audit> spec = DynamicSpecifications.bySearchFilter(filters.values(), Audit.class);
return auditDao.findAll(spec, pageRequest);
}
/**
* 审批audit页面(resources)的分页查询.
*
* @param searchParams
* 页面传递过来的参数
* @param pageNumber
* @param pageSize
* @return
*/
public Page<Audit> getAuditResourcesPageable(Map<String, Object> searchParams, int pageNumber, int pageSize) {
PageRequest pageRequest = buildPageRequest(pageNumber, pageSize, new Sort(Direction.DESC, "serviceTag.id"));
Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
filters.put("audit.auditFlow.user.id", new SearchFilter("auditFlow.user.id", Operator.EQ, getCurrentUserId()));
filters.put("audit.serviceTag.id", new SearchFilter("serviceTag.id", Operator.NotNull, null));
Specification<Audit> spec = DynamicSpecifications.bySearchFilter(filters.values(), Audit.class);
return auditDao.findAll(spec, pageRequest);
}
/**
* 根据流程类型flowType 获得指定用户的审批流程
*
* @param userId
* 用户Id
* @param flowType
* 流程类型
* @return
*/
public AuditFlow findAuditFlowByUserIdAndFlowType(Integer userId, Integer flowType) {
return auditFlowDao.findByUserIdAndFlowType(userId, flowType);
}
/**
* 根据流程类型flowType和审批顺序获得审批流程
*
* @param auditOrder
* 审批顺序
* @param flowType
* 流程类型
* @return
*/
public AuditFlow findAuditFlowByAuditOrderAndFlowType(Integer auditOrder, Integer flowType) {
return auditFlowDao.findByAuditOrderAndFlowType(auditOrder, flowType);
}
/**
* 校验是否已审批.
*
* <pre>
* 由于审批记录都先于审批操作写入,所以直接查询审批记录表,
* 看是否有该申请及当前用户所在审批流程且创建时间为空的记录.
* 如果有则说明可以审批,否则表示已审批。
* </pre>
*
* @param applyId
* 服务申请ID
* @param userId
* 审批人ID
* @return true :已审批 ; false:未审批
*/
private boolean isAudited(Integer applyId, Integer serviceTagId, Integer userId) {
boolean isAudited = false;
User user = AccountConstant.FROM_PAGE_USER_ID.equals(userId) ? comm.accountService.getCurrentUser()
: comm.accountService.getUser(userId);
Integer flowType = AuditConstant.FlowType.资源申请_变更的审批流程.toInteger();
AuditFlow auditFlow = this.findAuditFlowByUserIdAndFlowType(user.getId(), flowType);
logger.info("--->user=" + user.getName() + ",auditFlow.auditOrder=" + auditFlow.getAuditOrder());
Audit audit = null;
if (applyId != null) {
audit = auditDao.findByApplyIdAndAuditFlowAndCreateTimeIsNull(applyId, auditFlow);
} else {
audit = auditDao.findByServiceTagIdAndAuditFlowAndCreateTimeIsNull(serviceTagId, auditFlow);
}
if (audit == null) {
logger.info("--->isAudited...");
isAudited = true;
}
return isAudited;
}
/**
* 校验服务申请Apply是否已审批
*
* @param apply
* 服务申请
* @param userId
* 审批人ID
* @return
*/
public boolean isAudited(Apply apply, Integer userId) {
return this.isAudited(apply.getId(), null, userId);
}
/**
* 校验服务变更Resources是否已审批
*
* @param serviceTag
* 服务标签
* @param userId
* 审批人ID
* @return
*/
public boolean isAudited(ServiceTag serviceTag, Integer userId) {
return this.isAudited(null, serviceTag.getId(), userId);
}
/**
* 服务申请Apply的审批.
*
* <pre>
* 首先根据审核结果的不同分为三种审批处理逻辑.
* 1-中间审批. 找到当前审批人的下级审批人,直接发送邮件.
* 2-终审审批. 首先拼装Redmine内容,并写入redmine;成功写入redmine后,创建工单对象.
* 3-审批退回. 发送退回邮件给Apply的申请人.
* </pre>
*
* @param audit
* 审批
* @param applyId
* 服务申请ApplyId
* @param userId
* 当前审批人
* @return
*/
@Transactional(readOnly = false)
public boolean saveAuditToApply(Audit audit, Integer applyId, Integer userId) {
Apply apply = comm.applyService.getApply(applyId);
User user = AccountConstant.FROM_PAGE_USER_ID.equals(userId) ? comm.accountService.getCurrentUser()
: comm.accountService.getUser(userId);
Integer flowType = AuditConstant.FlowType.资源申请_变更的审批流程.toInteger();
AuditFlow auditFlow = this.findAuditFlowByUserIdAndFlowType(user.getId(), flowType);
audit.setApply(apply);
audit.setAuditFlow(auditFlow);
audit.setCreateTime(new Date());
audit.setStatus(AuditConstant.AuditStatus.有效.toInteger());
if (audit.getResult().equals(AuditConstant.Result.不同意且退回.toString())) {
logger.info("--->申请Apply审批退回...");
apply.setStatus(ApplyConstant.Status.已退回.toInteger());
String contentText = "你的服务申请 " + apply.getTitle() + " 已退回!<a href=\""
+ CONFIG_LOADER.getProperty("APPLY_URL") + "\">→点击进行处理</a><br>";
// 发送退回通知邮件
comm.simpleMailService.sendNotificationMail(apply.getUser().getEmail(), "服务申请/变更退回邮件", contentText);
} else {
if (auditFlow.getIsFinal()) { // 终审人
logger.info("--->申请Apply终审审批...");
apply.setStatus(ApplyConstant.Status.已审批.toInteger());
// 拼装Redmine内容
String description = comm.redmineUtilService.applyRedmineDesc(apply);
// 写入工单Issue到Redmine
Issue issue = new Issue();
Integer trackerId = RedmineConstant.Tracker.支持.toInteger();
Tracker tracker = new Tracker(trackerId, RedmineConstant.Tracker.get(trackerId));
issue.setTracker(tracker);
issue.setSubject(apply.getTitle());
issue.setPriorityId(apply.getPriority());
issue.setDescription(description);
Integer projectId = RedmineConstant.Project.SobeyCloud运营.toInteger();
// 初始化第一接收人
RedmineManager mgr = RedmineService.FIRST_REDMINE_ASSIGNEE_REDMINEMANAGER;
if (apply.getTitle().indexOf(ApplyConstant.ServiceType.get(ApplyConstant.ServiceType.MDN.toInteger())) > 0) {
mgr = RedmineService.MDN_REDMINE_ASSIGNEE_REDMINEMANAGER;
} else if (apply.getTitle().indexOf(
ApplyConstant.ServiceType.get(ApplyConstant.ServiceType.云生产.toInteger())) > 0) {
mgr = RedmineService.CP_REDMINE_ASSIGNEE_REDMINEMANAGER;
} else if (apply.getTitle().indexOf(
ApplyConstant.ServiceType.get(ApplyConstant.ServiceType.监控.toInteger())) > 0) {
mgr = RedmineService.MONITOR_REDMINE_ASSIGNEE_REDMINEMANAGER;
}
boolean isCreated = RedmineService.createIssue(issue, projectId.toString(), mgr);
logger.info("--->申请Apply Redmine isCreated?" + isCreated);
if (isCreated) { // 写入Redmine成功
Integer assignee = RedmineService.FIRST_REDMINE_ASSIGNEE;
if (apply.getTitle().indexOf(
ApplyConstant.ServiceType.get(ApplyConstant.ServiceType.MDN.toInteger())) > 0) {
assignee = RedmineService.MDN_REDMINE_ASSIGNEE;
} else if (apply.getTitle().indexOf(
ApplyConstant.ServiceType.get(ApplyConstant.ServiceType.云生产.toInteger())) > 0) {
assignee = RedmineService.CP_REDMINE_ASSIGNEE;
} else if (apply.getTitle().indexOf(
ApplyConstant.ServiceType.get(ApplyConstant.ServiceType.监控.toInteger())) > 0) {
assignee = RedmineService.MONITOR_REDMINE_ASSIGNEE;
}
issue = RedmineService.getIssueBySubject(issue.getSubject(), mgr);
RedmineIssue redmineIssue = new RedmineIssue();
redmineIssue.setProjectId(projectId);
redmineIssue.setTrackerId(issue.getTracker().getId());
redmineIssue.setSubject(issue.getSubject());
redmineIssue.setAssignee(assignee);
redmineIssue.setStatus(RedmineConstant.Status.新建.toInteger());
redmineIssue.setIssueId(issue.getId());
redmineIssue.setApplyId(applyId);
comm.operateService.saveOrUpdate(redmineIssue);
// 指派人的User
User assigneeUser = comm.accountService.findUserByRedmineUserId(assignee);
// 发送工单处理邮件
comm.templateMailService.sendApplyOperateNotificationMail(apply, assigneeUser);
} else {
return false;
}
} else { // 不是终审人
logger.info("--->申请Apply 中间审批...");
// 当前审批人的的下一级审批人的审批顺序.如当前审批人的审批顺序是1的话,下一个就是2.
Integer auditOrder = auditFlow.getAuditOrder() + 1;
AuditFlow nextAuditFlow = this.findAuditFlowByAuditOrderAndFlowType(auditOrder, flowType);
apply.setAuditFlow(nextAuditFlow);
apply.setStatus(ApplyConstant.Status.审批中.toInteger());
// 发送邮件到下一个审批人
comm.templateMailService.sendApplyNotificationMail(apply, nextAuditFlow);
// 插入一条下级审批人所用到的audit.
this.saveSubAudit(userId, apply);
}
}
comm.applyService.saveOrUpateApply(apply);
this.saveOrUpdateAudit(audit);
return true;
}
/**
* 资源变更Resources的审批.
*
* <pre>
* 1-中间审批. 找到当前审批人的下级审批人,直接发送邮件.
* 2-终审审批. 首先拼装Redmine内容,并写入redmine;成功写入redmine后,创建工单对象.
* 3-审批退回. 发送退回邮件给资源变更Resources的申请人.
* </pre>
*
* @param audit
* 审批
* @param serviceTagId
* 服务标签serviceTagId
* @param userId
* 当前审批人
* @return
*/
@Transactional(readOnly = false)
public boolean saveAuditToResources(Audit audit, Integer serviceTagId, Integer userId) {
ServiceTag serviceTag = comm.serviceTagService.getServiceTag(serviceTagId);
List<Resources> resourcesList = comm.resourcesService.getCommitedResourcesListByServiceTagId(serviceTagId);
// true:通过页面审批 user为当前用户 ; false:通过邮件直接审批同意 根据传递过来的userId获得ID
User user = AccountConstant.FROM_PAGE_USER_ID.equals(userId) ? comm.accountService.getCurrentUser()
: comm.accountService.getUser(userId);
Integer flowType = AuditConstant.FlowType.资源申请_变更的审批流程.toInteger();
AuditFlow auditFlow = this.findAuditFlowByUserIdAndFlowType(user.getId(), flowType);
audit.setServiceTag(serviceTag);
audit.setAuditFlow(auditFlow);
audit.setCreateTime(new Date());
audit.setStatus(AuditConstant.AuditStatus.有效.toInteger());
if (audit.getResult().equals(AuditConstant.Result.不同意且退回.toString())) {
logger.info("--->资源变更Resource审批退回...");
serviceTag.setStatus(ResourcesConstant.Status.已退回.toInteger());
String contentText = "你的资源变更 " + serviceTag.getName() + " 已退回!<a href=\""
+ CONFIG_LOADER.getProperty("RESOURCE_URL") + "\">→点击进行处理</a><br>";
// 发送退回通知邮件
comm.simpleMailService.sendNotificationMail(serviceTag.getUser().getEmail(), "服务申请/变更退回邮件", contentText);
for (Resources resources : resourcesList) {
/* 资源的变更项还原至变更前 */
resources = comm.resourcesService.restoreResources(resources);
resources.setStatus(ResourcesConstant.Status.已退回.toInteger());
comm.resourcesService.saveOrUpdate(resources);
}
} else {
if (auditFlow.getIsFinal()) { // 终审人
logger.info("--->资源变更Resource终审审批...");
serviceTag.setStatus(ResourcesConstant.Status.已审批.toInteger());
String description = comm.redmineUtilService.resourcesRedmineDesc(serviceTag);
// 写入工单Issue到Redmine
Issue issue = new Issue();
Integer trackerId = RedmineConstant.Tracker.支持.toInteger();
Tracker tracker = new Tracker(trackerId, RedmineConstant.Tracker.get(trackerId));
issue.setTracker(tracker);
issue.setSubject(comm.applyService.generateTitle(serviceTag.getUser().getLoginName(), "change"));
issue.setPriorityId(serviceTag.getPriority());
issue.setDescription(description);
Integer projectId = RedmineConstant.Project.SobeyCloud运营.toInteger();
// 初始化第一接收人
RedmineManager mgr = RedmineService.FIRST_REDMINE_ASSIGNEE_REDMINEMANAGER;
boolean isCreated = RedmineService.createIssue(issue, projectId.toString(), mgr);
logger.info("--->资源变更Resource Redmine isCreated?" + isCreated);
if (isCreated) { // 写入Redmine成功
Integer assignee = RedmineService.FIRST_REDMINE_ASSIGNEE;
issue = RedmineService.getIssueBySubject(issue.getSubject(), mgr);
RedmineIssue redmineIssue = new RedmineIssue();
redmineIssue.setProjectId(projectId);
redmineIssue.setTrackerId(issue.getTracker().getId());
redmineIssue.setSubject(issue.getSubject());
redmineIssue.setAssignee(assignee);
redmineIssue.setStatus(RedmineConstant.Status.新建.toInteger());
redmineIssue.setIssueId(issue.getId());
redmineIssue.setServiceTagId(serviceTagId);
comm.operateService.saveOrUpdate(redmineIssue);
// 指派人的User
User assigneeUser = comm.accountService.findUserByRedmineUserId(assignee);
// 发送工单处理邮件
comm.templateMailService.sendResourcesOperateNotificationMail(serviceTag, assigneeUser);
for (Resources resources : resourcesList) {
// 写入redmine成功后,资源状态也随之改变为 4.已审批
resources.setStatus(ResourcesConstant.Status.已审批.toInteger());
comm.resourcesService.saveOrUpdate(resources);
}
} else {
return false;
}
} else { // 不是终审人
logger.info("--->资源变更Resource 中间审批...");
// 当前审批人的的下一级审批人的审批顺序.如当前审批人的审批顺序是1的话,下一个就是2.
Integer auditOrder = auditFlow.getAuditOrder() + 1;
AuditFlow nextAuditFlow = this.findAuditFlowByAuditOrderAndFlowType(auditOrder, flowType);
serviceTag.setAuditFlow(nextAuditFlow);
serviceTag.setStatus(ResourcesConstant.Status.审批中.toInteger());
// 发送邮件到下一个审批人
comm.templateMailService.sendResourcesNotificationMail(serviceTag, nextAuditFlow, audit);
for (Resources resources : resourcesList) {
// 更改资源的状态为 2.审批中.
resources.setStatus(ResourcesConstant.Status.审批中.toInteger());
comm.resourcesService.saveOrUpdate(resources);
}
// 插入一条下级审批人所用到的audit.
Audit nextAudit = this.saveSubAudit(userId, serviceTag);
/* 将变更详情拷贝一份,用于审批记录页面查看和历史记录. */
comm.changeHistoryService.copyHistory(resourcesList, nextAudit);
}
}
comm.serviceTagService.saveOrUpdate(serviceTag);
this.saveOrUpdateAudit(audit);
return true;
}
/**
* 初始化所有老审批记录.将指定服务标签下的审批记录全部设置为已过期状态.
*
* @param serviceTagId
*/
@Transactional(readOnly = false)
public void initAuditStatus(ServiceTag serviceTag) {
List<Audit> audits = auditDao.findByServiceTagId(serviceTag.getId());
for (Audit audit : audits) {
audit.setStatus(AuditConstant.AuditStatus.已过期.toInteger());
this.saveOrUpdateAudit(audit);
}
}
/**
* 初始化所有老审批记录.将指定服务申请下的审批记录全部设置为已过期状态.
*
* @param serviceTagId
*/
@Transactional(readOnly = false)
public void initAuditStatus(Apply apply) {
List<Audit> audits = auditDao.findByApplyId(apply.getId());
for (Audit audit : audits) {
audit.setStatus(AuditConstant.AuditStatus.已过期.toInteger());
this.saveOrUpdateAudit(audit);
}
}
/**
* 插入一条下级审批人所用到的audit.
*
* <pre>
* 服务申请时,Apply不能为null, ServiceTag为null
* 资源变更时,ServiceTag不能为null, Apply为null
* </pre>
*
*
* @param userId
* 当前审批人ID
* @param apply
* 服务申请
* @param serviceTag
* 资源变更
* @return
*/
@Transactional(readOnly = false)
private Audit saveSubAudit(Integer userId, Apply apply, ServiceTag serviceTag) {
User user = comm.accountService.getUser(userId);
// 上级领导
User leader = comm.accountService.getUser(user.getLeaderId());
// 上级领导的审批流程
Integer flowType = AuditConstant.FlowType.资源申请_变更的审批流程.toInteger();
AuditFlow auditFlow = comm.auditService.findAuditFlowByUserIdAndFlowType(leader.getId(), flowType);
Audit audit = new Audit();
audit.setApply(apply);
audit.setServiceTag(serviceTag);
audit.setAuditFlow(auditFlow);
audit.setStatus(AuditConstant.AuditStatus.待审批.toInteger());
return this.saveOrUpdateAudit(audit);
}
/**
* 插入一条下级审批人所用到的audit(服务申请Apply)
*
* @param userId
* 当前审批人ID
* @param apply
* 服务申请单
* @return
*/
@Transactional(readOnly = false)
public Audit saveSubAudit(Integer userId, Apply apply) {
return this.saveSubAudit(userId, apply, null);
}
/**
* 插入一条下级审批人所用到的audit(资源变更Resources)
*
* @param userId
* 当前审批人ID
* @param serviceTag
* 服务标签
* @return
*/
@Transactional(readOnly = false)
public Audit saveSubAudit(Integer userId, ServiceTag serviceTag) {
return this.saveSubAudit(userId, null, serviceTag);
}
}