办公V3
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package com.klp.hrm.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.bo.HrmFlowCcBo;
|
||||
import com.klp.hrm.domain.vo.HrmFlowCcVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHrmFlowCcService {
|
||||
|
||||
TableDataInfo<HrmFlowCcVo> queryPageList(HrmFlowCcBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmFlowCcVo> queryList(HrmFlowCcBo bo);
|
||||
|
||||
Boolean insert(HrmFlowCcBo bo);
|
||||
|
||||
/**
|
||||
* 批量新增抄送
|
||||
*/
|
||||
Boolean insertBatch(HrmFlowCcBo bo);
|
||||
|
||||
Boolean deleteByIds(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 标记已读
|
||||
*/
|
||||
Boolean markRead(Long ccId, Long userId);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ public interface IHrmFlowInstanceService {
|
||||
|
||||
TableDataInfo<HrmFlowInstanceVo> queryPageList(HrmFlowInstanceBo bo, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询当前登录用户“我的申请”(我发起的流程实例)分页列表
|
||||
*/
|
||||
TableDataInfo<HrmFlowInstanceVo> queryMyInstancePageList(HrmFlowInstanceBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmFlowInstanceVo> queryList(HrmFlowInstanceBo bo);
|
||||
|
||||
Boolean insertByBo(HrmFlowInstanceBo bo);
|
||||
|
||||
@@ -36,4 +36,14 @@ public interface IHrmFlowTaskService {
|
||||
* 撤回/取消
|
||||
*/
|
||||
Boolean withdraw(Long taskId, Long actionUserId, String remark);
|
||||
|
||||
/**
|
||||
* 转发/转办当前待办给其他审批人
|
||||
*/
|
||||
Boolean transfer(Long taskId, Long newAssigneeUserId, Long actionUserId, String remark);
|
||||
|
||||
/**
|
||||
* 根据业务类型 + 业务ID 查询当前待办任务(pending),用于详情页自动带出 currentTaskId
|
||||
*/
|
||||
HrmFlowTaskVo queryTodoByBiz(String bizType, Long bizId, Long assigneeUserId);
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.klp.hrm.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.bo.HrmGradeBo;
|
||||
import com.klp.hrm.domain.vo.HrmGradeVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHrmGradeService {
|
||||
HrmGradeVo queryById(Long gradeId);
|
||||
|
||||
TableDataInfo<HrmGradeVo> queryPageList(HrmGradeBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmGradeVo> queryList(HrmGradeBo bo);
|
||||
|
||||
Boolean insertByBo(HrmGradeBo bo);
|
||||
|
||||
Boolean updateByBo(HrmGradeBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.klp.hrm.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.bo.HrmOrgBo;
|
||||
import com.klp.hrm.domain.vo.HrmOrgVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHrmOrgService {
|
||||
HrmOrgVo queryById(Long orgId);
|
||||
|
||||
TableDataInfo<HrmOrgVo> queryPageList(HrmOrgBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmOrgVo> queryList(HrmOrgBo bo);
|
||||
|
||||
Boolean insertByBo(HrmOrgBo bo);
|
||||
|
||||
Boolean updateByBo(HrmOrgBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.klp.hrm.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.bo.HrmOvertimeReqBo;
|
||||
import com.klp.hrm.domain.vo.HrmOvertimeReqVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHrmOvertimeReqService {
|
||||
|
||||
HrmOvertimeReqVo queryById(Long bizId);
|
||||
|
||||
TableDataInfo<HrmOvertimeReqVo> queryPageList(HrmOvertimeReqBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmOvertimeReqVo> queryList(HrmOvertimeReqBo bo);
|
||||
|
||||
Boolean insertByBo(HrmOvertimeReqBo bo);
|
||||
|
||||
Boolean updateByBo(HrmOvertimeReqBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.klp.hrm.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.bo.HrmPositionBo;
|
||||
import com.klp.hrm.domain.vo.HrmPositionVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHrmPositionService {
|
||||
HrmPositionVo queryById(Long positionId);
|
||||
|
||||
TableDataInfo<HrmPositionVo> queryPageList(HrmPositionBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmPositionVo> queryList(HrmPositionBo bo);
|
||||
|
||||
Boolean insertByBo(HrmPositionBo bo);
|
||||
|
||||
Boolean updateByBo(HrmPositionBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.klp.hrm.service;
|
||||
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.bo.HrmReimburseReqBo;
|
||||
import com.klp.hrm.domain.vo.HrmReimburseReqVo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public interface IHrmReimburseReqService {
|
||||
|
||||
HrmReimburseReqVo queryById(Long bizId);
|
||||
|
||||
TableDataInfo<HrmReimburseReqVo> queryPageList(HrmReimburseReqBo bo, PageQuery pageQuery);
|
||||
|
||||
List<HrmReimburseReqVo> queryList(HrmReimburseReqBo bo);
|
||||
|
||||
Boolean insertByBo(HrmReimburseReqBo bo);
|
||||
|
||||
Boolean updateByBo(HrmReimburseReqBo bo);
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import com.klp.hrm.domain.HrmLeaveReq;
|
||||
import com.klp.hrm.domain.HrmReimburseReq;
|
||||
import com.klp.hrm.domain.HrmTravelReq;
|
||||
import com.klp.hrm.domain.HrmSealReq;
|
||||
import com.klp.hrm.mapper.HrmLeaveReqMapper;
|
||||
import com.klp.hrm.mapper.HrmReimburseReqMapper;
|
||||
import com.klp.hrm.mapper.HrmTravelReqMapper;
|
||||
import com.klp.hrm.mapper.HrmSealReqMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 将流程结果同步到业务表状态。
|
||||
* 业务表状态规范:pending/approved/rejected
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class BizStatusSyncHelper {
|
||||
|
||||
private final HrmLeaveReqMapper leaveReqMapper;
|
||||
private final HrmTravelReqMapper travelReqMapper;
|
||||
private final HrmSealReqMapper sealReqMapper;
|
||||
private final HrmReimburseReqMapper reimburseReqMapper;
|
||||
|
||||
public void setBizApproved(String bizType, Long bizId) {
|
||||
if (bizType == null || bizId == null) return;
|
||||
if ("leave".equalsIgnoreCase(bizType)) {
|
||||
HrmLeaveReq u = new HrmLeaveReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("approved");
|
||||
leaveReqMapper.updateById(u);
|
||||
} else if ("travel".equalsIgnoreCase(bizType)) {
|
||||
HrmTravelReq u = new HrmTravelReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("approved");
|
||||
travelReqMapper.updateById(u);
|
||||
} else if ("seal".equalsIgnoreCase(bizType)) {
|
||||
HrmSealReq u = new HrmSealReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("approved");
|
||||
sealReqMapper.updateById(u);
|
||||
} else if ("reimburse".equalsIgnoreCase(bizType)) {
|
||||
HrmReimburseReq u = new HrmReimburseReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("approved");
|
||||
reimburseReqMapper.updateById(u);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBizPending(String bizType, Long bizId) {
|
||||
if (bizType == null || bizId == null) return;
|
||||
if ("leave".equalsIgnoreCase(bizType)) {
|
||||
HrmLeaveReq u = new HrmLeaveReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("pending");
|
||||
leaveReqMapper.updateById(u);
|
||||
} else if ("travel".equalsIgnoreCase(bizType)) {
|
||||
HrmTravelReq u = new HrmTravelReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("pending");
|
||||
travelReqMapper.updateById(u);
|
||||
} else if ("seal".equalsIgnoreCase(bizType)) {
|
||||
HrmSealReq u = new HrmSealReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("pending");
|
||||
sealReqMapper.updateById(u);
|
||||
} else if ("reimburse".equalsIgnoreCase(bizType)) {
|
||||
HrmReimburseReq u = new HrmReimburseReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("pending");
|
||||
reimburseReqMapper.updateById(u);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBizRejected(String bizType, Long bizId) {
|
||||
if (bizType == null || bizId == null) return;
|
||||
if ("leave".equalsIgnoreCase(bizType)) {
|
||||
HrmLeaveReq u = new HrmLeaveReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("rejected");
|
||||
leaveReqMapper.updateById(u);
|
||||
} else if ("travel".equalsIgnoreCase(bizType)) {
|
||||
HrmTravelReq u = new HrmTravelReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("rejected");
|
||||
travelReqMapper.updateById(u);
|
||||
} else if ("seal".equalsIgnoreCase(bizType)) {
|
||||
HrmSealReq u = new HrmSealReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("rejected");
|
||||
sealReqMapper.updateById(u);
|
||||
} else if ("reimburse".equalsIgnoreCase(bizType)) {
|
||||
HrmReimburseReq u = new HrmReimburseReq();
|
||||
u.setBizId(bizId);
|
||||
u.setStatus("rejected");
|
||||
reimburseReqMapper.updateById(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.klp.common.core.domain.entity.SysDept;
|
||||
import com.klp.common.core.domain.entity.SysUser;
|
||||
import com.klp.common.exception.ServiceException;
|
||||
import com.klp.hrm.domain.HrmEmployee;
|
||||
import com.klp.hrm.domain.HrmFlowNode;
|
||||
import com.klp.hrm.mapper.HrmEmployeeMapper;
|
||||
import com.klp.system.mapper.SysDeptMapper;
|
||||
import com.klp.system.mapper.SysUserMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 流程审批人解析器:根据节点 approverRule/approverValue 解析出 assigneeUserId。
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class FlowAssigneeHelper {
|
||||
|
||||
private final HrmEmployeeMapper employeeMapper;
|
||||
private final SysDeptMapper deptMapper;
|
||||
private final SysUserMapper userMapper;
|
||||
|
||||
public List<Long> resolveAssigneeUserIds(HrmFlowNode node, Long startUserId) {
|
||||
if (node == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String rule = node.getApproverRule();
|
||||
|
||||
// 默认:如果没配规则,就当发起人
|
||||
if (rule == null || rule.trim().isEmpty() || "initiator".equalsIgnoreCase(rule)) {
|
||||
return startUserId == null ? Collections.emptyList() : Collections.singletonList(startUserId);
|
||||
}
|
||||
|
||||
switch (rule.toLowerCase()) {
|
||||
case "fixed_user":
|
||||
return resolveFixedUser(node);
|
||||
case "dept_leader":
|
||||
case "leader": // 兼容 "leader" 写法
|
||||
return resolveDeptLeader(startUserId);
|
||||
default:
|
||||
throw new ServiceException("审批人规则 '" + rule + "' 正在开发中,暂不支持");
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> resolveFixedUser(HrmFlowNode node) {
|
||||
String v = node.getApproverValue();
|
||||
if (v == null || v.trim().isEmpty()) {
|
||||
throw new ServiceException("节点未配置指定人员");
|
||||
}
|
||||
List<String> arr;
|
||||
try {
|
||||
arr = JSONUtil.toList(JSONUtil.parseArray(v), String.class);
|
||||
} catch (Exception e) {
|
||||
// 兼容非 JSON
|
||||
arr = Collections.singletonList(v);
|
||||
}
|
||||
|
||||
// 允许 value 既可能是 userId,也可能是 empId
|
||||
return arr.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.trim().isEmpty())
|
||||
.map(this::toUserId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Long> resolveDeptLeader(Long startUserId) {
|
||||
if (startUserId == null) {
|
||||
throw new ServiceException("无法解析部门负责人:发起人ID为空");
|
||||
}
|
||||
|
||||
// 1. 根据发起人ID查询员工信息,获取其部门ID
|
||||
HrmEmployee starter = employeeMapper.selectOne(Wrappers.<HrmEmployee>lambdaQuery().eq(HrmEmployee::getUserId, startUserId).last("limit 1"));
|
||||
if (starter == null || starter.getDeptId() == null) {
|
||||
throw new ServiceException("无法解析部门负责人:发起人未关联员工或部门信息");
|
||||
}
|
||||
|
||||
// 2. 根据部门ID查询部门信息,获取负责人姓名
|
||||
SysDept dept = deptMapper.selectById(starter.getDeptId());
|
||||
if (dept == null) {
|
||||
throw new ServiceException("无法解析部门负责人:部门信息不存在");
|
||||
}
|
||||
if (!StringUtils.hasText(dept.getLeader())) {
|
||||
throw new ServiceException("无法解析部门负责人:部门 '" + dept.getDeptName() + "' 未配置负责人");
|
||||
}
|
||||
|
||||
// 3. sys_dept.leader 存的是 nick_name:优先按 nickName 匹配,兜底按 userName 匹配
|
||||
SysUser leaderUser = userMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
|
||||
.eq(SysUser::getNickName, dept.getLeader())
|
||||
.last("limit 1"));
|
||||
if (leaderUser == null) {
|
||||
leaderUser = userMapper.selectOne(Wrappers.<SysUser>lambdaQuery()
|
||||
.eq(SysUser::getUserName, dept.getLeader())
|
||||
.last("limit 1"));
|
||||
}
|
||||
if (leaderUser == null) {
|
||||
throw new ServiceException("无法解析部门负责人:部门负责人(nickName/userName)='" + dept.getLeader() + "' 未匹配到系统用户");
|
||||
}
|
||||
|
||||
return Collections.singletonList(leaderUser.getUserId());
|
||||
}
|
||||
|
||||
private Long toUserId(String s) {
|
||||
// 纯数字:可能是 userId 或 empId
|
||||
Long id;
|
||||
try {
|
||||
id = Long.parseLong(s);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 先当 userId 直接用
|
||||
// 但为了兼容你们前端可能传 empId,这里做一次 hrm_employee.user_id 映射
|
||||
HrmEmployee emp = employeeMapper.selectOne(Wrappers.<HrmEmployee>lambdaQuery().eq(HrmEmployee::getEmpId, id));
|
||||
if (emp != null && emp.getUserId() != null) {
|
||||
return emp.getUserId();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import com.klp.hrm.domain.bo.HrmEmpOrgPositionBo;
|
||||
import com.klp.hrm.domain.vo.HrmEmpOrgPositionVo;
|
||||
import com.klp.hrm.mapper.HrmEmpOrgPositionMapper;
|
||||
import com.klp.hrm.service.IHrmEmpOrgPositionService;
|
||||
import com.klp.system.service.ISysDeptService;
|
||||
import com.klp.system.service.ISysPostService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -23,23 +25,58 @@ import java.util.List;
|
||||
public class HrmEmpOrgPositionServiceImpl implements IHrmEmpOrgPositionService {
|
||||
|
||||
private final HrmEmpOrgPositionMapper baseMapper;
|
||||
private final ISysDeptService deptService;
|
||||
private final ISysPostService postService;
|
||||
|
||||
@Override
|
||||
public HrmEmpOrgPositionVo queryById(Long relId) {
|
||||
return baseMapper.selectVoById(relId);
|
||||
HrmEmpOrgPositionVo vo = baseMapper.selectVoById(relId);
|
||||
fillNames(vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmEmpOrgPositionVo> queryPageList(HrmEmpOrgPositionBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmEmpOrgPosition> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmEmpOrgPositionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
if (result.getRecords() != null) {
|
||||
result.getRecords().forEach(this::fillNames);
|
||||
}
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmEmpOrgPositionVo> queryList(HrmEmpOrgPositionBo bo) {
|
||||
LambdaQueryWrapper<HrmEmpOrgPosition> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
List<HrmEmpOrgPositionVo> list = baseMapper.selectVoList(lqw);
|
||||
if (list != null) {
|
||||
list.forEach(this::fillNames);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void fillNames(HrmEmpOrgPositionVo vo) {
|
||||
if (vo == null) return;
|
||||
if (vo.getDeptId() != null) {
|
||||
try {
|
||||
com.klp.common.core.domain.entity.SysDept dept = deptService.selectDeptById(vo.getDeptId());
|
||||
if (dept != null) {
|
||||
vo.setDeptName(dept.getDeptName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略查询失败
|
||||
}
|
||||
}
|
||||
if (vo.getPostId() != null) {
|
||||
try {
|
||||
com.klp.system.domain.SysPost post = postService.selectPostById(vo.getPostId());
|
||||
if (post != null) {
|
||||
vo.setPostName(post.getPostName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略查询失败
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,8 +102,8 @@ public class HrmEmpOrgPositionServiceImpl implements IHrmEmpOrgPositionService {
|
||||
LambdaQueryWrapper<HrmEmpOrgPosition> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getRelId() != null, HrmEmpOrgPosition::getRelId, bo.getRelId());
|
||||
lqw.eq(bo.getEmpId() != null, HrmEmpOrgPosition::getEmpId, bo.getEmpId());
|
||||
lqw.eq(bo.getOrgId() != null, HrmEmpOrgPosition::getOrgId, bo.getOrgId());
|
||||
lqw.eq(bo.getPositionId() != null, HrmEmpOrgPosition::getPositionId, bo.getPositionId());
|
||||
lqw.eq(bo.getDeptId() != null, HrmEmpOrgPosition::getDeptId, bo.getDeptId());
|
||||
lqw.eq(bo.getPostId() != null, HrmEmpOrgPosition::getPostId, bo.getPostId());
|
||||
lqw.eq(bo.getIsPrimary() != null, HrmEmpOrgPosition::getIsPrimary, bo.getIsPrimary());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@@ -64,11 +64,12 @@ public class HrmEmployeeServiceImpl implements IHrmEmployeeService {
|
||||
private LambdaQueryWrapper<HrmEmployee> buildQueryWrapper(HrmEmployeeBo bo) {
|
||||
LambdaQueryWrapper<HrmEmployee> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getEmpId() != null, HrmEmployee::getEmpId, bo.getEmpId());
|
||||
lqw.eq(bo.getUserId() != null, HrmEmployee::getUserId, bo.getUserId());
|
||||
lqw.like(bo.getEmpNo() != null, HrmEmployee::getEmpNo, bo.getEmpNo());
|
||||
lqw.like(bo.getEmpName() != null, HrmEmployee::getEmpName, bo.getEmpName());
|
||||
lqw.eq(bo.getStatus() != null, HrmEmployee::getStatus, bo.getStatus());
|
||||
lqw.eq(bo.getMainOrgId() != null, HrmEmployee::getMainOrgId, bo.getMainOrgId());
|
||||
lqw.eq(bo.getMainPositionId() != null, HrmEmployee::getMainPositionId, bo.getMainPositionId());
|
||||
lqw.eq(bo.getDeptId() != null, HrmEmployee::getDeptId, bo.getDeptId());
|
||||
lqw.eq(bo.getPostId() != null, HrmEmployee::getPostId, bo.getPostId());
|
||||
lqw.orderByAsc(HrmEmployee::getEmpNo);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmFlowCc;
|
||||
import com.klp.hrm.domain.bo.HrmFlowCcBo;
|
||||
import com.klp.hrm.domain.vo.HrmFlowCcVo;
|
||||
import com.klp.hrm.mapper.HrmFlowCcMapper;
|
||||
import com.klp.hrm.service.IHrmFlowCcService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class HrmFlowCcServiceImpl implements IHrmFlowCcService {
|
||||
|
||||
private final HrmFlowCcMapper baseMapper;
|
||||
private final com.klp.hrm.mapper.HrmLeaveReqMapper leaveReqMapper;
|
||||
private final com.klp.hrm.mapper.HrmTravelReqMapper travelReqMapper;
|
||||
private final com.klp.hrm.mapper.HrmReimburseReqMapper reimburseReqMapper;
|
||||
private final com.klp.hrm.mapper.HrmSealReqMapper sealReqMapper;
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmFlowCcVo> queryPageList(HrmFlowCcBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmFlowCc> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmFlowCcVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
fillBizDisplay(result.getRecords());
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmFlowCcVo> queryList(HrmFlowCcBo bo) {
|
||||
LambdaQueryWrapper<HrmFlowCc> lqw = buildQueryWrapper(bo);
|
||||
List<HrmFlowCcVo> list = baseMapper.selectVoList(lqw);
|
||||
fillBizDisplay(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insert(HrmFlowCcBo bo) {
|
||||
HrmFlowCc add = BeanUtil.toBean(bo, HrmFlowCc.class);
|
||||
return baseMapper.insert(add) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean insertBatch(HrmFlowCcBo bo) {
|
||||
if (bo == null) {
|
||||
return false;
|
||||
}
|
||||
List<Long> ids = bo.getCcUserIds();
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
// 兼容单人
|
||||
if (bo.getCcUserId() == null) {
|
||||
return false;
|
||||
}
|
||||
return insert(bo);
|
||||
}
|
||||
|
||||
boolean ok = true;
|
||||
for (Long uid : ids) {
|
||||
if (uid == null) {
|
||||
continue;
|
||||
}
|
||||
HrmFlowCc one = BeanUtil.toBean(bo, HrmFlowCc.class);
|
||||
one.setCcId(null);
|
||||
one.setCcUserId(uid);
|
||||
one.setReadFlag(0);
|
||||
ok = baseMapper.insert(one) > 0 && ok;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteByIds(Collection<Long> ids) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean markRead(Long ccId, Long userId) {
|
||||
if (ccId == null || userId == null) {
|
||||
return false;
|
||||
}
|
||||
return baseMapper.update(
|
||||
null,
|
||||
Wrappers.<HrmFlowCc>lambdaUpdate()
|
||||
.set(HrmFlowCc::getReadFlag, 1)
|
||||
.eq(HrmFlowCc::getCcId, ccId)
|
||||
.eq(HrmFlowCc::getCcUserId, userId)
|
||||
.eq(HrmFlowCc::getDelFlag, 0)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmFlowCc> buildQueryWrapper(HrmFlowCcBo bo) {
|
||||
LambdaQueryWrapper<HrmFlowCc> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getCcUserId() != null, HrmFlowCc::getCcUserId, bo.getCcUserId());
|
||||
lqw.eq(bo.getInstId() != null, HrmFlowCc::getInstId, bo.getInstId());
|
||||
lqw.eq(bo.getBizType() != null, HrmFlowCc::getBizType, bo.getBizType());
|
||||
lqw.eq(bo.getReadFlag() != null, HrmFlowCc::getReadFlag, bo.getReadFlag());
|
||||
lqw.orderByDesc(HrmFlowCc::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回填业务展示字段:bizTypeName + bizTitle
|
||||
*/
|
||||
private void fillBizDisplay(List<HrmFlowCcVo> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (HrmFlowCcVo vo : list) {
|
||||
if (vo == null) {
|
||||
continue;
|
||||
}
|
||||
String bt = vo.getBizType();
|
||||
Long bid = vo.getBizId();
|
||||
if (bt == null || bid == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 默认兜底
|
||||
vo.setBizTypeName(bt);
|
||||
vo.setBizTitle(vo.getRemark());
|
||||
|
||||
try {
|
||||
if ("leave".equalsIgnoreCase(bt)) {
|
||||
vo.setBizTypeName("请假申请");
|
||||
com.klp.hrm.domain.HrmLeaveReq req = leaveReqMapper.selectById(bid);
|
||||
if (req != null) {
|
||||
vo.setBizTitle(req.getLeaveType() + " | " + req.getReason());
|
||||
}
|
||||
} else if ("travel".equalsIgnoreCase(bt)) {
|
||||
vo.setBizTypeName("出差申请");
|
||||
com.klp.hrm.domain.HrmTravelReq req = travelReqMapper.selectById(bid);
|
||||
if (req != null) {
|
||||
vo.setBizTitle(req.getDestination() + " | " + req.getReason());
|
||||
}
|
||||
} else if ("reimburse".equalsIgnoreCase(bt)) {
|
||||
vo.setBizTypeName("报销申请");
|
||||
com.klp.hrm.domain.HrmReimburseReq req = reimburseReqMapper.selectById(bid);
|
||||
if (req != null) {
|
||||
vo.setBizTitle(req.getReimburseType() + " | " + req.getTotalAmount());
|
||||
}
|
||||
} else if ("seal".equalsIgnoreCase(bt)) {
|
||||
vo.setBizTypeName("用印申请");
|
||||
com.klp.hrm.domain.HrmSealReq req = sealReqMapper.selectById(bid);
|
||||
if (req != null) {
|
||||
vo.setBizTitle(req.getSealType() + " | " + req.getPurpose());
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// 不影响列表返回
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
@@ -34,6 +33,7 @@ public class HrmFlowInstanceServiceImpl implements IHrmFlowInstanceService {
|
||||
private final HrmFlowNodeMapper nodeMapper;
|
||||
private final HrmFlowTaskMapper taskMapper;
|
||||
private final HrmFormDataMapper formDataMapper;
|
||||
private final FlowAssigneeHelper assigneeHelper;
|
||||
|
||||
@Override
|
||||
public HrmFlowInstanceVo queryById(Long instId) {
|
||||
@@ -43,7 +43,44 @@ public class HrmFlowInstanceServiceImpl implements IHrmFlowInstanceService {
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long startInstance(HrmFlowStartBo bo) {
|
||||
// 找首节点
|
||||
// 无模板:自选审批人(一次性审批)
|
||||
if (bo.getTplId() == null) {
|
||||
if (bo.getManualAssigneeUserId() == null) {
|
||||
throw new RuntimeException("未配置流程模板且未指定审批人,无法启动");
|
||||
}
|
||||
HrmFlowInstance inst = new HrmFlowInstance();
|
||||
inst.setTplId(0L);
|
||||
inst.setBizType(bo.getBizType());
|
||||
inst.setBizId(bo.getBizId());
|
||||
inst.setStatus("running");
|
||||
inst.setCurrentNodeId(0L);
|
||||
inst.setStartUserId(bo.getStartUserId());
|
||||
baseMapper.insert(inst);
|
||||
|
||||
// 保存表单(可选)
|
||||
if (bo.getContentJson() != null) {
|
||||
HrmFormData form = new HrmFormData();
|
||||
form.setBizType(bo.getBizType());
|
||||
form.setBizId(bo.getBizId());
|
||||
form.setContentJson(bo.getContentJson());
|
||||
formDataMapper.insert(form);
|
||||
}
|
||||
|
||||
// 创建唯一待办:指派给自选审批人
|
||||
HrmFlowTask task = new HrmFlowTask();
|
||||
task.setInstId(inst.getInstId());
|
||||
task.setNodeId(0L);
|
||||
task.setAssigneeUserId(bo.getManualAssigneeUserId());
|
||||
task.setStatus("pending");
|
||||
// 关键:写入业务关联字段,便于审批中心联查业务数据
|
||||
task.setBizType(bo.getBizType());
|
||||
task.setBizId(bo.getBizId());
|
||||
taskMapper.insert(task);
|
||||
|
||||
return inst.getInstId();
|
||||
}
|
||||
|
||||
// 有模板:找首节点
|
||||
HrmFlowNode firstNode = nodeMapper.selectOne(Wrappers.<HrmFlowNode>lambdaQuery()
|
||||
.eq(HrmFlowNode::getTplId, bo.getTplId())
|
||||
.orderByAsc(HrmFlowNode::getOrderNo)
|
||||
@@ -70,14 +107,20 @@ public class HrmFlowInstanceServiceImpl implements IHrmFlowInstanceService {
|
||||
formDataMapper.insert(form);
|
||||
}
|
||||
|
||||
// 创建首个待办
|
||||
// 创建首个待办:按首节点规则分配审批人,而不是写死发起人
|
||||
List<Long> assignees = assigneeHelper.resolveAssigneeUserIds(firstNode, bo.getStartUserId());
|
||||
if (assignees == null || assignees.isEmpty()) {
|
||||
throw new RuntimeException("未解析到首节点审批人,无法启动流程");
|
||||
}
|
||||
// 简化:目前每个节点只创建一个待办(取第一个审批人)
|
||||
HrmFlowTask task = new HrmFlowTask();
|
||||
task.setInstId(inst.getInstId());
|
||||
task.setNodeId(firstNode.getNodeId());
|
||||
//task.setAssigneeUserId(Objects.requireNonNullElse(bo.getStartUserId(), 0L));
|
||||
// 修复:使用三元运算符替代 Objects.requireNonNullElse
|
||||
task.setAssigneeUserId(bo.getStartUserId() != null ? bo.getStartUserId() : 0L);
|
||||
task.setAssigneeUserId(assignees.get(0));
|
||||
task.setStatus("pending");
|
||||
// 关键:写入业务关联字段,便于审批中心联查业务数据
|
||||
task.setBizType(bo.getBizType());
|
||||
task.setBizId(bo.getBizId());
|
||||
taskMapper.insert(task);
|
||||
|
||||
return inst.getInstId();
|
||||
@@ -90,6 +133,19 @@ public class HrmFlowInstanceServiceImpl implements IHrmFlowInstanceService {
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmFlowInstanceVo> queryMyInstancePageList(HrmFlowInstanceBo bo, PageQuery pageQuery) {
|
||||
// “我的申请”= 当前登录用户发起的流程实例
|
||||
// 这里不信任前端传 startUserId,统一以登录态为准
|
||||
Long userId = com.klp.common.helper.LoginHelper.getUserId();
|
||||
LambdaQueryWrapper<HrmFlowInstance> lqw = buildQueryWrapper(bo);
|
||||
lqw.eq(userId != null, HrmFlowInstance::getStartUserId, userId);
|
||||
// 默认按发起时间倒序(如果表里没createTime字段,这里可改成instId倒序)
|
||||
lqw.orderByDesc(HrmFlowInstance::getInstId);
|
||||
Page<HrmFlowInstanceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmFlowInstanceVo> queryList(HrmFlowInstanceBo bo) {
|
||||
LambdaQueryWrapper<HrmFlowInstance> lqw = buildQueryWrapper(bo);
|
||||
|
||||
@@ -4,30 +4,24 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmFlowTask;
|
||||
import com.klp.hrm.domain.HrmFlowAction;
|
||||
import com.klp.hrm.domain.HrmFlowInstance;
|
||||
import com.klp.hrm.domain.HrmFlowNode;
|
||||
import com.klp.hrm.domain.*;
|
||||
import com.klp.hrm.domain.bo.HrmFlowTaskBo;
|
||||
import com.klp.hrm.domain.bo.HrmSealStampBo;
|
||||
import com.klp.hrm.domain.vo.HrmFlowTaskVo;
|
||||
import com.klp.hrm.mapper.HrmFlowActionMapper;
|
||||
import com.klp.hrm.mapper.HrmFlowInstanceMapper;
|
||||
import com.klp.hrm.mapper.HrmFlowNodeMapper;
|
||||
import com.klp.hrm.mapper.HrmFlowTaskMapper;
|
||||
import com.klp.hrm.mapper.*;
|
||||
import com.klp.hrm.service.IHrmFlowTaskService;
|
||||
import com.klp.hrm.service.IHrmSealReqService;
|
||||
import com.klp.hrm.service.IHrmFlowCcService;
|
||||
import com.klp.hrm.domain.bo.HrmFlowCcBo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
@@ -38,6 +32,16 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
private final HrmFlowInstanceMapper instanceMapper;
|
||||
private final HrmFlowNodeMapper nodeMapper;
|
||||
private final IHrmSealReqService sealReqService;
|
||||
private final IHrmFlowCcService ccService;
|
||||
private final FlowAssigneeHelper assigneeHelper;
|
||||
private final BizStatusSyncHelper bizStatusSyncHelper;
|
||||
|
||||
// 注入四个业务Mapper
|
||||
private final HrmLeaveReqMapper leaveReqMapper;
|
||||
private final HrmTravelReqMapper travelReqMapper;
|
||||
private final HrmSealReqMapper sealReqMapper;
|
||||
private final HrmReimburseReqMapper reimburseReqMapper;
|
||||
private final ObjectMapper objectMapper; // Spring Boot 默认提供
|
||||
|
||||
@Override
|
||||
public HrmFlowTaskVo queryById(Long taskId) {
|
||||
@@ -48,15 +52,66 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
public TableDataInfo<HrmFlowTaskVo> queryPageList(HrmFlowTaskBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmFlowTask> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmFlowTaskVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
// 分页查询同样需要填充业务数据
|
||||
if (result.getRecords() != null && !result.getRecords().isEmpty()) {
|
||||
fillBizData(result.getRecords());
|
||||
}
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmFlowTaskVo> queryList(HrmFlowTaskBo bo) {
|
||||
LambdaQueryWrapper<HrmFlowTask> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
List<HrmFlowTaskVo> tasks = baseMapper.selectVoList(lqw);
|
||||
if (tasks != null && !tasks.isEmpty()) {
|
||||
fillBizData(tasks);
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private void fillBizData(List<HrmFlowTaskVo> tasks) {
|
||||
// 1. 按 bizType 分组,并收集 bizId
|
||||
Map<String, List<Long>> bizIdsByType = tasks.stream()
|
||||
.filter(t -> t.getBizType() != null && t.getBizId() != null)
|
||||
.collect(Collectors.groupingBy(
|
||||
HrmFlowTaskVo::getBizType,
|
||||
Collectors.mapping(HrmFlowTaskVo::getBizId, Collectors.toList())
|
||||
));
|
||||
|
||||
// 2. 批量查询各个业务表
|
||||
Map<String, Object> bizDataMap = new HashMap<>();
|
||||
|
||||
bizIdsByType.forEach((bizType, bizIds) -> {
|
||||
if (bizIds.isEmpty()) return;
|
||||
switch (bizType) {
|
||||
case "leave":
|
||||
leaveReqMapper.selectBatchIds(bizIds).forEach(d -> bizDataMap.put("leave_" + d.getBizId(), d));
|
||||
break;
|
||||
case "travel":
|
||||
travelReqMapper.selectBatchIds(bizIds).forEach(d -> bizDataMap.put("travel_" + d.getBizId(), d));
|
||||
break;
|
||||
case "seal":
|
||||
sealReqMapper.selectBatchIds(bizIds).forEach(d -> bizDataMap.put("seal_" + d.getBizId(), d));
|
||||
break;
|
||||
case "reimburse":
|
||||
reimburseReqMapper.selectBatchIds(bizIds).forEach(d -> bizDataMap.put("reimburse_" + d.getBizId(), d));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 3. 将业务数据回填到 task 的 bizData 字段
|
||||
tasks.forEach(task -> {
|
||||
String key = task.getBizType() + "_" + task.getBizId();
|
||||
Object data = bizDataMap.get(key);
|
||||
if (data != null) {
|
||||
// 将实体对象转换为 Map<String, Object>,方便前端使用
|
||||
Map<String, Object> dataMap = objectMapper.convertValue(data, Map.class);
|
||||
task.setBizData(dataMap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(HrmFlowTaskBo bo) {
|
||||
@@ -87,6 +142,31 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
if (inst == null) {
|
||||
return false;
|
||||
}
|
||||
// 无模板一次性审批(tplId=0 或 nodeId=0):直接结束流程
|
||||
if (inst.getTplId() != null && inst.getTplId() == 0L) {
|
||||
// 记录动作
|
||||
saveAction(taskId, inst.getInstId(), "approve", remark, actionUserId);
|
||||
if (stampBo != null) {
|
||||
saveAction(taskId, inst.getInstId(), "stamp", "盖章", actionUserId);
|
||||
}
|
||||
task.setStatus("approved");
|
||||
baseMapper.updateById(task);
|
||||
inst.setStatus("approved");
|
||||
inst.setCurrentNodeId(null);
|
||||
instanceMapper.updateById(inst);
|
||||
// 若业务为用印,更新用印状态为已批准,并执行盖章
|
||||
if ("seal".equalsIgnoreCase(inst.getBizType())) {
|
||||
sealReqService.updateStatus(inst.getBizId(), "approved");
|
||||
if (stampBo != null) {
|
||||
// 盖章动作也写入流转历史
|
||||
saveAction(taskId, inst.getInstId(), "stamp", "盖章", actionUserId);
|
||||
sealReqService.stampWithJava(inst.getBizId(), stampBo);
|
||||
}
|
||||
}
|
||||
bizStatusSyncHelper.setBizApproved(inst.getBizType(), inst.getBizId());
|
||||
return true;
|
||||
}
|
||||
|
||||
HrmFlowNode currentNode = nodeMapper.selectById(task.getNodeId());
|
||||
if (currentNode == null) {
|
||||
return false;
|
||||
@@ -103,14 +183,48 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
.gt(HrmFlowNode::getOrderNo, currentNode.getOrderNo())
|
||||
.orderByAsc(HrmFlowNode::getOrderNo)
|
||||
.last("limit 1"));
|
||||
|
||||
// 若后续存在抄送节点,需要:写抄送记录 + 自动继续流转到下一个非抄送节点
|
||||
while (next != null && "cc".equalsIgnoreCase(next.getNodeType())) {
|
||||
// 写抄送记录
|
||||
List<Long> ccUserIds = assigneeHelper.resolveAssigneeUserIds(next, inst.getStartUserId());
|
||||
if (ccUserIds != null && !ccUserIds.isEmpty()) {
|
||||
HrmFlowCcBo ccBo = new HrmFlowCcBo();
|
||||
ccBo.setInstId(inst.getInstId());
|
||||
ccBo.setBizType(inst.getBizType());
|
||||
ccBo.setBizId(inst.getBizId());
|
||||
ccBo.setNodeId(next.getNodeId());
|
||||
// hrm_flow_node 没有 nodeName 字段,这里用 remark 作为可读名兜底
|
||||
ccBo.setNodeName(next.getRemark());
|
||||
ccBo.setFromUserId(actionUserId);
|
||||
ccBo.setCcUserIds(ccUserIds);
|
||||
ccBo.setReadFlag(0);
|
||||
ccBo.setRemark("系统自动抄送");
|
||||
ccService.insertBatch(ccBo);
|
||||
}
|
||||
|
||||
// 继续找下一个节点
|
||||
HrmFlowNode tmp = next;
|
||||
next = nodeMapper.selectOne(Wrappers.<HrmFlowNode>lambdaQuery()
|
||||
.eq(HrmFlowNode::getTplId, inst.getTplId())
|
||||
.gt(HrmFlowNode::getOrderNo, tmp.getOrderNo())
|
||||
.orderByAsc(HrmFlowNode::getOrderNo)
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
if (next != null) {
|
||||
inst.setCurrentNodeId(next.getNodeId());
|
||||
inst.setStatus("running");
|
||||
instanceMapper.updateById(inst);
|
||||
// 创建下一节点待办:按下一节点规则分配审批人
|
||||
List<Long> assignees = assigneeHelper.resolveAssigneeUserIds(next, inst.getStartUserId());
|
||||
if (assignees == null || assignees.isEmpty()) {
|
||||
throw new RuntimeException("未解析到下一节点审批人,无法流转");
|
||||
}
|
||||
HrmFlowTask nextTask = new HrmFlowTask();
|
||||
nextTask.setInstId(inst.getInstId());
|
||||
nextTask.setNodeId(next.getNodeId());
|
||||
nextTask.setAssigneeUserId(actionUserId != null ? actionUserId : 0L);
|
||||
nextTask.setAssigneeUserId(assignees.get(0));
|
||||
nextTask.setStatus("pending");
|
||||
baseMapper.insert(nextTask);
|
||||
} else {
|
||||
@@ -144,10 +258,12 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
task.setStatus("rejected");
|
||||
baseMapper.updateById(task);
|
||||
inst.setStatus("rejected");
|
||||
inst.setCurrentNodeId(null);
|
||||
instanceMapper.updateById(inst);
|
||||
if ("seal".equalsIgnoreCase(inst.getBizType())) {
|
||||
sealReqService.updateStatus(inst.getBizId(), "rejected");
|
||||
}
|
||||
bizStatusSyncHelper.setBizRejected(inst.getBizType(), inst.getBizId());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -165,6 +281,23 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
saveAction(taskId, inst.getInstId(), "withdraw", remark, actionUserId);
|
||||
task.setStatus("withdraw");
|
||||
baseMapper.updateById(task);
|
||||
// 无模板一次性审批:撤回后业务回到 pending,并重新生成一个待办(仍然只允许一次审批)
|
||||
if (inst.getTplId() != null && inst.getTplId() == 0L) {
|
||||
inst.setStatus("running");
|
||||
instanceMapper.updateById(inst);
|
||||
|
||||
HrmFlowTask reTask = new HrmFlowTask();
|
||||
reTask.setInstId(inst.getInstId());
|
||||
reTask.setNodeId(0L);
|
||||
// 撤回后回到“原审批人”(撤回前的办理人)
|
||||
reTask.setAssigneeUserId(task.getAssigneeUserId());
|
||||
reTask.setStatus("pending");
|
||||
baseMapper.insert(reTask);
|
||||
|
||||
bizStatusSyncHelper.setBizPending(inst.getBizType(), inst.getBizId());
|
||||
return true;
|
||||
}
|
||||
|
||||
// 撤回后重新生成当前节点待办,实例回到running
|
||||
inst.setStatus("running");
|
||||
instanceMapper.updateById(inst);
|
||||
@@ -191,6 +324,43 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
actionMapper.insert(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean transfer(Long taskId, Long newAssigneeUserId, Long actionUserId, String remark) {
|
||||
HrmFlowTask task = baseMapper.selectById(taskId);
|
||||
if (task == null) {
|
||||
return false;
|
||||
}
|
||||
if (!"pending".equalsIgnoreCase(task.getStatus())) {
|
||||
return false;
|
||||
}
|
||||
HrmFlowInstance inst = instanceMapper.selectById(task.getInstId());
|
||||
if (inst == null) {
|
||||
return false;
|
||||
}
|
||||
// 记录动作
|
||||
saveAction(taskId, inst.getInstId(), "transfer", remark, actionUserId);
|
||||
|
||||
// 更新办理人
|
||||
HrmFlowTask u = new HrmFlowTask();
|
||||
u.setTaskId(taskId);
|
||||
u.setAssigneeUserId(newAssigneeUserId);
|
||||
return baseMapper.updateById(u) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HrmFlowTaskVo queryTodoByBiz(String bizType, Long bizId, Long assigneeUserId) {
|
||||
// 只取“待办 pending”的一条(理论上同一 biz 同一时刻最多一条待办)
|
||||
LambdaQueryWrapper<HrmFlowTask> lqw = Wrappers.<HrmFlowTask>lambdaQuery()
|
||||
.eq(bizType != null, HrmFlowTask::getBizType, bizType)
|
||||
.eq(bizId != null, HrmFlowTask::getBizId, bizId)
|
||||
.eq(assigneeUserId != null, HrmFlowTask::getAssigneeUserId, assigneeUserId)
|
||||
.eq(HrmFlowTask::getStatus, "pending")
|
||||
.orderByDesc(HrmFlowTask::getTaskId)
|
||||
.last("limit 1");
|
||||
return baseMapper.selectVoOne(lqw);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmFlowTask> buildQueryWrapper(HrmFlowTaskBo bo) {
|
||||
LambdaQueryWrapper<HrmFlowTask> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getTaskId() != null, HrmFlowTask::getTaskId, bo.getTaskId());
|
||||
@@ -198,6 +368,8 @@ public class HrmFlowTaskServiceImpl implements IHrmFlowTaskService {
|
||||
lqw.eq(bo.getNodeId() != null, HrmFlowTask::getNodeId, bo.getNodeId());
|
||||
lqw.eq(bo.getAssigneeUserId() != null, HrmFlowTask::getAssigneeUserId, bo.getAssigneeUserId());
|
||||
lqw.eq(bo.getStatus() != null, HrmFlowTask::getStatus, bo.getStatus());
|
||||
lqw.eq(bo.getBizType() != null, HrmFlowTask::getBizType, bo.getBizType());
|
||||
lqw.eq(bo.getBizId() != null, HrmFlowTask::getBizId, bo.getBizId());
|
||||
return lqw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmGrade;
|
||||
import com.klp.hrm.domain.bo.HrmGradeBo;
|
||||
import com.klp.hrm.domain.vo.HrmGradeVo;
|
||||
import com.klp.hrm.mapper.HrmGradeMapper;
|
||||
import com.klp.hrm.service.IHrmGradeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class HrmGradeServiceImpl implements IHrmGradeService {
|
||||
|
||||
private final HrmGradeMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public HrmGradeVo queryById(Long gradeId) {
|
||||
return baseMapper.selectVoById(gradeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmGradeVo> queryPageList(HrmGradeBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmGrade> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmGradeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmGradeVo> queryList(HrmGradeBo bo) {
|
||||
LambdaQueryWrapper<HrmGrade> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(HrmGradeBo bo) {
|
||||
HrmGrade add = BeanUtil.toBean(bo, HrmGrade.class);
|
||||
return baseMapper.insert(add) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(HrmGradeBo bo) {
|
||||
HrmGrade update = BeanUtil.toBean(bo, HrmGrade.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmGrade> buildQueryWrapper(HrmGradeBo bo) {
|
||||
LambdaQueryWrapper<HrmGrade> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getGradeId() != null, HrmGrade::getGradeId, bo.getGradeId());
|
||||
lqw.like(bo.getGradeCode() != null, HrmGrade::getGradeCode, bo.getGradeCode());
|
||||
lqw.like(bo.getGradeName() != null, HrmGrade::getGradeName, bo.getGradeName());
|
||||
lqw.orderByAsc(HrmGrade::getLevelNo);
|
||||
return lqw;
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,8 @@ public class HrmHeadcountPlanServiceImpl implements IHrmHeadcountPlanService {
|
||||
private LambdaQueryWrapper<HrmHeadcountPlan> buildQueryWrapper(HrmHeadcountPlanBo bo) {
|
||||
LambdaQueryWrapper<HrmHeadcountPlan> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getPlanId() != null, HrmHeadcountPlan::getPlanId, bo.getPlanId());
|
||||
lqw.eq(bo.getOrgId() != null, HrmHeadcountPlan::getOrgId, bo.getOrgId());
|
||||
lqw.eq(bo.getPositionId() != null, HrmHeadcountPlan::getPositionId, bo.getPositionId());
|
||||
lqw.eq(bo.getDeptId() != null, HrmHeadcountPlan::getDeptId, bo.getDeptId());
|
||||
lqw.eq(bo.getPostId() != null, HrmHeadcountPlan::getPostId, bo.getPostId());
|
||||
lqw.eq(bo.getYear() != null, HrmHeadcountPlan::getYear, bo.getYear());
|
||||
lqw.eq(bo.getMonth() != null, HrmHeadcountPlan::getMonth, bo.getMonth());
|
||||
lqw.orderByDesc(HrmHeadcountPlan::getYear).orderByDesc(HrmHeadcountPlan::getMonth);
|
||||
|
||||
@@ -6,10 +6,15 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.common.helper.LoginHelper;
|
||||
import com.klp.hrm.domain.HrmFlowTemplate;
|
||||
import com.klp.hrm.domain.HrmLeaveReq;
|
||||
import com.klp.hrm.domain.bo.HrmFlowStartBo;
|
||||
import com.klp.hrm.domain.bo.HrmLeaveReqBo;
|
||||
import com.klp.hrm.domain.vo.HrmLeaveReqVo;
|
||||
import com.klp.hrm.mapper.HrmFlowTemplateMapper;
|
||||
import com.klp.hrm.mapper.HrmLeaveReqMapper;
|
||||
import com.klp.hrm.service.IHrmFlowInstanceService;
|
||||
import com.klp.hrm.service.IHrmLeaveReqService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -23,6 +28,8 @@ import java.util.List;
|
||||
public class HrmLeaveReqServiceImpl implements IHrmLeaveReqService {
|
||||
|
||||
private final HrmLeaveReqMapper baseMapper;
|
||||
private final HrmFlowTemplateMapper flowTemplateMapper;
|
||||
private final IHrmFlowInstanceService flowInstanceService;
|
||||
|
||||
@Override
|
||||
public HrmLeaveReqVo queryById(Long bizId) {
|
||||
@@ -47,7 +54,48 @@ public class HrmLeaveReqServiceImpl implements IHrmLeaveReqService {
|
||||
public Boolean insertByBo(HrmLeaveReqBo bo) {
|
||||
HrmLeaveReq add = BeanUtil.toBean(bo, HrmLeaveReq.class);
|
||||
add.setStatus(defaultStatus(add.getStatus()));
|
||||
return baseMapper.insert(add) > 0;
|
||||
boolean ok = baseMapper.insert(add) > 0;
|
||||
|
||||
// 当提交为 pending 时,自动启动流程实例(hrm_flow_instance/hrm_flow_task)
|
||||
if (ok && "pending".equalsIgnoreCase(add.getStatus())) {
|
||||
|
||||
Long startUserId = LoginHelper.getUserId();
|
||||
|
||||
// 1) 优先:前端明确选择了模板(tplId 不为空)
|
||||
HrmFlowTemplate tpl = null;
|
||||
if (bo.getTplId() != null) {
|
||||
tpl = flowTemplateMapper.selectOne(Wrappers.<HrmFlowTemplate>lambdaQuery()
|
||||
.eq(HrmFlowTemplate::getTplId, bo.getTplId())
|
||||
.eq(HrmFlowTemplate::getBizType, "leave")
|
||||
.eq(HrmFlowTemplate::getEnabled, 1)
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
// 2) 手动审批:前端选择了手动审批人时,不允许兜底去找模板,否则会意外走到模板里的规则(例如 dept_leader)
|
||||
boolean manualMode = bo.getTplId() == null && bo.getManualAssigneeUserId() != null;
|
||||
|
||||
// 3) 兜底:只有在既没有 tplId、也没有手动审批人的情况下,才自动选择最新启用模板
|
||||
if (!manualMode && tpl == null) {
|
||||
tpl = flowTemplateMapper.selectOne(Wrappers.<HrmFlowTemplate>lambdaQuery()
|
||||
.eq(HrmFlowTemplate::getBizType, "leave")
|
||||
.eq(HrmFlowTemplate::getEnabled, 1)
|
||||
.orderByDesc(HrmFlowTemplate::getVersion)
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
HrmFlowStartBo start = new HrmFlowStartBo();
|
||||
if (tpl != null) {
|
||||
start.setTplId(tpl.getTplId());
|
||||
}
|
||||
start.setManualAssigneeUserId(bo.getManualAssigneeUserId());
|
||||
start.setBizType("leave");
|
||||
start.setBizId(add.getBizId());
|
||||
start.setStartUserId(startUserId);
|
||||
|
||||
flowInstanceService.startInstance(start);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmOrg;
|
||||
import com.klp.hrm.domain.bo.HrmOrgBo;
|
||||
import com.klp.hrm.domain.vo.HrmOrgVo;
|
||||
import com.klp.hrm.mapper.HrmOrgMapper;
|
||||
import com.klp.hrm.service.IHrmOrgService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class HrmOrgServiceImpl implements IHrmOrgService {
|
||||
|
||||
private final HrmOrgMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public HrmOrgVo queryById(Long orgId) {
|
||||
return baseMapper.selectVoById(orgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmOrgVo> queryPageList(HrmOrgBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmOrg> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmOrgVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmOrgVo> queryList(HrmOrgBo bo) {
|
||||
LambdaQueryWrapper<HrmOrg> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(HrmOrgBo bo) {
|
||||
HrmOrg add = BeanUtil.toBean(bo, HrmOrg.class);
|
||||
validEntityBeforeSave(add);
|
||||
return baseMapper.insert(add) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(HrmOrgBo bo) {
|
||||
HrmOrg update = BeanUtil.toBean(bo, HrmOrg.class);
|
||||
validEntityBeforeSave(update);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
// 可按需添加校验
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmOrg> buildQueryWrapper(HrmOrgBo bo) {
|
||||
LambdaQueryWrapper<HrmOrg> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getOrgId() != null, HrmOrg::getOrgId, bo.getOrgId());
|
||||
lqw.eq(bo.getParentId() != null, HrmOrg::getParentId, bo.getParentId());
|
||||
lqw.like(bo.getOrgCode() != null, HrmOrg::getOrgCode, bo.getOrgCode());
|
||||
lqw.like(bo.getOrgName() != null, HrmOrg::getOrgName, bo.getOrgName());
|
||||
lqw.eq(bo.getOrgType() != null, HrmOrg::getOrgType, bo.getOrgType());
|
||||
lqw.eq(bo.getStatus() != null, HrmOrg::getStatus, bo.getStatus());
|
||||
lqw.orderByAsc(HrmOrg::getParentId).orderByAsc(HrmOrg::getOrderNum);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
private void validEntityBeforeSave(HrmOrg entity) {
|
||||
// 可添加唯一性校验: org_code
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmOvertimeReq;
|
||||
import com.klp.hrm.domain.bo.HrmOvertimeReqBo;
|
||||
import com.klp.hrm.domain.vo.HrmOvertimeReqVo;
|
||||
import com.klp.hrm.mapper.HrmOvertimeReqMapper;
|
||||
import com.klp.hrm.service.IHrmOvertimeReqService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class HrmOvertimeReqServiceImpl implements IHrmOvertimeReqService {
|
||||
|
||||
private final HrmOvertimeReqMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public HrmOvertimeReqVo queryById(Long bizId) {
|
||||
return baseMapper.selectVoById(bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmOvertimeReqVo> queryPageList(HrmOvertimeReqBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmOvertimeReq> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmOvertimeReqVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmOvertimeReqVo> queryList(HrmOvertimeReqBo bo) {
|
||||
LambdaQueryWrapper<HrmOvertimeReq> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(HrmOvertimeReqBo bo) {
|
||||
HrmOvertimeReq add = BeanUtil.toBean(bo, HrmOvertimeReq.class);
|
||||
add.setStatus(defaultStatus(add.getStatus()));
|
||||
return baseMapper.insert(add) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(HrmOvertimeReqBo bo) {
|
||||
HrmOvertimeReq update = BeanUtil.toBean(bo, HrmOvertimeReq.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmOvertimeReq> buildQueryWrapper(HrmOvertimeReqBo bo) {
|
||||
LambdaQueryWrapper<HrmOvertimeReq> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getBizId() != null, HrmOvertimeReq::getBizId, bo.getBizId());
|
||||
lqw.eq(bo.getEmpId() != null, HrmOvertimeReq::getEmpId, bo.getEmpId());
|
||||
lqw.eq(bo.getOtType() != null, HrmOvertimeReq::getOtType, bo.getOtType());
|
||||
lqw.eq(bo.getStatus() != null, HrmOvertimeReq::getStatus, bo.getStatus());
|
||||
lqw.eq(bo.getStartTime() != null, HrmOvertimeReq::getStartTime, bo.getStartTime());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
private String defaultStatus(String status) {
|
||||
return status == null ? "draft" : status;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmPosition;
|
||||
import com.klp.hrm.domain.bo.HrmPositionBo;
|
||||
import com.klp.hrm.domain.vo.HrmPositionVo;
|
||||
import com.klp.hrm.mapper.HrmPositionMapper;
|
||||
import com.klp.hrm.service.IHrmPositionService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class HrmPositionServiceImpl implements IHrmPositionService {
|
||||
|
||||
private final HrmPositionMapper baseMapper;
|
||||
|
||||
@Override
|
||||
public HrmPositionVo queryById(Long positionId) {
|
||||
return baseMapper.selectVoById(positionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmPositionVo> queryPageList(HrmPositionBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmPosition> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmPositionVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmPositionVo> queryList(HrmPositionBo bo) {
|
||||
LambdaQueryWrapper<HrmPosition> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(HrmPositionBo bo) {
|
||||
HrmPosition add = BeanUtil.toBean(bo, HrmPosition.class);
|
||||
return baseMapper.insert(add) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(HrmPositionBo bo) {
|
||||
HrmPosition update = BeanUtil.toBean(bo, HrmPosition.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmPosition> buildQueryWrapper(HrmPositionBo bo) {
|
||||
LambdaQueryWrapper<HrmPosition> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getPositionId() != null, HrmPosition::getPositionId, bo.getPositionId());
|
||||
lqw.like(bo.getPositionCode() != null, HrmPosition::getPositionCode, bo.getPositionCode());
|
||||
lqw.like(bo.getPositionName() != null, HrmPosition::getPositionName, bo.getPositionName());
|
||||
lqw.eq(bo.getGradeId() != null, HrmPosition::getGradeId, bo.getGradeId());
|
||||
lqw.eq(bo.getStatus() != null, HrmPosition::getStatus, bo.getStatus());
|
||||
lqw.orderByAsc(HrmPosition::getOrderNum);
|
||||
return lqw;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import com.klp.common.helper.LoginHelper;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmFlowTemplate;
|
||||
import com.klp.hrm.domain.HrmReimburseReq;
|
||||
import com.klp.hrm.domain.bo.HrmFlowStartBo;
|
||||
import com.klp.hrm.domain.bo.HrmReimburseReqBo;
|
||||
import com.klp.hrm.domain.vo.HrmReimburseReqVo;
|
||||
import com.klp.hrm.mapper.HrmFlowTemplateMapper;
|
||||
import com.klp.hrm.mapper.HrmReimburseReqMapper;
|
||||
import com.klp.hrm.service.IHrmFlowInstanceService;
|
||||
import com.klp.hrm.service.IHrmReimburseReqService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class HrmReimburseReqServiceImpl implements IHrmReimburseReqService {
|
||||
|
||||
private final HrmReimburseReqMapper baseMapper;
|
||||
private final HrmFlowTemplateMapper flowTemplateMapper;
|
||||
private final IHrmFlowInstanceService flowInstanceService;
|
||||
|
||||
@Override
|
||||
public HrmReimburseReqVo queryById(Long bizId) {
|
||||
return baseMapper.selectVoById(bizId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<HrmReimburseReqVo> queryPageList(HrmReimburseReqBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<HrmReimburseReq> lqw = buildQueryWrapper(bo);
|
||||
Page<HrmReimburseReqVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HrmReimburseReqVo> queryList(HrmReimburseReqBo bo) {
|
||||
LambdaQueryWrapper<HrmReimburseReq> lqw = buildQueryWrapper(bo);
|
||||
return baseMapper.selectVoList(lqw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean insertByBo(HrmReimburseReqBo bo) {
|
||||
HrmReimburseReq add = BeanUtil.toBean(bo, HrmReimburseReq.class);
|
||||
add.setStatus(defaultStatus(add.getStatus()));
|
||||
boolean ok = baseMapper.insert(add) > 0;
|
||||
|
||||
if (ok && "pending".equalsIgnoreCase(add.getStatus())) {
|
||||
// 选择启用的最高版本模板(允许无模板:走自选审批人一次性审批)
|
||||
HrmFlowTemplate tpl = flowTemplateMapper.selectOne(Wrappers.<HrmFlowTemplate>lambdaQuery()
|
||||
.eq(HrmFlowTemplate::getBizType, "reimburse")
|
||||
.eq(HrmFlowTemplate::getEnabled, 1)
|
||||
.orderByDesc(HrmFlowTemplate::getVersion)
|
||||
.last("limit 1"));
|
||||
|
||||
Long startUserId = LoginHelper.getUserId();
|
||||
HrmFlowStartBo start = new HrmFlowStartBo();
|
||||
if (tpl != null) {
|
||||
start.setTplId(tpl.getTplId());
|
||||
}
|
||||
start.setManualAssigneeUserId(bo.getManualAssigneeUserId());
|
||||
start.setBizType("reimburse");
|
||||
start.setBizId(add.getBizId());
|
||||
start.setStartUserId(startUserId);
|
||||
flowInstanceService.startInstance(start);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(HrmReimburseReqBo bo) {
|
||||
HrmReimburseReq update = BeanUtil.toBean(bo, HrmReimburseReq.class);
|
||||
return baseMapper.updateById(update) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<HrmReimburseReq> buildQueryWrapper(HrmReimburseReqBo bo) {
|
||||
LambdaQueryWrapper<HrmReimburseReq> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getBizId() != null, HrmReimburseReq::getBizId, bo.getBizId());
|
||||
lqw.eq(bo.getEmpId() != null, HrmReimburseReq::getEmpId, bo.getEmpId());
|
||||
lqw.eq(bo.getReimburseType() != null, HrmReimburseReq::getReimburseType, bo.getReimburseType());
|
||||
lqw.eq(bo.getStatus() != null, HrmReimburseReq::getStatus, bo.getStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
private String defaultStatus(String status) {
|
||||
return status == null ? "draft" : status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.common.exception.ServiceException;
|
||||
import com.klp.common.helper.LoginHelper;
|
||||
import com.klp.hrm.config.StampProperties;
|
||||
import com.klp.hrm.domain.HrmSealReq;
|
||||
import com.klp.hrm.domain.bo.HrmSealReqBo;
|
||||
@@ -72,14 +73,20 @@ public class HrmSealReqServiceImpl implements IHrmSealReqService {
|
||||
add.setStatus(defaultStatus(add.getStatus()));
|
||||
validEntityBeforeSave(add);
|
||||
boolean ok = baseMapper.insert(add) > 0;
|
||||
// 提交即发起流程(可选)
|
||||
if (ok && bo.getFlowTplId() != null) {
|
||||
|
||||
// 只要传入了 tplId 或 manualAssigneeUserId,就代表需要启动流程
|
||||
Long tplId = bo.getTplId() != null ? bo.getTplId() : bo.getFlowTplId();
|
||||
boolean shouldStartFlow = tplId != null || bo.getManualAssigneeUserId() != null;
|
||||
|
||||
if (ok && shouldStartFlow) {
|
||||
HrmFlowStartBo start = new HrmFlowStartBo();
|
||||
start.setTplId(bo.getFlowTplId());
|
||||
start.setTplId(tplId);
|
||||
start.setManualAssigneeUserId(bo.getManualAssigneeUserId());
|
||||
start.setBizType("seal");
|
||||
start.setBizId(add.getBizId());
|
||||
start.setStartUserId(bo.getStartUserId());
|
||||
start.setStartUserId(LoginHelper.getUserId());
|
||||
start.setContentJson(bo.getContentJson());
|
||||
|
||||
flowInstanceService.startInstance(start);
|
||||
// 更新状态为流转中
|
||||
updateStatus(add.getBizId(), "running");
|
||||
@@ -171,20 +178,70 @@ public class HrmSealReqServiceImpl implements IHrmSealReqService {
|
||||
PDPage page = document.getPage(pageIndex);
|
||||
PDRectangle mediaBox = page.getMediaBox();
|
||||
|
||||
// 坐标约定说明:
|
||||
// 1) PdfStamper emitStamp():输出的是“左下角为原点”的 viewport 像素坐标(xPx,yPx)。
|
||||
// 2) PDFBox:使用“左下角为原点”的 PDF 用户单位(pt)。
|
||||
// 3) 因此:后端只做比例换算(px -> pt),不要再做 y 翻转。
|
||||
|
||||
byte[] imgBytes = IoUtil.readBytes(imgIn);
|
||||
PDImageXObject image = PDImageXObject.createFromByteArray(document, imgBytes, "stamp");
|
||||
|
||||
float width = cmd.getWidthPx() != null ? cmd.getWidthPx() : image.getWidth();
|
||||
float height = cmd.getHeightPx() != null ? cmd.getHeightPx() : image.getHeight();
|
||||
float x = cmd.getXPx();
|
||||
float y = cmd.getYPx();
|
||||
// 方案B:严格使用印章原图尺寸(不使用前端传来的 widthPx/heightPx)
|
||||
float stampW = image.getWidth();
|
||||
float stampH = image.getHeight();
|
||||
|
||||
// 保证不越界
|
||||
// 坐标换算:前端 xPx/yPx 是基于 pdf.js 渲染后的 viewport 像素;PDFBox 使用的是 PDF 用户单位(pt)。
|
||||
// 前端 PdfStamper emitStamp() 输出的 yPx 已经是“左下角为原点”的 viewport 像素坐标。
|
||||
float pdfW = mediaBox.getWidth();
|
||||
float pdfH = mediaBox.getHeight();
|
||||
|
||||
float x;
|
||||
float y;
|
||||
if (cmd.getViewportWidth() != null && cmd.getViewportHeight() != null
|
||||
&& cmd.getViewportWidth() > 0 && cmd.getViewportHeight() > 0) {
|
||||
float ratioX = pdfW / cmd.getViewportWidth();
|
||||
float ratioY = pdfH / cmd.getViewportHeight();
|
||||
x = cmd.getXPx() * ratioX;
|
||||
|
||||
// yPx 的约定:
|
||||
// - PdfStamper emitStamp() 输出的是“左下角为原点”的 viewport 像素坐标
|
||||
// - PDFBox 也以左下角为原点
|
||||
// 因此只需要做比例换算,不要再翻转。
|
||||
y = cmd.getYPx() * ratioY;
|
||||
} else {
|
||||
// 兼容:若老客户端未传 viewport 尺寸,则按“直接当作 PDF 坐标”处理(可能有偏差)
|
||||
x = cmd.getXPx();
|
||||
y = cmd.getYPx();
|
||||
}
|
||||
|
||||
// 方案B1:等比例缩放到“外接框不超过 4cm x 4cm”(保持比例)
|
||||
// PDFBox 的单位是 pt:1 inch = 72pt,1cm = 72/2.54 ≈ 28.346pt
|
||||
final float maxCm = 4.0f;
|
||||
final float maxPt = maxCm * 72.0f / 2.54f;
|
||||
// 缩放因子:让宽高都不超过 maxPt
|
||||
float scale = Math.min(maxPt / stampW, maxPt / stampH);
|
||||
// 不放大(避免小图被放大到很糊);如需允许放大,可删除这行限制
|
||||
scale = Math.min(scale, 1.0f);
|
||||
|
||||
float width = stampW * scale;
|
||||
float height = stampH * scale;
|
||||
|
||||
log.info("[stamp] pdfW={},pdfH={}, viewportW={},viewportH={}, ratioX={},ratioY={}, xPx={},yPx={}, x={},y={}, stampW={},stampH={}, drawW={},drawH={}",
|
||||
pdfW, pdfH,
|
||||
cmd.getViewportWidth(), cmd.getViewportHeight(),
|
||||
(cmd.getViewportWidth()!=null&&cmd.getViewportWidth()>0)?(pdfW/cmd.getViewportWidth()):null,
|
||||
(cmd.getViewportHeight()!=null&&cmd.getViewportHeight()>0)?(pdfH/cmd.getViewportHeight()):null,
|
||||
cmd.getXPx(), cmd.getYPx(),
|
||||
x, y,
|
||||
stampW, stampH,
|
||||
width, height);
|
||||
|
||||
// 保证不越界:方案B下不裁剪宽高(否则会变形/不是原图大小),而是回退到页面内。
|
||||
if (x + width > mediaBox.getWidth()) {
|
||||
width = mediaBox.getWidth() - x;
|
||||
x = Math.max(0, mediaBox.getWidth() - width);
|
||||
}
|
||||
if (y + height > mediaBox.getHeight()) {
|
||||
height = mediaBox.getHeight() - y;
|
||||
y = Math.max(0, mediaBox.getHeight() - height);
|
||||
}
|
||||
|
||||
try (PDPageContentStream contentStream = new PDPageContentStream(document, page,
|
||||
@@ -197,9 +254,8 @@ public class HrmSealReqServiceImpl implements IHrmSealReqService {
|
||||
OssClient storage = OssFactory.instance();
|
||||
UploadResult uploadResult = storage.uploadSuffix(bos.toByteArray(), ".pdf", "application/pdf");
|
||||
return uploadResult.getUrl();
|
||||
} catch (ServiceException se) {
|
||||
throw se;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("PDF盖章失败", e);
|
||||
throw new ServiceException("PDF盖章失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package com.klp.hrm.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.klp.common.helper.LoginHelper;
|
||||
import com.klp.hrm.domain.HrmFlowTemplate;
|
||||
import com.klp.hrm.domain.HrmTravelReq;
|
||||
import com.klp.hrm.domain.bo.HrmFlowStartBo;
|
||||
import com.klp.hrm.domain.bo.HrmTravelReqBo;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.hrm.domain.HrmTravelReq;
|
||||
import com.klp.hrm.domain.bo.HrmTravelReqBo;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.klp.hrm.domain.vo.HrmTravelReqVo;
|
||||
import com.klp.hrm.mapper.HrmFlowTemplateMapper;
|
||||
import com.klp.hrm.mapper.HrmTravelReqMapper;
|
||||
import com.klp.hrm.service.IHrmFlowInstanceService;
|
||||
import com.klp.hrm.service.IHrmTravelReqService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -23,6 +28,8 @@ import java.util.List;
|
||||
public class HrmTravelReqServiceImpl implements IHrmTravelReqService {
|
||||
|
||||
private final HrmTravelReqMapper baseMapper;
|
||||
private final HrmFlowTemplateMapper flowTemplateMapper;
|
||||
private final IHrmFlowInstanceService flowInstanceService;
|
||||
|
||||
@Override
|
||||
public HrmTravelReqVo queryById(Long bizId) {
|
||||
@@ -47,7 +54,33 @@ public class HrmTravelReqServiceImpl implements IHrmTravelReqService {
|
||||
public Boolean insertByBo(HrmTravelReqBo bo) {
|
||||
HrmTravelReq add = BeanUtil.toBean(bo, HrmTravelReq.class);
|
||||
add.setStatus(defaultStatus(add.getStatus()));
|
||||
return baseMapper.insert(add) > 0;
|
||||
boolean ok = baseMapper.insert(add) > 0;
|
||||
|
||||
// 业务表状态规范:pending/approved/rejected
|
||||
// 当提交为 pending 时,自动启动流程实例(hrm_flow_instance/hrm_flow_task)
|
||||
if (ok && "pending".equalsIgnoreCase(add.getStatus())) {
|
||||
// 选择启用的最高版本模板(允许无模板:走自选审批人一次性审批)
|
||||
HrmFlowTemplate tpl = flowTemplateMapper.selectOne(Wrappers.<HrmFlowTemplate>lambdaQuery()
|
||||
.eq(HrmFlowTemplate::getBizType, "travel")
|
||||
.eq(HrmFlowTemplate::getEnabled, 1)
|
||||
.orderByDesc(HrmFlowTemplate::getVersion)
|
||||
.last("limit 1"));
|
||||
|
||||
Long startUserId = LoginHelper.getUserId();
|
||||
|
||||
HrmFlowStartBo start = new HrmFlowStartBo();
|
||||
if (tpl != null) {
|
||||
start.setTplId(tpl.getTplId());
|
||||
}
|
||||
start.setManualAssigneeUserId(bo.getManualAssigneeUserId());
|
||||
start.setBizType("travel");
|
||||
start.setBizId(add.getBizId());
|
||||
start.setStartUserId(startUserId);
|
||||
// 暂不保存 contentJson
|
||||
flowInstanceService.startInstance(start);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user