This commit is contained in:
2025-03-09 20:15:18 +08:00
18 changed files with 995 additions and 33 deletions

View File

@@ -90,7 +90,7 @@ public class EmployeeOnboardingController extends BaseController {
@Log(title = "入职管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody EmployeeOnboardingBo bo) {
public R<Void> edit(@RequestBody EmployeeOnboardingBo bo) {
return toAjax(iEmployeeOnboardingService.updateByBo(bo));
}

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();
}
/**
* 导出薪资管理列表
*/
@@ -105,4 +113,10 @@ public class OaSalaryController extends BaseController {
@PathVariable Long[] salaryIds) {
return toAjax(iOaSalaryService.deleteWithValidByIds(Arrays.asList(salaryIds), true));
}
@PostMapping("/calc")
public void calc(@RequestBody OaSalaryBo bo) {
iOaSalaryService.calcSalary(bo);
}
}

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;
/**
@@ -56,5 +57,10 @@ public class OaSalaryBo extends BaseEntity {
*/
private String remark;
/** 以下二参数是用来分析材料并且计算的 */
private String monthStr;
private String filePath;
}

View File

@@ -0,0 +1,59 @@
package com.ruoyi.oa.domain.vo;
import lombok.Data;
import java.util.List;
@Data
public class CalcResultVo {
/** 迟到次数 */
private Long lateCount;
/** 早退十分钟次数 */
private Long leaveEarly10Count;
/** 早退30分钟次数 */
private Long leaveEarly30Count;
/** 旷工次数 */
private Long absentCount;
/** 旷工半天次数 */
private Long absent05Count;
/** 未打卡次数 */
private Long notAttendance;
/** 迟到日期 */
private List<String> lateDates;
/** 早退10分钟日期 */
private List<String> leaveEarly10Dates;
/** 早退30分钟日期 */
private List<String> leaveEarly30Dates;
/** 矿工日期 */
private List<String> absentDates;
/** 矿工半天日期 */
private List<String> absent05Dates;
/** 未打卡日期 */
private List<String> notAttendanceDates;
/** 迟到罚金 */
private Long lateFee;
/** 早退罚金 */
private Long leaveEarlyFee;
/** 打卡罚金 */
private Long notAttendanceFee;
/** 综合罚金 */
private Long sumFee;
}

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;
@@ -46,4 +47,17 @@ public interface IOaSalaryService {
* 校验并批量删除薪资管理信息
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 计算工资
* @param bo
*/
void calcSalary(OaSalaryBo bo);
/**
* 导入当月所有人的记录
* @return
*/
R<Void> importSalaryUser();
}

View File

@@ -130,6 +130,6 @@ public class EmployeeOnboardingServiceImpl implements IEmployeeOnboardingService
@Override
public EmployeeOnboardingVo queryByUserId(Long userId) {
return baseMapper.selectVoById(userId);
return baseMapper.selectVoByUserId(userId);
}
}

View File

@@ -1,13 +1,39 @@
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;
import com.ruoyi.oa.domain.vo.OaSalaryVo;
@@ -15,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业务层处理
@@ -31,6 +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;
/**
* 查询薪资管理
*/
@@ -49,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();
}
/**
* 查询薪资管理列表
*/
@@ -109,4 +177,280 @@ 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);
}
}
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);
}

View File

