This commit is contained in:
2025-03-09 19:04:02 +08:00
parent 3d22504f11
commit 6cfa7d7528
9 changed files with 713 additions and 25 deletions

View File

@@ -48,6 +48,14 @@ public class OaSalaryController extends BaseController {
return iOaSalaryService.queryPageList(bo, pageQuery);
}
/**
* 批量写入user到salary中
*/
@GetMapping("/import")
public R<Void> importSalaryUser() {
return iOaSalaryService.importSalaryUser();
}
/**
* 导出薪资管理列表
*/

View File

@@ -44,6 +44,7 @@ public class OaSalaryBo extends BaseEntity {
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@NotBlank(message = "发放时间不能为空")
private Date payTime;
/**

View File

@@ -18,4 +18,7 @@ import org.apache.ibatis.annotations.Param;
public interface OaSalaryMapper extends BaseMapperPlus<OaSalaryMapper, OaSalary, OaSalaryVo> {
Page<OaSalaryVo> selectStaffVoPage(Page<Object> build,@Param(Constants.WRAPPER) Wrapper<OaSalary> lqw);
OaSalary selectVoByUserId(Long userId);
}

View File

@@ -1,5 +1,6 @@
package com.ruoyi.oa.service;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.oa.domain.OaSalary;
import com.ruoyi.oa.domain.vo.OaSalaryVo;
import com.ruoyi.oa.domain.bo.OaSalaryBo;
@@ -53,4 +54,10 @@ public interface IOaSalaryService {
* @param bo
*/
void calcSalary(OaSalaryBo bo);
/**
* 导入当月所有人的记录
* @return
*/
R<Void> importSalaryUser();
}

View File

