This commit is contained in:
2025-12-30 13:47:53 +08:00
parent f1637501b2
commit a623c5673f
137 changed files with 11031 additions and 4043 deletions

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {
// 不影响列表返回
}
}
}
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 的单位是 pt1 inch = 72pt1cm = 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());
}

View File

@@ -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