@@ -0,0 +1,167 @@
package com.ruoyi.oa.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.oa.domain.vo.CalcResultVo;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
public class OwnHttpUtils {
public static String getJsonName(String monthStr, String filePath) {
// 目标接口地址
String jsonBody = String.format("{\"month_str\":\"%s\",\"file_path\":\"%s\"}", monthStr, filePath);
return getResult("dakaji_fenxi",jsonBody);
}
public static CalcResultVo getBaseCalc(String fileName, String personName) {
// 目标接口地址
String jsonBody = String.format("{\"file_name\":\"%s\",\"person_name\":\"%s\"}", fileName, personName);
jsonBody = getResult("get_person_info",jsonBody);
System.out.println(jsonBody);
// 1. 将 JSON 字符串解析成 JSONObject
JSONObject jsonObject = JSON.parseObject(jsonBody);
CalcResultVo calcResultVo = new CalcResultVo();
// 2. 获取数值类型
Long lateCount = jsonObject.getLong("迟到次数");
calcResultVo.setLateCount(lateCount);
Long leaveEarly10Count = jsonObject.getLong("早退10分钟次数");
Long leaveEarly30Count = jsonObject.getLong("早退30分钟次数");
Long absentCount = jsonObject.getLong("旷工次数");
Long absent05Count = jsonObject.getLong("旷工半天次数");
Long notAttendance = jsonObject.getLong("未打卡次数");
// 3. 获取数组类型(列表),使用 getJSONArray 再转成 Java 的 List 或数组
JSONArray lateDatesArr = jsonObject.getJSONArray("迟到日期");
if (lateDatesArr != null) {
// 如果想要 List<String> 形式,可以这样:
List<String> lateDates = lateDatesArr.toJavaList(String.class);
calcResultVo.setLateDates(lateDates);
}
// 3. 获取数组类型(列表),使用 getJSONArray 再转成 Java 的 List 或数组
JSONArray leaveEarly10Dates = jsonObject.getJSONArray("早退10分钟日期");
if (leaveEarly10Dates != null) {
// 如果想要 List<String> 形式,可以这样:
List<String> lateDates = leaveEarly10Dates.toJavaList(String.class);
calcResultVo.setLeaveEarly10Dates(lateDates);
}
// 3. 获取数组类型(列表),使用 getJSONArray 再转成 Java 的 List 或数组
JSONArray leaveEarly30Dates = jsonObject.getJSONArray("早退30分钟日期");
if (leaveEarly30Dates != null) {
// 如果想要 List<String> 形式,可以这样:
List<String> lateDates = leaveEarly30Dates.toJavaList(String.class);
calcResultVo.setLeaveEarly30Dates(lateDates);
}
// 3. 获取数组类型(列表),使用 getJSONArray 再转成 Java 的 List 或数组
JSONArray absentDates = jsonObject.getJSONArray("旷工日期");
if (absentDates != null) {
// 如果想要 List<String> 形式,可以这样:
List<String> lateDates = absentDates.toJavaList(String.class);
calcResultVo.setAbsentDates(lateDates);
}
// 3. 获取数组类型(列表),使用 getJSONArray 再转成 Java 的 List 或数组
JSONArray absent05Dates = jsonObject.getJSONArray("旷工半天日期");
if (absent05Dates != null) {
// 如果想要 List<String> 形式,可以这样:
List<String> lateDates = absent05Dates.toJavaList(String.class);
calcResultVo.setAbsent05Dates(lateDates);
}
// 3. 获取数组类型(列表),使用 getJSONArray 再转成 Java 的 List 或数组
JSONArray notAttendanceDates = jsonObject.getJSONArray("未打卡日期");
if (notAttendanceDates != null) {
// 如果想要 List<String> 形式,可以这样:
List<String> lateDates = notAttendanceDates.toJavaList(String.class);
calcResultVo.setNotAttendanceDates(lateDates);
}
// 例如获取 迟到罚金
Long lateFee = jsonObject.getLong("迟到罚金");
Long leaveEarlyFee = jsonObject.getLong("早退罚金");
Long notAttendanceFee = jsonObject.getLong("打卡罚金");
calcResultVo.setLateCount(lateCount);
calcResultVo.setLeaveEarly10Count(leaveEarly10Count);
calcResultVo.setLeaveEarly30Count(leaveEarly30Count);
calcResultVo.setAbsentCount(absentCount);
calcResultVo.setAbsent05Count(absent05Count);
calcResultVo.setNotAttendance(notAttendance);
calcResultVo.setLateFee(lateFee);
calcResultVo.setLeaveEarlyFee(leaveEarlyFee);
calcResultVo.setNotAttendanceFee(notAttendanceFee);
return calcResultVo;
}
public static void main(String[] args) {
String fileName = "/home/ubuntu/lyq/fad_oa_kqdeal/202411.json";
String personName = "丁苗松";
String jsonBody = String.format("{\"file_name\":\"%s\",\"person_name\":\"%s\"}", fileName, personName);
}
private static String getResult(String apiName, String jsonBody) {
try {
String baseUrl = "http://49.232.154.205:23108/";
// 3. 打开连接
URL url = new URL(baseUrl+apiName);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 4. 设置请求方式为 POST
connection.setRequestMethod("POST");
// 5. 设置请求头 Content-Type: application/json
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
// 允许写输出流
connection.setDoOutput(true);
// 6. 通过输出流发送 JSON 请求体
try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonBody.getBytes("UTF-8");
os.write(input, 0, input.length);
}
// 7. 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("Response Code : " + responseCode);
// 8. 如果响应成功(200),读取响应体
if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine);
}
connection.disconnect();
return response.toString();
}
} else {
System.out.println("Request failed. Response code: " + responseCode);
connection.disconnect();
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@@ -31,18 +31,17 @@
<update id="updateByUserId" parameterType="com.ruoyi.oa.domain.EmployeeOnboarding">
update employee_onboarding
<set>
<if test="idPhoto != null and idPhoto != 0">id_photo = #{idPhoto},</if>
<if test="joiningDate != null and joiningDate != ''">joining_date = #{joiningDate},</if>
<if test="idPhoto != null">id_photo = #{idPhoto},</if>
<if test="joiningDate != null">joining_date = #{joiningDate},</if>
<if test="managerId != null and managerId != ''">manager_id = #{managerId},</if>
<if test="highestDegree != null and highestDegree != ''">highest_degree = #{highestDegree},</if>
<if test="remark != null and remark != ''">remark = #{remark},</if>
<if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
<if test="birthDate != null and birthDate != ''">birth_date = #{birthDate},</if>
<if test="ethnicity != null and ethnicity != ''">ethnicity = #{ethnicity},</if>
<if test="confirmDate != null and confirmDate != ''">confirm_date = #{confirmDate},</if>
<if test="confirmDate != null">confirm_date = #{confirmDate},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="maritalStatus != null and maritalStatus != ''">marital_status = #{maritalStatus},</if>
<if test="politicalStatus != null and politicalStatus != ''">political_status = #{politicalStatus},</if>
@@ -79,6 +78,7 @@
<select id="selectVoByUserId" resultType="com.ruoyi.oa.domain.vo.EmployeeOnboardingVo">
SELECT
eo.onboarding_id,
eo.user_id,
su.nick_name,
eo.joining_date,

View File

@@ -21,31 +21,57 @@
<select id="selectStaffVoPage" resultMap="OaSalaryResult">
SELECT
SELECT
os.salary_id,
os.user_id,
os.pay_time,
os.base_salary,
(os.base_salary * DAY(LAST_DAY(#{payTime})) +
COALESCE(SUM(CASE
WHEN osi.flag = 1 THEN osi.price
WHEN osi.flag = 0 AND osi.type = 3 THEN -2 * osi.price
WHEN osi.flag = 0 THEN -osi.price
ELSE 0
END), 0)
) AS real_salary,
(
os.base_salary * DAY(LAST_DAY(#{payTime}))
+ COALESCE(SUM(
CASE
WHEN osi.flag = 1 THEN osi.price
WHEN osi.flag = 0 THEN -osi.price
ELSE 0
END
), 0)
) AS real_salary,
osi.salary_item_id,
osi.sign_time,
su.nick_name
FROM oa_salary os
LEFT JOIN oa_salary_item osi ON osi.salary_id = os.salary_id
FROM oa_salary os
LEFT JOIN oa_salary_item osi
ON osi.salary_id = os.salary_id
AND YEAR(osi.sign_time) = YEAR(#{payTime})
AND MONTH(osi.sign_time) = MONTH(#{payTime})
LEFT JOIN sys_user su on os.user_id = su.user_id
WHERE YEAR(os.pay_time) = YEAR(#{payTime})
AND MONTH(os.pay_time) = MONTH(#{payTime})
GROUP BY os.salary_id, os.user_id, os.pay_time, os.base_salary, osi.salary_item_id, osi.type, osi.reason, osi.price, osi.sign_time;
${ew.getCustomSqlSegment}
LEFT JOIN sys_user su ON os.user_id = su.user_id
<!-- 这里使用 <where> 标签统一管理条件 -->
<where>
YEAR(os.pay_time) = YEAR(#{payTime})
AND MONTH(os.pay_time) = MONTH(#{payTime})
<!-- 如果 ew 中带了自定义条件,就在这里追加 -->
<if test="ew != null and ew.customSqlSegment != null and ew.customSqlSegment != ''">
AND ${ew.customSqlSegment}
</if>
</where>
GROUP BY
os.salary_id,
os.user_id,
os.pay_time,
os.base_salary,
osi.salary_item_id,
osi.type,
osi.reason,
osi.price,
osi.sign_time
</select>
<select id="selectVoByUserId" resultType="com.ruoyi.oa.domain.OaSalary">
select * from oa_salary where user_id = #{userId}
</select>

View File

@@ -5,8 +5,8 @@ VUE_APP_TITLE = 福安德综合办公系统
ENV = 'development'
# 若依管理系统/开发环境
# VUE_APP_BASE_API = '/prod-api'
VUE_APP_BASE_API = 'http://110.41.139.73:8080'
VUE_APP_BASE_API = '/prod-api'
# VUE_APP_BASE_API = 'http://110.41.139.73:8080'
# 应用访问路径 例如使用前缀 /admin/
VUE_APP_CONTEXT_PATH = '/'

View File

@@ -6,7 +6,7 @@
<el-card style="">
<el-row>
<div>
<h1>差旅费报销申请单</h1>
<h1>报销申请单</h1>
</div>
</el-row>
</el-card>

View File

@@ -0,0 +1,329 @@
<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<!-- <el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" clearable placeholder="请选择" size="small">
<el-option
v-for="item in categoryOptions"
:key="item.categoryId"
:label="item.categoryName"
:value="item.code">
</el-option>
</el-select>
</el-form-item>-->
<el-form-item label="提交时间">
<el-date-picker
v-model="dateRange"
style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
plain
icon="el-icon-edit"
size="mini"
:disabled="btnLoading"
@click="handleStart"
>发起差旅申请
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="ownProcessList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center"/>
<!-- <el-table-column label="流程编号" align="center" prop="procInsId" :show-overflow-tooltip="true"/>-->
<el-table-column label="差旅详情" align="left" prop="procVars" :show-overflow-tooltip="true"/>
<!-- <el-table-column label="流程类别" align="center" prop="category" :formatter="categoryFormat" />-->
<el-table-column label="当前节点" width="150" align="center" prop="taskName"/>
<el-table-column label="提交时间" align="center" prop="createTime" width="160"/>
<el-table-column label="当前状态" align="center" width="100">
<template slot-scope="scope">
<dict-tag :options="dict.type.wf_process_status" :value="scope.row.processStatus"/>
</template>
</el-table-column>
<el-table-column label="耗时" align="center" prop="duration" width="130"/>
<el-table-column label="操作" align="center" width="240" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
type="text"
size="mini"
icon="el-icon-tickets"
@click="handleFlowRecord(scope.row)"
v-hasPermi="['workflow:process:query']"
>详情
</el-button>
<el-button
type="text"
size="mini"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-if="scope.row.finishTime"
v-hasPermi="['workflow:process:remove']"
>删除
</el-button>
<el-button
type="text"
size="mini"
icon="el-icon-circle-close"
@click="handleStop(scope.row)"
v-hasPermi="['workflow:process:cancel']"
>取消
</el-button>
<el-button
type="text"
size="mini"
icon="el-icon-refresh-right"
v-hasPermi="['workflow:process:start']"
@click="handleAgain(scope.row)"
>重新发起
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
import {listOwnProcess, stopProcess, delProcess, listProcess} from '@/api/workflow/process';
import {listAllCategory} from '@/api/workflow/category';
import {detailProcess} from '@/api/workflow/process'
export default {
name: "Own",
dicts: ['wf_process_status'],
components: {},
data() {
return {
// 遮罩层
loading: true,
processLoading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
categoryOptions: [],
processTotal: 0,
// 我发起的流程列表数据
ownProcessList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
process: {},
src: "",
definitionList: [],
// 日期范围
dateRange: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
processKey: undefined,
processName: undefined,
category: "trip"
},
// 表单参数
form: {},
// 表单校验
rules: {},
btnLoading:true
};
},
created() {
this.getCategoryList();
},
beforeRouteEnter(to, from, next) {
next(vm => {
vm.getList()
})
},
methods: {
handleStart() {
const row = this.process
this.$router.push({
path: '/workflow/process/start/' + row.deploymentId,
query: {
definitionId: row.definitionId,
}
})
},
/** 查询流程分类列表 */
getCategoryList() {
listAllCategory().then(response => this.categoryOptions = response.data)
},
/** 查询流程定义列表 */
getList() {
this.loading = true;
this.btnLoading = true;
listOwnProcess(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
listProcess(this.queryParams).then(response => {
this.process = response.rows[response.rows.length - 1];
this.btnLoading = false;
})
this.ownProcessList = response.rows;
this.total = response.total;
this.loading = false;
});
},
/**流程标题搜索**/
searchList(list, str) {
let arr = [];
list.forEach((item) => {
if (item.procVars.description.indexOf(str) > -1) {
arr.push(item)
}
})
return arr;
},
//对象数组排序(文本类)
/* compare(propertyName, order) {
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (order == 0) {
if (value2 < value1) {
return -1;
} else if (value2 > value1) {
return 1;
} else {
return 0;
}
}
if (order == 1) {
if (value2 > value1) {
return -1;
} else if (value2 < value1) {
return 1;
} else {
return 0;
}
}
}
},*/
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: null,
name: null,
category: null,
key: null,
tenantId: null,
deployTime: null,
derivedFrom: null,
derivedFromRoot: null,
parentDeploymentId: null,
engineVersion: null
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.procInsId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
handleAgain(row) {
this.$router.push({
path: '/workflow/process/start/' + row.deployId,
query: {
definitionId: row.procDefId,
procInsId: row.procInsId
}
})
// console.log(row);
},
/** 取消流程申请 */
handleStop(row) {
const params = {
procInsId: row.procInsId
}
stopProcess(params).then(res => {
this.$modal.msgSuccess(res.msg);
this.getList();
});
},
/** 流程流转记录 */
handleFlowRecord(row) {
this.$router.push({
path: '/workflow/process/detail/' + row.procInsId,
query: {
processed: false
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.procInsId || this.ids;
this.$confirm('是否确认删除流程定义编号为"' + ids + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function () {
return delProcess(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
})
},
/** 导出按钮操作 */
handleExport() {
this.download('workflow/process/ownExport', {
...this.queryParams
}, `wf_own_process_${new Date().getTime()}.xlsx`)
},
categoryFormat(row, column) {
return this.categoryOptions.find(k => k.code === row.category)?.categoryName ?? '';
}
}
};
</script>

View File

@@ -5,7 +5,7 @@
<el-card style="">
<el-row>
<div>
<h1>差旅费报销申请单</h1>
<h1>报销申请单</h1>
</div>
<el-button type="primary">
<i class="el-icon-check" @click="addTripClaim">提交</i>

View File

@@ -71,7 +71,7 @@
<el-card style="">
<el-row>
<div>
<h1>差旅费报销申请单</h1>
<h1>报销申请单</h1>
</div>
</el-row>
</el-card>

View File

@@ -299,7 +299,7 @@ export default {
this.loading = false;
this.form = response.data;
this.open = true;
this.title = "修改差旅费报销";
this.title = "修改报销";
});
},
/** 提交按钮 */
@@ -330,7 +330,7 @@ export default {
/** 删除按钮操作 */
handleDelete(row) {
const claimIds = row.claimId || this.ids;
this.$modal.confirm('是否确认删除差旅费报销编号为"' + claimIds + '"的数据项?').then(() => {
this.$modal.confirm('是否确认删除报销编号为"' + claimIds + '"的数据项?').then(() => {
this.loading = true;
return delOaClaim(claimIds);
}).then(() => {