@@ -1,19 +1,38 @@
package com.ruoyi.oa.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.service.UserService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.flowable.utils.TaskUtils;
import com.ruoyi.oa.domain.OaSalaryItem;
import com.ruoyi.oa.domain.vo.CalcResultVo;
import com.ruoyi.oa.domain.vo.SysOaHolidayVo;
import com.ruoyi.oa.mapper.OaSalaryItemMapper;
import com.ruoyi.oa.service.IOaSalaryItemService;
import com.ruoyi.oa.service.ISysOaHolidayService;
import com.ruoyi.oa.utils.OwnHttpUtils;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.workflow.mapper.WfDeployFormMapper;
import com.ruoyi.workflow.service.IWfTaskService;
import liquibase.pro.packaged.A;
import lombok.RequiredArgsConstructor;
import org.apache.catalina.User;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.Deployment;
import org.flowable.task.api.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.oa.domain.bo.OaSalaryBo;
@@ -22,9 +41,15 @@ import com.ruoyi.oa.domain.OaSalary;
import com.ruoyi.oa.mapper.OaSalaryMapper;
import com.ruoyi.oa.service.IOaSalaryService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
/**
* 薪资管理Service业务层处理
@@ -38,9 +63,29 @@ public class OaSalaryServiceImpl implements IOaSalaryService {
private final OaSalaryMapper baseMapper;
private final IWfTaskService wfTaskService;
private final WfDeployFormMapper deployFormMapper;
@Resource
private RepositoryService repositoryService;
@Resource
private TaskService taskService;
@Resource
private HistoryService historyService;
@Autowired
private ISysUserService userService;
@Autowired
private ISysOaHolidayService holidayService;
@Autowired
private OaSalaryItemMapper salaryItemMapper;
/**
* 查询薪资管理
*/
@@ -59,6 +104,19 @@ public class OaSalaryServiceImpl implements IOaSalaryService {
return TableDataInfo.build(result);
}
@Override
public R<Void> importSalaryUser() {
SysUser sysUser = new SysUser();
List<SysUser> sysUsers = userService.selectUserList(sysUser);
for (SysUser user : sysUsers) {
OaSalary oaSalary = new OaSalary();
oaSalary.setUserId(user.getUserId());
oaSalary.setBaseSalary(Double.valueOf(user.getLaborCost()));
oaSalary.setPayTime(new Date());
baseMapper.insert(oaSalary);
}
return R.ok();
}
/**
* 查询薪资管理列表
*/
@@ -120,23 +178,279 @@ public class OaSalaryServiceImpl implements IOaSalaryService {
return baseMapper.deleteBatchIds(ids) > 0;
}
/**
* 计算薪资
* @param bo
*/
@Override
public void calcSalary(OaSalaryBo bo) {
// 1先获取对应的json路径 "/home/ubuntu/lyq/fad_oa_kqdeal/202411.json"
String jsonPath = OwnHttpUtils.getJsonName(bo.getMonthStr(), bo.getFilePath());
// 2拿到所有的用户
SysUser sysUser = new SysUser();
Date yearMonth = StringToDate(combineYearMonth(bo.getMonthStr()));
List<SysUser> sysUsers = userService.selectUserList(sysUser);
// 3遍历user列表 拿出所有的nickName分别拿到他们的json 进行第二步分析
for (SysUser user : sysUsers) {
OaSalary salary = baseMapper.selectVoByUserId(user.getUserId());
if (salary == null) {
return;
}
// 3.1 拿到一个人打卡机的记录
CalcResultVo res = OwnHttpUtils.getBaseCalc(jsonPath,user.getNickName());
// 3.2 通过打卡机拿到的记录去和差旅表、请假申请表、假期表做比对
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
.startedBy(String.valueOf(user.getUserId()))
.orderByProcessInstanceStartTime()
.desc();
// 3.3 查询到他所有的任务 其中包括了差旅申请和请假申请的事
List<HistoricProcessInstance> list = historicProcessInstanceQuery.list();
for (HistoricProcessInstance historicProcessInstance : list) {
Deployment deployment = repositoryService.createDeploymentQuery()
.deploymentId(historicProcessInstance.getDeploymentId()).singleResult();
// 这个deployment可以拿到他的分类信息 这里我需要的是 trip和absence
if (deployment.getCategory()=="absence" || deployment.getCategory()=="trip") {
TaskQuery taskQuery = taskService.createTaskQuery()
.taskCategory(deployment.getCategory());
taskQuery.list().forEach(task -> {
Map<String, Object> processVariables = task.getProcessVariables();
String startTime = String.valueOf(processVariables.get("startTime"));
String endTime = String.valueOf(processVariables.get("endTime"));
List<String> timeRange = getDateRange(startTime, endTime);
if (deployment.getCategory().equals("absence")){
// 这里的item为1时则为是上午 否则为下午
Long startTimeItem = (Long) processVariables.get("startTimeItem");
Long endTimeItem = (Long) processVariables.get("endTimeItem");
}
// 计算缺席的某天是否请假或者出差
List<String> absentDates = res.getAbsentDates();
List<String> realDates = new ArrayList<>();
List<String> real05Dates = new ArrayList<>();
for (String absentDate : absentDates) {
// 形如2024-11-01
String fullDate = combineYearMonthAndDay(bo.getMonthStr(), absentDate);
boolean contains = timeRange.contains(fullDate);
// 如果为false 则说明此天没有请假或者出差则将其记录
if (!contains) {
realDates.add(fullDate);
}
}
for (String real05Date : real05Dates) {
String fullDate = combineYearMonthAndDay(bo.getMonthStr(), real05Date);
boolean contains = timeRange.contains(fullDate);
if (!contains) {
real05Dates.add(fullDate);
}
}
// 这次已经将缺勤拿出来了 现在还要判断今天是否为节假日
List<String> realDate2 =new ArrayList<>();
for (String realDate : realDates) {
SysOaHolidayVo sysOaHolidayVo = holidayService.queryHolidayByDate(StringToDate(realDate));
if (sysOaHolidayVo != null && sysOaHolidayVo.getType()!=0 ) {
// 如果不为节假日则加入进去
realDate2.add(realDate);
}
}
List<String> real05Date2 =new ArrayList<>();
for (String real05Date : real05Dates) {
SysOaHolidayVo sysOaHolidayVo = holidayService.queryHolidayByDate(StringToDate(real05Date));
if (sysOaHolidayVo != null && sysOaHolidayVo.getType()!=0 ) {
// 如果不为节假日则加入进去
real05Date2.add(real05Date);
}
// TODO 思考这里有两种情况 首先可能是车间员工他们有一个打卡表可以通过Attendence表进行比对但是是否还会出现上下午的问题呢
// TODO 第二种情况是职工,他们只能通过差旅报销去判断
}
buildItemAbsent(realDate2, salary,1.0,yearMonth);
buildItemAbsent(real05Date2, salary,0.5,yearMonth);
});
}
}
// 3.4创建其他的item包括了好多罚金
// 3.4.1 迟到罚金
StringBuilder sb= new StringBuilder();
for (String lateDate : res.getLateDates()) {
sb.append(lateDate);
sb.append(",");
}
sb.append("迟到日期");
OaSalaryItem oaSalaryItem = new OaSalaryItem();
oaSalaryItem.setSalaryId(salary.getSalaryId());
oaSalaryItem.setPrice(Double.valueOf(res.getLateFee()));
oaSalaryItem.setType(4L);
oaSalaryItem.setFlag(0L);
oaSalaryItem.setReason(sb.toString());
oaSalaryItem.setSignTime(yearMonth);
salaryItemMapper.insert(oaSalaryItem);
// 3.4.2 早退罚金
sb= new StringBuilder();
for (String lateDate : res.getLeaveEarly10Dates()) {
sb.append(lateDate);
sb.append(",");
}
sb.append("早退日期十分钟");
for (String lateDate : res.getLeaveEarly30Dates()) {
sb.append(lateDate);
sb.append(",");
}
sb.append("早退日期三十分钟");
oaSalaryItem = new OaSalaryItem();
oaSalaryItem.setSalaryId(salary.getSalaryId());
oaSalaryItem.setPrice(Double.valueOf(res.getLeaveEarlyFee()));
oaSalaryItem.setType(5L);
oaSalaryItem.setFlag(0L);
oaSalaryItem.setReason(sb.toString());
oaSalaryItem.setSignTime(yearMonth);
salaryItemMapper.insert(oaSalaryItem);
// 3.4.3 打卡罚金
sb= new StringBuilder();
for (String lateDate : res.getLateDates()) {
sb.append(lateDate);
sb.append(",");
}
sb.append("未打卡");
oaSalaryItem = new OaSalaryItem();
oaSalaryItem.setSalaryId(salary.getSalaryId());
oaSalaryItem.setPrice(Double.valueOf(res.getNotAttendanceFee()));
oaSalaryItem.setType(6L);
oaSalaryItem.setFlag(0L);
oaSalaryItem.setReason(sb.toString());
oaSalaryItem.setSignTime(yearMonth);
salaryItemMapper.insert(oaSalaryItem);
}
}
private void buildItemAbsent(List<String> realDate2, OaSalary salary,Double flag,Date yearMonth) {
// 最终这个为缺勤日期
OaSalaryItem oaSalaryItem = new OaSalaryItem();
StringBuilder sb = new StringBuilder();
// 接下来将他拼接成一个字符串为扣款原因矿工问题
for (String s : realDate2) {
sb.append(s);
sb.append(",");
}
if (flag>0.5){
sb.append("旷工整天");
}else{
sb.append("旷工半天");
}
oaSalaryItem.setType(3L);
oaSalaryItem.setReason(String.valueOf(sb));
oaSalaryItem.setPrice(2* salary.getBaseSalary()* realDate2.size()*flag);
oaSalaryItem.setFlag(0L);
oaSalaryItem.setSignTime(yearMonth);
oaSalaryItem.setSalaryId(salary.getSalaryId());
salaryItemMapper.insert(oaSalaryItem);
}
/**
* 获取从 startTime 到 endTime含首尾的所有日期列表
*
* @param startTime 字符串格式 "yyyy-MM-dd" 的开始日期
* @param endTime 字符串格式 "yyyy-MM-dd" 的结束日期
* @return 日期列表,每个元素形如 "yyyy-MM-dd"
*/
public static List<String> getDateRange(String startTime, String endTime) {
// 1. 定义解析与输出格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
// 2. 解析成 LocalDate
LocalDate startDate = LocalDate.parse(startTime, formatter);
LocalDate endDate = LocalDate.parse(endTime, formatter);
// 若开始日期大于结束日期,则抛异常(也可根据需求改成自动调换顺序)
if (startDate.isAfter(endDate)) {
throw new IllegalArgumentException("开始日期不得晚于结束日期: "
+ startTime + " > " + endTime);
}
// 3. 循环遍历日期,加入列表
List<String> dateList = new ArrayList<>();
LocalDate currentDate = startDate;
while (!currentDate.isAfter(endDate)) {
// 每次加入 "yyyy-MM-dd" 格式字符串
dateList.add(currentDate.format(formatter));
// 加 1 天
currentDate = currentDate.plusDays(1);
}
return dateList;
} catch (DateTimeParseException e) {
// 如果传入的日期字符串格式不对,抛出异常或自行处理
throw new IllegalArgumentException("日期格式错误,要求 yyyy-MM-dd", e);
}
}
/**
* 将形如 "202411" (yyyyMM) 和 "11-04" (MM-dd) 拼装成 "2024-11-04" (yyyy-MM-dd)
*
* @param yearMonth 形如 "202411" 的字符串 (前4位为年份, 后2位为月份)
* @param monthDay 形如 "11-04" 的字符串 (前2位为月份, 后2位为天数)
* @return 拼装好的 "yyyy-MM-dd"
*/
private String combineYearMonthAndDay(String yearMonth, String monthDay) {
// 1. 拆解第一个参数: "202411" => 年 (2024), 月 (11)
String year = yearMonth.substring(0, 4); // "2024"
String monthFromYearMonth = yearMonth.substring(4, 6); // "11"
// 2. 拆解第二个参数: "11-04" => 月 (11), 日 (04)
String[] parts = monthDay.split("-");
if (parts.length != 2) {
throw new IllegalArgumentException("传入的 monthDay 格式不正确, 应形如 'MM-dd'。实际传入: " + monthDay);
}
String monthFromMonthDay = parts[0]; // "11"
String day = parts[1]; // "04"
// 3. 校验两个“月”是否一致(可选)
if (!monthFromYearMonth.equals(monthFromMonthDay)) {
throw new IllegalArgumentException("yearMonth 与 monthDay 中的 '月' 不匹配: "
+ monthFromYearMonth + " != " + monthFromMonthDay);
}
// 4. 组合成 yyyy-MM-dd 格式
return year + "-" + monthFromYearMonth + "-" + day;
}
/**
* 将形如 "202411" (yyyyMM) 和 "11-04" (MM-dd) 拼装成 "2024-11-04" (yyyy-MM-dd)
*
* @param yearMonth 形如 "202411" 的字符串 (前4位为年份, 后2位为月份)
* @return 拼装好的 "yyyy-MM-dd"
*/
private String combineYearMonth(String yearMonth) {
// 1. 拆解第一个参数: "202411" => 年 (2024), 月 (11)
String year = yearMonth.substring(0, 4); // "2024"
String monthFromYearMonth = yearMonth.substring(4, 6); // "11"
// 4. 组合成 yyyy-MM-dd 格式
return year + "-" + monthFromYearMonth;
}
private Date StringToDate(String dateStr) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 1. 先解析成 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.parse(dateStr, formatter);
// 2. 转换为 Instant指定时区
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
// 3. 转换成旧版 Date
return Date.from(instant);
}
private String DateToString(Date date) {
// 2. 构造解析器,指定格式。例如 "yyyy-MM-dd"
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 3. 调用 parse() 将字符串转为 Date
return sdf.format(date);
}
}

View File

@@ -120,7 +120,7 @@ public class SysOaHolidayServiceImpl implements ISysOaHolidayService {
@Override
public SysOaHolidayVo queryHolidayByDate(Date date) {
LambdaQueryWrapper<SysOaHoliday> lqw = Wrappers.lambdaQuery();
lqw.eq(SysOaHoliday::getHolidayTime, date);
lqw.like(SysOaHoliday::getHolidayTime, date);
return baseMapper.selectVoOne(lqw);
}